diff --git a/DEPS b/DEPS index 8c238ed..dce7009e 100644 --- a/DEPS +++ b/DEPS
@@ -38,7 +38,6 @@ 'checkout_android', 'checkout_android_prebuilts_build_tools', 'checkout_android_native_support', - 'checkout_google_benchmark', # TODO(https://crbug.com/1404759): Remove. 'checkout_ios_webkit', 'checkout_nacl', 'checkout_openxr', @@ -101,10 +100,6 @@ # Checkout fuzz archive. Should not need in builders. 'checkout_clusterfuzz_data': False, - # Always check out Google Benchmark. - # TODO(https://crbug.com/1404759): Remove. - 'checkout_google_benchmark': True, - # By default, checkout JavaScript coverage node modules. These packages # are used to post-process raw v8 coverage reports into IstanbulJS compliant # output. @@ -309,7 +304,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': '84c4178323358215907271d3d5c7f9a108b638f7', + 'skia_revision': '132ad29f753f51b0582d4af59ac07a7652ceb320', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. @@ -317,7 +312,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': '6fb7883d606bfbc139bf0bdd77031df343a08ff4', + 'angle_revision': 'a8401f03fc092f354eb2f253d5d3e34e612171c5', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -396,7 +391,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': 'f93a07d387318cd634a81f809f7d79cd2fca1fb3', + 'devtools_frontend_revision': '8c409dff40103fb74f44ae35282b4ff53a6ff8c0', # 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. @@ -436,7 +431,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': '7e799673f9b4ba77ddd90e3bd5f03e279b2df979', + 'dawn_revision': '9313aab0fdba908ee85068eadac873656ba275e5', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -460,7 +455,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling nearby # and whatever else without interference from each other. - 'nearby_revision': '9c3b5fb3773b8111268093559c81bc2228a78282', + 'nearby_revision': 'acf0f09a3f4477abefd77be746fbf79a68fa38ca', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling securemessage # and whatever else without interference from each other. @@ -784,7 +779,7 @@ 'src/clank': { 'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' + - '524780845a516b04b971b6aae2bf902c8ab636bb', + '94bc28912c716e12d24f6e38fde5f4a3d9623c61', 'condition': 'checkout_android and checkout_src_internal', }, @@ -883,7 +878,7 @@ 'packages': [ { 'package': 'chromium/rts/model/linux-amd64', - 'version': 'zTy7ygiOjhmJiO3DIWuDLiAngbbkY-Ju8347XTpRflwC', + 'version': 'ntGEmAkwZvomHehfxoL0MNxzaMbQJnqNt8AUG_Napf8C', }, ], 'dep_type': 'cipd', @@ -894,7 +889,7 @@ 'packages': [ { 'package': 'chromium/rts/model/mac-amd64', - 'version': '0hQIr_2i9vDHKUXzD7MLr3i9yZz0zBs-yJMKStyRok0C', + 'version': '3QP1LZ76UbYRZ8DBjBoD79lFCgWlouNv3C5ugvjAUM0C', }, ], 'dep_type': 'cipd', @@ -1110,7 +1105,7 @@ Var('chromium_git') + '/angle/angle.git' + '@' + Var('angle_revision'), 'src/third_party/content_analysis_sdk/src': - Var('chromium_git') + '/external/github.com/chromium/content_analysis_sdk.git' + '@' + '01c34145717ce1a0b241f6f6534e9f1072cd6645', + Var('chromium_git') + '/external/github.com/chromium/content_analysis_sdk.git' + '@' + 'dc9f53c343bd7165114fa32dd10d87fbffea9152', 'src/third_party/dav1d/libdav1d': Var('chromium_git') + '/external/github.com/videolan/dav1d.git' + '@' + 'd426d1c91075b9c552b12dd052af1cd0368f05a2', @@ -1230,7 +1225,7 @@ Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), 'src/third_party/devtools-frontend-internal': { - 'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '1db088071cef2aeb61751577e750255f05c58c0b', + 'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + 'fb2e75faf5bb484eefa949ee430191deedf9e741', 'condition': 'checkout_src_internal', }, @@ -1696,7 +1691,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '376827e2c401e933e60f4ae143e0a31b3ee16fc6', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '722a85f82b43d34dca78d6d8bc84cf7eab33a5a6', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1841,7 +1836,7 @@ 'dep_type': 'cipd', }, - 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@46e5f8237dea2c86a24f5267205ee95b9a0f735d', + 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@e21f987f900d0814f3e1d1c2dc52bb5c73e0eb60', 'src/third_party/vulkan_memory_allocator': Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'ebe84bec02c041d28f902da0214bf442743fc907', @@ -1878,7 +1873,7 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'd1b65aa5a88f6efd900604dfcda840154e9f16e2', 'src/third_party/webgpu-cts/src': - Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '315f6d30a6c6521c3cb0092ef257d00e04a1259b', + Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '44557234f3147426ec29a5b989c77d66d5be9a9a', 'src/third_party/webrtc': Var('webrtc_git') + '/src.git' + '@' + '2f7071a57aa9fc06f6de526ed4e10aecc7b00c83', @@ -1958,7 +1953,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': Var('chrome_git') + '/chrome/src-internal.git@e7fe16ffa2e9a560ff9225314d5c995e446b08c0', + 'url': Var('chrome_git') + '/chrome/src-internal.git@24810a25b9a401ad6faff5230cb1b16a415c61ab', 'condition': 'checkout_src_internal', }, @@ -1999,7 +1994,7 @@ 'packages': [ { 'package': 'chromeos_internal/apps/media_app/app', - 'version': 'ibykjpRSlWYbpz1VDBXtVwk2Wrdg-VMICK5OBNr_OswC', + 'version': 'jVTS8xp63zvg7fEHZ5DxxVGQ3L0RETb_JMNVKn8vsfQC', }, ], 'condition': 'checkout_chromeos and checkout_src_internal', @@ -3645,6 +3640,17 @@ 'dep_type': 'cipd', }, + 'src/third_party/android_deps/libs/org_mockito_mockito_subclass': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/org_mockito_mockito_subclass', + 'version': 'version:2@5.1.1.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + 'src/third_party/android_deps/libs/org_objenesis_objenesis': { 'packages': [ {
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc index f2f1aaa..b86085c 100644 --- a/android_webview/browser/aw_content_browser_client.cc +++ b/android_webview/browser/aw_content_browser_client.cc
@@ -1048,11 +1048,11 @@ render_frame_host, feature); } -bool AwContentBrowserClient::ShouldAllowInsecurePrivateNetworkRequests( +bool AwContentBrowserClient::ShouldAllowInsecureLocalNetworkRequests( content::BrowserContext* browser_context, const url::Origin& origin) { // Webview does not implement support for deprecation trials, so webview apps - // broken by Private Network Access restrictions cannot help themselves by + // broken by Local Network Access restrictions cannot help themselves by // registering for the trial. // See crbug.com/1255675. return true;
diff --git a/android_webview/browser/aw_content_browser_client.h b/android_webview/browser/aw_content_browser_client.h index 268e1b5d9..9ef948ad 100644 --- a/android_webview/browser/aw_content_browser_client.h +++ b/android_webview/browser/aw_content_browser_client.h
@@ -236,7 +236,7 @@ override; void LogWebFeatureForCurrentPage(content::RenderFrameHost* render_frame_host, blink::mojom::WebFeature feature) override; - bool ShouldAllowInsecurePrivateNetworkRequests( + bool ShouldAllowInsecureLocalNetworkRequests( content::BrowserContext* browser_context, const url::Origin& origin) override; content::SpeechRecognitionManagerDelegate*
diff --git a/android_webview/browser/gfx/vulkan_gl_interop.cc b/android_webview/browser/gfx/vulkan_gl_interop.cc index c1271bb..86200014 100644 --- a/android_webview/browser/gfx/vulkan_gl_interop.cc +++ b/android_webview/browser/gfx/vulkan_gl_interop.cc
@@ -15,8 +15,15 @@ #include "gpu/vulkan/vulkan_function_pointers.h" #include "gpu/vulkan/vulkan_image.h" #include "gpu/vulkan/vulkan_implementation.h" +#include "third_party/skia/include/core/SkAlphaType.h" +#include "third_party/skia/include/core/SkBlendMode.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkColorSpace.h" +#include "third_party/skia/include/core/SkColorType.h" +#include "third_party/skia/include/core/SkImage.h" +#include "third_party/skia/include/core/SkPaint.h" +#include "third_party/skia/include/core/SkSamplingOptions.h" +#include "third_party/skia/include/gpu/GrTypes.h" #include "third_party/skia/include/gpu/GrBackendSemaphore.h" #include "third_party/skia/include/gpu/vk/GrVkBackendContext.h" #include "third_party/skia/include/gpu/vk/GrVkExtensions.h"
diff --git a/android_webview/browser/gfx/vulkan_gl_interop.h b/android_webview/browser/gfx/vulkan_gl_interop.h index f0147da..f650480 100644 --- a/android_webview/browser/gfx/vulkan_gl_interop.h +++ b/android_webview/browser/gfx/vulkan_gl_interop.h
@@ -20,6 +20,7 @@ class GrVkSecondaryCBDrawContext; class SkColorSpace; +class SkImage; namespace gpu { class VulkanImage;
diff --git a/ash/BUILD.gn b/ash/BUILD.gn index f34f914..3e2a045 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn
@@ -2102,6 +2102,8 @@ "wallpaper/wallpaper_utils/wallpaper_file_utils.h", "wallpaper/wallpaper_utils/wallpaper_language.cc", "wallpaper/wallpaper_utils/wallpaper_language.h", + "wallpaper/wallpaper_utils/wallpaper_online_variant_utils.cc", + "wallpaper/wallpaper_utils/wallpaper_online_variant_utils.h", "wallpaper/wallpaper_utils/wallpaper_resizer.cc", "wallpaper/wallpaper_utils/wallpaper_resizer.h", "wallpaper/wallpaper_utils/wallpaper_resizer_observer.h", @@ -3347,6 +3349,7 @@ "wallpaper/wallpaper_utils/wallpaper_ephemeral_user_unittest.cc", "wallpaper/wallpaper_utils/wallpaper_file_utils_unittest.cc", "wallpaper/wallpaper_utils/wallpaper_language_unittest.cc", + "wallpaper/wallpaper_utils/wallpaper_online_variant_utils_unittest.cc", "wallpaper/wallpaper_utils/wallpaper_resizer_unittest.cc", "wallpaper/wallpaper_window_state_manager_unittest.cc", "window_user_data_unittest.cc",
diff --git a/ash/accelerators/debug_commands.cc b/ash/accelerators/debug_commands.cc index 89cf427..e189129 100644 --- a/ash/accelerators/debug_commands.cc +++ b/ash/accelerators/debug_commands.cc
@@ -16,6 +16,7 @@ #include "ash/public/cpp/accelerators.h" #include "ash/public/cpp/debug_utils.h" #include "ash/public/cpp/system/toast_data.h" +#include "ash/resources/vector_icons/vector_icons.h" #include "ash/root_window_controller.h" #include "ash/session/session_controller_impl.h" #include "ash/shell.h" @@ -231,6 +232,35 @@ /*velocity_y=*/0.f); } +// Toast debug shortcut constants. +const std::u16string oneline_toast_text = u"SystemUI toast text string"; +const std::u16string multiline_toast_text = + u"SystemUI toast text string that breaks to two lines due to accomodate " + u"long strings or translations. The text container has a max-width of " + u"512px."; + +void HandleShowToast() { + // Iterates through all toast variations, which are a combination of having + // multi-line text, dismiss button, and a leading icon. + // `has_multiline_text` changes value every 4 iterations. + // `has_dismiss_button` changes value every 2 iterations. + // `has_leading_icon` changes value every iteration. + static int index = 0; + bool has_multiline_text = (index / 4) % 2; + bool has_dismiss_button = (index / 2) % 2; + bool has_leading_icon = index % 2; + index++; + + Shell::Get()->toast_manager()->Show(ToastData( + /*id=*/"id", ToastCatalogName::kDebugCommand, + has_multiline_text ? multiline_toast_text : oneline_toast_text, + ToastData::kDefaultToastDuration, + /*visible_on_lock_screen=*/true, has_dismiss_button, + /*custom_dismiss_text=*/u"Button", + /*dismiss_callback=*/base::RepeatingClosure(), + has_leading_icon ? kSystemMenuBusinessIcon : gfx::kNoneIcon)); +} + } // namespace void PrintUIHierarchies() { @@ -277,11 +307,7 @@ HandlePrintWindowHierarchy(); break; case DEBUG_SHOW_TOAST: - Shell::Get()->toast_manager()->Show(ToastData( - /*id=*/"id", ToastCatalogName::kDebugCommand, /*text=*/u"Toast", - ToastData::kDefaultToastDuration, - /*visible_on_lock_screen=*/false, /*has_dismiss_button=*/true, - /*custom_dismiss_text=*/u"Dismiss")); + HandleShowToast(); break; case DEBUG_SYSTEM_UI_STYLE_VIEWER: SystemUIComponentsStyleViewerView::CreateAndShowWidget();
diff --git a/ash/ambient/ambient_controller.cc b/ash/ambient/ambient_controller.cc index 8da2de7..c4841d4 100644 --- a/ash/ambient/ambient_controller.cc +++ b/ash/ambient/ambient_controller.cc
@@ -11,6 +11,7 @@ #include "ash/ambient/ambient_constants.h" #include "ash/ambient/ambient_managed_photo_controller.h" +#include "ash/ambient/ambient_ui_settings.h" #include "ash/ambient/ambient_weather_controller.h" #include "ash/ambient/metrics/ambient_multi_screen_metrics_recorder.h" #include "ash/ambient/model/ambient_animation_photo_config.h" @@ -49,6 +50,7 @@ #include "base/metrics/user_metrics.h" #include "base/time/time.h" #include "base/timer/timer.h" +#include "base/values.h" #include "build/buildflag.h" #include "cc/paint/skottie_wrapper.h" #include "chromeos/ash/components/assistant/buildflags.h" @@ -179,8 +181,13 @@ ambient::prefs::kAmbientModePhotoRefreshIntervalSeconds, kPhotoRefreshInterval.InSeconds()); + // |ambient::prefs::kAmbientTheme| is for legacy purposes only. It is being + // migrated to |ambient::prefs::kAmbientUiSettings|, which is the newer + // version of these settings. registry->RegisterIntegerPref(ambient::prefs::kAmbientTheme, static_cast<int>(kDefaultAmbientTheme)); + registry->RegisterDictionaryPref(ambient::prefs::kAmbientUiSettings, + base::Value::Dict()); registry->RegisterDoublePref( ambient::prefs::kAmbientModeAnimationPlaybackSpeed, @@ -273,7 +280,7 @@ // ambient mode has just started. if (start_time_) { auto elapsed = base::Time::Now() - start_time_.value(); - AmbientTheme theme = GetCurrentTheme(); + AmbientTheme theme = GetCurrentUiSettings().theme(); DVLOG(2) << "Exit ambient mode. Elapsed time: " << elapsed; ambient::RecordAmbientModeTimeElapsed( elapsed, Shell::Get()->IsInTabletMode(), theme); @@ -674,8 +681,8 @@ weak_ptr_factory_.GetWeakPtr())); pref_change_registrar_->Add( - ambient::prefs::kAmbientTheme, - base::BindRepeating(&AmbientController::OnThemePrefChanged, + ambient::prefs::kAmbientUiSettings, + base::BindRepeating(&AmbientController::OnAmbientUiSettingsChanged, weak_ptr_factory_.GetWeakPtr())); pref_change_registrar_->Add( @@ -687,7 +694,6 @@ OnLockScreenInactivityTimeoutPrefChanged(); OnLockScreenBackgroundTimeoutPrefChanged(); OnPhotoRefreshIntervalPrefChanged(); - OnThemePrefChanged(); OnAnimationPlaybackSpeedChanged(); if (ash::features::IsAmbientModeManagedScreensaverEnabled()) { @@ -728,7 +734,7 @@ {ambient::prefs::kAmbientModeLockScreenBackgroundTimeoutSeconds, ambient::prefs::kAmbientModeLockScreenInactivityTimeoutSeconds, ambient::prefs::kAmbientModePhotoRefreshIntervalSeconds, - ambient::prefs::kAmbientTheme, + ambient::prefs::kAmbientUiSettings, ambient::prefs::kAmbientModeAnimationPlaybackSpeed}) { if (pref_change_registrar_->IsObserved(pref_name)) pref_change_registrar_->Remove(pref_name); @@ -743,7 +749,6 @@ ambient_photo_controller_.reset(); ambient_managed_photo_controller_.reset(); - current_theme_from_pref_.reset(); } } @@ -777,48 +782,30 @@ ambient::prefs::kAmbientModePhotoRefreshIntervalSeconds))); } -void AmbientController::OnThemePrefChanged() { - absl::optional<AmbientTheme> previous_theme_from_pref = - current_theme_from_pref_; - DCHECK(GetPrimaryUserPrefService()); - int current_theme_as_int = - GetPrimaryUserPrefService()->GetInteger(ambient::prefs::kAmbientTheme); - // Gracefully handle pref having invalid value in case pref storage is - // corrupted somehow. - if (current_theme_as_int < 0 || - current_theme_as_int > static_cast<int>(AmbientTheme::kMaxValue)) { - LOG(WARNING) << "Loaded invalid ambient theme from pref storage: " - << current_theme_as_int << ". Default to " - << ToString(kDefaultAmbientTheme); - current_theme_as_int = static_cast<int>(kDefaultAmbientTheme); - } - current_theme_from_pref_ = static_cast<AmbientTheme>(current_theme_as_int); - - if (previous_theme_from_pref.has_value()) { - DVLOG(4) << "AmbientTheme changed from " - << ToString(*previous_theme_from_pref) << " to " - << ToString(*current_theme_from_pref_); - // For a given topic category, the topics downloaded from IMAX and saved to - // cache differ from theme to theme: - // 1) Slideshow mode keeps primary/related photos paired within a topic, - // whereas animated themes split the photos into 2 separate topics. - // 2) The resolution of the photos downloaded from FIFE may differ between - // themes, depending on the image assets' sizes in the animation file. - // For this reason, it is better to not re-use the cache when switching - // between themes. - // - // There are corner cases here where the theme may change and the program - // crashes before the cache gets cleared below. This is intentionally not - // accounted for because it's not worth the added complexity. If this - // should happen, re-using the cache will still work without fatal behavior. - // The UI may just not be optimal. Furthermore, the cache gradually gets - // overwritten with topics reflecting the new theme anyways, so ambient mode - // should not be stuck with a mismatched cache indefinitely. - DCHECK(ambient_photo_controller_); +void AmbientController::OnAmbientUiSettingsChanged() { + DVLOG(4) << "AmbientUiSettings changed to " + << GetCurrentUiSettings().ToString(); + // For a given topic category, the topics downloaded from IMAX and saved to + // cache differ from theme to theme: + // 1) Slideshow mode keeps primary/related photos paired within a topic, + // whereas animated themes split the photos into 2 separate topics. + // 2) The resolution of the photos downloaded from FIFE may differ between + // themes, depending on the image assets' sizes in the animation file. + // For this reason, it is better to not re-use the cache when switching + // between themes. + // + // There are corner cases here where the theme may change and the program + // crashes before the cache gets cleared below. This is intentionally not + // accounted for because it's not worth the added complexity. If this + // should happen, re-using the cache will still work without fatal behavior. + // The UI may just not be optimal. Furthermore, the cache gradually gets + // overwritten with topics reflecting the new theme anyways, so ambient mode + // should not be stuck with a mismatched cache indefinitely. + if (ambient_photo_controller_) { ambient_photo_controller_->ClearCache(); } else { - DVLOG(4) << "AmbientTheme initialized to " - << ToString(*current_theme_from_pref_); + LOG(WARNING) << "AmbientUiSettings changed while ambient mode pref is " + "disabled. Can't clear ambient photo cache."; } } @@ -884,7 +871,7 @@ std::unique_ptr<views::Widget> AmbientController::CreateWidget( aura::Window* container) { - AmbientTheme current_theme = GetCurrentTheme(); + AmbientTheme current_theme = GetCurrentUiSettings().theme(); auto container_view = std::make_unique<AmbientContainerView>( &delegate_, ambient_animation_progress_tracker_.get(), AmbientAnimationStaticResources::Create(current_theme, @@ -951,7 +938,7 @@ // model/controller with the appropriate config each time before calling // StartScreenUpdate(). DCHECK(!ambient_photo_controller_->IsScreenUpdateActive()); - AmbientTheme current_theme = GetCurrentTheme(); + AmbientTheme current_theme = GetCurrentUiSettings().theme(); DVLOG(4) << "Loaded ambient theme " << ToString(current_theme); AmbientPhotoConfig photo_config; @@ -992,7 +979,8 @@ AssistantInteractionController::Get()->GetModel()->AddObserver(this); multi_screen_metrics_recorder_ = - std::make_unique<AmbientMultiScreenMetricsRecorder>(GetCurrentTheme()); + std::make_unique<AmbientMultiScreenMetricsRecorder>( + GetCurrentUiSettings().theme()); frame_rate_controller_ = std::make_unique<AmbientAnimationFrameRateController>( Shell::Get()->frame_throttling_controller()); @@ -1001,9 +989,9 @@ StartRefreshingImages(); } -AmbientTheme AmbientController::GetCurrentTheme() const { - DCHECK(current_theme_from_pref_); - return *current_theme_from_pref_; +AmbientUiSettings AmbientController::GetCurrentUiSettings() const { + CHECK(GetPrimaryUserPrefService()); + return AmbientUiSettings::ReadFromPrefService(*GetPrimaryUserPrefService()); } void AmbientController::MaybeDismissUIOnMouseMove() {
diff --git a/ash/ambient/ambient_controller.h b/ash/ambient/ambient_controller.h index 576ec71..d1d80b1 100644 --- a/ash/ambient/ambient_controller.h +++ b/ash/ambient/ambient_controller.h
@@ -59,6 +59,7 @@ class AmbientManagedPhotoController; class AmbientMultiScreenMetricsRecorder; class AmbientPhotoController; +class AmbientUiSettings; class AmbientWeatherController; // Class to handle all ambient mode functionalities. @@ -196,7 +197,7 @@ void StopRefreshingImages(); void MaybeStartScreenSaver(); void MaybeDismissUIOnMouseMove(); - AmbientTheme GetCurrentTheme() const; + AmbientUiSettings GetCurrentUiSettings() const; // Invoked when the auto-show timer in |InactivityMonitor| gets fired after // device being inactive for a specific amount of time. @@ -216,7 +217,7 @@ void OnLockScreenInactivityTimeoutPrefChanged(); void OnLockScreenBackgroundTimeoutPrefChanged(); void OnPhotoRefreshIntervalPrefChanged(); - void OnThemePrefChanged(); + void OnAmbientUiSettingsChanged(); void OnAnimationPlaybackSpeedChanged(); AmbientAccessTokenController* access_token_controller_for_testing() { @@ -291,10 +292,6 @@ // TODO(safarli): Remove this workaround when b/266234711 is fixed. bool last_mouse_event_was_move_ = false; - // Not set until the AmbientTheme is initially read from pref - // storage when ambient mode is enabled. - absl::optional<AmbientTheme> current_theme_from_pref_; - std::unique_ptr<AmbientMultiScreenMetricsRecorder> multi_screen_metrics_recorder_;
diff --git a/ash/ambient/ambient_ui_settings.cc b/ash/ambient/ambient_ui_settings.cc index d2801c2..5b465839 100644 --- a/ash/ambient/ambient_ui_settings.cc +++ b/ash/ambient/ambient_ui_settings.cc
@@ -52,14 +52,19 @@ // static AmbientUiSettings AmbientUiSettings::ReadFromPrefService( PrefService& pref_service) { + const base::Value::Dict& settings_dict = + pref_service.GetDict(ambient::prefs::kAmbientUiSettings); absl::optional<AmbientUiSettings> settings_loaded = - CreateFromDict(pref_service.GetDict(ambient::prefs::kAmbientUiSettings)); + CreateFromDict(settings_dict); if (settings_loaded) { return *settings_loaded; } else { - // This should only happen if pref storage was corrupted on disc. - LOG(ERROR) << "Loaded invalid AmbientUiSettings from pref. Using default."; - pref_service.ClearPref(ambient::prefs::kAmbientUiSettings); + if (!settings_dict.empty()) { + // This should only happen if pref storage was corrupted on disc. + LOG(ERROR) + << "Loaded invalid AmbientUiSettings from pref. Using default."; + pref_service.ClearPref(ambient::prefs::kAmbientUiSettings); + } return AmbientUiSettings(); } }
diff --git a/ash/ambient/test/ambient_ash_test_base.cc b/ash/ambient/test/ambient_ash_test_base.cc index add284c..00a47a80 100644 --- a/ash/ambient/test/ambient_ash_test_base.cc +++ b/ash/ambient/test/ambient_ash_test_base.cc
@@ -14,6 +14,7 @@ #include "ash/ambient/ambient_managed_photo_controller.h" #include "ash/ambient/ambient_photo_cache.h" #include "ash/ambient/ambient_photo_controller.h" +#include "ash/ambient/ambient_ui_settings.h" #include "ash/ambient/test/ambient_ash_test_helper.h" #include "ash/ambient/ui/ambient_animation_view.h" #include "ash/ambient/ui/ambient_background_image_view.h" @@ -45,6 +46,7 @@ #include "base/test/scoped_run_loop_timeout.h" #include "base/threading/scoped_blocking_call.h" #include "base/time/time.h" +#include "base/values.h" #include "chromeos/ash/components/login/auth/auth_metrics_recorder.h" #include "chromeos/dbus/power/fake_power_manager_client.h" #include "chromeos/dbus/power/power_manager_client.h" @@ -229,9 +231,14 @@ } } +void AmbientAshTestBase::SetAmbientUiSettings( + const AmbientUiSettings& settings) { + settings.WriteToPrefService( + *Shell::Get()->session_controller()->GetActivePrefService()); +} + void AmbientAshTestBase::SetAmbientTheme(AmbientTheme theme) { - Shell::Get()->session_controller()->GetActivePrefService()->SetInteger( - ambient::prefs::kAmbientTheme, static_cast<int>(theme)); + SetAmbientUiSettings(AmbientUiSettings(theme)); } void AmbientAshTestBase::DisableJitter() {
diff --git a/ash/ambient/test/ambient_ash_test_base.h b/ash/ambient/test/ambient_ash_test_base.h index 2f87139..4efa1a0 100644 --- a/ash/ambient/test/ambient_ash_test_base.h +++ b/ash/ambient/test/ambient_ash_test_base.h
@@ -36,6 +36,7 @@ class AmbientAccessTokenController; class AmbientContainerView; class AmbientPhotoController; +class AmbientUiSettings; class FakeAmbientBackendControllerImpl; class MediaStringView; @@ -52,11 +53,15 @@ // Enables/disables ambient mode for the currently active user session. void SetAmbientModeEnabled(bool enabled); - // Sets the AmbientTheme to use when ShowAmbientScreen() is called. - // To reflect real world usage, the incoming |theme| does not take effect + // Sets the |AmbientUiSettings| to use when ShowAmbientScreen() is called. + // To reflect real world usage, the incoming settings do not take effect // immediately if the test is currently displaying the ambient screen. In that - // case, the ambient screen must be closed, and the new |theme| will take + // case, the ambient screen must be closed, and the new settings will take // effect with the next call to ShowAmbientScreen(). + void SetAmbientUiSettings(const AmbientUiSettings& settings); + + // Convenient form of the above that only sets |AmbientUiSettings::theme| and + // leaves the rest of the settings unset. void SetAmbientTheme(AmbientTheme theme); // Sets jitters configs to zero for pixel testing.
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index b07f547..32124fad 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd
@@ -3192,10 +3192,10 @@ </message> <!-- Accelerator Actions--> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_BRIGHTNESS_DOWN" translateable="false" desc="Label for accelerator action - Turn brightness down."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_BRIGHTNESS_DOWN" desc="Label for accelerator action - Turn brightness down."> Turn brightness down </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_BRIGHTNESS_UP" translateable="false" desc="Label for accelerator action - Turn brightness up."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_BRIGHTNESS_UP" desc="Label for accelerator action - Turn brightness up."> Turn brightness up </message> <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_CYCLE_BACKWARD_MRU" translateable="false" desc="Label for accelerator action - Cycle backwards in the MRU window list."> @@ -3240,7 +3240,7 @@ <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_FOCUS_NEXT_PANE" translateable="false" desc="Label for accelerator action - Next pane."> Next pane </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_FOCUS_PIP" translateable="false" desc="Label for accelerator action - Focus on the Picture-in-Picture window."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_FOCUS_PIP" desc="Label for accelerator action - Focus on the Picture-in-Picture window."> Focus on the Picture-in-Picture window </message> <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_FOCUS_PREVIOUS_PANE" translateable="false" desc="Label for accelerator action - Previous pane."> @@ -3252,10 +3252,10 @@ <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_KEYBOARD_BACKLIGHT_TOGGLE" translateable="false" desc="Label for accelerator action - Toggle keyboard backlight."> Toggle keyboard backlight </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_KEYBOARD_BRIGHTNESS_DOWN" translateable="false" desc="Label for accelerator action - Dim keyboard."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_KEYBOARD_BRIGHTNESS_DOWN" desc="Label for accelerator action - Dim keyboard."> Dim keyboard </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_KEYBOARD_BRIGHTNESS_UP" translateable="false" desc="Label for accelerator action - Make keyboard brighter."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_KEYBOARD_BRIGHTNESS_UP" desc="Label for accelerator action - Make keyboard brighter."> Make keyboard brighter </message> <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_LAUNCH_APP" translateable="false" desc="Label for accelerator action - Click or tap shelf icons 1-8."> @@ -3291,10 +3291,10 @@ <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_MEDIA_PLAY" translateable="false" desc="Label for accelerator action - Play media."> Play media </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_MEDIA_PLAY_PAUSE" translateable="false" desc="Label for accelerator action - Play or pause media."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_MEDIA_PLAY_PAUSE" desc="Label for accelerator action - Play or pause media."> Play or pause media </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_MEDIA_PREV_TRACK" translateable="false" desc="Label for accelerator action - Go to previous track."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_MEDIA_PREV_TRACK" desc="Label for accelerator action - Go to previous track."> Go to previous track </message> <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_MEDIA_REWIND" translateable="false" desc="Label for accelerator action - Rewind media."> @@ -3351,28 +3351,28 @@ <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_RESTORE_TAB" translateable="false" desc="Label for accelerator action - Reopen the last tab or window closed."> Reopen the last tab or window closed </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_ROTATE_SCREEN" translateable="false" desc="Label for accelerator action - Rotate window 90 degrees clockwise."> - Rotate window 90 degrees clockwise + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_ROTATE_SCREEN" desc="Label for accelerator action - Rotate screen 90 degrees clockwise."> + Rotate screen 90 degrees clockwise </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_SCALE_UI_DOWN" translateable="false" desc="Label for accelerator action - Zoom in on screen."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_SCALE_UI_DOWN" desc="Label for accelerator action - Zoom in on screen."> Zoom in on screen </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_SCALE_UI_RESET" translateable="false" desc="Label for accelerator action - Reset zoom level."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_SCALE_UI_RESET" desc="Label for accelerator action - Reset zoom level."> Reset zoom level </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_SCALE_UI_UP" translateable="false" desc="Label for accelerator action - Zoom out on screen."> - Zoom out on screen + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_SCALE_UI_UP" desc="Label for accelerator action - Zoom out of screen."> + Zoom out of screen </message> <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_SHOW_EMOJI_PICKER" translateable="false" desc="Label for accelerator action - Open Emoji Picker."> Open Emoji Picker </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_IME_MENU_BUBBLE" translateable="false" desc="Label for accelerator action - Show list of available input methods."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_IME_MENU_BUBBLE" desc="Label for accelerator action - Show list of available input methods."> Show list of available input methods </message> <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_SHOW_SHORTCUT_VIEWER" desc="Label for accelerator action - Open Keyboard shortcuts app."> Open Keyboard shortcuts app </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_SHOW_STYLUS_TOOLS" translateable="false" desc="Label for accelerator action - Show stylus tools."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_SHOW_STYLUS_TOOLS" desc="Label for accelerator action - Show stylus tools."> Show stylus tools </message> <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_SHOW_TASK_MANAGER" desc="Label for accelerator action - Open task manager."> @@ -3387,17 +3387,17 @@ <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_SUSPEND" desc="Label for accelerator action - Put device in sleep mode."> Put device in sleep mode </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_SWAP_PRIMARY_DISPLAY" translateable="false" desc="Label for accelerator action - Swap primary monitor."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_SWAP_PRIMARY_DISPLAY" desc="Label for accelerator action - Swap primary monitor."> Swap primary monitor </message> <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_SWITCH_IME" translateable="false" desc="Label for accelerator action - Switch input method."> Switch input method </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_SWITCH_TO_LAST_USED_IME" translateable="false" desc="Label for accelerator action - Switch to last language selected."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_SWITCH_TO_LAST_USED_IME" desc="Label for accelerator action - Switch to last language selected."> Switch to last language selected </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_SWITCH_TO_NEXT_IME" translateable="false" desc="Label for accelerator action - Switch to next available language."> - Switch to next available language + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_SWITCH_TO_NEXT_IME" desc="Label for accelerator action - Switch to next available input method."> + Switch to next available input method </message> <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_SWITCH_TO_NEXT_USER" desc="Label for accelerator action - Switch to next user."> Switch to next user @@ -3447,7 +3447,7 @@ <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_MAXIMIZED" desc="Label for accelerator action - Maximize window."> Maximize window </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_MIRROR_MODE" translateable="false" desc="Label for accelerator action - Mirror monitors."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_MIRROR_MODE" desc="Label for accelerator action - Mirror monitors."> Mirror monitors </message> <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_OVERVIEW" desc="Label for accelerator action - Overview mode."> @@ -3465,13 +3465,13 @@ <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_UNPIN" desc="Label for accelerator action - Unpin app from shelf."> Unpin app from shelf </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_VOLUME_DOWN" translateable="false" desc="Label for accelerator action - Turn volume down."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_VOLUME_DOWN" desc="Label for accelerator action - Turn volume down."> Turn volume down </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_VOLUME_MUTE" translateable="false" desc="Label for accelerator action - Mute sound."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_VOLUME_MUTE" desc="Label for accelerator action - Mute sound."> Mute sound </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_VOLUME_UP" translateable="false" desc="Label for accelerator action - Turn volume up."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_VOLUME_UP" desc="Label for accelerator action - Turn volume up."> Turn volume up </message> <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_WINDOW_CYCLE_SNAP_LEFT" translateable="false" desc="Label for accelerator action - Keep window on left."> @@ -3564,31 +3564,31 @@ <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_DESKS_TOGGLE_ASSIGN_TO_ALL_DESKS" translateable="false" desc="Label for accelerator action - Assign active window to all desks."> Assign active window to all desks </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_PRIVACY_SCREEN" translateable="false" desc="Label for accelerator action - Toggle privacy screen."> - Toggle privacy screen + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_PRIVACY_SCREEN" desc="Label for accelerator action - Turn on/off privacy screen."> + Turn on/off privacy screen </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_KEYBOARD_BACKLIGHT" translateable="false" desc="Label for accelerator action - Toggle keyboard backlight."> - Toggle keyboard backlight + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_KEYBOARD_BACKLIGHT" desc="Label for accelerator action - Turn on/off keyboard backlight."> + Turn on/off keyboard backlight </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_MICROPHONE_MUTE" translateable="false" desc="Label for accelerator action - Mute or unmute microphone."> - Mute or unmute microphone + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_MICROPHONE_MUTE" desc="Label for accelerator action - Turn on/off microphone."> + Turn on/off microphone </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_PLAY_MEDIA" translateable="false" desc="Label for accelerator action - Play media."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_PLAY_MEDIA" desc="Label for accelerator action - Play media."> Play media </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_PAUSE_MEDIA" translateable="false" desc="Label for accelerator action - Pause media."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_PAUSE_MEDIA" desc="Label for accelerator action - Pause media."> Pause media </message> <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_PLAY_PAUSE_MEDIA" translateable="false" desc="Label for accelerator action - Play or pause media."> Play or pause media </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_NEXT_TRACK" translateable="false" desc="Label for accelerator action - Go to next track."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_NEXT_TRACK" desc="Label for accelerator action - Go to next track."> Go to next track </message> <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_PREVIOUS_TRACK" translateable="false" desc="Label for accelerator action - Go to previous track."> Go to previous track </message> - <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_FAST_FORWARD_MEDIA" translateable="false" desc="Label for accelerator action - Fast forward media."> + <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_FAST_FORWARD_MEDIA" desc="Label for accelerator action - Fast forward media."> Fast forward media </message> <message name="IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_SYSTEM_TRAY_BUBBLE" desc="Label for accelerator action - Open Quick Settings by selecting the time."> @@ -3732,7 +3732,7 @@ <message name="IDS_BROWSER_ACCELERATOR_DESCRIPTION_PAGE_DOWN" translateable="false" desc="Label for accelerator action - Page down."> Page down </message> - <message name="IDS_BROWSER_ACCELERATOR_DESCRIPTION_RIGHT_CLICK" translateable="false" desc="Label for accelerator action - Right click a link."> + <message name="IDS_BROWSER_ACCELERATOR_DESCRIPTION_RIGHT_CLICK" desc="Label for accelerator action - Right click a link."> Right-click a link </message> <message name="IDS_BROWSER_ACCELERATOR_DESCRIPTION_AUTO_COMPLETE" translateable="false" desc="Label for accelerator action - Add www. and .com to the address bar, then open website.">
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_BRIGHTNESS_DOWN.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_BRIGHTNESS_DOWN.png.sha1 new file mode 100644 index 0000000..f4394ac6 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_BRIGHTNESS_DOWN.png.sha1
@@ -0,0 +1 @@ +d6da114b1936bd5cbc20704efea6c14cb6773e1c \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_BRIGHTNESS_UP.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_BRIGHTNESS_UP.png.sha1 new file mode 100644 index 0000000..f4394ac6 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_BRIGHTNESS_UP.png.sha1
@@ -0,0 +1 @@ +d6da114b1936bd5cbc20704efea6c14cb6773e1c \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_FAST_FORWARD_MEDIA.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_FAST_FORWARD_MEDIA.png.sha1 new file mode 100644 index 0000000..302a9d4 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_FAST_FORWARD_MEDIA.png.sha1
@@ -0,0 +1 @@ +4d6f9796e62d82704a6503086b1f9fc67cc10f25 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_FOCUS_PIP.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_FOCUS_PIP.png.sha1 new file mode 100644 index 0000000..302a9d4 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_FOCUS_PIP.png.sha1
@@ -0,0 +1 @@ +4d6f9796e62d82704a6503086b1f9fc67cc10f25 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_KEYBOARD_BRIGHTNESS_DOWN.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_KEYBOARD_BRIGHTNESS_DOWN.png.sha1 new file mode 100644 index 0000000..ae439d4f --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_KEYBOARD_BRIGHTNESS_DOWN.png.sha1
@@ -0,0 +1 @@ +ee51e24c7ad2e2093a2cd95decf34e966074c994 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_KEYBOARD_BRIGHTNESS_UP.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_KEYBOARD_BRIGHTNESS_UP.png.sha1 new file mode 100644 index 0000000..ae439d4f --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_KEYBOARD_BRIGHTNESS_UP.png.sha1
@@ -0,0 +1 @@ +ee51e24c7ad2e2093a2cd95decf34e966074c994 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_MEDIA_PLAY_PAUSE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_MEDIA_PLAY_PAUSE.png.sha1 new file mode 100644 index 0000000..302a9d4 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_MEDIA_PLAY_PAUSE.png.sha1
@@ -0,0 +1 @@ +4d6f9796e62d82704a6503086b1f9fc67cc10f25 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_MEDIA_PREV_TRACK.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_MEDIA_PREV_TRACK.png.sha1 new file mode 100644 index 0000000..302a9d4 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_MEDIA_PREV_TRACK.png.sha1
@@ -0,0 +1 @@ +4d6f9796e62d82704a6503086b1f9fc67cc10f25 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_NEXT_TRACK.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_NEXT_TRACK.png.sha1 new file mode 100644 index 0000000..302a9d4 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_NEXT_TRACK.png.sha1
@@ -0,0 +1 @@ +4d6f9796e62d82704a6503086b1f9fc67cc10f25 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_PAUSE_MEDIA.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_PAUSE_MEDIA.png.sha1 new file mode 100644 index 0000000..302a9d4 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_PAUSE_MEDIA.png.sha1
@@ -0,0 +1 @@ +4d6f9796e62d82704a6503086b1f9fc67cc10f25 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_PLAY_MEDIA.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_PLAY_MEDIA.png.sha1 new file mode 100644 index 0000000..302a9d4 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_PLAY_MEDIA.png.sha1
@@ -0,0 +1 @@ +4d6f9796e62d82704a6503086b1f9fc67cc10f25 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_ROTATE_SCREEN.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_ROTATE_SCREEN.png.sha1 new file mode 100644 index 0000000..ac803ed --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_ROTATE_SCREEN.png.sha1
@@ -0,0 +1 @@ +5e2f48a4fea91775cef49f99e9b2cf115d5b1257 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SCALE_UI_DOWN.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SCALE_UI_DOWN.png.sha1 new file mode 100644 index 0000000..f4394ac6 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SCALE_UI_DOWN.png.sha1
@@ -0,0 +1 @@ +d6da114b1936bd5cbc20704efea6c14cb6773e1c \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SCALE_UI_RESET.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SCALE_UI_RESET.png.sha1 new file mode 100644 index 0000000..f4394ac6 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SCALE_UI_RESET.png.sha1
@@ -0,0 +1 @@ +d6da114b1936bd5cbc20704efea6c14cb6773e1c \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SCALE_UI_UP.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SCALE_UI_UP.png.sha1 new file mode 100644 index 0000000..f4394ac6 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SCALE_UI_UP.png.sha1
@@ -0,0 +1 @@ +d6da114b1936bd5cbc20704efea6c14cb6773e1c \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SHOW_STYLUS_TOOLS.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SHOW_STYLUS_TOOLS.png.sha1 new file mode 100644 index 0000000..ae439d4f --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SHOW_STYLUS_TOOLS.png.sha1
@@ -0,0 +1 @@ +ee51e24c7ad2e2093a2cd95decf34e966074c994 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SWAP_PRIMARY_DISPLAY.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SWAP_PRIMARY_DISPLAY.png.sha1 new file mode 100644 index 0000000..f4394ac6 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SWAP_PRIMARY_DISPLAY.png.sha1
@@ -0,0 +1 @@ +d6da114b1936bd5cbc20704efea6c14cb6773e1c \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SWITCH_TO_LAST_USED_IME.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SWITCH_TO_LAST_USED_IME.png.sha1 new file mode 100644 index 0000000..ae439d4f --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SWITCH_TO_LAST_USED_IME.png.sha1
@@ -0,0 +1 @@ +ee51e24c7ad2e2093a2cd95decf34e966074c994 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SWITCH_TO_NEXT_IME.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SWITCH_TO_NEXT_IME.png.sha1 new file mode 100644 index 0000000..ae439d4f --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_SWITCH_TO_NEXT_IME.png.sha1
@@ -0,0 +1 @@ +ee51e24c7ad2e2093a2cd95decf34e966074c994 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_IME_MENU_BUBBLE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_IME_MENU_BUBBLE.png.sha1 new file mode 100644 index 0000000..ae439d4f --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_IME_MENU_BUBBLE.png.sha1
@@ -0,0 +1 @@ +ee51e24c7ad2e2093a2cd95decf34e966074c994 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_KEYBOARD_BACKLIGHT.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_KEYBOARD_BACKLIGHT.png.sha1 new file mode 100644 index 0000000..ae439d4f --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_KEYBOARD_BACKLIGHT.png.sha1
@@ -0,0 +1 @@ +ee51e24c7ad2e2093a2cd95decf34e966074c994 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_MICROPHONE_MUTE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_MICROPHONE_MUTE.png.sha1 new file mode 100644 index 0000000..302a9d4 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_MICROPHONE_MUTE.png.sha1
@@ -0,0 +1 @@ +4d6f9796e62d82704a6503086b1f9fc67cc10f25 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_MIRROR_MODE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_MIRROR_MODE.png.sha1 new file mode 100644 index 0000000..f4394ac6 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_MIRROR_MODE.png.sha1
@@ -0,0 +1 @@ +d6da114b1936bd5cbc20704efea6c14cb6773e1c \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_PRIVACY_SCREEN.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_PRIVACY_SCREEN.png.sha1 new file mode 100644 index 0000000..f4394ac6 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_PRIVACY_SCREEN.png.sha1
@@ -0,0 +1 @@ +d6da114b1936bd5cbc20704efea6c14cb6773e1c \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_VOLUME_DOWN.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_VOLUME_DOWN.png.sha1 new file mode 100644 index 0000000..302a9d4 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_VOLUME_DOWN.png.sha1
@@ -0,0 +1 @@ +4d6f9796e62d82704a6503086b1f9fc67cc10f25 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_VOLUME_MUTE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_VOLUME_MUTE.png.sha1 new file mode 100644 index 0000000..302a9d4 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_VOLUME_MUTE.png.sha1
@@ -0,0 +1 @@ +4d6f9796e62d82704a6503086b1f9fc67cc10f25 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_VOLUME_UP.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_VOLUME_UP.png.sha1 new file mode 100644 index 0000000..302a9d4 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ACCELERATOR_DESCRIPTION_VOLUME_UP.png.sha1
@@ -0,0 +1 @@ +4d6f9796e62d82704a6503086b1f9fc67cc10f25 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_BROWSER_ACCELERATOR_DESCRIPTION_RIGHT_CLICK.png.sha1 b/ash/ash_strings_grd/IDS_BROWSER_ACCELERATOR_DESCRIPTION_RIGHT_CLICK.png.sha1 new file mode 100644 index 0000000..ae439d4f --- /dev/null +++ b/ash/ash_strings_grd/IDS_BROWSER_ACCELERATOR_DESCRIPTION_RIGHT_CLICK.png.sha1
@@ -0,0 +1 @@ +ee51e24c7ad2e2093a2cd95decf34e966074c994 \ No newline at end of file
diff --git a/ash/capture_mode/capture_mode_toast_controller.cc b/ash/capture_mode/capture_mode_toast_controller.cc index 388b6ba..23111c1a 100644 --- a/ash/capture_mode/capture_mode_toast_controller.cc +++ b/ash/capture_mode/capture_mode_toast_controller.cc
@@ -171,8 +171,8 @@ kShellWindowId_MenuContainer))); toast_contents_view_ = capture_toast_widget_->SetContentsView(std::make_unique<SystemToastStyle>( - /*dismiss_callback=*/base::DoNothing(), label, /*dismiss_text=*/u"", - /*is_managed=*/false)); + /*dismiss_callback=*/base::DoNothing(), label, + /*dismiss_text=*/std::u16string())); // We animate the `capture_toast_widget_` explicitly in `ShowCaptureToast()` // and `MaybeDismissCaptureToast()`. Any default visibility animations added
diff --git a/ash/components/arc/arc_util.h b/ash/components/arc/arc_util.h index a90f68d..be4f336 100644 --- a/ash/components/arc/arc_util.h +++ b/ash/components/arc/arc_util.h
@@ -84,8 +84,9 @@ constexpr int kMaxArcVersion = 999; // How long ARCVM /data migration notification and dialog are dismissible. +constexpr int kArcVmDataMigrationNumberOfDismissibleDays = 30; constexpr base::TimeDelta kArcVmDataMigrationDismissibleTimeDelta = - base::Days(30); + base::Days(kArcVmDataMigrationNumberOfDismissibleDays); // Returns true if ARC is installed and the current device is officially // supported to run ARC.
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc index ab5e10f..e3a163b0 100644 --- a/ash/constants/ash_features.cc +++ b/ash/constants/ash_features.cc
@@ -2013,12 +2013,6 @@ "UseAuthFactors", base::FEATURE_ENABLED_BY_DEFAULT); -// When enabled, WebAuthN uses auth session based authentication -// instead of legacy CheckKey. -BASE_FEATURE(kUseAuthsessionForWebAuthN, - "UseAuthsessionForWebAuthN", - base::FEATURE_ENABLED_BY_DEFAULT); - // When enabled, the login shelf view is placed in its own widget instead of // sharing the shelf widget with other components. BASE_FEATURE(kUseLoginShelfWidget, @@ -3183,10 +3177,6 @@ return base::FeatureList::IsEnabled(kUploadOfficeToCloud); } -bool IsUseAuthsessionForWebAuthNEnabled() { - return base::FeatureList::IsEnabled(kUseAuthsessionForWebAuthN); -} - bool IsUseLoginShelfWidgetEnabled() { return base::FeatureList::IsEnabled(kUseLoginShelfWidget); }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h index d1ab44b..4a0091a 100644 --- a/ash/constants/ash_features.h +++ b/ash/constants/ash_features.h
@@ -575,8 +575,6 @@ COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kTrilinearFiltering); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kUploadOfficeToCloud); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kUseAuthFactors); -COMPONENT_EXPORT(ASH_CONSTANTS) -BASE_DECLARE_FEATURE(kUseAuthsessionForWebAuthN); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kUseLoginShelfWidget); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kUseMessagesStagingUrl); COMPONENT_EXPORT(ASH_CONSTANTS) @@ -875,7 +873,6 @@ COMPONENT_EXPORT(ASH_CONSTANTS) bool IsTrafficCountersEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsTrilinearFilteringEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsUploadOfficeToCloudEnabled(); -COMPONENT_EXPORT(ASH_CONSTANTS) bool IsUseAuthsessionForWebAuthNEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsUseLoginShelfWidgetEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsUseStorkSmdsServerAddressEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsVideoConferenceEnabled();
diff --git a/ash/in_session_auth/webauthn_dialog_controller_impl.cc b/ash/in_session_auth/webauthn_dialog_controller_impl.cc index b39c0b9..82b46ed 100644 --- a/ash/in_session_auth/webauthn_dialog_controller_impl.cc +++ b/ash/in_session_auth/webauthn_dialog_controller_impl.cc
@@ -50,23 +50,19 @@ weak_factory_.GetWeakPtr(), account_id, origin_name, auth_methods, source_window); - if (ash::features::IsUseAuthsessionForWebAuthNEnabled()) { - auto on_auth_session_started = [](base::OnceClosure continuation, - bool is_auth_session_started) { - if (!is_auth_session_started) { - LOG(ERROR) - << "Failed to start cryptohome auth session, exiting dialog early"; - return; - } - std::move(continuation).Run(); - }; + auto on_auth_session_started = [](base::OnceClosure continuation, + bool is_auth_session_started) { + if (!is_auth_session_started) { + LOG(ERROR) + << "Failed to start cryptohome auth session, exiting dialog early"; + return; + } + std::move(continuation).Run(); + }; - client_->StartAuthSession( - base::BindOnce(on_auth_session_started, std::move(continuation))); - return; - } - - std::move(continuation).Run(); + client_->StartAuthSession( + base::BindOnce(on_auth_session_started, std::move(continuation))); + return; } void WebAuthNDialogControllerImpl::CheckAuthFactorAvailability( @@ -165,9 +161,7 @@ } void WebAuthNDialogControllerImpl::ProcessFinalCleanups() { - if (ash::features::IsUseAuthsessionForWebAuthNEnabled()) - client_->InvalidateAuthSession(); - + client_->InvalidateAuthSession(); dialog_.reset(); source_window_tracker_.RemoveAll(); }
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn index da6bfe42..aacacbc 100644 --- a/ash/public/cpp/BUILD.gn +++ b/ash/public/cpp/BUILD.gn
@@ -461,6 +461,7 @@ sources = [ "accelerators_util_unittest.cc", "ambient/ambient_metrics_unittest.cc", + "ambient/ambient_prefs_unittest.cc", "android_intent_helper_unittest.cc", "app_list/app_list_config_provider_unittest.cc", "holding_space/holding_space_image_unittest.cc", @@ -488,6 +489,7 @@ "//base", "//base/test:test_support", "//chromeos/ui/vector_icons", + "//components/prefs:test_support", "//mojo/public/cpp/bindings", "//services/data_decoder/public/mojom", "//skia",
diff --git a/ash/public/cpp/ambient/ambient_prefs.cc b/ash/public/cpp/ambient/ambient_prefs.cc index 2294c24..8363c0c 100644 --- a/ash/public/cpp/ambient/ambient_prefs.cc +++ b/ash/public/cpp/ambient/ambient_prefs.cc
@@ -4,7 +4,11 @@ #include <string> +#include "ash/constants/ambient_theme.h" +#include "ash/constants/ambient_video.h" #include "ash/public/cpp/ambient/ambient_prefs.h" +#include "base/logging.h" +#include "components/prefs/pref_service.h" namespace ash { namespace ambient { @@ -52,6 +56,52 @@ constexpr char kAmbientModeManagedScreensaverImageDisplayIntervalSeconds[] = "ash.ambient.managed_screensaver.image_display_interval_seconds"; +void MigrateDeprecatedPrefs(PrefService& pref_service) { + // The largest |AmbientTheme| value possible with the old pref + // |kAmbientTheme|. + static constexpr ash::AmbientTheme kLegacyMaxAmbientTheme = + ash::AmbientTheme::kFloatOnBy; + + if (pref_service.HasPrefPath(ash::ambient::prefs::kAmbientUiSettings)) { + DVLOG(4) << "PrefService has already been migrated to new scheme."; + // See comments at end of function. + pref_service.ClearPref(ash::ambient::prefs::kAmbientTheme); + return; + } + + if (!pref_service.HasPrefPath(ash::ambient::prefs::kAmbientTheme)) { + DVLOG(4) + << "PrefService does not have legacy ambient theme. Nothing to migrate"; + return; + } + + int current_theme_as_int = + pref_service.GetInteger(ash::ambient::prefs::kAmbientTheme); + if (current_theme_as_int < 0 || + current_theme_as_int > static_cast<int>(kLegacyMaxAmbientTheme)) { + // This should be very rare. It can only happen if the pref storage is + // corrupted somehow. + LOG(WARNING) << "Loaded invalid ambient theme from pref storage: " + << current_theme_as_int << ". Not migrating to new scheme."; + pref_service.ClearPref(ash::ambient::prefs::kAmbientTheme); + return; + } + + // The |kVideo| theme should not be possible here. It's checked above and + // counted as an invalid theme in the old pref storage. + base::Value::Dict converted_pref; + converted_pref.Set(ash::ambient::prefs::kAmbientUiSettingsFieldTheme, + current_theme_as_int); + pref_service.SetDict(ash::ambient::prefs::kAmbientUiSettings, + std::move(converted_pref)); + // The legacy pref |kAmbientTheme| is intentionally not erased here to avoid + // the corner case where erasure of the old pref happens to get committed to + // storage but the new pref does not, which would lose the old pref value + // permanently. Wait until the next time MigrateAmbientThemePref() is called, + // if the new pref is detected, that confirms it's committed permanently to + // storage, and it's safe to erase the legacy pref. +} + } // namespace prefs } // namespace ambient } // namespace ash
diff --git a/ash/public/cpp/ambient/ambient_prefs.h b/ash/public/cpp/ambient/ambient_prefs.h index 472a233..f23aeb0f 100644 --- a/ash/public/cpp/ambient/ambient_prefs.h +++ b/ash/public/cpp/ambient/ambient_prefs.h
@@ -7,6 +7,8 @@ #include "ash/public/cpp/ash_public_export.h" +class PrefService; + namespace ash { namespace ambient { namespace prefs { @@ -81,6 +83,10 @@ ASH_PUBLIC_EXPORT extern const char kAmbientModeManagedScreensaverImageDisplayIntervalSeconds[]; +// Migrates from the legacy |ambient::prefs::kAmbientTheme| to the new +// |ambient::prefs::kAmbientUiSettings|. +ASH_PUBLIC_EXPORT void MigrateDeprecatedPrefs(PrefService& pref_service); + } // namespace prefs } // namespace ambient } // namespace ash
diff --git a/ash/public/cpp/ambient/ambient_prefs_unittest.cc b/ash/public/cpp/ambient/ambient_prefs_unittest.cc new file mode 100644 index 0000000..3f328b12 --- /dev/null +++ b/ash/public/cpp/ambient/ambient_prefs_unittest.cc
@@ -0,0 +1,60 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/public/cpp/ambient/ambient_prefs.h" + +#include "ash/constants/ambient_theme.h" +#include "ash/constants/ambient_video.h" +#include "base/values.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/testing_pref_service.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace ash::ambient::prefs { +namespace { + +using ::testing::_; +using ::testing::Eq; +using ::testing::Mock; + +class AmbientPrefsTest : public ::testing::Test { + protected: + AmbientPrefsTest() { + test_pref_service_.registry()->RegisterIntegerPref( + kAmbientTheme, static_cast<int>(kDefaultAmbientTheme)); + test_pref_service_.registry()->RegisterDictionaryPref(kAmbientUiSettings, + base::Value::Dict()); + } + + TestingPrefServiceSimple test_pref_service_; +}; + +TEST_F(AmbientPrefsTest, MigrateDeprecatedPrefs) { + test_pref_service_.SetInteger(kAmbientTheme, + static_cast<int>(AmbientTheme::kFeelTheBreeze)); + MigrateDeprecatedPrefs(test_pref_service_); + const base::Value::Dict& new_pref = + test_pref_service_.GetDict(kAmbientUiSettings); + EXPECT_THAT(new_pref.FindInt(kAmbientUiSettingsFieldTheme), + Eq(static_cast<int>(AmbientTheme::kFeelTheBreeze))); + + MigrateDeprecatedPrefs(test_pref_service_); + EXPECT_FALSE(test_pref_service_.HasPrefPath(ambient::prefs::kAmbientTheme)); +} + +TEST_F(AmbientPrefsTest, MigrateDeprecatedPrefsRejectsInvalidLegacySettings) { + // This should not be possible by design. The video option was not introduced + // as part of the legacy pref. + test_pref_service_.SetInteger(ambient::prefs::kAmbientTheme, + static_cast<int>(AmbientTheme::kVideo)); + + MigrateDeprecatedPrefs(test_pref_service_); + // Resorts to default since legacy pref is invalid. + EXPECT_TRUE(test_pref_service_.GetDict(kAmbientUiSettings).empty()); + EXPECT_FALSE(test_pref_service_.HasPrefPath(ambient::prefs::kAmbientTheme)); +} + +} // namespace +} // namespace ash::ambient::prefs
diff --git a/ash/public/cpp/system/toast_data.cc b/ash/public/cpp/system/toast_data.cc index fc50759..29f257e8b 100644 --- a/ash/public/cpp/system/toast_data.cc +++ b/ash/public/cpp/system/toast_data.cc
@@ -33,7 +33,8 @@ bool visible_on_lock_screen, bool has_dismiss_button, const std::u16string& custom_dismiss_text, - base::RepeatingClosure dismiss_callback) + base::RepeatingClosure dismiss_callback, + const gfx::VectorIcon& leading_icon) : id(std::move(id)), catalog_name(catalog_name), text(text), @@ -41,6 +42,7 @@ visible_on_lock_screen(visible_on_lock_screen), dismiss_text(GetDismissText(custom_dismiss_text, has_dismiss_button)), dismiss_callback(std::move(dismiss_callback)), + leading_icon(&leading_icon), time_created(base::TimeTicks::Now()) {} ToastData::ToastData(ToastData&& other) = default;
diff --git a/ash/public/cpp/system/toast_data.h b/ash/public/cpp/system/toast_data.h index 6a6f9fe..b9b6d235 100644 --- a/ash/public/cpp/system/toast_data.h +++ b/ash/public/cpp/system/toast_data.h
@@ -11,6 +11,7 @@ #include "ash/public/cpp/ash_public_export.h" #include "base/functional/callback.h" #include "base/time/time.h" +#include "ui/gfx/paint_vector_icon.h" namespace ash { @@ -42,7 +43,8 @@ bool visible_on_lock_screen = false, bool has_dismiss_button = false, const std::u16string& custom_dismiss_text = std::u16string(), - base::RepeatingClosure dismiss_callback = base::RepeatingClosure()); + base::RepeatingClosure dismiss_callback = base::RepeatingClosure(), + const gfx::VectorIcon& leading_icon = gfx::kNoneIcon); ToastData(ToastData&& other); ToastData& operator=(ToastData&& other); ~ToastData(); @@ -53,11 +55,11 @@ base::TimeDelta duration; bool visible_on_lock_screen; std::u16string dismiss_text; - bool is_managed = false; bool persist_on_hover = false; bool show_on_all_root_windows = false; // TODO(b/259100049): We should turn this into a `OnceClosure`. base::RepeatingClosure dismiss_callback; + const gfx::VectorIcon* leading_icon; base::OnceClosure expired_callback; base::TimeTicks time_created; base::TimeTicks time_start_showing;
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc b/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc index 934d7733..10a3b3e 100644 --- a/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc +++ b/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
@@ -1561,6 +1561,20 @@ // changes. The metrics team plans on implementing a way to mock out the // structured metrics client in the near future. We should follow up and // implement proper tests for these functions once that is available. +void RecordStructuredDiscoveryNotificationShown(const Device& device) { + QP_LOG(INFO) << __func__; + int model_id; + if (!base::HexStringToInt(device.metadata_id(), &model_id)) { + return; + } + int version = ConvertFastPairVersionToInt(device.version()); + metrics::structured::events::v2::fast_pair::DiscoveryNotificationShown() + .SetProtocol(static_cast<int>(device.protocol())) + .SetModelId(model_id) + .SetFastPairVersion(version) + .Record(); +} + void RecordStructuredPairingStarted(const Device& device) { QP_LOG(INFO) << __func__; int model_id;
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_metrics.h b/ash/quick_pair/common/fast_pair/fast_pair_metrics.h index cef119d..39c402a7 100644 --- a/ash/quick_pair/common/fast_pair/fast_pair_metrics.h +++ b/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
@@ -476,6 +476,9 @@ void RecordSavedDevicesCount(int num_devices); COMPONENT_EXPORT(QUICK_PAIR_COMMON) +void RecordStructuredDiscoveryNotificationShown(const Device& device); + +COMPONENT_EXPORT(QUICK_PAIR_COMMON) void RecordStructuredPairingStarted(const Device& device); COMPONENT_EXPORT(QUICK_PAIR_COMMON)
diff --git a/ash/quick_pair/keyed_service/quick_pair_metrics_logger.cc b/ash/quick_pair/keyed_service/quick_pair_metrics_logger.cc index ad03438..024b6a9 100644 --- a/ash/quick_pair/keyed_service/quick_pair_metrics_logger.cc +++ b/ash/quick_pair/keyed_service/quick_pair_metrics_logger.cc
@@ -314,6 +314,7 @@ *device, FastPairEngagementFlowEvent::kDiscoveryUiShown); GetDeviceMetadataAndLogEngagementFunnelWithMetadata( device, FastPairEngagementFlowEvent::kDiscoveryUiShown); + RecordStructuredDiscoveryNotificationShown(*device); } void QuickPairMetricsLogger::OnPairingStart(scoped_refptr<Device> device) {
diff --git a/ash/shelf/shelf_config.cc b/ash/shelf/shelf_config.cc index 0bef5fa..25b62ae7d 100644 --- a/ash/shelf/shelf_config.cc +++ b/ash/shelf/shelf_config.cc
@@ -18,6 +18,8 @@ #include "base/functional/bind.h" #include "base/metrics/histogram_functions.h" #include "base/scoped_observation.h" +#include "chromeos/constants/chromeos_features.h" +#include "ui/chromeos/styles/cros_tokens_color_mappings.h" namespace ash { @@ -480,19 +482,33 @@ } SkColor ShelfConfig::GetMaximizedShelfColor(const views::Widget* widget) const { - return SkColorSetA(GetDefaultShelfColor(widget), 0xFF); // 100% opacity + if (!chromeos::features::IsJellyEnabled()) { + return SkColorSetA(GetDefaultShelfColor(widget), 0xFF); // 100% opacity + } + return widget->GetColorProvider()->GetColor(cros_tokens::kCrosSysSystemBase); } ui::ColorId ShelfConfig::GetShelfBaseLayerColorId() const { - if (!in_tablet_mode_) - return kColorAshShieldAndBase80; + if (!chromeos::features::IsJellyEnabled()) { + if (!in_tablet_mode_) { + return kColorAshShieldAndBase80; + } - if (!is_in_app_) - return kColorAshShieldAndBase60; + if (!is_in_app_) { + return kColorAshShieldAndBase60; + } - return DarkLightModeControllerImpl::Get()->IsDarkModeEnabled() - ? kColorAshShieldAndBase90 - : kColorAshShieldAndBaseOpaque; + return DarkLightModeControllerImpl::Get()->IsDarkModeEnabled() + ? kColorAshShieldAndBase90 + : kColorAshShieldAndBaseOpaque; + } + + if (in_tablet_mode_ && is_in_app_) { + // In tablet mode with an app, we use the same opaque color as maximized. + return cros_tokens::kCrosSysSystemBase; + } + + return cros_tokens::kCrosSysSystemBaseElevated; } SkColor ShelfConfig::GetDefaultShelfColor(const views::Widget* widget) const {
diff --git a/ash/style/system_toast_style.cc b/ash/style/system_toast_style.cc index a90cc4b..9431aa4 100644 --- a/ash/style/system_toast_style.cc +++ b/ash/style/system_toast_style.cc
@@ -25,6 +25,7 @@ #include "ui/gfx/geometry/insets.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/gfx/text_utils.h" +#include "ui/gfx/vector_icon_types.h" #include "ui/views/background.h" #include "ui/views/controls/focus_ring.h" #include "ui/views/controls/image_view.h" @@ -38,14 +39,19 @@ // UI constants in DIP (Density Independent Pixel). constexpr int kToastTextMaximumWidth = 512; -constexpr int kOneLineHorizontalSpacing = 16; -constexpr int kTwoLineHorizontalSpacing = 24; -constexpr int kSpacingBetweenLabelAndButton = 16; -constexpr int kOnelineButtonPadding = 2; -constexpr int kTwolineButtonRightSpacing = 12; -constexpr int kToastLabelVerticalSpacing = 8; -constexpr int kManagedIconSize = 32; -constexpr int kTwolineVerticalPadding = 12; + +constexpr int kOneLineHorizontalPadding = 16; +constexpr int kTwoLineHorizontalPadding = 24; + +constexpr int kOneLineVerticalPadding = 8; +constexpr int kTwoLineVerticalPadding = 12; + +constexpr int kOneLineButtonPadding = 2; +constexpr int kTwoLineButtonRightPadding = 12; + +constexpr int kLeadingIconSize = 20; +constexpr int kLeadingIconLeftPadding = 18; +constexpr int kLeadingIconRightPadding = 14; // The label inside SystemToastStyle, which allows two lines at maximum. class SystemToastInnerLabel : public views::Label { @@ -73,18 +79,43 @@ BEGIN_METADATA(SystemToastInnerLabel, views::Label) END_METADATA -// Returns the vertical padding for the layout given button presence and -// `two_line`. -int ComputeVerticalSpacing(bool has_button, bool two_line) { - if (two_line) - return kTwolineVerticalPadding; +// Returns the vertical padding for the layout given the presence of the dismiss +// button and whether the toast is multi-line. +int ComputeVerticalPadding(bool has_button, bool two_line) { + if (two_line) { + return kTwoLineVerticalPadding; + } // For one line, the button is taller so it determines the height of the toast // so we use the button's padding. - if (has_button) - return kOnelineButtonPadding; + return has_button ? kOneLineButtonPadding : kOneLineVerticalPadding; +} - return kToastLabelVerticalSpacing; +// Computes the outer insets for the Box Layout container that holds toast +// elements. Horizontal spacing may vary depending if there's a dismiss button +// or a leading icon present. +gfx::Insets ComputeInsets(bool has_button, bool two_line, bool has_icon) { + int left_inset; + int right_inset; + + if (has_icon) { + left_inset = kLeadingIconLeftPadding; + } else { + left_inset = + two_line ? kTwoLineHorizontalPadding : kOneLineHorizontalPadding; + } + + if (has_button) { + right_inset = two_line ? kTwoLineButtonRightPadding : kOneLineButtonPadding; + } else { + right_inset = + two_line ? kTwoLineHorizontalPadding : kOneLineHorizontalPadding; + } + + const int vertical_insets = ComputeVerticalPadding(has_button, two_line); + + return gfx::Insets::TLBR(vertical_insets, left_inset, vertical_insets, + right_inset); } } // namespace @@ -92,18 +123,21 @@ SystemToastStyle::SystemToastStyle(base::RepeatingClosure dismiss_callback, const std::u16string& text, const std::u16string& dismiss_text, - const bool is_managed) - : scoped_a11y_overrider_( + const gfx::VectorIcon& leading_icon) + : leading_icon_(&leading_icon), + scoped_a11y_overrider_( std::make_unique<ScopedA11yOverrideWindowSetter>()) { SetPaintToLayer(); layer()->SetFillsBoundsOpaquely(false); layer()->SetBackgroundBlur(ColorProvider::kBackgroundBlurSigma); SetBackground(views::CreateThemedSolidBackground(kColorAshShieldAndBase80)); - if (is_managed) { - managed_icon_ = AddChildView(std::make_unique<views::ImageView>()); - managed_icon_->SetPreferredSize( - gfx::Size(kManagedIconSize, kManagedIconSize)); + if (!leading_icon_->is_empty()) { + leading_icon_view_ = AddChildView(std::make_unique<views::ImageView>()); + leading_icon_view_->SetPreferredSize(gfx::Size( + kLeadingIconSize + kLeadingIconRightPadding, kLeadingIconSize)); + leading_icon_view_->SetBorder(views::CreateEmptyBorder( + gfx::Insets::TLBR(0, 0, 0, kLeadingIconRightPadding))); } label_ = AddChildView(std::make_unique<SystemToastInnerLabel>(text)); @@ -120,19 +154,10 @@ // are needed. label_->GetPreferredSize(); const bool two_line = label_->GetRequiredLines() > 1; - const int vertical_spacing = ComputeVerticalSpacing(!!button_, two_line); - - auto insets = - gfx::Insets::VH(vertical_spacing, two_line ? kTwoLineHorizontalSpacing - : kOneLineHorizontalSpacing); - if (button_) { - insets.set_right(two_line ? kTwolineButtonRightSpacing - : kOnelineButtonPadding); - } auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>( - views::BoxLayout::Orientation::kHorizontal, insets, - button_ ? kSpacingBetweenLabelAndButton : 0)); + views::BoxLayout::Orientation::kHorizontal, + ComputeInsets(!!button_, two_line, !leading_icon.is_empty()))); layout->set_cross_axis_alignment( views::BoxLayout::CrossAxisAlignment::kCenter); layout->SetFlexForView(label_, 1); @@ -140,11 +165,9 @@ int toast_height = GetPreferredSize().height(); const float toast_corner_radius = toast_height / 2.0f; layer()->SetRoundedCornerRadius(gfx::RoundedCornersF(toast_corner_radius)); - if (features::IsDarkLightModeEnabled()) { - SetBorder(std::make_unique<views::HighlightBorder>( - toast_corner_radius, views::HighlightBorder::Type::kHighlightBorder1, - /*use_light_colors=*/false)); - } + SetBorder(std::make_unique<views::HighlightBorder>( + toast_corner_radius, views::HighlightBorder::Type::kHighlightBorder1, + /*use_light_colors=*/false)); // Since system toast has a very large corner radius, we should use the shadow // on texture layer. Refer to `ash::SystemShadowOnTextureLayer` for more @@ -199,10 +222,10 @@ void SystemToastStyle::OnThemeChanged() { views::View::OnThemeChanged(); - if (managed_icon_) { - managed_icon_->SetImage(gfx::CreateVectorIcon( - kSystemMenuBusinessIcon, - GetColorProvider()->GetColor(cros_tokens::kIconColorPrimary))); + if (leading_icon_view_) { + leading_icon_view_->SetImage(gfx::CreateVectorIcon( + *leading_icon_, + GetColorProvider()->GetColor(cros_tokens::kCrosSysOnSurface))); } SchedulePaint();
diff --git a/ash/style/system_toast_style.h b/ash/style/system_toast_style.h index 9a0afab..79fe089c 100644 --- a/ash/style/system_toast_style.h +++ b/ash/style/system_toast_style.h
@@ -8,6 +8,7 @@ #include "ash/ash_export.h" #include "base/functional/callback_forward.h" #include "ui/base/metadata/metadata_header_macros.h" +#include "ui/gfx/paint_vector_icon.h" #include "ui/views/view.h" namespace views { @@ -21,12 +22,10 @@ class ScopedA11yOverrideWindowSetter; class SystemShadow; -// A view that has rounded corner with label and button inside. The label shows -// the information. The button inside is optional and has certain functionality -// e.g. dismiss the view or retry. A managed icon will be put ahead of the label -// inside if `is_managed` is true. -// TODO(crbug/1289478): Migrate the `managed_icon_` to `Quick settings toast`, -// which includes an icon on the left. +// A view used to present Toasts. It has rounded corners and may have a +// dismiss button if a `dismiss_text` is provided, and a `leading_icon` if one +// is provided. The spacing surrounding the elements may change if the text has +// one or two lines. class ASH_EXPORT SystemToastStyle : public views::View { public: METADATA_HEADER(SystemToastStyle); @@ -34,7 +33,7 @@ SystemToastStyle(base::RepeatingClosure dismiss_callback, const std::u16string& text, const std::u16string& dismiss_text, - const bool is_managed); + const gfx::VectorIcon& leading_icon = gfx::kNoneIcon); SystemToastStyle(const SystemToastStyle&) = delete; SystemToastStyle& operator=(const SystemToastStyle&) = delete; ~SystemToastStyle() override; @@ -53,13 +52,18 @@ views::LabelButton* button() const { return button_; } private: + friend class ToastManagerImplTest; + // views::View: void AddedToWidget() override; void OnThemeChanged() override; + const gfx::VectorIcon* const leading_icon_ = nullptr; + + // Owned by views hierarchy. views::Label* label_ = nullptr; views::LabelButton* button_ = nullptr; - views::ImageView* managed_icon_ = nullptr; + views::ImageView* leading_icon_view_ = nullptr; // Tells the toast if the dismiss button is already highlighted if one exists. bool is_dismiss_button_highlighted_ = false;
diff --git a/ash/system/privacy_hub/camera_privacy_switch_controller.cc b/ash/system/privacy_hub/camera_privacy_switch_controller.cc index 4dc1c7e9..19017ad 100644 --- a/ash/system/privacy_hub/camera_privacy_switch_controller.cc +++ b/ash/system/privacy_hub/camera_privacy_switch_controller.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "ash/constants/ash_features.h" #include "ash/constants/ash_pref_names.h" #include "ash/public/cpp/sensor_disabled_notification_delegate.h" #include "ash/public/cpp/session/session_observer.h" @@ -19,6 +20,7 @@ #include "base/check.h" #include "components/prefs/pref_service.h" #include "media/capture/video/chromeos/camera_hal_dispatcher_impl.h" +#include "media/capture/video/chromeos/mojom/cros_camera_service.mojom-shared.h" namespace ash { @@ -92,8 +94,8 @@ base::BindRepeating(&CameraPrivacySwitchController::OnPreferenceChanged, base::Unretained(this))); - // Make sure to add camera observers after pref_change_registrar_ is created - // because OnCameraSWPrivacySwitchStateChanged accesses a pref value. + // Add camera observers after `pref_change_registrar_` is created because + // `OnCameraSWPrivacySwitchStateChanged` accesses a pref value. if (!is_camera_observer_added_) { // Subscribe to the camera HW/SW privacy switch events. auto device_id_to_privacy_switch_state = @@ -123,10 +125,25 @@ const CameraSWPrivacySwitchSetting pref_val = GetUserSwitchPreference(); switch_api_->SetCameraSWPrivacySwitch(pref_val); + if (features::IsVideoConferenceEnabled()) { + // The `VideoConferenceTrayController` shows this info as a toast. + return; + } + turn_sw_switch_on_notification_.Hide(); - if (active_applications_using_camera_count_ == 0) + if (features::IsPrivacyIndicatorsEnabled()) { + // Always remove the sensor disabled notification if the sensor was unmuted. + if (pref_val == CameraSWPrivacySwitchSetting::kEnabled) { + GetPrivacyHubNotificationController()->RemoveSoftwareSwitchNotification( + SensorDisabledNotificationDelegate::Sensor::kCamera); + } return; + } + + if (active_applications_using_camera_count_ == 0) { + return; + } if (pref_val == CameraSWPrivacySwitchSetting::kDisabled) { camera_used_while_deactivated_ = true; @@ -175,8 +192,23 @@ const std::string& device_id, cros::mojom::CameraPrivacySwitchState state) { camera_privacy_switch_state_ = state; + + if (features::IsVideoConferenceEnabled()) { + // The `VideoConferenceTrayController` shows this info as a toast. + return; + } + + if (features::IsPrivacyIndicatorsEnabled()) { + // Always hide if the switch was turned off. + if (camera_privacy_switch_state_ == + cros::mojom::CameraPrivacySwitchState::OFF) { + turn_sw_switch_on_notification_.Hide(); + } + return; + } + // Issue a notification if camera is disabled by HW switch, but not by the SW - // switch and there is multiple cameras. + // switch and there are multiple cameras. if (state == cros::mojom::CameraPrivacySwitchState::ON && GetUserSwitchPreference() == CameraSWPrivacySwitchSetting::kEnabled && camera_count_ > 1) { @@ -214,7 +246,35 @@ active_applications_using_camera_count_--; } - if (GetUserSwitchPreference() != CameraSWPrivacySwitchSetting::kDisabled) { + const bool camera_muted_by_sw = + GetUserSwitchPreference() == CameraSWPrivacySwitchSetting::kDisabled; + + if (features::IsVideoConferenceEnabled()) { + // The `VideoConferenceTrayController` shows this info as a toast. + return; + } + + if (features::IsPrivacyIndicatorsEnabled()) { + // NOTE: This logic mirrors the logic in + // `MicrophonePrivacySwitchController`. + if (active_applications_using_camera_count_ == 0) { + // Always remove the notification when active applications go to 0. + GetPrivacyHubNotificationController()->RemoveSoftwareSwitchNotification( + SensorDisabledNotificationDelegate::Sensor::kCamera); + } else if (application_added) { + if (camera_muted_by_sw) { + GetPrivacyHubNotificationController()->ShowSoftwareSwitchNotification( + SensorDisabledNotificationDelegate::Sensor::kCamera); + } + } else { + // Application removed, update the notifications message. + GetPrivacyHubNotificationController()->UpdateSoftwareSwitchNotification( + SensorDisabledNotificationDelegate::Sensor::kCamera); + } + return; + } + + if (!camera_muted_by_sw) { return; }
diff --git a/ash/system/privacy_hub/camera_privacy_switch_controller_unittest.cc b/ash/system/privacy_hub/camera_privacy_switch_controller_unittest.cc index e7190e67..dcb4441 100644 --- a/ash/system/privacy_hub/camera_privacy_switch_controller_unittest.cc +++ b/ash/system/privacy_hub/camera_privacy_switch_controller_unittest.cc
@@ -7,15 +7,20 @@ #include <utility> #include <vector> +#include "ash/constants/ash_features.h" #include "ash/constants/ash_pref_names.h" +#include "ash/constants/ash_switches.h" #include "ash/public/cpp/sensor_disabled_notification_delegate.h" #include "ash/session/session_controller_impl.h" #include "ash/shell.h" #include "ash/strings/grit/ash_strings.h" #include "ash/system/privacy_hub/privacy_hub_controller.h" #include "ash/system/privacy_hub/privacy_hub_metrics.h" +#include "ash/system/privacy_hub/privacy_hub_notification.h" #include "ash/system/privacy_hub/privacy_hub_notification_controller.h" +#include "ash/system/video_conference/fake_video_conference_tray_controller.h" #include "ash/test/ash_test_base.h" +#include "base/command_line.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "components/prefs/pref_change_registrar.h" @@ -70,15 +75,60 @@ return message_center::MessageCenter::Get()->FindNotificationById(id); } +// Mutes the camera on construction, and unmutes the camera when the object goes +// out of scope. +class ScopedCameraMuteToggler { + public: + explicit ScopedCameraMuteToggler(bool software_switch) + : camera_privacy_switch_controller_( + Shell::Get()->privacy_hub_controller()->camera_controller()), + software_switch_(software_switch) { + if (software_switch_) { + Shell::Get()->session_controller()->GetActivePrefService()->SetBoolean( + prefs::kUserCameraAllowed, /*value=*/false); + } else { + camera_privacy_switch_controller_.OnCameraHWPrivacySwitchStateChanged( + std::string(), cros::mojom::CameraPrivacySwitchState::ON); + } + } + + ~ScopedCameraMuteToggler() { + if (software_switch_) { + Shell::Get()->session_controller()->GetActivePrefService()->SetBoolean( + prefs::kUserCameraAllowed, /*value=*/true); + } else { + camera_privacy_switch_controller_.OnCameraHWPrivacySwitchStateChanged( + std::string(), cros::mojom::CameraPrivacySwitchState::OFF); + } + } + + private: + CameraPrivacySwitchController& camera_privacy_switch_controller_; + const bool software_switch_; +}; + } // namespace -class PrivacyHubCameraControllerTests : public AshTestBase { - protected: - PrivacyHubCameraControllerTests() +class PrivacyHubCameraControllerTestBase : public AshTestBase { + public: + PrivacyHubCameraControllerTestBase() : AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) { scoped_feature_list_.InitAndEnableFeature(ash::features::kCrosPrivacyHub); } + ~PrivacyHubCameraControllerTestBase() override = default; + + // AshTestBase: + void SetUp() override { + AshTestBase::SetUp(); + + auto mock_switch = std::make_unique<::testing::NiceMock<MockSwitchAPI>>(); + mock_switch_ = mock_switch.get(); + + controller_ = &Shell::Get()->privacy_hub_controller()->camera_controller(); + controller_->SetCameraPrivacySwitchAPIForTest(std::move(mock_switch)); + } + void SetUserPref(bool allowed) { Shell::Get()->session_controller()->GetActivePrefService()->SetBoolean( prefs::kUserCameraAllowed, allowed); @@ -91,17 +141,6 @@ ->GetBoolean(prefs::kUserCameraAllowed); } - // AshTestBase: - void SetUp() override { - AshTestBase::SetUp(); - - auto mock_switch = std::make_unique<::testing::NiceMock<MockSwitchAPI>>(); - mock_switch_ = mock_switch.get(); - - controller_ = &Shell::Get()->privacy_hub_controller()->camera_controller(); - controller_->SetCameraPrivacySwitchAPIForTest(std::move(mock_switch)); - } - void LaunchAppAccessingCamera(const std::u16string& app_name) { delegate_.LaunchAppAccessingCamera(app_name); controller_->ActiveApplicationsChanged(/*application_added=*/true); @@ -113,14 +152,51 @@ } ::testing::NiceMock<MockSwitchAPI>* mock_switch_; + CameraPrivacySwitchController* controller_; base::test::ScopedFeatureList scoped_feature_list_; const base::HistogramTester histogram_tester_; FakeSensorDisabledNotificationDelegate delegate_; }; +class PrivacyHubCameraControllerTest + : public PrivacyHubCameraControllerTestBase, + public testing::WithParamInterface<std::tuple<bool, bool>> { + protected: + PrivacyHubCameraControllerTest() { + std::vector<base::test::FeatureRef> enabled_features{}; + if (IsPrivacyIndicatorsEnabled()) { + enabled_features.push_back(features::kPrivacyIndicators); + } + if (IsVideoConferenceEnabled()) { + fake_video_conference_tray_controller_ = + std::make_unique<FakeVideoConferenceTrayController>(); + enabled_features.push_back(features::kVideoConference); + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kCameraEffectsSupportedByHardware); + } + scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list_->InitWithFeatures(enabled_features, {}); + } + + ~PrivacyHubCameraControllerTest() override { + fake_video_conference_tray_controller_.reset(); + } + bool IsPrivacyIndicatorsEnabled() { return std::get<0>(GetParam()); } + + bool IsVideoConferenceEnabled() { return std::get<1>(GetParam()); } + + std::unique_ptr<FakeVideoConferenceTrayController> + fake_video_conference_tray_controller_; + + std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_; +}; + +INSTANTIATE_TEST_SUITE_P(All, + PrivacyHubCameraControllerTest, + testing::Combine(testing::Bool(), testing::Bool())); // Test reaction on UI action. -TEST_F(PrivacyHubCameraControllerTests, UIAction) { +TEST_P(PrivacyHubCameraControllerTest, UIAction) { const std::vector<bool> user_pref_sequence{false, true, true, false, true}; const int number_of_changes = [&]() { int cnt = 0; @@ -149,7 +225,7 @@ } } -TEST_F(PrivacyHubCameraControllerTests, OnCameraSoftwarePrivacySwitchChanged) { +TEST_P(PrivacyHubCameraControllerTest, OnCameraSoftwarePrivacySwitchChanged) { // When |prefs::kUserCameraAllowed| is true and CrOS Camera Service // communicates the SW privacy switch state as UNKNOWN or ON, the states // mismatch and SetCameraSWPrivacySwitch(kEnabled) should be called to correct @@ -196,7 +272,7 @@ cros::mojom::CameraPrivacySwitchState::ON); } -TEST_F(PrivacyHubCameraControllerTests, +TEST_P(PrivacyHubCameraControllerTest, OnCameraHardwarePrivacySwitchChangedMultipleCameras) { CameraPrivacySwitchController& controller = Shell::Get()->privacy_hub_controller()->camera_controller(); @@ -221,10 +297,16 @@ EXPECT_EQ(cros::mojom::CameraPrivacySwitchState::ON, controller.HWSwitchState()); + // The notifications don't show with Privacy Indicators or Video Conference + // enabled. + if (IsPrivacyIndicatorsEnabled() || IsVideoConferenceEnabled()) { + return; + } + message_center::MessageCenter* const message_center = message_center::MessageCenter::Get(); // This particular notification ("Do you want to disable all cameras?") should - // appear only there are multiple cameras. + // appear only if there are multiple cameras. EXPECT_TRUE(FindNotificationById( kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId)); // User pref didn't change. @@ -261,7 +343,7 @@ 1); } -TEST_F(PrivacyHubCameraControllerTests, +TEST_P(PrivacyHubCameraControllerTest, OnCameraHardwarePrivacySwitchChangedOneCamera) { CameraPrivacySwitchController& controller = Shell::Get()->privacy_hub_controller()->camera_controller(); @@ -303,7 +385,7 @@ } // This test is a regression test for b/253407315 -TEST_F(PrivacyHubCameraControllerTests, +TEST_P(PrivacyHubCameraControllerTest, OnCameraHardwarePrivacySwitchChangedNotificationClearing) { CameraPrivacySwitchController& controller = Shell::Get()->privacy_hub_controller()->camera_controller(); @@ -312,11 +394,20 @@ controller.OnCameraHWPrivacySwitchStateChanged( "0", cros::mojom::CameraPrivacySwitchState::ON); - const message_center::Notification* const notification = FindNotificationById( + + if (IsPrivacyIndicatorsEnabled() || IsVideoConferenceEnabled()) { + LaunchAppAccessingCamera(u"app_name"); + } + + auto* notification = FindNotificationById( kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId); - EXPECT_TRUE(notification); - // User should be able to clear the notification manually - EXPECT_FALSE(notification->rich_notification_data().pinned); + if (IsVideoConferenceEnabled() || IsPrivacyIndicatorsEnabled()) { + EXPECT_FALSE(notification); + } else { + EXPECT_TRUE(notification); + // User should be able to clear the notification manually + EXPECT_FALSE(notification->rich_notification_data().pinned); + } // Notification should be cleared when hardware mute is disabled controller.OnCameraHWPrivacySwitchStateChanged( "0", cros::mojom::CameraPrivacySwitchState::OFF); @@ -324,7 +415,7 @@ kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId)); } -TEST_F(PrivacyHubCameraControllerTests, +TEST_P(PrivacyHubCameraControllerTest, CameraOffNotificationRemoveViaClickOnButton) { SetUserPref(false); message_center::MessageCenter* const message_center = @@ -335,9 +426,17 @@ // An application starts accessing the camera. controller_->ActiveApplicationsChanged(/*application_added=*/true); + + auto* notification = FindNotificationById( + PrivacyHubNotificationController::kCombinedNotificationId); + if (IsVideoConferenceEnabled()) { + // No Notification should be sent. Return early because the rest of the test + // tests notification behavior. + EXPECT_FALSE(notification); + return; + } // A notification should be fired. - EXPECT_TRUE(FindNotificationById( - PrivacyHubNotificationController::kCombinedNotificationId)); + EXPECT_TRUE(notification); EXPECT_FALSE(GetUserPref()); EXPECT_EQ(histogram_tester_.GetBucketCount( @@ -358,7 +457,7 @@ 1); } -TEST_F(PrivacyHubCameraControllerTests, +TEST_P(PrivacyHubCameraControllerTest, CameraOffNotificationRemoveViaClickOnBody) { SetUserPref(false); controller_->OnCameraCountChanged(2); @@ -370,20 +469,28 @@ // An application starts accessing the camera. controller_->ActiveApplicationsChanged(/*application_added=*/true); - // A notification should be fired. - EXPECT_TRUE(FindNotificationById( - PrivacyHubNotificationController::kCombinedNotificationId)); - EXPECT_FALSE(GetUserPref()); - // Enabling camera via clicking on the body should open the privacy hub - // settings page. - message_center->ClickOnNotification( + auto* notification = FindNotificationById( PrivacyHubNotificationController::kCombinedNotificationId); - - // The user pref should not be changed. + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(notification); + } else { + // A notification should be fired. + EXPECT_TRUE(notification); + } EXPECT_FALSE(GetUserPref()); - EXPECT_FALSE(FindNotificationById( - PrivacyHubNotificationController::kCombinedNotificationId)); + + if (!IsVideoConferenceEnabled()) { + // Enabling camera via clicking on the body should open the privacy hub + // settings page. + message_center->ClickOnNotification( + PrivacyHubNotificationController::kCombinedNotificationId); + + // The user pref should not be changed. + EXPECT_FALSE(GetUserPref()); + EXPECT_FALSE(FindNotificationById( + PrivacyHubNotificationController::kCombinedNotificationId)); + } SetUserPref(true); @@ -396,12 +503,29 @@ ->camera_controller() .OnCameraHWPrivacySwitchStateChanged( "0", cros::mojom::CameraPrivacySwitchState::ON); + if (IsVideoConferenceEnabled() || IsPrivacyIndicatorsEnabled()) { + // No notification is fired for switch changes during the capture session. + // But one will be fired if a new session starts. + EXPECT_FALSE(FindNotificationById( + PrivacyHubNotificationController::kCombinedNotificationId)); + EXPECT_TRUE(GetUserPref()); - // A notification should be fired. - EXPECT_TRUE(FindNotificationById( - kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId)); + // Adds a second application. + controller_->ActiveApplicationsChanged(/*application_added=*/true); + } + + notification = FindNotificationById( + kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId); + if (IsVideoConferenceEnabled() || IsPrivacyIndicatorsEnabled()) { + EXPECT_FALSE(notification); + } else { + EXPECT_TRUE(notification); + } EXPECT_TRUE(GetUserPref()); + if (IsVideoConferenceEnabled()) { + return; + } // Clicking on the body should open the privacy hub settings page. message_center->ClickOnNotification( kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId); @@ -412,17 +536,22 @@ kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId)); } -TEST_F(PrivacyHubCameraControllerTests, - CameraOffNotificationRemoveViaUserPref) { +TEST_P(PrivacyHubCameraControllerTest, CameraOffNotificationRemoveViaUserPref) { SetUserPref(false); ASSERT_FALSE(FindNotificationById( PrivacyHubNotificationController::kCombinedNotificationId)); // An application starts accessing the camera. controller_->ActiveApplicationsChanged(/*application_added=*/true); - // A notification should be fired. - EXPECT_TRUE(FindNotificationById( - PrivacyHubNotificationController::kCombinedNotificationId)); + auto* notification = FindNotificationById( + PrivacyHubNotificationController::kCombinedNotificationId); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(notification); + } else { + // A notification should be fired. + EXPECT_TRUE(FindNotificationById( + PrivacyHubNotificationController::kCombinedNotificationId)); + } EXPECT_FALSE(GetUserPref()); // Enabling camera via the user pref should clear the notification. @@ -432,7 +561,7 @@ PrivacyHubNotificationController::kCombinedNotificationId)); } -TEST_F(PrivacyHubCameraControllerTests, InSessionSwitchNotification) { +TEST_P(PrivacyHubCameraControllerTest, InSessionSwitchNotification) { SetUserPref(true); message_center::MessageCenter* const message_center = message_center::MessageCenter::Get(); @@ -442,12 +571,26 @@ // An application starts accessing the camera. controller_->ActiveApplicationsChanged(/*application_added=*/true); - // Disable camera + // Disable the camera after the application count has changed. SetUserPref(false); + if (IsPrivacyIndicatorsEnabled() || IsVideoConferenceEnabled()) { + EXPECT_FALSE(FindNotificationById( + PrivacyHubNotificationController::kCombinedNotificationId)); + EXPECT_FALSE(GetUserPref()); + // Trigger the notification by simulating a second application accessing the + // camera. + controller_->ActiveApplicationsChanged(/*application_added=*/true); + } + auto* notification = FindNotificationById( + PrivacyHubNotificationController::kCombinedNotificationId); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(notification); + EXPECT_FALSE(GetUserPref()); + return; + } // A notification should be fired. - EXPECT_TRUE(FindNotificationById( - PrivacyHubNotificationController::kCombinedNotificationId)); + EXPECT_TRUE(notification); EXPECT_FALSE(GetUserPref()); EXPECT_EQ(histogram_tester_.GetBucketCount( @@ -470,7 +613,7 @@ // Tests if camera software switch notification is removed when the number of // apps accessing the camera becomes 0. -TEST_F(PrivacyHubCameraControllerTests, +TEST_P(PrivacyHubCameraControllerTest, NotificationRemovedWhenNoActiveApplication) { SetUserPref(true); @@ -484,13 +627,37 @@ // Disabling camera using the software switch. SetUserPref(false); - // Notification `PrivacyHubNotificationController::kCombinedNotificationId` - // should pop up. - EXPECT_TRUE(FindNotificationById( - PrivacyHubNotificationController::kCombinedNotificationId)); + if (IsPrivacyIndicatorsEnabled() || IsVideoConferenceEnabled()) { + // No notification pops up if the switch is modified during the stream. + EXPECT_FALSE(FindNotificationById( + PrivacyHubNotificationController::kCombinedNotificationId)); + + controller_->ActiveApplicationsChanged(/*application_added=*/true); + } + + auto* notification = FindNotificationById( + PrivacyHubNotificationController::kCombinedNotificationId); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(notification); + } else { + EXPECT_TRUE(notification); + } // The only active application stops accessing the camera the camera. controller_->ActiveApplicationsChanged(/*application_added=*/false); + if (IsPrivacyIndicatorsEnabled() || IsVideoConferenceEnabled()) { + // The notification should still be shown, because two apps were accessing + // the camera. + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(FindNotificationById( + PrivacyHubNotificationController::kCombinedNotificationId)); + } else { + EXPECT_TRUE(FindNotificationById( + PrivacyHubNotificationController::kCombinedNotificationId)); + } + + controller_->ActiveApplicationsChanged(/*application_added=*/false); + } // Existing notification // `PrivacyHubNotificationController::kCombinedNotificationId` should be @@ -500,7 +667,10 @@ } // Tests if the camera software switch notification contains proper text. -TEST_F(PrivacyHubCameraControllerTests, NotificationText) { +TEST_P(PrivacyHubCameraControllerTest, NotificationText) { + if (IsVideoConferenceEnabled()) { + return; + } // Disabling camera using the software switch. SetUserPref(false); EXPECT_FALSE(FindNotificationById( @@ -564,7 +734,10 @@ notification->message()); } -TEST_F(PrivacyHubCameraControllerTests, MetricCollection) { +TEST_P(PrivacyHubCameraControllerTest, MetricCollection) { + if (IsVideoConferenceEnabled()) { + return; + } EXPECT_EQ(histogram_tester_.GetBucketCount( privacy_hub_metrics:: kPrivacyHubCameraEnabledFromNotificationHistogram, @@ -603,4 +776,211 @@ 1); } +class PrivacyIndicatorAndVideoConferenceCameraControllerTest + : public PrivacyHubCameraControllerTestBase, + public testing::WithParamInterface<std::tuple<bool, bool>> { + public: + PrivacyIndicatorAndVideoConferenceCameraControllerTest() { + std::vector<base::test::FeatureRef> enabled_features{ + ash::features::kCrosPrivacyHub}; + if (IsPrivacyIndicatorsEnabled()) { + enabled_features.push_back(features::kPrivacyIndicators); + } + if (IsVideoConferenceEnabled()) { + fake_video_conference_tray_controller_ = + std::make_unique<FakeVideoConferenceTrayController>(); + enabled_features.push_back(features::kVideoConference); + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kCameraEffectsSupportedByHardware); + } + scoped_feature_list_.InitWithFeatures(enabled_features, {}); + } + ~PrivacyIndicatorAndVideoConferenceCameraControllerTest() override { + fake_video_conference_tray_controller_.reset(); + } + + bool IsPrivacyIndicatorsEnabled() { return std::get<0>(GetParam()); } + + bool IsVideoConferenceEnabled() { return std::get<1>(GetParam()); } + + std::unique_ptr<FakeVideoConferenceTrayController> + fake_video_conference_tray_controller_; + base::test::ScopedFeatureList scoped_feature_list_; +}; + +INSTANTIATE_TEST_SUITE_P(All, + PrivacyIndicatorAndVideoConferenceCameraControllerTest, + testing::Combine(testing::Bool(), testing::Bool())); + +// With VcControls or Privacy Indicators enabled, tests that no notification +// shows up if the switches are toggled when the number of capturing apps does +// not change. +TEST_P(PrivacyIndicatorAndVideoConferenceCameraControllerTest, + NoNotificationDuringCaptureSession) { + if (!IsPrivacyIndicatorsEnabled() && !IsVideoConferenceEnabled()) { + return; + } + + const std::u16string app_name = u"app_name"; + for (bool app_running : {false, true}) { + if (app_running) { + LaunchAppAccessingCamera(app_name); + EXPECT_FALSE(FindNotificationById( + PrivacyHubNotificationController::kCombinedNotificationId)); + } + // Disable camera using the software switch. No notification should show. + SetUserPref(/*allowed=*/false); + + EXPECT_FALSE(FindNotificationById( + PrivacyHubNotificationController::kCombinedNotificationId)); + + // Re-enable the camera, there still should be no notification. + SetUserPref(/*allowed=*/true); + + EXPECT_FALSE(FindNotificationById( + PrivacyHubNotificationController::kCombinedNotificationId)); + + // Repeat the test with the hardware switch. + CameraPrivacySwitchController& controller = + Shell::Get()->privacy_hub_controller()->camera_controller(); + controller.OnCameraHWPrivacySwitchStateChanged( + std::string(), cros::mojom::CameraPrivacySwitchState::ON); + + EXPECT_FALSE(FindNotificationById( + kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId)); + + controller.OnCameraHWPrivacySwitchStateChanged( + std::string(), cros::mojom::CameraPrivacySwitchState::OFF); + + EXPECT_FALSE(FindNotificationById( + kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId)); + + if (app_running) { + CloseAppAccessingCamera(app_name); + } + } +} + +// With VcControls or Privacy Indicators enabled, tests that a notification +// shows up when the number of apps capturing the camera increases if the switch +// is muted. +TEST_P(PrivacyIndicatorAndVideoConferenceCameraControllerTest, + NotificationShowsIfNewAppStartsCapturing) { + if (!IsPrivacyIndicatorsEnabled() && !IsVideoConferenceEnabled()) { + return; + } + for (bool software_switch : {true, false}) { + SCOPED_TRACE(::testing::Message() + << "software_switch: " << software_switch); + auto scoped_software_switch_toggler = + ScopedCameraMuteToggler(/*software_switch=*/software_switch); + ASSERT_FALSE(FindNotificationById( + software_switch + ? PrivacyHubNotificationController::kCombinedNotificationId + : kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId)); + + // Simulate an app accessing the camera, a notification should show. + LaunchAppAccessingCamera(u"app_name"); + + auto* notification = FindNotificationById( + software_switch + ? PrivacyHubNotificationController::kCombinedNotificationId + : kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId); + if (IsVideoConferenceEnabled() || !software_switch) { + EXPECT_FALSE(notification); + } else { + EXPECT_TRUE(notification); + } + } +} + +// With VcControls or Privacy Indicators enabled, tests that turning camera +// access back on hides the notification. +TEST_P(PrivacyIndicatorAndVideoConferenceCameraControllerTest, + EnablingCameraAccessHidesNotification) { + for (bool software_switch : {true, false}) { + auto scoped_software_switch_toggler = + std::make_unique<ScopedCameraMuteToggler>( + /*software_switch=*/software_switch); + + // Simulate an app accessing the camera, a notification should show. + LaunchAppAccessingCamera(u"app_name"); + auto* notification = FindNotificationById( + PrivacyHubNotificationController::kCombinedNotificationId); + if (IsVideoConferenceEnabled() || !software_switch) { + EXPECT_FALSE(notification); + } else { + EXPECT_TRUE(notification); + } + // Reverses the switch, the notification should go away. + scoped_software_switch_toggler.reset(); + + EXPECT_FALSE(FindNotificationById( + software_switch + ? PrivacyHubNotificationController::kCombinedNotificationId + : kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId)); + } +} + +// With VcControls or Privacy Indicators enabled, tests that a notification +// shows up if muted and then a capture starts. +TEST_P(PrivacyIndicatorAndVideoConferenceCameraControllerTest, + NotificationWhenMutedOnCaptureStart) { + for (bool software_switch : {true, false}) { + auto scoped_software_switch_toggler = + std::make_unique<ScopedCameraMuteToggler>( + /*software_switch=*/software_switch); + const std::u16string app_name = u"app_name"; + LaunchAppAccessingCamera(app_name); + + auto* notification = FindNotificationById( + software_switch + ? PrivacyHubNotificationController::kCombinedNotificationId + : kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId); + if (IsVideoConferenceEnabled() || !software_switch) { + EXPECT_FALSE(notification); + } else { + EXPECT_TRUE(notification); + } + + CloseAppAccessingCamera(app_name); + + EXPECT_FALSE(FindNotificationById( + software_switch + ? PrivacyHubNotificationController::kCombinedNotificationId + : kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId)); + } +} + +// With VcControls or Privacy Indicators enabled, tests that a notification goes +// away when capturing apps drop to 0. +TEST_P(PrivacyIndicatorAndVideoConferenceCameraControllerTest, + NotificationGoesAwayWhenAppsGoToZero) { + for (bool software_switch : {true, false}) { + auto scoped_software_switch_toggler = + std::make_unique<ScopedCameraMuteToggler>( + /*software_switch=*/software_switch); + const std::u16string app_name = u"app_name"; + LaunchAppAccessingCamera(app_name); + + auto* notification = FindNotificationById( + software_switch + ? PrivacyHubNotificationController::kCombinedNotificationId + : kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId); + if (IsVideoConferenceEnabled() || !software_switch) { + EXPECT_FALSE(notification); + } else { + EXPECT_TRUE(notification); + } + + // Remove the capturing app, the notification should disappear. + CloseAppAccessingCamera(app_name); + + EXPECT_FALSE(FindNotificationById( + software_switch + ? PrivacyHubNotificationController::kCombinedNotificationId + : kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId)); + } +} + } // namespace ash
diff --git a/ash/system/privacy_hub/microphone_privacy_switch_controller.cc b/ash/system/privacy_hub/microphone_privacy_switch_controller.cc index fb938a1..117ebc7 100644 --- a/ash/system/privacy_hub/microphone_privacy_switch_controller.cc +++ b/ash/system/privacy_hub/microphone_privacy_switch_controller.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "ash/constants/ash_features.h" #include "ash/constants/ash_pref_names.h" #include "ash/public/cpp/sensor_disabled_notification_delegate.h" #include "ash/public/cpp/session/session_observer.h" @@ -80,8 +81,18 @@ mic_muted_by_mute_switch_ = CrasAudioHandler::Get()->input_muted_by_microphone_mute_switch(); - if (input_stream_count_) { - SetMicrophoneNotificationVisible(mic_mute_on_); + // The `VideoConferenceTrayController` shows this info as a toast. + if (!features::IsVideoConferenceEnabled()) { + if (features::IsPrivacyIndicatorsEnabled()) { + // Only show the notification when a stream starts (handled in + // `OnNumberOfInputStreamsWithPermissionChanged()`), and always dismiss + // the notification if mute is turned off. + if (!mic_mute_on_) { + SetMicrophoneNotificationVisible(mic_mute_on_); + } + } else if (input_stream_count_) { + SetMicrophoneNotificationVisible(mic_mute_on_); + } } // `pref_change_registrar_` is only initialized after a user logs in. @@ -112,19 +123,37 @@ mic_muted_by_mute_switch_ = muted; - if (input_stream_count_) { + if (features::IsVideoConferenceEnabled()) { + // The `VideoConferenceTrayController` shows this info as a toast. + return; + } + + if (features::IsPrivacyIndicatorsEnabled()) { + // If the hardware switch is on, call `SetMicrophoneNotificationVisible()` + // to ensure the text is updated to reflect the hw switch. Only show the + // notification when a stream starts (handled in + // `OnNumberOfInputStreamsWithPermissionChanged()`), and always dismiss the + // notification if mute is turned off. + if (mic_muted_by_mute_switch_ || !mic_mute_on_) { + SetMicrophoneNotificationVisible(mic_mute_on_); + } + } else if (input_stream_count_) { SetMicrophoneNotificationVisible(mic_mute_on_); } } void MicrophonePrivacySwitchController:: OnNumberOfInputStreamsWithPermissionChanged() { + if (features::IsVideoConferenceEnabled()) { + // The `VideoConferenceTrayController` shows this info as a toast. + return; + } // Catches the case where a mic-using app is launched while the mic is muted. const size_t input_stream_count = CountActiveInputStreams(); const bool stream_count_increased = input_stream_count > input_stream_count_; input_stream_count_ = input_stream_count; - if (!input_stream_count_) { + if (input_stream_count_ == 0) { SetMicrophoneNotificationVisible(false); } else if (stream_count_increased) { SetMicrophoneNotificationVisible(input_stream_count_ && mic_mute_on_); @@ -152,6 +181,11 @@ void MicrophonePrivacySwitchController::SetMicrophoneNotificationVisible( const bool visible) { + if (features::IsVideoConferenceEnabled()) { + // The `VideoConferenceTrayController` shows this info as a toast. + return; + } + PrivacyHubNotificationController* const privacy_hub_notification_controller = Shell::Get()->system_notification_controller()->privacy_hub(); @@ -173,6 +207,11 @@ } void MicrophonePrivacySwitchController::UpdateMicrophoneNotification() { + if (features::IsVideoConferenceEnabled()) { + // The `VideoConferenceTrayController` shows this info as a toast. + return; + } + PrivacyHubNotificationController* const privacy_hub_notification_controller = Shell::Get()->system_notification_controller()->privacy_hub();
diff --git a/ash/system/privacy_hub/microphone_privacy_switch_controller.h b/ash/system/privacy_hub/microphone_privacy_switch_controller.h index 2956063..165958a 100644 --- a/ash/system/privacy_hub/microphone_privacy_switch_controller.h +++ b/ash/system/privacy_hub/microphone_privacy_switch_controller.h
@@ -14,8 +14,8 @@ namespace ash { -// This controller keeps the kUserMicrophoneAllowed preference and the state of -// the system input mute in sync. +// This controller keeps the `kUserMicrophoneAllowed` preference and the state +// of the system input mute in sync. class ASH_EXPORT MicrophonePrivacySwitchController : public CrasAudioHandler::AudioObserver, public SessionObserver { @@ -34,11 +34,11 @@ void OnInputMutedByMicrophoneMuteSwitchChanged(bool muted) override; void OnNumberOfInputStreamsWithPermissionChanged() override; - // SessionObserver + // SessionObserver: void OnActiveUserPrefServiceChanged(PrefService* pref_service) override; private: - // A callback that is invoked when the user changes kUserMicrophoneAllowed + // A callback that is invoked when the user changes `kUserMicrophoneAllowed` // preference from the Privacy Hub UI. void OnPreferenceChanged();
diff --git a/ash/system/privacy_hub/microphone_privacy_switch_controller_unittest.cc b/ash/system/privacy_hub/microphone_privacy_switch_controller_unittest.cc index e5b21fb..244612d 100644 --- a/ash/system/privacy_hub/microphone_privacy_switch_controller_unittest.cc +++ b/ash/system/privacy_hub/microphone_privacy_switch_controller_unittest.cc
@@ -10,6 +10,7 @@ #include "ash/constants/ash_features.h" #include "ash/constants/ash_pref_names.h" +#include "ash/constants/ash_switches.h" #include "ash/public/cpp/privacy_hub_delegate.h" #include "ash/public/cpp/sensor_disabled_notification_delegate.h" #include "ash/public/cpp/test/test_new_window_delegate.h" @@ -19,7 +20,9 @@ #include "ash/system/privacy_hub/privacy_hub_controller.h" #include "ash/system/privacy_hub/privacy_hub_metrics.h" #include "ash/system/privacy_hub/privacy_hub_notification_controller.h" +#include "ash/system/video_conference/fake_video_conference_tray_controller.h" #include "ash/test/ash_test_base.h" +#include "base/command_line.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "chromeos/ash/components/audio/cras_audio_handler.h" @@ -95,34 +98,61 @@ } // namespace -class PrivacyHubMicrophoneControllerTest : public AshTestBase { +// Test fixture used to test privacy hub specific behavior. +// The two bool params are as follows, and every combination of the two are ran: +// 0. `IsPrivacyIndicatorsEnabled()` +// 1. `IsVideoConferenceEnabled()` +class PrivacyHubMicrophoneControllerTest + : public AshTestBase, + public testing::WithParamInterface<std::tuple<bool, bool>> { public: PrivacyHubMicrophoneControllerTest() : AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) { - scoped_feature_list_.InitAndEnableFeature(ash::features::kCrosPrivacyHub); + std::vector<base::test::FeatureRef> enabled_features{ + ash::features::kCrosPrivacyHub}; + if (IsPrivacyIndicatorsEnabled()) { + enabled_features.push_back(features::kPrivacyIndicators); + } + if (IsVideoConferenceEnabled()) { + fake_video_conference_tray_controller_ = + std::make_unique<FakeVideoConferenceTrayController>(); + enabled_features.push_back(features::kVideoConference); + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kCameraEffectsSupportedByHardware); + } + scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list_->InitWithFeatures(enabled_features, {}); auto delegate = std::make_unique<MockNewWindowDelegate>(); new_window_delegate_ = delegate.get(); window_delegate_provider_ = std::make_unique<TestNewWindowDelegateProvider>(std::move(delegate)); } - ~PrivacyHubMicrophoneControllerTest() override = default; + ~PrivacyHubMicrophoneControllerTest() override { + fake_video_conference_tray_controller_.reset(); + new_window_delegate_ = nullptr; + window_delegate_provider_.reset(); + } // AshTestBase: void SetUp() override { AshTestBase::SetUp(); - // This makes sure a global instance of SensorDisabledNotificationDelegate + // This makes sure a global instance of `SensorDisabledNotificationDelegate` // is created before running tests. Shell::Get()->privacy_hub_controller()->set_frontend(&mock_frontend_); } void TearDown() override { SetMicrophoneMuteSwitchState(/*muted=*/false); + AshTestBase::TearDown(); } - protected: + bool IsPrivacyIndicatorsEnabled() { return std::get<0>(GetParam()); } + + bool IsVideoConferenceEnabled() { return std::get<1>(GetParam()); } + void SetUserPref(bool allowed) { Shell::Get()->session_controller()->GetActivePrefService()->SetBoolean( prefs::kUserMicrophoneAllowed, allowed); @@ -210,10 +240,16 @@ MockNewWindowDelegate* new_window_delegate_ = nullptr; std::unique_ptr<TestNewWindowDelegateProvider> window_delegate_provider_; FakeSensorDisabledNotificationDelegate delegate_; - base::test::ScopedFeatureList scoped_feature_list_; + std::unique_ptr<FakeVideoConferenceTrayController> + fake_video_conference_tray_controller_; + std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_; }; -TEST_F(PrivacyHubMicrophoneControllerTest, SetSystemMuteOnLogin) { +INSTANTIATE_TEST_SUITE_P(All, + PrivacyHubMicrophoneControllerTest, + testing::Combine(testing::Bool(), testing::Bool())); + +TEST_P(PrivacyHubMicrophoneControllerTest, SetSystemMuteOnLogin) { for (bool microphone_allowed : {false, true, false}) { const bool microphone_muted = !microphone_allowed; SetUserPref(microphone_allowed); @@ -230,14 +266,14 @@ } } -TEST_F(PrivacyHubMicrophoneControllerTest, OnPreferenceChanged) { +TEST_P(PrivacyHubMicrophoneControllerTest, OnPreferenceChanged) { for (bool microphone_allowed : {false, true, false}) { SetUserPref(microphone_allowed); EXPECT_EQ(CrasAudioHandler::Get()->IsInputMuted(), !microphone_allowed); } } -TEST_F(PrivacyHubMicrophoneControllerTest, OnInputMuteChanged) { +TEST_P(PrivacyHubMicrophoneControllerTest, OnInputMuteChanged) { for (bool microphone_muted : {false, true, false}) { const bool microphone_allowed = !microphone_muted; @@ -247,7 +283,7 @@ } } -TEST_F(PrivacyHubMicrophoneControllerTest, OnMicrophoneMuteSwitchValueChanged) { +TEST_P(PrivacyHubMicrophoneControllerTest, OnMicrophoneMuteSwitchValueChanged) { EXPECT_CALL(mock_frontend_, MicrophoneHardwareToggleChanged(_)); Shell::Get() ->privacy_hub_controller() @@ -255,7 +291,7 @@ .OnInputMutedByMicrophoneMuteSwitchChanged(true); } -TEST_F(PrivacyHubMicrophoneControllerTest, SimpleMuteUnMute) { +TEST_P(PrivacyHubMicrophoneControllerTest, SimpleMuteUnMute) { // No notification initially. EXPECT_FALSE(IsAnyMicNotificationVisible()); @@ -270,7 +306,7 @@ EXPECT_FALSE(IsAnyMicNotificationVisible()); } -TEST_F(PrivacyHubMicrophoneControllerTest, LaunchAppUsingMicrophone) { +TEST_P(PrivacyHubMicrophoneControllerTest, LaunchAppUsingMicrophone) { // No notification initially. EXPECT_FALSE(IsAnyMicNotificationVisible()); @@ -287,19 +323,22 @@ // Launch an app that's using the mic. The microphone mute notification should // show as a popup. LaunchApp(u"junior"); - - EXPECT_TRUE(GetSWSwitchNotification()); - EXPECT_TRUE(GetSWSwitchPopupNotification()); - // Notification should not be pinned. - EXPECT_FALSE(GetSWSwitchNotification()->rich_notification_data().pinned); - + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(GetSWSwitchNotification()); + EXPECT_FALSE(GetSWSwitchPopupNotification()); + } else { + EXPECT_TRUE(GetSWSwitchNotification()); + EXPECT_TRUE(GetSWSwitchPopupNotification()); + // Notification should not be pinned. + EXPECT_FALSE(GetSWSwitchNotification()->rich_notification_data().pinned); + } // Unmute again, notification goes down. UnMuteMicrophone(); EXPECT_FALSE(IsAnyMicNotificationVisible()); } -TEST_F(PrivacyHubMicrophoneControllerTest, +TEST_P(PrivacyHubMicrophoneControllerTest, SilentNotificationOnMuteWhileMicInUse) { // No notification initially. EXPECT_FALSE(IsAnyMicNotificationVisible()); @@ -310,22 +349,33 @@ EXPECT_FALSE(IsAnyMicNotificationVisible()); - // Mute the mic, a notification should be shown and also popup. + // Mute the mic, a notification should be shown and also popup. If Video + // Conference or Privacy Indicators are enabled, no notifier is sent if muted + // during a session. MuteMicrophone(); - EXPECT_TRUE(GetSWSwitchNotification()); - EXPECT_TRUE(GetSWSwitchPopupNotification()); + if (IsPrivacyIndicatorsEnabled() || IsVideoConferenceEnabled()) { + EXPECT_FALSE(GetSWSwitchNotification()); + EXPECT_FALSE(GetSWSwitchPopupNotification()); + } else { + EXPECT_TRUE(GetSWSwitchNotification()); + EXPECT_TRUE(GetSWSwitchPopupNotification()); + } } -TEST_F(PrivacyHubMicrophoneControllerTest, +TEST_P(PrivacyHubMicrophoneControllerTest, ShowPopupNotificationOnStreamAddition) { // Launch an app while microphone is muted. MuteMicrophone(); LaunchApp(u"junior"); - EXPECT_TRUE(GetSWSwitchNotification()); - EXPECT_TRUE(GetSWSwitchPopupNotification()); - + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(GetSWSwitchNotification()); + EXPECT_FALSE(GetSWSwitchPopupNotification()); + } else { + EXPECT_TRUE(GetSWSwitchNotification()); + EXPECT_TRUE(GetSWSwitchPopupNotification()); + } // Mark the notification as read. MarkPopupAsShown(PrivacyHubNotificationController::kCombinedNotificationId); @@ -334,18 +384,28 @@ // Add an app, and verify the notification popup gets shown. LaunchApp(u"rose"); - EXPECT_TRUE(GetSWSwitchNotification()); - EXPECT_TRUE(GetSWSwitchPopupNotification()); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(GetSWSwitchNotification()); + EXPECT_FALSE(GetSWSwitchPopupNotification()); + } else { + EXPECT_TRUE(GetSWSwitchNotification()); + EXPECT_TRUE(GetSWSwitchPopupNotification()); + } } -TEST_F(PrivacyHubMicrophoneControllerTest, RemovingStreamDoesNotShowPopup) { +TEST_P(PrivacyHubMicrophoneControllerTest, RemovingStreamDoesNotShowPopup) { // Launch 2 apps while microphone is muted. MuteMicrophone(); LaunchApp(u"junior"); LaunchApp(u"rose"); - EXPECT_TRUE(GetSWSwitchNotification()); - EXPECT_TRUE(GetSWSwitchPopupNotification()); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(GetSWSwitchNotification()); + EXPECT_FALSE(GetSWSwitchPopupNotification()); + } else { + EXPECT_TRUE(GetSWSwitchNotification()); + EXPECT_TRUE(GetSWSwitchPopupNotification()); + } // Mark the notification as read. MarkPopupAsShown(PrivacyHubNotificationController::kCombinedNotificationId); @@ -356,7 +416,11 @@ // reshown. CloseApp(u"rose"); - EXPECT_TRUE(GetSWSwitchNotification()); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(GetSWSwitchNotification()); + } else { + EXPECT_TRUE(GetSWSwitchNotification()); + } EXPECT_FALSE(GetSWSwitchPopupNotification()); // The notification should be removed if all apps are closed. @@ -365,12 +429,18 @@ EXPECT_FALSE(GetSWSwitchNotification()); } -TEST_F(PrivacyHubMicrophoneControllerTest, SwMuteNotificationActionButton) { +TEST_P(PrivacyHubMicrophoneControllerTest, SwMuteNotificationActionButton) { MuteMicrophone(); LaunchApp(u"junior"); // The mute notification should have an action button. message_center::Notification* notification = GetSWSwitchNotification(); + + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(notification); + return; + } + ASSERT_TRUE(notification); EXPECT_EQ(1u, notification->buttons().size()); @@ -387,12 +457,18 @@ 1); } -TEST_F(PrivacyHubMicrophoneControllerTest, SwMuteNotificationActionBody) { +TEST_P(PrivacyHubMicrophoneControllerTest, SwMuteNotificationActionBody) { MuteMicrophone(); LaunchApp(u"junior"); // The mute notification should have an action button. message_center::Notification* notification = GetSWSwitchNotification(); + + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(notification); + return; + } + ASSERT_TRUE(notification); EXPECT_EQ(1u, notification->buttons().size()); @@ -404,7 +480,7 @@ EXPECT_FALSE(GetSWSwitchNotification()); } -TEST_F(PrivacyHubMicrophoneControllerTest, HwMuteNotificationActionButton) { +TEST_P(PrivacyHubMicrophoneControllerTest, HwMuteNotificationActionButton) { SetMicrophoneMuteSwitchState(/*muted=*/true); LaunchApp(u"junior"); @@ -413,6 +489,12 @@ // should have a "Learn more" button. EXPECT_FALSE(GetSWSwitchNotification()); message_center::Notification* notification = GetHWSwitchNotification(); + + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(notification); + return; + } + ASSERT_TRUE(notification); EXPECT_EQ(1u, notification->buttons().size()); @@ -430,11 +512,17 @@ EXPECT_FALSE(IsAnyMicNotificationVisible()); } -TEST_F(PrivacyHubMicrophoneControllerTest, HwMuteNotificationActionBody) { +TEST_P(PrivacyHubMicrophoneControllerTest, HwMuteNotificationActionBody) { SetMicrophoneMuteSwitchState(/*muted=*/true); LaunchApp(u"junior"); message_center::Notification* notification = GetHWSwitchNotification(); + + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(notification); + return; + } + ASSERT_TRUE(notification); // Check that clicking the body has no effect but notification disappears. @@ -446,7 +534,7 @@ EXPECT_FALSE(GetHWSwitchNotification()); } -TEST_F(PrivacyHubMicrophoneControllerTest, +TEST_P(PrivacyHubMicrophoneControllerTest, TogglingMuteSwitchRemovesNotificationActionButton) { // Mute microphone, and activate an audio input stream. MuteMicrophone(); @@ -454,22 +542,31 @@ // The mute notification should have an action button. message_center::Notification* notification = GetSWSwitchNotification(); - ASSERT_TRUE(notification); - EXPECT_EQ(1u, notification->buttons().size()); - EXPECT_EQ(l10n_util::GetStringUTF16( - IDS_MICROPHONE_MUTED_NOTIFICATION_ACTION_BUTTON), - notification->buttons()[0].title); - // Toggle microphone mute switch and verify that new notification appears with - // a "Learn more" button. + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(notification); + } else { + ASSERT_TRUE(notification); + EXPECT_EQ(1u, notification->buttons().size()); + EXPECT_EQ(l10n_util::GetStringUTF16( + IDS_MICROPHONE_MUTED_NOTIFICATION_ACTION_BUTTON), + notification->buttons()[0].title); + } + // Toggle microphone mute switch and verify that new notification appears + // with a "Learn more" button. SetMicrophoneMuteSwitchState(/*muted=*/true); EXPECT_FALSE(GetSWSwitchNotification()); + notification = GetHWSwitchNotification(); - ASSERT_TRUE(notification); - EXPECT_EQ(1u, notification->buttons().size()); - EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_LEARN_MORE), - notification->buttons()[0].title); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(notification); + } else { + ASSERT_TRUE(notification); + EXPECT_EQ(1u, notification->buttons().size()); + EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_LEARN_MORE), + notification->buttons()[0].title); + } SetMicrophoneMuteSwitchState(/*muted=*/false); ASSERT_FALSE(CrasAudioHandler::Get()->IsInputMuted()); @@ -477,23 +574,35 @@ EXPECT_FALSE(IsAnyMicNotificationVisible()); } -TEST_F(PrivacyHubMicrophoneControllerTest, - TogglingMuteSwitchCreatesHWPopupAndRemovesSWPopup) { +TEST_P(PrivacyHubMicrophoneControllerTest, + TogglingMuteSwitchDoesNotHideNotificationPopup) { // Mute microphone, and activate an audio input stream. MuteMicrophone(); LaunchApp(u"junior"); - // Verify the notification popup is shown. - EXPECT_TRUE(GetSWSwitchNotification()); - EXPECT_TRUE(GetSWSwitchPopupNotification()); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(GetSWSwitchNotification()); + EXPECT_FALSE(GetSWSwitchPopupNotification()); + } else { + // Verify the notification popup is shown. + EXPECT_TRUE(GetSWSwitchNotification()); + EXPECT_TRUE(GetSWSwitchPopupNotification()); + } // Toggle microphone mute switch and verify that toggling mute switch creates // new hardware switch pop up notification and the software switch // notification is removed. SetMicrophoneMuteSwitchState(/*muted=*/true); - EXPECT_TRUE(GetHWSwitchNotification()); - EXPECT_TRUE(GetHWSwitchNotification()); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(GetHWSwitchNotification()); + EXPECT_FALSE(GetHWSwitchNotification()); + } else { + // Verify the notification popup is shown. + EXPECT_TRUE(GetHWSwitchNotification()); + EXPECT_TRUE(GetHWSwitchNotification()); + } + // The software switch notification is instantly hidden. EXPECT_FALSE(GetSWSwitchNotification()); @@ -507,34 +616,50 @@ EXPECT_FALSE(GetHWSwitchNotification()); } -TEST_F(PrivacyHubMicrophoneControllerTest, +TEST_P(PrivacyHubMicrophoneControllerTest, RemovingAllInputStreamsWhileHwSwitchToggled) { SetMicrophoneMuteSwitchState(/*muted=*/true); LaunchApp(u"junior"); - EXPECT_TRUE(GetHWSwitchNotification()); - EXPECT_TRUE(GetHWSwitchPopupNotification()); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(GetHWSwitchNotification()); + EXPECT_FALSE(GetHWSwitchNotification()); + } else { + EXPECT_TRUE(GetHWSwitchNotification()); + EXPECT_TRUE(GetHWSwitchPopupNotification()); + } CloseApp(u"junior"); EXPECT_FALSE(GetHWSwitchNotification()); } -TEST_F(PrivacyHubMicrophoneControllerTest, +TEST_P(PrivacyHubMicrophoneControllerTest, ToggleMicrophoneMuteSwitchWhileInputStreamActive) { // Launch an app using microphone, and toggle mute switch. LaunchApp(u"junior"); SetMicrophoneMuteSwitchState(/*muted=*/true); - // Notification should be shown and also popup. - EXPECT_TRUE(GetHWSwitchNotification()); - EXPECT_TRUE(GetHWSwitchPopupNotification()); + // Notification should be shown and also popup. If Video Conference or Privacy + // Indicators are enabled, this notification is not sent. + if (IsPrivacyIndicatorsEnabled() || IsVideoConferenceEnabled()) { + EXPECT_FALSE(GetHWSwitchNotification()); + EXPECT_FALSE(GetHWSwitchPopupNotification()); + } else { + EXPECT_TRUE(GetHWSwitchNotification()); + EXPECT_TRUE(GetHWSwitchPopupNotification()); + } // Add another audio input stream, and verify the notification popup shows. LaunchApp(u"junior1"); - EXPECT_TRUE(GetHWSwitchNotification()); - EXPECT_TRUE(GetHWSwitchPopupNotification()); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(GetHWSwitchNotification()); + EXPECT_FALSE(GetHWSwitchPopupNotification()); + } else { + EXPECT_TRUE(GetHWSwitchNotification()); + EXPECT_TRUE(GetHWSwitchPopupNotification()); + } // Mark notification as read, and then remove an audio input stream. MarkPopupAsShown(PrivacyHubNotificationController:: @@ -545,17 +670,25 @@ CloseApp(u"junior1"); // Verify that notification popup is not reshown. - EXPECT_TRUE(GetHWSwitchNotification()); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(GetHWSwitchNotification()); + } else { + EXPECT_TRUE(GetHWSwitchNotification()); + } EXPECT_FALSE(GetHWSwitchPopupNotification()); // Adding another stream shows a popup again. LaunchApp(u"rose"); - - EXPECT_TRUE(GetHWSwitchNotification()); - EXPECT_TRUE(GetHWSwitchPopupNotification()); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(GetHWSwitchNotification()); + EXPECT_FALSE(GetHWSwitchPopupNotification()); + } else { + EXPECT_TRUE(GetHWSwitchNotification()); + EXPECT_TRUE(GetHWSwitchPopupNotification()); + } } -TEST_F(PrivacyHubMicrophoneControllerTest, NotificationText) { +TEST_P(PrivacyHubMicrophoneControllerTest, NotificationText) { // No notification initially. EXPECT_FALSE(IsAnyMicNotificationVisible()); @@ -568,51 +701,71 @@ // determined. LaunchApp(absl::nullopt); - EXPECT_TRUE(GetSWSwitchNotification()); - EXPECT_TRUE(GetSWSwitchPopupNotification()); - EXPECT_EQ(l10n_util::GetStringUTF16( - IDS_MICROPHONE_MUTED_BY_SW_SWITCH_NOTIFICATION_TITLE), - GetSWSwitchNotification()->title()); - // The notification body should not contain any app name. - EXPECT_EQ( - l10n_util::GetStringUTF16(IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE), - GetSWSwitchNotification()->message()); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(GetSWSwitchNotification()); + EXPECT_FALSE(GetSWSwitchPopupNotification()); + } else { + EXPECT_TRUE(GetSWSwitchNotification()); + EXPECT_TRUE(GetSWSwitchPopupNotification()); + EXPECT_EQ(l10n_util::GetStringUTF16( + IDS_MICROPHONE_MUTED_BY_SW_SWITCH_NOTIFICATION_TITLE), + GetSWSwitchNotification()->title()); + // The notification body should not contain any app name. + EXPECT_EQ( + l10n_util::GetStringUTF16(IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE), + GetSWSwitchNotification()->message()); + } // Launch an app that's using the mic, the name of the app can be determined. LaunchApp(u"app1"); - EXPECT_TRUE(GetSWSwitchNotification()); - EXPECT_TRUE(GetSWSwitchPopupNotification()); - // The notification body should contain name of the app. - EXPECT_EQ( - l10n_util::GetStringFUTF16( - IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE_WITH_ONE_APP_NAME, u"app1"), - GetSWSwitchNotification()->message()); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(GetSWSwitchNotification()); + EXPECT_FALSE(GetSWSwitchPopupNotification()); + } else { + EXPECT_TRUE(GetSWSwitchNotification()); + EXPECT_TRUE(GetSWSwitchPopupNotification()); + // The notification body should contain name of the app. + EXPECT_EQ(l10n_util::GetStringFUTF16( + IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE_WITH_ONE_APP_NAME, + u"app1"), + GetSWSwitchNotification()->message()); + } // Launch another app that's using the mic, the name of the app can be // determined. LaunchApp(u"app2"); - EXPECT_TRUE(GetSWSwitchNotification()); - EXPECT_TRUE(GetSWSwitchPopupNotification()); - // The notification body should contain the two available app names in the - // order of most recently launched. - EXPECT_EQ(l10n_util::GetStringFUTF16( - IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE_WITH_TWO_APP_NAMES, - u"app2", u"app1"), - GetSWSwitchNotification()->message()); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(GetSWSwitchNotification()); + EXPECT_FALSE(GetSWSwitchPopupNotification()); + } else { + EXPECT_TRUE(GetSWSwitchNotification()); + EXPECT_TRUE(GetSWSwitchPopupNotification()); + // The notification body should contain the two available app names in the + // order of most recently launched. + EXPECT_EQ(l10n_util::GetStringFUTF16( + IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE_WITH_TWO_APP_NAMES, + u"app2", u"app1"), + GetSWSwitchNotification()->message()); + } // Launch yet another app that's using the mic, the name of the app can be // determined. LaunchApp(u"app3"); - EXPECT_TRUE(GetSWSwitchNotification()); - EXPECT_TRUE(GetSWSwitchPopupNotification()); - // As more that two apps are attempting to use the microphone, we fall back to - // displaying the generic message in the notification. - EXPECT_EQ( - l10n_util::GetStringUTF16(IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE), - GetSWSwitchNotification()->message()); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(GetSWSwitchNotification()); + EXPECT_FALSE(GetSWSwitchPopupNotification()); + } else { + EXPECT_TRUE(GetSWSwitchNotification()); + EXPECT_TRUE(GetSWSwitchPopupNotification()); + // As more that two apps are attempting to use the microphone, we fall back + // to displaying the generic message in the notification. + EXPECT_EQ( + l10n_util::GetStringUTF16(IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE), + GetSWSwitchNotification()->message()); + } EXPECT_FALSE( ui::MicrophoneMuteSwitchMonitor::Get()->microphone_mute_switch_on()); @@ -620,18 +773,22 @@ // Toggle the hw switch. SetMicrophoneMuteSwitchState(/*muted=*/true); - - EXPECT_TRUE(GetHWSwitchNotification()); - EXPECT_TRUE(GetHWSwitchPopupNotification()); - EXPECT_EQ(l10n_util::GetStringUTF16( - IDS_MICROPHONE_MUTED_BY_HW_SWITCH_NOTIFICATION_TITLE), - GetHWSwitchNotification()->title()); - EXPECT_EQ( - l10n_util::GetStringUTF16(IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE), - GetHWSwitchNotification()->message()); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(GetHWSwitchNotification()); + EXPECT_FALSE(GetHWSwitchPopupNotification()); + } else { + EXPECT_TRUE(GetHWSwitchNotification()); + EXPECT_TRUE(GetHWSwitchPopupNotification()); + EXPECT_EQ(l10n_util::GetStringUTF16( + IDS_MICROPHONE_MUTED_BY_HW_SWITCH_NOTIFICATION_TITLE), + GetHWSwitchNotification()->title()); + EXPECT_EQ( + l10n_util::GetStringUTF16(IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE), + GetHWSwitchNotification()->message()); + } } -TEST_F(PrivacyHubMicrophoneControllerTest, NotificationUpdatedWhenAppClosed) { +TEST_P(PrivacyHubMicrophoneControllerTest, NotificationUpdatedWhenAppClosed) { // No notification initially. EXPECT_FALSE(IsAnyMicNotificationVisible()); @@ -646,11 +803,15 @@ LaunchApp(app1); message_center::Notification* notification_ptr = GetSWSwitchNotification(); - ASSERT_TRUE(notification_ptr); - EXPECT_EQ( - l10n_util::GetStringFUTF16( - IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE_WITH_ONE_APP_NAME, app1), - notification_ptr->message()); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(notification_ptr); + } else { + ASSERT_TRUE(notification_ptr); + EXPECT_EQ( + l10n_util::GetStringFUTF16( + IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE_WITH_ONE_APP_NAME, app1), + notification_ptr->message()); + } // Launch app2 that's also accessing the mic, the microphone mute notification // should be displayed again with both the application names in the @@ -659,22 +820,30 @@ LaunchApp(app2); notification_ptr = GetSWSwitchNotification(); - ASSERT_TRUE(notification_ptr); - EXPECT_EQ(l10n_util::GetStringFUTF16( - IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE_WITH_TWO_APP_NAMES, - app2, app1), - notification_ptr->message()); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(notification_ptr); + } else { + ASSERT_TRUE(notification_ptr); + EXPECT_EQ(l10n_util::GetStringFUTF16( + IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE_WITH_TWO_APP_NAMES, + app2, app1), + notification_ptr->message()); + } // Close one of the applications. The notification message should be updated // to only contain the name of the other application. CloseApp(app1); notification_ptr = GetSWSwitchNotification(); - ASSERT_TRUE(notification_ptr); - EXPECT_EQ( - l10n_util::GetStringFUTF16( - IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE_WITH_ONE_APP_NAME, app2), - notification_ptr->message()); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(notification_ptr); + } else { + ASSERT_TRUE(notification_ptr); + EXPECT_EQ( + l10n_util::GetStringFUTF16( + IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE_WITH_ONE_APP_NAME, app2), + notification_ptr->message()); + } // Test the HW switch notification case. // HW switch is turned ON. @@ -683,22 +852,30 @@ LaunchApp(app1); notification_ptr = GetHWSwitchNotification(); - ASSERT_TRUE(notification_ptr); - EXPECT_EQ(l10n_util::GetStringFUTF16( - IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE_WITH_TWO_APP_NAMES, - app1, app2), - notification_ptr->message()); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(notification_ptr); + } else { + ASSERT_TRUE(notification_ptr); + EXPECT_EQ(l10n_util::GetStringFUTF16( + IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE_WITH_TWO_APP_NAMES, + app1, app2), + notification_ptr->message()); + } // Closing one of the applications should remove the name of that application // from the hw switch notification message. CloseApp(app2); notification_ptr = GetHWSwitchNotification(); - ASSERT_TRUE(notification_ptr); - EXPECT_EQ( - l10n_util::GetStringFUTF16( - IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE_WITH_ONE_APP_NAME, app1), - notification_ptr->message()); + if (IsVideoConferenceEnabled()) { + EXPECT_FALSE(notification_ptr); + } else { + ASSERT_TRUE(notification_ptr); + EXPECT_EQ( + l10n_util::GetStringFUTF16( + IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE_WITH_ONE_APP_NAME, app1), + notification_ptr->message()); + } } } // namespace ash
diff --git a/ash/system/privacy_hub/privacy_hub_notification_controller.cc b/ash/system/privacy_hub/privacy_hub_notification_controller.cc index 540f67a..aef3655 100644 --- a/ash/system/privacy_hub/privacy_hub_notification_controller.cc +++ b/ash/system/privacy_hub/privacy_hub_notification_controller.cc
@@ -4,6 +4,7 @@ #include "ash/system/privacy_hub/privacy_hub_notification_controller.h" +#include "ash/constants/ash_features.h" #include "ash/public/cpp/new_window_delegate.h" #include "ash/public/cpp/sensor_disabled_notification_delegate.h" #include "ash/public/cpp/system_tray_client.h"
diff --git a/ash/system/toast/toast_manager_impl.cc b/ash/system/toast/toast_manager_impl.cc index 0c0f3cd..210bc03 100644 --- a/ash/system/toast/toast_manager_impl.cc +++ b/ash/system/toast/toast_manager_impl.cc
@@ -277,10 +277,10 @@ DCHECK(current_toast_data_); new_overlay = std::make_unique<ToastOverlay>( this, current_toast_data_->text, current_toast_data_->dismiss_text, - current_toast_data_->duration, + *current_toast_data_->leading_icon, current_toast_data_->duration, current_toast_data_->visible_on_lock_screen && locked_, - current_toast_data_->is_managed, current_toast_data_->persist_on_hover, - root_window, current_toast_data_->dismiss_callback); + current_toast_data_->persist_on_hover, root_window, + current_toast_data_->dismiss_callback); new_overlay->Show(true); // We only want to record this value when the first instance of the toast is
diff --git a/ash/system/toast/toast_manager_unittest.cc b/ash/system/toast/toast_manager_unittest.cc index d498722..8e2eacd 100644 --- a/ash/system/toast/toast_manager_unittest.cc +++ b/ash/system/toast/toast_manager_unittest.cc
@@ -9,6 +9,7 @@ #include "ash/constants/notifier_catalogs.h" #include "ash/public/cpp/shelf_config.h" #include "ash/public/cpp/system/toast_data.h" +#include "ash/resources/vector_icons/vector_icons.h" #include "ash/root_window_controller.h" #include "ash/screen_util.h" #include "ash/session/session_controller_impl.h" @@ -16,6 +17,7 @@ #include "ash/shelf/shelf.h" #include "ash/shell.h" #include "ash/strings/grit/ash_strings.h" +#include "ash/style/system_toast_style.h" #include "ash/test/ash_test_base.h" #include "ash/wm/tablet_mode/tablet_mode_controller.h" #include "ash/wm/work_area_insets.h" @@ -34,6 +36,7 @@ #include "ui/compositor/test/layer_animation_stopped_waiter.h" #include "ui/compositor/test/test_utils.h" #include "ui/display/manager/display_manager.h" +#include "ui/gfx/vector_icon_types.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/widget/widget.h" @@ -139,6 +142,13 @@ return overlay ? overlay->dismiss_text_ : std::u16string(); } + bool CurrentToastHasLeadingIcon() { + ToastOverlay* overlay = + GetCurrentOverlay(Shell::GetRootWindowForNewWindows()); + return overlay && overlay->overlay_view_ && + !overlay->overlay_view_->leading_icon_->is_empty(); + } + void ClickDismissButton( aura::Window* root_window = Shell::GetRootWindowForNewWindows()) { views::LabelButton* dismiss_button = GetDismissButton(root_window); @@ -172,6 +182,14 @@ return id; } + void ShowToastWithLeadingIcon(const gfx::VectorIcon& icon) { + manager()->Show(ToastData( + "id", ToastCatalogName::kToastManagerUnittest, u"text", + ToastData::kDefaultToastDuration, /*visible_on_lock_screen=*/false, + /*has_dismiss_button=*/false, /*custom_dismiss_text=*/u"", + /*dismiss_callback=*/base::DoNothing(), icon)); + } + void CancelToast(const std::string& id) { manager()->Cancel(id); } void ReplaceToast(const std::string& id, @@ -1152,4 +1170,12 @@ EXPECT_FALSE(GetCurrentOverlay(root_window)); } +// Tests that toasts add a leading icon when one is provided. +TEST_F(ToastManagerImplTest, ToastWithLeadingIcon) { + ShowToastWithLeadingIcon(gfx::kNoneIcon); + EXPECT_FALSE(CurrentToastHasLeadingIcon()); + ShowToastWithLeadingIcon(kSystemMenuBusinessIcon); + EXPECT_TRUE(CurrentToastHasLeadingIcon()); +} + } // namespace ash
diff --git a/ash/system/toast/toast_overlay.cc b/ash/system/toast/toast_overlay.cc index 80a6f10..8f6ce7f 100644 --- a/ash/system/toast/toast_overlay.cc +++ b/ash/system/toast/toast_overlay.cc
@@ -143,9 +143,9 @@ ToastOverlay::ToastOverlay(Delegate* delegate, const std::u16string& text, const std::u16string& dismiss_text, + const gfx::VectorIcon& leading_icon, base::TimeDelta duration, bool show_on_lock_screen, - bool is_managed, bool persist_on_hover, aura::Window* root_window, base::RepeatingClosure dismiss_callback) @@ -158,7 +158,7 @@ base::Unretained(this)), text, dismiss_text, - is_managed)), + leading_icon)), display_observer_(std::make_unique<ToastDisplayObserver>(this)), root_window_(root_window), dismiss_callback_(std::move(dismiss_callback)),
diff --git a/ash/system/toast/toast_overlay.h b/ash/system/toast/toast_overlay.h index a23f22b..8d5fa3c 100644 --- a/ash/system/toast/toast_overlay.h +++ b/ash/system/toast/toast_overlay.h
@@ -15,6 +15,7 @@ #include "base/timer/timer.h" #include "ui/compositor/layer_animation_observer.h" #include "ui/gfx/geometry/size.h" +#include "ui/gfx/paint_vector_icon.h" namespace aura { class Window; @@ -51,16 +52,18 @@ static constexpr int kOffset = 8; // Creates the Toast overlay UI. `text` is the message to be shown, and - // `dismiss_text` is the message for the button to dismiss the toast message. - // The dismiss button will only be displayed if `dismiss_text` is not empty. - // `dismiss_callback` will be called when the button is pressed. If - // `is_managed` is true, a managed icon will be added to the toast. + // `dismiss_text` is the dismiss button's text. The dismiss button will only + // be displayed if `dismiss_text` is not empty. `dismiss_callback` will be + // called when the dismiss button is pressed. An icon will show on the left + // side if `leading_icon` is not empty. + // To test different Toast UI variations, enable debug shortcuts by building + // with flag `--ash-debug-shortcuts` and use command "Shift + Ctrl + Alt + O". ToastOverlay(Delegate* delegate, const std::u16string& text, const std::u16string& dismiss_text, + const gfx::VectorIcon& leading_icon, base::TimeDelta duration, bool show_on_lock_screen, - bool is_managed, bool persist_on_hover, aura::Window* root_window, base::RepeatingClosure dismiss_callback);
diff --git a/ash/wallpaper/online_wallpaper_variant_info_fetcher.cc b/ash/wallpaper/online_wallpaper_variant_info_fetcher.cc index f3fd575..0c102bf2 100644 --- a/ash/wallpaper/online_wallpaper_variant_info_fetcher.cc +++ b/ash/wallpaper/online_wallpaper_variant_info_fetcher.cc
@@ -9,6 +9,7 @@ #include "ash/public/cpp/wallpaper/online_wallpaper_variant.h" #include "ash/public/cpp/wallpaper/wallpaper_controller_client.h" #include "ash/public/cpp/wallpaper/wallpaper_info.h" +#include "ash/wallpaper/wallpaper_utils/wallpaper_online_variant_utils.h" #include "ash/webui/personalization_app/proto/backdrop_wallpaper.pb.h" #include "base/functional/bind.h" #include "base/functional/callback.h" @@ -20,77 +21,6 @@ namespace ash { namespace { -// Note: This method should only be called by -// |IsSuitableOnlineWallpaperVariant()| and |FirstValidVariant()|. -// -// Not all wallpapers have variants that map 1:1 to the checkpoints. D/L -// wallpapers are an example. In order to gracefully support these wallpapers, -// this method accepts a boolean |match_subtype| so that if true, variants with -// |backdrop::Image_ImageType_IMAGE_TYPE_LIGHT_MODE| is also considered valid -// for |ScheduleCheckpoint::kMorning| and |ScheduleCheckpoint::kLateAfternoon|. -bool IsSuitableOnlineWallpaperVariantInternal( - const OnlineWallpaperVariant& variant, - ScheduleCheckpoint checkpoint, - bool match_subtype) { - if (variant.type == backdrop::Image_ImageType_IMAGE_TYPE_UNKNOWN) { - return true; - } - switch (checkpoint) { - case ScheduleCheckpoint::kSunrise: - //`kDisabled` is equivalent to Light mode. - case ScheduleCheckpoint::kDisabled: - return variant.type == backdrop::Image_ImageType_IMAGE_TYPE_LIGHT_MODE; - case ScheduleCheckpoint::kMorning: - return variant.type == - backdrop::Image_ImageType_IMAGE_TYPE_MORNING_MODE || - (match_subtype && - variant.type == backdrop::Image_ImageType_IMAGE_TYPE_LIGHT_MODE); - case ScheduleCheckpoint::kLateAfternoon: - return variant.type == - backdrop::Image_ImageType_IMAGE_TYPE_LATE_AFTERNOON_MODE || - (match_subtype && - variant.type == backdrop::Image_ImageType_IMAGE_TYPE_LIGHT_MODE); - case ScheduleCheckpoint::kSunset: - //`kEnabled` is equivalent to Dark mode. - case ScheduleCheckpoint::kEnabled: - return variant.type == backdrop::Image_ImageType_IMAGE_TYPE_DARK_MODE; - } -} - -// Checks if the given |variant| is suitable for the current system's -// checkpoint. -bool IsSuitableOnlineWallpaperVariant(const OnlineWallpaperVariant& variant, - ScheduleCheckpoint checkpoint) { - return IsSuitableOnlineWallpaperVariantInternal(variant, checkpoint, - /*match_subtype=*/true); -} - -// Returns a pointer to the first matching variant in |variants| if one -// exists. -const OnlineWallpaperVariant* FirstValidVariant( - const std::vector<OnlineWallpaperVariant>& variants, - ScheduleCheckpoint checkpoint) { - // Attempt to find the exact 1:1 match for |variant| and |checkpoint|. - auto iter = - base::ranges::find_if(variants, [checkpoint](const auto& variant) { - return IsSuitableOnlineWallpaperVariantInternal( - variant, checkpoint, - /*match_subtype=*/false); - }); - if (iter != variants.end()) { - return &(*iter); - } - // Attempt to find a subtype |variant| for |checkpoint|. - iter = base::ranges::find_if(variants, [checkpoint](const auto& variant) { - return IsSuitableOnlineWallpaperVariantInternal(variant, checkpoint, - /*match_subtype=*/true); - }); - if (iter != variants.end()) { - return &(*iter); - } - return nullptr; -} - // The filtered results from a set of backdrop::Images for a given |asset_id| // and |mode| value. class VariantMatches {
diff --git a/ash/wallpaper/wallpaper_utils/wallpaper_online_variant_utils.cc b/ash/wallpaper/wallpaper_utils/wallpaper_online_variant_utils.cc new file mode 100644 index 0000000..0a9f099 --- /dev/null +++ b/ash/wallpaper/wallpaper_utils/wallpaper_online_variant_utils.cc
@@ -0,0 +1,79 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/wallpaper/wallpaper_utils/wallpaper_online_variant_utils.h" + +#include "base/ranges/algorithm.h" + +namespace ash { + +namespace { + +// Not all wallpapers have variants that map 1:1 to the checkpoints. D/L +// wallpapers are an example. In order to gracefully support these wallpapers, +// this method accepts a boolean |match_subtype| so that if true, variants with +// |backdrop::Image_ImageType_IMAGE_TYPE_LIGHT_MODE| is also considered valid +// for |ScheduleCheckpoint::kMorning| and |ScheduleCheckpoint::kLateAfternoon|. +bool IsSuitableOnlineWallpaperVariantInternal( + const OnlineWallpaperVariant& variant, + ScheduleCheckpoint checkpoint, + bool match_subtype) { + if (variant.type == backdrop::Image_ImageType_IMAGE_TYPE_UNKNOWN) { + return true; + } + switch (checkpoint) { + case ScheduleCheckpoint::kSunrise: + // `kDisabled` is equivalent to Light mode. + case ScheduleCheckpoint::kDisabled: + return variant.type == backdrop::Image_ImageType_IMAGE_TYPE_LIGHT_MODE; + case ScheduleCheckpoint::kMorning: + return variant.type == + backdrop::Image_ImageType_IMAGE_TYPE_MORNING_MODE || + (match_subtype && + variant.type == backdrop::Image_ImageType_IMAGE_TYPE_LIGHT_MODE); + case ScheduleCheckpoint::kLateAfternoon: + return variant.type == + backdrop::Image_ImageType_IMAGE_TYPE_LATE_AFTERNOON_MODE || + (match_subtype && + variant.type == backdrop::Image_ImageType_IMAGE_TYPE_LIGHT_MODE); + case ScheduleCheckpoint::kSunset: + // `kEnabled` is equivalent to Dark mode. + case ScheduleCheckpoint::kEnabled: + return variant.type == backdrop::Image_ImageType_IMAGE_TYPE_DARK_MODE; + } +} + +} // namespace + +bool IsSuitableOnlineWallpaperVariant(const OnlineWallpaperVariant& variant, + ScheduleCheckpoint checkpoint) { + return IsSuitableOnlineWallpaperVariantInternal(variant, checkpoint, + /*match_subtype=*/true); +} + +const OnlineWallpaperVariant* FirstValidVariant( + const std::vector<OnlineWallpaperVariant>& variants, + ScheduleCheckpoint checkpoint) { + // Attempt to find the exact 1:1 match for |variant| and |checkpoint|. + auto iter = + base::ranges::find_if(variants, [checkpoint](const auto& variant) { + return IsSuitableOnlineWallpaperVariantInternal( + variant, checkpoint, + /*match_subtype=*/false); + }); + if (iter != variants.end()) { + return &(*iter); + } + // Attempt to find a subtype |variant| for |checkpoint|. + iter = base::ranges::find_if(variants, [checkpoint](const auto& variant) { + return IsSuitableOnlineWallpaperVariantInternal(variant, checkpoint, + /*match_subtype=*/true); + }); + if (iter != variants.end()) { + return &(*iter); + } + return nullptr; +} + +} // namespace ash
diff --git a/ash/wallpaper/wallpaper_utils/wallpaper_online_variant_utils.h b/ash/wallpaper/wallpaper_utils/wallpaper_online_variant_utils.h new file mode 100644 index 0000000..0532dc0 --- /dev/null +++ b/ash/wallpaper/wallpaper_utils/wallpaper_online_variant_utils.h
@@ -0,0 +1,28 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_WALLPAPER_WALLPAPER_UTILS_WALLPAPER_ONLINE_VARIANT_UTILS_H_ +#define ASH_WALLPAPER_WALLPAPER_UTILS_WALLPAPER_ONLINE_VARIANT_UTILS_H_ + +#include "ash/ash_export.h" +#include "ash/public/cpp/schedule_enums.h" +#include "ash/public/cpp/wallpaper/online_wallpaper_variant.h" + +namespace ash { + +// Checks if the given |variant| is suitable for the current system's +// checkpoint. +ASH_EXPORT bool IsSuitableOnlineWallpaperVariant( + const OnlineWallpaperVariant& variant, + ScheduleCheckpoint checkpoint); + +// Returns a pointer to the first matching variant in |variants| if one +// exists. +ASH_EXPORT const OnlineWallpaperVariant* FirstValidVariant( + const std::vector<OnlineWallpaperVariant>& variants, + ScheduleCheckpoint checkpoint); + +} // namespace ash + +#endif // ASH_WALLPAPER_WALLPAPER_UTILS_WALLPAPER_ONLINE_VARIANT_UTILS_H_
diff --git a/ash/wallpaper/wallpaper_utils/wallpaper_online_variant_utils_unittest.cc b/ash/wallpaper/wallpaper_utils/wallpaper_online_variant_utils_unittest.cc new file mode 100644 index 0000000..3715e4f1 --- /dev/null +++ b/ash/wallpaper/wallpaper_utils/wallpaper_online_variant_utils_unittest.cc
@@ -0,0 +1,82 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/wallpaper/wallpaper_utils/wallpaper_online_variant_utils.h" + +#include <vector> + +#include "ash/public/cpp/schedule_enums.h" +#include "ash/public/cpp/wallpaper/online_wallpaper_variant.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace ash { + +namespace { + +TEST(WallpaperOnlineVariantUtilsTest, FirstValidVariant) { + const OnlineWallpaperVariant dark_variant = + OnlineWallpaperVariant(1, GURL("http://example.com/1"), + backdrop::Image_ImageType_IMAGE_TYPE_DARK_MODE); + const OnlineWallpaperVariant light_variant = + OnlineWallpaperVariant(2, GURL("http://example.com/2"), + backdrop::Image_ImageType_IMAGE_TYPE_LIGHT_MODE); + const OnlineWallpaperVariant morning_variant = + OnlineWallpaperVariant(3, GURL("http://example.com/3"), + backdrop::Image_ImageType_IMAGE_TYPE_MORNING_MODE); + const OnlineWallpaperVariant late_afternoon_variant = OnlineWallpaperVariant( + 4, GURL("http://example.com/4"), + backdrop::Image_ImageType_IMAGE_TYPE_LATE_AFTERNOON_MODE); + + std::vector<OnlineWallpaperVariant> variants = { + light_variant, dark_variant, morning_variant, late_afternoon_variant}; + EXPECT_EQ(morning_variant, + *FirstValidVariant(variants, ScheduleCheckpoint::kMorning)); +} + +TEST(WallpaperOnlineVariantUtilsTest, IsSuitableOnlineWallpaperVariant) { + const std::map<ScheduleCheckpoint, backdrop::Image::ImageType> + expected_mapping = { + {ScheduleCheckpoint::kSunrise, + backdrop::Image_ImageType_IMAGE_TYPE_LIGHT_MODE}, + {ScheduleCheckpoint::kMorning, + backdrop::Image_ImageType_IMAGE_TYPE_MORNING_MODE}, + {ScheduleCheckpoint::kLateAfternoon, + backdrop::Image_ImageType_IMAGE_TYPE_LATE_AFTERNOON_MODE}, + {ScheduleCheckpoint::kSunset, + backdrop::Image_ImageType_IMAGE_TYPE_DARK_MODE}}; + + for (const auto& mapping_pair : expected_mapping) { + const OnlineWallpaperVariant variant = OnlineWallpaperVariant( + 1, GURL("http://example.com"), mapping_pair.second); + EXPECT_TRUE(IsSuitableOnlineWallpaperVariant(variant, mapping_pair.first)); + } +} + +TEST(WallpaperOnlineVariantUtilsTest, + IsSuitableOnlineWallpaperVariant_MatchSubType) { + const std::map<ScheduleCheckpoint, backdrop::Image::ImageType> + expected_mapping = {{ScheduleCheckpoint::kSunrise, + backdrop::Image_ImageType_IMAGE_TYPE_LIGHT_MODE}, + {ScheduleCheckpoint::kMorning, + backdrop::Image_ImageType_IMAGE_TYPE_LIGHT_MODE}, + {ScheduleCheckpoint::kLateAfternoon, + backdrop::Image_ImageType_IMAGE_TYPE_LIGHT_MODE}, + {ScheduleCheckpoint::kSunset, + backdrop::Image_ImageType_IMAGE_TYPE_DARK_MODE}, + {ScheduleCheckpoint::kDisabled, + backdrop::Image_ImageType_IMAGE_TYPE_LIGHT_MODE}, + {ScheduleCheckpoint::kEnabled, + backdrop::Image_ImageType_IMAGE_TYPE_DARK_MODE}}; + + for (const auto& mapping_pair : expected_mapping) { + const OnlineWallpaperVariant variant = OnlineWallpaperVariant( + 1, GURL("http://example.com"), mapping_pair.second); + EXPECT_TRUE(IsSuitableOnlineWallpaperVariant(variant, mapping_pair.first)); + } +} + +} // namespace + +} // namespace ash
diff --git a/ash/webui/personalization_app/mojom/personalization_app.mojom b/ash/webui/personalization_app/mojom/personalization_app.mojom index 84aa31f..105ccab 100644 --- a/ash/webui/personalization_app/mojom/personalization_app.mojom +++ b/ash/webui/personalization_app/mojom/personalization_app.mojom
@@ -302,7 +302,7 @@ SetWallpaperObserver(pending_remote<WallpaperObserver> observer); // Sets the given backdrop wallpaper as the user's background. - SelectWallpaper(uint64 image_asset_id, bool preview_mode) => + SelectWallpaper(uint64 unit_id, bool preview_mode) => (bool success); // Select the device default image as the user's background. Not all devices
diff --git a/ash/webui/personalization_app/resources/BUILD.gn b/ash/webui/personalization_app/resources/BUILD.gn index 512ddeb..72a14b0 100644 --- a/ash/webui/personalization_app/resources/BUILD.gn +++ b/ash/webui/personalization_app/resources/BUILD.gn
@@ -122,6 +122,7 @@ "js/user/user_reducers.ts", "js/user/user_selectors.ts", "js/user/user_state.ts", + "js/user/utils.ts", "js/user/webcam_utils_proxy.ts", "js/utils.ts", "js/wallpaper/constants.ts",
diff --git a/ash/webui/personalization_app/resources/js/personalization_app.ts b/ash/webui/personalization_app/resources/js/personalization_app.ts index 526c2975..e6978057e 100644 --- a/ash/webui/personalization_app/resources/js/personalization_app.ts +++ b/ash/webui/personalization_app/resources/js/personalization_app.ts
@@ -99,7 +99,7 @@ export {UserPreview} from './user/user_preview_element.js'; export {UserSubpage} from './user/user_subpage_element.js'; export {GetUserMediaProxy, getWebcamUtils, setWebcamUtilsForTesting} from './user/webcam_utils_proxy.js'; -export {getCountText, getNumberOfGridItemsPerRow, getSanitizedDefaultImageUrl, staticColorIds} from './utils.js'; +export {getCountText, getNumberOfGridItemsPerRow, staticColorIds} from './utils.js'; export {DefaultImageSymbol, DisplayableImage, kDefaultImageSymbol, kMaximumLocalImagePreviews} from './wallpaper/constants.js'; export {GooglePhotosAlbums} from './wallpaper/google_photos_albums_element.js'; export {GooglePhotosCollection, GooglePhotosTab} from './wallpaper/google_photos_collection_element.js';
diff --git a/ash/webui/personalization_app/resources/js/user/avatar_list_element.html b/ash/webui/personalization_app/resources/js/user/avatar_list_element.html index ddd2094..ae46b223 100644 --- a/ash/webui/personalization_app/resources/js/user/avatar_list_element.html +++ b/ash/webui/personalization_app/resources/js/user/avatar_list_element.html
@@ -116,7 +116,7 @@ <paper-ripple class="circle"></paper-ripple> <div class="image-border-container"> <img class$="[[getImageClassForOption_(item)]]" - src="[[item.imgSrc]]" + src$="[[getAvatarUrl_(item.imgSrc)]]" style$="[[getImgBackgroundStyle_(item.imgSrc, item.defaultImageIndex)]]" on-error="onImgError_"> <iron-icon icon$="[[item.icon]]"></iron-icon>
diff --git a/ash/webui/personalization_app/resources/js/user/avatar_list_element.ts b/ash/webui/personalization_app/resources/js/user/avatar_list_element.ts index 3ff2ed4..b922580 100644 --- a/ash/webui/personalization_app/resources/js/user/avatar_list_element.ts +++ b/ash/webui/personalization_app/resources/js/user/avatar_list_element.ts
@@ -13,13 +13,14 @@ import {DefaultUserImage, UserImage} from '../../personalization_app.mojom-webui.js'; import {setErrorAction} from '../personalization_actions.js'; import {WithPersonalizationStore} from '../personalization_store.js'; -import {decodeString16, getCheckmarkIcon, getSanitizedDefaultImageUrl, isNonEmptyArray, isSelectionEvent} from '../utils.js'; +import {decodeString16, getCheckmarkIcon, isNonEmptyArray, isSelectionEvent} from '../utils.js'; import {AvatarCamera, AvatarCameraMode} from './avatar_camera_element.js'; import {getTemplate} from './avatar_list_element.html.js'; import {fetchDefaultUserImages} from './user_controller.js'; import {getUserProvider} from './user_interface_provider.js'; import {selectLastExternalUserImageUrl} from './user_selectors.js'; +import {getAvatarUrl} from './utils.js'; export interface AvatarList { $: {avatarCamera: AvatarCamera}; @@ -194,7 +195,7 @@ options.push({ id: `defaultUserImage-${defaultImage.index}`, class: 'image-container', - imgSrc: getSanitizedDefaultImageUrl(defaultImage.url).url, + imgSrc: defaultImage.url.url, icon: getCheckmarkIcon(), title: decodeString16(defaultImage.title), defaultImageIndex: defaultImage.index, @@ -408,11 +409,17 @@ // If the image is a default avatar loaded from gstatic resources, // return a static encoded background image. if (defaultImageIndex) { - return `background-image: url('` + url + `&staticEncode=true')`; + assert( + !url.startsWith('chrome://image/'), + 'The URL shouldn\'t be sanitized'); + return `background-image: url('${getAvatarUrl(url)}&staticEncode=true')`; } - return ''; } + + private getAvatarUrl_(url: string): string { + return getAvatarUrl(url); + } } customElements.define(AvatarList.is, AvatarList);
diff --git a/ash/webui/personalization_app/resources/js/user/user_preview_element.html b/ash/webui/personalization_app/resources/js/user/user_preview_element.html index aba3bce..1dc4c87 100644 --- a/ash/webui/personalization_app/resources/js/user/user_preview_element.html +++ b/ash/webui/personalization_app/resources/js/user/user_preview_element.html
@@ -207,7 +207,7 @@ aria-label$="[[getImageContainerAriaLabel_(path, imageIsEnterpriseManaged_)]]"> <template is="dom-if" if="[[imageUrl_]]"> <template is="dom-if" if="[[imageIsEnterpriseManaged_]]"> - <img id="avatar" class="managed" src$="[[imageUrl_.url]]" + <img id="avatar" class="managed" src$="[[getAvatarUrl_(imageUrl_.url)]]" alt="$i18n{managedSetting}" title="$i18n{managedSetting}"> <div id="enterpriseIconContainer"> <template is="dom-if" if="[[!isPersonalizationJellyEnabled_]]"> @@ -244,7 +244,11 @@ </div> <paper-ripple class="circle"></paper-ripple> <div id="imageBorderContainer"> - <img tabindex="0" id="avatar" class="clickable" src$="[[imageUrl_.url]]" + <img + tabindex="0" + id="avatar" + class="clickable" + src$="[[getAvatarUrl_(imageUrl_.url)]]" alt$="[[getImageAlt_(image_)]]" role="button" on-click="onClickUserSubpageLink_" @@ -255,7 +259,7 @@ </template> <template is="dom-if" if="[[shouldShowSubpageView_(path, imageIsEnterpriseManaged_)]]"> - <img id="avatar2" src$="[[imageUrl_.url]]" + <img id="avatar2" src$="[[getAvatarUrl_(imageUrl_.url)]]" alt$="[[getImageAlt_(image_)]]"> </template> </template>
diff --git a/ash/webui/personalization_app/resources/js/user/user_preview_element.ts b/ash/webui/personalization_app/resources/js/user/user_preview_element.ts index 5af883c..b9dd077 100644 --- a/ash/webui/personalization_app/resources/js/user/user_preview_element.ts +++ b/ash/webui/personalization_app/resources/js/user/user_preview_element.ts
@@ -26,6 +26,7 @@ import {getUserProvider} from './user_interface_provider.js'; import {getTemplate} from './user_preview_element.html.js'; import {selectUserImageUrl} from './user_selectors.js'; +import {getAvatarUrl} from './utils.js'; class AvatarChangedEvent extends CustomEvent<{text: string}> { constructor() { @@ -158,13 +159,19 @@ * images . Static image loads faster and will provide a smooth experience * when the animated image complete loading. */ - private getImgBackgroundStyle_(url: Url|null): string { + private getImgBackgroundStyle_(url: string|null): string { // Only add background image for default user images. - if (!this.image_ || this.image_.invalidImage || !this.image_.defaultImage) { + if (!this.image_ || this.image_.invalidImage || !this.image_.defaultImage || + !url) { return ''; } + assert( + !url.startsWith('chrome://image/'), 'The url should not be sanitized'); + return `background-image: url('${getAvatarUrl(url)}&staticEncode=true')`; + } - return `background-image: url('` + url + `&staticEncode=true')`; + private getAvatarUrl_(url: string): string { + return getAvatarUrl(url); } private shouldShowDeprecatedImageSourceInfo_(image: UserImage|null): boolean {
diff --git a/ash/webui/personalization_app/resources/js/user/user_selectors.ts b/ash/webui/personalization_app/resources/js/user/user_selectors.ts index 97638c66..c70553d 100644 --- a/ash/webui/personalization_app/resources/js/user/user_selectors.ts +++ b/ash/webui/personalization_app/resources/js/user/user_selectors.ts
@@ -8,7 +8,8 @@ import {UserImage} from '../../personalization_app.mojom-webui.js'; import {PersonalizationState} from '../personalization_state.js'; -import {getSanitizedDefaultImageUrl} from '../utils.js'; + +import {AVATAR_PLACEHOLDER_URL} from './utils.js'; /** * @fileoverview Utility functions to derive a user image URL to display from @@ -59,14 +60,19 @@ } /** + * The placeholder url is used as the user image url for invalid or unknown + * urls. + */ +const placeHolderUrl = { + url: AVATAR_PLACEHOLDER_URL, +}; + +/** * Derive a user image |Url| from |PersonalizationState|. Return a |Url| rather * than |string| to avoid copies on potentially large data URLs. */ export function selectUserImageUrl(state: PersonalizationState): Url|null { const userImage = state.user.image; - const placeHolderUrl = { - url: 'chrome://theme/IDR_PROFILE_AVATAR_PLACEHOLDER_LARGE', - }; if (!userImage) { return null; @@ -81,7 +87,7 @@ case 'invalidImage': return placeHolderUrl; case 'defaultImage': - return getSanitizedDefaultImageUrl(userImage.defaultImage!.url); + return userImage.defaultImage!.url; case 'profileImage': return state.user.profileImage; case 'externalImage':
diff --git a/ash/webui/personalization_app/resources/js/user/utils.ts b/ash/webui/personalization_app/resources/js/user/utils.ts new file mode 100644 index 0000000..5d585c2 --- /dev/null +++ b/ash/webui/personalization_app/resources/js/user/utils.ts
@@ -0,0 +1,26 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview Utility functions to be used for avatars. + */ + +/** The placeholder url for an avatar, rendered if the avatar url is invalid. */ +export const AVATAR_PLACEHOLDER_URL: string = + 'chrome://theme/IDR_PROFILE_AVATAR_PLACEHOLDER_LARGE'; + +/** + * Returns the avatar url. If necessary, prefixes the url with the sanitizing + * string. + */ +export function getAvatarUrl(url: string): string { + if (!url) { + return ''; + } + if (url.startsWith('data') || url.startsWith('blob') || + url === AVATAR_PLACEHOLDER_URL) { + return url; + } + return `chrome://image/?url=${url}`; +}
diff --git a/ash/webui/personalization_app/resources/js/utils.ts b/ash/webui/personalization_app/resources/js/utils.ts index 2311b339..64e73ca 100644 --- a/ash/webui/personalization_app/resources/js/utils.ts +++ b/ash/webui/personalization_app/resources/js/utils.ts
@@ -95,14 +95,6 @@ return str ? str.data.map(ch => String.fromCodePoint(ch)).join('') : ''; } -/** - * Append chrome://image/? scheme prefix to sanitize the given Url if the cloud - * migration is enabled. - */ -export function getSanitizedDefaultImageUrl(url: Url): Url { - return {url: 'chrome://image/?url=' + url.url}; -} - export function isImageDataUrl(maybeDataUrl: Url|null| undefined): maybeDataUrl is Url { return !!maybeDataUrl && typeof maybeDataUrl.url === 'string' &&
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_controller.ts b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_controller.ts index 8ca888c..2935b30 100644 --- a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_controller.ts +++ b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_controller.ts
@@ -358,7 +358,7 @@ const {success} = await (() => { if (isWallpaperImage(image)) { return provider.selectWallpaper( - image.assetId, /*preview_mode=*/ shouldPreview); + image.unitId, /*preview_mode=*/ shouldPreview); } else if (isDefaultImage(image)) { return provider.selectDefaultImage(); } else if (isFilePath(image)) {
diff --git a/ash/webui/personalization_app/test/fake_personalization_app_wallpaper_provider.cc b/ash/webui/personalization_app/test/fake_personalization_app_wallpaper_provider.cc index d45c7af..2ca458e 100644 --- a/ash/webui/personalization_app/test/fake_personalization_app_wallpaper_provider.cc +++ b/ash/webui/personalization_app/test/fake_personalization_app_wallpaper_provider.cc
@@ -24,6 +24,7 @@ constexpr char kFakeCollectionId[] = "fake_collection_id"; constexpr uint64_t kFakeAssetId = 77; +constexpr uint64_t kFakeUnitId = 3; constexpr char kDataUrlPrefix[] = "data:image/png;base64,"; } // namespace @@ -71,7 +72,7 @@ image.set_asset_id(kFakeAssetId); image.set_image_url(kDataUrlPrefix); image.add_attribution()->set_text("test"); - image.set_unit_id(3); + image.set_unit_id(kFakeUnitId); image.set_image_type(backdrop::Image_ImageType_IMAGE_TYPE_UNKNOWN); images.push_back(image); std::move(callback).Run(std::move(images)); @@ -133,16 +134,17 @@ } void FakePersonalizationAppWallpaperProvider::SelectWallpaper( - uint64_t image_asset_id, + uint64_t unit_id, bool preview_mode, SelectWallpaperCallback callback) { - DCHECK_EQ(image_asset_id, kFakeAssetId); + DCHECK_EQ(unit_id, kFakeUnitId); std::move(callback).Run(/*success=*/true); wallpaper_receiver_.FlushForTesting(); WallpaperInfo wallpaper_info; wallpaper_info.type = WallpaperType::kOnline; - wallpaper_info.asset_id = image_asset_id; + wallpaper_info.asset_id = kFakeAssetId; + wallpaper_info.unit_id = kFakeUnitId; wallpaper_info.layout = WallpaperLayout::WALLPAPER_LAYOUT_CENTER_CROPPED; wallpaper_info.collection_id = kFakeCollectionId; SendOnWallpaperChanged(wallpaper_info);
diff --git a/ash/webui/personalization_app/test/fake_personalization_app_wallpaper_provider.h b/ash/webui/personalization_app/test/fake_personalization_app_wallpaper_provider.h index 59b6b397..804402ef 100644 --- a/ash/webui/personalization_app/test/fake_personalization_app_wallpaper_provider.h +++ b/ash/webui/personalization_app/test/fake_personalization_app_wallpaper_provider.h
@@ -88,7 +88,7 @@ mojo::PendingRemote<ash::personalization_app::mojom::WallpaperObserver> observer) override; - void SelectWallpaper(uint64_t image_asset_id, + void SelectWallpaper(uint64_t unit_Id, bool preview_mode, SelectWallpaperCallback callback) override;
diff --git a/ash/webui/shortcut_customization_ui/backend/BUILD.gn b/ash/webui/shortcut_customization_ui/backend/BUILD.gn index ed8960a..8db87c0 100644 --- a/ash/webui/shortcut_customization_ui/backend/BUILD.gn +++ b/ash/webui/shortcut_customization_ui/backend/BUILD.gn
@@ -28,6 +28,7 @@ "//ash/public/mojom", "//ash/webui/shortcut_customization_ui/backend/search:mojo_bindings", "//ash/webui/shortcut_customization_ui/mojom", + "//base", "//chromeos/ash/components/local_search_service:local_search_service", "//chromeos/ash/components/local_search_service/public/cpp:cpp", "//chromeos/ash/components/local_search_service/public/mojom",
diff --git a/ash/webui/shortcut_customization_ui/backend/search/fake_search_data.h b/ash/webui/shortcut_customization_ui/backend/search/fake_search_data.h index 83d41b8..122fe6a 100644 --- a/ash/webui/shortcut_customization_ui/backend/search/fake_search_data.h +++ b/ash/webui/shortcut_customization_ui/backend/search/fake_search_data.h
@@ -14,7 +14,7 @@ namespace ash::shortcut_ui::fake_search_data { -enum FakeActionIds { kAction1 = 1, kAction2 = 2, kAction3 = 3 }; +enum FakeActionIds { kAction1 = 1, kAction2 = 2, kAction3 = 3, kAction4 = 4 }; ash::mojom::AcceleratorInfoPtr CreateFakeStandardAcceleratorInfo();
diff --git a/ash/webui/shortcut_customization_ui/backend/search/search.mojom b/ash/webui/shortcut_customization_ui/backend/search/search.mojom index a67d4820..422bd04 100644 --- a/ash/webui/shortcut_customization_ui/backend/search/search.mojom +++ b/ash/webui/shortcut_customization_ui/backend/search/search.mojom
@@ -21,6 +21,19 @@ double relevance_score; }; +// Used to observe changes to search results. +interface SearchResultsAvailabilityObserver { + // Called when the availability of one or more search results has changed. In + // this context, "availability" refers to whether a search result can be + // returned based on the user's current state. E.g. some results might only be + // shown if an external keyboard is plugged in. Clients can use this function + // to ensure that they do not show "stale" results which are no longer + // actionable by the user. Note that the observer doesn't provide the new + // search results, so clients will have to redo the SearchHandler.Search() + // call to retrieve new results. + OnSearchResultsAvailabilityChanged(); +}; + // Provides search results. Implemented in the browser process; // intended to be called from the Shortcuts app TS and Launcher C++. interface SearchHandler { @@ -38,4 +51,9 @@ // would return an empty result array. Search(mojo_base.mojom.String16 query, uint32 max_num_results) => (array<SearchResult> results); + + // Adds an observer of search result availability. Disconnected observers are + // discarded; to stop observing, close the connection. + AddSearchResultsAvailabilityObserver( + pending_remote<SearchResultsAvailabilityObserver> observer); };
diff --git a/ash/webui/shortcut_customization_ui/backend/search/search_handler.cc b/ash/webui/shortcut_customization_ui/backend/search/search_handler.cc index df33184..ebb3b44 100644 --- a/ash/webui/shortcut_customization_ui/backend/search/search_handler.cc +++ b/ash/webui/shortcut_customization_ui/backend/search/search_handler.cc
@@ -11,15 +11,31 @@ #include "ash/public/mojom/accelerator_info.mojom-shared.h" #include "ash/public/mojom/accelerator_info.mojom.h" #include "ash/webui/shortcut_customization_ui/backend/search/fake_search_data.h" -#include "ash/webui/shortcut_customization_ui/backend/search/search.mojom-forward.h" #include "ash/webui/shortcut_customization_ui/backend/search/search.mojom.h" +#include "ash/webui/shortcut_customization_ui/backend/search/search_concept.h" #include "base/check.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/base/accelerators/accelerator.h" namespace ash::shortcut_ui { -SearchHandler::SearchHandler() = default; -SearchHandler::~SearchHandler() = default; +SearchHandler::SearchHandler( + SearchConceptRegistry* search_concept_registry, + local_search_service::LocalSearchServiceProxy* local_search_service_proxy) + : search_concept_registry_(search_concept_registry) { + DCHECK(local_search_service_proxy); + local_search_service_proxy->GetIndex( + local_search_service::IndexId::kShortcutsApp, + local_search_service::Backend::kLinearMap, + index_remote_.BindNewPipeAndPassReceiver()); + DCHECK(index_remote_.is_bound()); + + search_concept_registry_->AddObserver(this); +} + +SearchHandler::~SearchHandler() { + search_concept_registry_->RemoveObserver(this); +} void SearchHandler::BindInterface( mojo::PendingReceiver<shortcut_customization::mojom::SearchHandler> @@ -27,17 +43,87 @@ receivers_.Add(this, std::move(pending_receiver)); } +void SearchHandler::OnRegistryUpdated() { + for (auto& observer : observers_) { + observer->OnSearchResultsAvailabilityChanged(); + } +} + +void SearchHandler::AddSearchResultsAvailabilityObserver( + mojo::PendingRemote< + shortcut_customization::mojom::SearchResultsAvailabilityObserver> + observer) { + observers_.Add(std::move(observer)); +} + void SearchHandler::Search(const std::u16string& query, uint32_t max_num_results, SearchCallback callback) { - // Searching is disabled unless the flag kSearchInShortcutsApp is enabled. - DCHECK(features::IsSearchInShortcutsAppEnabled()); + index_remote_->Find( + query, max_num_results, + base::BindOnce(&SearchHandler::OnFindComplete, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); +} - // Until we implement real search using the LocalSearchService, temporarily - // return fake search results. - // TODO(cambickel): Replace these fake results with an actual call to the - // LocalSearchService. - std::move(callback).Run(fake_search_data::CreateFakeSearchResultList()); +void SearchHandler::OnFindComplete( + SearchCallback callback, + local_search_service::ResponseStatus response_status, + const absl::optional<std::vector<local_search_service::Result>>& + local_search_service_results) { + if (response_status != local_search_service::ResponseStatus::kSuccess) { + LOG(ERROR) << "Cannot search; LocalSearchService returned " + << static_cast<int>(response_status) + << ". Returning empty results array."; + std::move(callback).Run({}); + return; + } + + std::move(callback).Run( + GenerateSearchResultsArray(local_search_service_results.value())); +} + +std::vector<shortcut_customization::mojom::SearchResultPtr> +SearchHandler::GenerateSearchResultsArray( + const std::vector<local_search_service::Result>& + local_search_service_results) const { + std::vector<shortcut_customization::mojom::SearchResultPtr> search_results; + for (const auto& result : local_search_service_results) { + shortcut_customization::mojom::SearchResultPtr result_ptr = + ResultToSearchResult(result); + if (result_ptr) { + search_results.push_back(std::move(result_ptr)); + } + } + + std::sort(search_results.begin(), search_results.end(), CompareSearchResults); + + return search_results; +} + +shortcut_customization::mojom::SearchResultPtr +SearchHandler::ResultToSearchResult( + const local_search_service::Result& result) const { + const SearchConcept* search_concept = + search_concept_registry_->GetSearchConceptById(result.id); + + // If the concept was not registered, no metadata is available. This can + // occur if the search concept was dynamically unregistered during the + // asynchronous Find() call. + if (!search_concept) { + return nullptr; + } + + return shortcut_customization::mojom::SearchResult::New( + /*accelerator_layout_info=*/search_concept->accelerator_layout_info + ->Clone(), + /*accelerator_infos=*/mojo::Clone(search_concept->accelerator_infos), + /*relevance_score=*/result.score); +} + +bool SearchHandler::CompareSearchResults( + const shortcut_customization::mojom::SearchResultPtr& first, + const shortcut_customization::mojom::SearchResultPtr& second) { + return first->relevance_score > second->relevance_score; } } // namespace ash::shortcut_ui \ No newline at end of file
diff --git a/ash/webui/shortcut_customization_ui/backend/search/search_handler.h b/ash/webui/shortcut_customization_ui/backend/search/search_handler.h index a88bd4f..8b04643 100644 --- a/ash/webui/shortcut_customization_ui/backend/search/search_handler.h +++ b/ash/webui/shortcut_customization_ui/backend/search/search_handler.h
@@ -7,7 +7,13 @@ #include <vector> +#include "ash/webui/shortcut_customization_ui/backend/search/search.mojom-forward.h" #include "ash/webui/shortcut_customization_ui/backend/search/search.mojom.h" +#include "ash/webui/shortcut_customization_ui/backend/search/search_concept_registry.h" +#include "base/gtest_prod_util.h" +#include "base/memory/raw_ptr.h" +#include "chromeos/ash/components/local_search_service/public/cpp/local_search_service_proxy.h" +#include "chromeos/ash/components/local_search_service/public/mojom/index.mojom.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/receiver_set.h" @@ -23,9 +29,12 @@ // results with SearchConceptRegistry. // // Searches which do not provide any matches result in an empty results array. -class SearchHandler : public shortcut_customization::mojom::SearchHandler { +class SearchHandler : public shortcut_customization::mojom::SearchHandler, + SearchConceptRegistry::Observer { public: - SearchHandler(); + SearchHandler(SearchConceptRegistry* search_concept_registry, + local_search_service::LocalSearchServiceProxy* + local_search_service_proxy); ~SearchHandler() override; SearchHandler(const SearchHandler& other) = delete; @@ -39,10 +48,44 @@ void Search(const std::u16string& query, uint32_t max_num_results, SearchCallback callback) override; + void AddSearchResultsAvailabilityObserver( + mojo::PendingRemote< + shortcut_customization::mojom::SearchResultsAvailabilityObserver> + observer) override; + + // SearchConceptRegistry::Observer:: + void OnRegistryUpdated() override; private: + FRIEND_TEST_ALL_PREFIXES(SearchHandlerTest, CompareSearchResults); + + void OnFindComplete( + SearchCallback callback, + local_search_service::ResponseStatus response_status, + const absl::optional<std::vector<local_search_service::Result>>& + local_search_service_results); + + std::vector<shortcut_customization::mojom::SearchResultPtr> + GenerateSearchResultsArray(const std::vector<local_search_service::Result>& + local_search_service_results) const; + + shortcut_customization::mojom::SearchResultPtr ResultToSearchResult( + const local_search_service::Result& result) const; + + // Returns true if |first| should be ranked before |second|. + static bool CompareSearchResults( + const shortcut_customization::mojom::SearchResultPtr& first, + const shortcut_customization::mojom::SearchResultPtr& second); + // Note: Expected to have multiple clients, so ReceiverSet/RemoteSet are used. mojo::ReceiverSet<shortcut_customization::mojom::SearchHandler> receivers_; + mojo::RemoteSet< + shortcut_customization::mojom::SearchResultsAvailabilityObserver> + observers_; + + raw_ptr<SearchConceptRegistry> search_concept_registry_; + mojo::Remote<local_search_service::mojom::Index> index_remote_; + base::WeakPtrFactory<SearchHandler> weak_ptr_factory_{this}; }; } // namespace ash::shortcut_ui
diff --git a/ash/webui/shortcut_customization_ui/backend/search/search_handler_unittest.cc b/ash/webui/shortcut_customization_ui/backend/search/search_handler_unittest.cc index dec70c8..2bb25e18 100644 --- a/ash/webui/shortcut_customization_ui/backend/search/search_handler_unittest.cc +++ b/ash/webui/shortcut_customization_ui/backend/search/search_handler_unittest.cc
@@ -3,22 +3,187 @@ // found in the LICENSE file. #include "ash/webui/shortcut_customization_ui/backend/search/search_handler.h" +#include <string> #include "ash/constants/ash_features.h" #include "ash/webui/shortcut_customization_ui/backend/search/fake_search_data.h" #include "ash/webui/shortcut_customization_ui/backend/search/search.mojom-test-utils.h" +#include "ash/webui/shortcut_customization_ui/backend/search/search_concept_registry.h" +#include "base/test/bind.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "mojo/public/cpp/bindings/remote.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/l10n/l10n_util.h" -namespace ash::shortcut_customization { -using shortcut_ui::fake_search_data::CreateFakeSearchResultList; +namespace ash::shortcut_ui { + +namespace { + +class FakeObserver + : public shortcut_customization::mojom::SearchResultsAvailabilityObserver { + public: + FakeObserver() = default; + ~FakeObserver() override = default; + + mojo::PendingRemote< + shortcut_customization::mojom::SearchResultsAvailabilityObserver> + GenerateRemote() { + mojo::PendingRemote< + shortcut_customization::mojom::SearchResultsAvailabilityObserver> + remote; + receiver_.Bind(remote.InitWithNewPipeAndPassReceiver()); + return remote; + } + + size_t num_calls() const { return num_calls_; } + + private: + // shortcut_customization::mojom::SearchResultsAvailabilityObserver: + void OnSearchResultsAvailabilityChanged() override { ++num_calls_; } + + size_t num_calls_ = 0; + mojo::Receiver< + shortcut_customization::mojom::SearchResultsAvailabilityObserver> + receiver_{this}; +}; + +std::vector<shortcut_ui::SearchConcept> GetTestSearchConcepts() { + std::vector<shortcut_ui::SearchConcept> concepts; + + { + std::vector<ash::mojom::AcceleratorInfoPtr> accelerator_info_list; + accelerator_info_list.emplace_back(ash::mojom::AcceleratorInfo::New( + /*type=*/ash::mojom::AcceleratorType::kDefault, + /*state=*/ash::mojom::AcceleratorState::kEnabled, + /*locked=*/true, + /*layout_properties=*/ + ash::mojom::LayoutStyleProperties::NewStandardAccelerator( + ash::mojom::StandardAcceleratorProperties::New( + ui::Accelerator( + /*key_code=*/ui::KeyboardCode::VKEY_SPACE, + /*modifiers=*/ui::EF_CONTROL_DOWN), + u"Space")))); + concepts.emplace_back( + fake_search_data::CreateFakeAcceleratorLayoutInfo( + /*description=*/u"Open launcher", + /*source=*/ash::mojom::AcceleratorSource::kAsh, + /*action=*/fake_search_data::FakeActionIds::kAction1, + /*style=*/ash::mojom::AcceleratorLayoutStyle::kDefault), + std::move(accelerator_info_list)); + } + + { + std::vector<ash::mojom::AcceleratorInfoPtr> accelerator_info_list; + accelerator_info_list.emplace_back(ash::mojom::AcceleratorInfo::New( + /*type=*/ash::mojom::AcceleratorType::kDefault, + /*state=*/ash::mojom::AcceleratorState::kEnabled, + /*locked=*/true, + /*layout_properties=*/ + ash::mojom::LayoutStyleProperties::NewStandardAccelerator( + ash::mojom::StandardAcceleratorProperties::New( + ui::Accelerator( + /*key_code=*/ui::KeyboardCode::VKEY_T, + /*modifiers=*/ui::EF_CONTROL_DOWN), + u"T")))); + concepts.emplace_back( + fake_search_data::CreateFakeAcceleratorLayoutInfo( + /*description=*/u"Open new tab", + /*source=*/ash::mojom::AcceleratorSource::kBrowser, + /*action=*/fake_search_data::FakeActionIds::kAction2, + /*style=*/ash::mojom::AcceleratorLayoutStyle::kDefault), + std::move(accelerator_info_list)); + } + + { + std::vector<ash::mojom::AcceleratorInfoPtr> accelerator_info_list; + accelerator_info_list.emplace_back(ash::mojom::AcceleratorInfo::New( + /*type=*/ash::mojom::AcceleratorType::kDefault, + /*state=*/ash::mojom::AcceleratorState::kEnabled, + /*locked=*/true, + /*layout_properties=*/ + ash::mojom::LayoutStyleProperties::NewStandardAccelerator( + ash::mojom::StandardAcceleratorProperties::New( + ui::Accelerator( + /*key_code=*/ui::KeyboardCode::VKEY_A, + /*modifiers=*/ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN), + u"A")))); + accelerator_info_list.emplace_back(ash::mojom::AcceleratorInfo::New( + /*type=*/ash::mojom::AcceleratorType::kDefault, + /*state=*/ash::mojom::AcceleratorState::kEnabled, + /*locked=*/true, + /*layout_properties=*/ + ash::mojom::LayoutStyleProperties::NewStandardAccelerator( + ash::mojom::StandardAcceleratorProperties::New( + ui::Accelerator( + /*key_code=*/ui::KeyboardCode::VKEY_BRIGHTNESS_DOWN, + /*modifiers=*/ui::EF_ALT_DOWN), + u"BrightnessDown")))); + + concepts.emplace_back( + fake_search_data::CreateFakeAcceleratorLayoutInfo( + /*description=*/u"Open the Foo app", + /*source=*/ash::mojom::AcceleratorSource::kAsh, + /*action=*/fake_search_data::kAction3, + /*style=*/ash::mojom::AcceleratorLayoutStyle::kDefault), + std::move(accelerator_info_list)); + } + + { + // Create a TextAccelerator. + std::vector<ash::mojom::TextAcceleratorPartPtr> text_parts; + text_parts.push_back(ash::mojom::TextAcceleratorPart::New( + u"Press ", ash::mojom::TextAcceleratorPartType::kPlainText)); + text_parts.push_back(ash::mojom::TextAcceleratorPart::New( + u"Ctrl", ash::mojom::TextAcceleratorPartType::kModifier)); + text_parts.push_back(ash::mojom::TextAcceleratorPart::New( + u"+", ash::mojom::TextAcceleratorPartType::kDelimiter)); + text_parts.push_back(ash::mojom::TextAcceleratorPart::New( + u"A", ash::mojom::TextAcceleratorPartType::kKey)); + + std::vector<ash::mojom::AcceleratorInfoPtr> text_accelerator_info_list; + text_accelerator_info_list.emplace_back(ash::mojom::AcceleratorInfo::New( + /*type=*/ash::mojom::AcceleratorType::kDefault, + /*state=*/ash::mojom::AcceleratorState::kEnabled, + /*locked=*/true, + /*layout_properties=*/ + ash::mojom::LayoutStyleProperties::NewTextAccelerator( + ash::mojom::TextAcceleratorProperties::New( + std::move(text_parts))))); + + // Add that TextAccelerator to the list of SearchConcepts. + concepts.emplace_back( + fake_search_data::CreateFakeAcceleratorLayoutInfo( + /*description=*/u"Select all text content", + /*source=*/ash::mojom::AcceleratorSource::kAsh, + /*action=*/fake_search_data::FakeActionIds::kAction4, + /*style=*/ash::mojom::AcceleratorLayoutStyle::kText), + std::move(text_accelerator_info_list)); + } + + return concepts; +} + +// Creates a search result with some default values. +shortcut_customization::mojom::SearchResultPtr CreateFakeSearchResult() { + return shortcut_customization::mojom::SearchResult::New( + /*accelerator_layout_info=*/fake_search_data:: + CreateFakeAcceleratorLayoutInfo( + u"Open launcher", ash::mojom::AcceleratorSource::kAsh, + fake_search_data::FakeActionIds::kAction1, + ash::mojom::AcceleratorLayoutStyle::kDefault), + /*accelerator_infos=*/fake_search_data::CreateFakeAcceleratorInfoList(), + /*relevance_score=*/0.5); +} + +} // namespace class SearchHandlerTest : public testing::Test { protected: - SearchHandlerTest() = default; + SearchHandlerTest() + : search_concept_registry_(*local_search_service_proxy_.get()), + handler_(&search_concept_registry_, local_search_service_proxy_.get()) { + } ~SearchHandlerTest() override = default; // testing::Test: @@ -26,30 +191,264 @@ scoped_feature_list_.InitWithFeatures( /*enabled_features=*/{features::kSearchInShortcutsApp}, /*disabled_features=*/{}); + handler_.BindInterface(handler_remote_.BindNewPipeAndPassReceiver()); + handler_remote_->AddSearchResultsAvailabilityObserver( + results_availability_observer_.GenerateRemote()); + handler_remote_.FlushForTesting(); + } + + void VerifySearchResultIsPresent( + const std::u16string description, + const std::vector<shortcut_customization::mojom::SearchResultPtr>& + search_results) const { + auto description_iterator = find_if( + search_results.begin(), search_results.end(), + [&description]( + const shortcut_customization::mojom::SearchResultPtr& result) { + return result->accelerator_layout_info->description == description; + }); + // The description should be present in the list of search results. + EXPECT_NE(description_iterator, search_results.end()); } base::test::TaskEnvironment task_environment_; + std::unique_ptr<local_search_service::LocalSearchServiceProxy> + local_search_service_proxy_ = + std::make_unique<local_search_service::LocalSearchServiceProxy>( + /*for_testing=*/true); + shortcut_ui::SearchConceptRegistry search_concept_registry_; + mojo::Remote<shortcut_customization::mojom::SearchHandler> handler_remote_; shortcut_ui::SearchHandler handler_; - mojo::Remote<mojom::SearchHandler> handler_remote_; base::test::ScopedFeatureList scoped_feature_list_; + FakeObserver results_availability_observer_; }; -// TODO(cambickel): Remove this test when we use the real LocalSearchService -// index, since this test tests against the fake data that we're temporarily -// returning from SearchHandler.Search(). -TEST_F(SearchHandlerTest, SearchWithFakeResults) { - std::vector<mojom::SearchResultPtr> fake_search_results = - shortcut_ui::fake_search_data::CreateFakeSearchResultList(); - std::vector<mojom::SearchResultPtr> search_results; - mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) - .Search(u"New tab", - /*max_num_results=*/3u, &search_results); - EXPECT_EQ(search_results.size(), fake_search_results.size()); - EXPECT_EQ(search_results[0]->accelerator_layout_info->description, - fake_search_results[0]->accelerator_layout_info->description); - EXPECT_EQ(search_results[1]->accelerator_layout_info->description, - fake_search_results[1]->accelerator_layout_info->description); +TEST_F(SearchHandlerTest, SearchResultsNormalUsage) { + search_concept_registry_.SetSearchConcepts(GetTestSearchConcepts()); + handler_remote_.FlushForTesting(); + task_environment_.RunUntilIdle(); + + // SearchHandler observer should be called after the registry is updated. + EXPECT_EQ(1u, results_availability_observer_.num_calls()); + + std::vector<shortcut_customization::mojom::SearchResultPtr> search_results; + + // A search with no matches should return no results. + shortcut_customization::mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(u"this search matches nothing!", + /*max_num_results=*/5u, &search_results); + EXPECT_EQ(search_results.size(), 0u); + + // The number of observer calls should not have changed, even though a search + // was completed. The observer is only called when the availability of results + // changes, i.e. when the index is updated. + EXPECT_EQ(1u, results_availability_observer_.num_calls()); + + // The descriptions for the fake shortcuts are "Open launcher", "Open new + // tab", "Open the Foo app", and "Select all text content". + // The query "Open" should match the first three Concepts. + shortcut_customization::mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(u"Open", + /*max_num_results=*/5u, &search_results); + EXPECT_EQ(search_results.size(), 3u); + VerifySearchResultIsPresent(/*description=*/u"Open launcher", + /*search_results=*/search_results); + VerifySearchResultIsPresent(/*description=*/u"Open new tab", + /*search_results=*/search_results); + VerifySearchResultIsPresent(/*description=*/u"Open the Foo app", + /*search_results=*/search_results); + + // Checking again that the observer was not called after the previous search. + EXPECT_EQ(1u, results_availability_observer_.num_calls()); + + // The query "open" should also match the first three Concepts (query case + // doesn't matter). + shortcut_customization::mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(u"open", + /*max_num_results=*/5u, &search_results); + EXPECT_EQ(search_results.size(), 3u); + VerifySearchResultIsPresent(/*description=*/u"Open launcher", + /*search_results=*/search_results); + VerifySearchResultIsPresent(/*description=*/u"Open new tab", + /*search_results=*/search_results); + VerifySearchResultIsPresent(/*description=*/u"Open the Foo app", + /*search_results=*/search_results); + + // For completeness, the query "OpEn" should also match the first three + // Concepts. + shortcut_customization::mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(u"OpEn", + /*max_num_results=*/5u, &search_results); + EXPECT_EQ(search_results.size(), 3u); + VerifySearchResultIsPresent(/*description=*/u"Open launcher", + /*search_results=*/search_results); + VerifySearchResultIsPresent(/*description=*/u"Open new tab", + /*search_results=*/search_results); + VerifySearchResultIsPresent(/*description=*/u"Open the Foo app", + /*search_results=*/search_results); + + // Searching for a specific shortcut should only include those results. + shortcut_customization::mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(u"Open new tab", + /*max_num_results=*/5u, &search_results); + EXPECT_EQ(search_results.size(), 1u); + EXPECT_EQ(search_results.at(0)->accelerator_layout_info->description, + u"Open new tab"); + + // Searching for a specific shortcut should work even if the query is a + // "fuzzy" match. + shortcut_customization::mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(u"Open tab", + /*max_num_results=*/5u, &search_results); + // In this case, the search service also returns the result for "Open the Foo + // app", but with a lower relevance_score. + EXPECT_EQ(search_results.size(), 2u); + EXPECT_EQ(search_results.at(0)->accelerator_layout_info->description, + u"Open new tab"); + EXPECT_EQ(search_results.at(1)->accelerator_layout_info->description, + u"Open the Foo app"); + // Expect that earlier search results have a higher relevance score. + EXPECT_GT(search_results.at(0)->relevance_score, + search_results.at(1)->relevance_score); + + // Clear the index and verify that searches return no results, and that the + // observer was called an additional time. + std::vector<SearchConcept> empty_search_concepts; + search_concept_registry_.SetSearchConcepts(std::move(empty_search_concepts)); + task_environment_.RunUntilIdle(); + shortcut_customization::mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(u"Open", + /*max_num_results=*/5u, &search_results); + EXPECT_EQ(search_results.size(), 0u); + EXPECT_EQ(results_availability_observer_.num_calls(), 2u); } -} // namespace ash::shortcut_customization \ No newline at end of file +TEST_F(SearchHandlerTest, SearchResultsEdgeCases) { + search_concept_registry_.SetSearchConcepts(GetTestSearchConcepts()); + handler_remote_.FlushForTesting(); + task_environment_.RunUntilIdle(); + std::vector<shortcut_customization::mojom::SearchResultPtr> search_results; + + // A search with no matches should return no results. + shortcut_customization::mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(u"this search matches nothing!", + /*max_num_results=*/5u, &search_results); + EXPECT_EQ(search_results.size(), 0u); + + // Testing some edge cases: searching with spaces on either side and in the + // middle. + shortcut_customization::mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(u" Open new tab ", + /*max_num_results=*/5u, &search_results); + // Turns out the search service doesn't match any shortcuts with a malformed + // query like this. + EXPECT_EQ(search_results.size(), 0u); + + // Searching with spaces on either side, but not in the middle. + shortcut_customization::mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(u" Open new tab ", + /*max_num_results=*/5u, &search_results); + // Turns out the search service doesn't match any shortcuts with a malformed + // query like this. + EXPECT_EQ(search_results.size(), 0u); +} + +TEST_F(SearchHandlerTest, SearchResultsSearchByKeys) { + search_concept_registry_.SetSearchConcepts(GetTestSearchConcepts()); + handler_remote_.FlushForTesting(); + task_environment_.RunUntilIdle(); + std::vector<shortcut_customization::mojom::SearchResultPtr> search_results; + + // Searching for the keys/modifiers of a shortcut should only work for text + // accelerators. In this case, all shortcuts contain "Ctrl" as one of their + // modifiers, but only one of them is a text accelerator, so only that one + // should be returned. + shortcut_customization::mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(u"ctrl", + /*max_num_results=*/5u, &search_results); + EXPECT_EQ(search_results.size(), 1u); + VerifySearchResultIsPresent(/*description=*/u"Select all text content", + /*search_results=*/search_results); + + // Verify that different various combinations of uppercase and lowercase work + // when querying by modifier. + shortcut_customization::mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(u"CTRL", + /*max_num_results=*/5u, &search_results); + EXPECT_EQ(search_results.size(), 1u); + VerifySearchResultIsPresent(/*description=*/u"Select all text content", + /*search_results=*/search_results); + shortcut_customization::mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(u"CtRl", + /*max_num_results=*/5u, &search_results); + EXPECT_EQ(search_results.size(), 1u); + VerifySearchResultIsPresent(/*description=*/u"Select all text content", + /*search_results=*/search_results); + + // The test concept "Open the Foo app" has "Alt + BrightnessDown" as one of + // its key combinations. Searching based on "BrightnessDown" should not return + // that shortcut as a SearchResult because that shortcut is a standard + // accelerator. + shortcut_customization::mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(u"BrightnessDown", + /*max_num_results=*/5u, &search_results); + EXPECT_EQ(search_results.size(), 0u); + + // Searching for text-based shortcuts should work. In this case, the query + // should match the shortcut "Select all text content" which has a shortcut + // "Press Ctrl+A". + shortcut_customization::mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(u"Press Ctrl+A", + /*max_num_results=*/5u, &search_results); + EXPECT_EQ(search_results.size(), 1u); + VerifySearchResultIsPresent(/*description=*/u"Select all text content", + /*search_results=*/search_results); + + // Searching for text-based shortcuts should work with an inexact query. + shortcut_customization::mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(u"Press", + /*max_num_results=*/5u, &search_results); + EXPECT_EQ(search_results.size(), 1u); + VerifySearchResultIsPresent(/*description=*/u"Select all text content", + /*search_results=*/search_results); +} + +TEST_F(SearchHandlerTest, CompareSearchResults) { + // Create two equal fake search results. + shortcut_customization::mojom::SearchResultPtr a = CreateFakeSearchResult(); + shortcut_customization::mojom::SearchResultPtr b = CreateFakeSearchResult(); + + // CompareSearchResults() returns true if the first parameter should be ranked + // higher than the second parameter. On a tie, this method should return + // false. Since the two fake search results are equal, it should return false + // regardless of the order of parameters. + EXPECT_FALSE(SearchHandler::CompareSearchResults(a, b)); + EXPECT_FALSE(SearchHandler::CompareSearchResults(b, a)); + + // Differ only on relevance score. + a->relevance_score = 0; + b->relevance_score = 1; + + // Comparison value should differ now that the relevance scores are different. + EXPECT_NE(SearchHandler::CompareSearchResults(b, a), + SearchHandler::CompareSearchResults(a, b)); + // CompareSearchResults() returns whether the first parameter should be higher + // ranked than the second parameter. + EXPECT_FALSE(SearchHandler::CompareSearchResults(a, b)); + EXPECT_TRUE(SearchHandler::CompareSearchResults(b, a)); + + // Differ only on relevance score, this time using less extreme values. + a->relevance_score = 0.123; + b->relevance_score = 0.789; + + // Comparison value should differ now that the relevance scores are different. + EXPECT_NE(SearchHandler::CompareSearchResults(b, a), + SearchHandler::CompareSearchResults(a, b)); + // CompareSearchResults() returns whether the first parameter should be higher + // ranked than the second parameter. + EXPECT_FALSE(SearchHandler::CompareSearchResults(a, b)); + EXPECT_TRUE(SearchHandler::CompareSearchResults(b, a)); +} + +} // namespace ash::shortcut_ui \ No newline at end of file
diff --git a/ash/webui/shortcut_customization_ui/resources/js/search/fake_shortcut_search_handler.ts b/ash/webui/shortcut_customization_ui/resources/js/search/fake_shortcut_search_handler.ts index e473376..002593b0 100644 --- a/ash/webui/shortcut_customization_ui/resources/js/search/fake_shortcut_search_handler.ts +++ b/ash/webui/shortcut_customization_ui/resources/js/search/fake_shortcut_search_handler.ts
@@ -5,6 +5,7 @@ import {FakeMethodResolver} from 'chrome://resources/ash/common/fake_method_resolver.js'; import {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js'; +import {SearchResultsAvailabilityObserverRemote} from '../../mojom-webui/ash/webui/shortcut_customization_ui/backend/search/search.mojom-webui.js'; import {MojoSearchResult} from '../shortcut_types'; import {ShortcutSearchHandlerInterface} from '../shortcut_types.js'; @@ -29,6 +30,11 @@ return this.methods.resolveMethod('search'); } + addSearchResultsAvailabilityObserver( + _observer: SearchResultsAvailabilityObserverRemote): void { + // Intentionally not implemented. + } + /** * Sets the value that will be returned when calling search(). */
diff --git a/ash/webui/shortcut_customization_ui/resources/js/shortcut_types.ts b/ash/webui/shortcut_customization_ui/resources/js/shortcut_types.ts index 56b231b..aabad00 100644 --- a/ash/webui/shortcut_customization_ui/resources/js/shortcut_types.ts +++ b/ash/webui/shortcut_customization_ui/resources/js/shortcut_types.ts
@@ -7,7 +7,7 @@ import * as AcceleratorConfigurationTypes from '../mojom-webui/ash/public/mojom/accelerator_configuration.mojom-webui.js'; import * as AcceleratorInfoTypes from '../mojom-webui/ash/public/mojom/accelerator_info.mojom-webui.js'; -import {SearchHandler, SearchHandlerInterface, SearchResult} from '../mojom-webui/ash/webui/shortcut_customization_ui/backend/search/search.mojom-webui.js'; +import {SearchHandler, SearchHandlerInterface, SearchResult, SearchResultsAvailabilityObserverRemote} from '../mojom-webui/ash/webui/shortcut_customization_ui/backend/search/search.mojom-webui.js'; import {AcceleratorConfigurationProviderInterface, AcceleratorResultData, AcceleratorsUpdatedObserverRemote} from '../mojom-webui/ash/webui/shortcut_customization_ui/mojom/shortcut_customization.mojom-webui.js'; @@ -185,6 +185,8 @@ export interface ShortcutSearchHandlerInterface extends SearchHandlerInterface { search(query: String16, maxNumResults: number): Promise<{results: MojoSearchResult[]}>; + addSearchResultsAvailabilityObserver( + observer: SearchResultsAvailabilityObserverRemote): void; } /**
diff --git a/ash/webui/shortcut_customization_ui/shortcuts_app_manager.cc b/ash/webui/shortcut_customization_ui/shortcuts_app_manager.cc index 0e40857..1d04d98 100644 --- a/ash/webui/shortcut_customization_ui/shortcuts_app_manager.cc +++ b/ash/webui/shortcut_customization_ui/shortcuts_app_manager.cc
@@ -24,7 +24,8 @@ if (features::IsSearchInShortcutsAppEnabled()) { search_concept_registry_ = std::make_unique<SearchConceptRegistry>(*local_search_service_proxy); - search_handler_ = std::make_unique<SearchHandler>(); + search_handler_ = std::make_unique<SearchHandler>( + search_concept_registry_.get(), local_search_service_proxy); } accelerator_configuration_provider_ = std::make_unique<AcceleratorConfigurationProvider>();
diff --git a/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java b/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java index aad543c..6272b36 100644 --- a/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java +++ b/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
@@ -34,9 +34,14 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.OptIn; import androidx.annotation.RequiresApi; +import androidx.core.os.BuildCompat; import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -305,6 +310,32 @@ return null; } + /** + * Sets the mode {@link ActivityOptions#MODE_BACKGROUND_ACTIVITY_START_ALLOWED} to the + * given {@link ActivityOptions}. The options can be used to send {@link PendingIntent} + * passed to Chrome from a backgrounded app. + * @param options {@ActivityOptions} to set the required mode to. + */ + @OptIn(markerClass = androidx.core.os.BuildCompat.PrereleaseSdkCheck.class) + public static void setActivityOptionsBackgroundActivityStartMode( + @NonNull ActivityOptions options) { + if (!BuildCompat.isAtLeastU()) return; + + // options.setPendingIntentBackgroundActivityStartMode( + // ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); + // TODO(crbug.com/1423489): Replace the reflection with the normal API. + try { + Method method = ActivityOptions.class.getMethod( + "setPendingIntentBackgroundActivityStartMode", int.class); + Field field = ActivityOptions.class.getField("MODE_BACKGROUND_ACTIVITY_START_ALLOWED"); + int mode = field.getInt(null); + method.invoke(options, mode); + } catch (IllegalAccessException | InvocationTargetException | NoSuchFieldException + | NoSuchMethodException | RuntimeException e) { + assert false : "PendingIntent from background activity may fail to run."; + } + } + // Access this via ContextUtils.getProcessName(). @SuppressWarnings("PrivateApi") static String getProcessName() {
diff --git a/base/android/jni_generator/golden/testProxyNativesWithNatives.golden b/base/android/jni_generator/golden/testProxyNativesWithNatives.golden index ad3ed1d..0ae45c2 100644 --- a/base/android/jni_generator/golden/testProxyNativesWithNatives.golden +++ b/base/android/jni_generator/golden/testProxyNativesWithNatives.golden
@@ -40,7 +40,6 @@ JNI_GENERATOR_EXPORT void Java_org_chromium_base_natives_GEN_1JNI_org_1chromium_1foo_1Foo_1foo( JNIEnv* env, jclass jcaller) { - TRACE_EVENT0("jni", "JNI_Foo_Foo"); return JNI_Foo_Foo(env); } @@ -57,7 +56,6 @@ jint y, jchar x, jshort z) { - TRACE_EVENT0("jni", "JNI_Foo_Bar"); return JNI_Foo_Bar(env, base::android::JavaParamRef<jstring>(env, s), y, x, z).Release(); } @@ -69,7 +67,6 @@ JNIEnv* env, jclass jcaller, jobjectArray a) { - TRACE_EVENT0("jni", "JNI_Foo_Foobar"); return JNI_Foo_Foobar(env, base::android::JavaParamRef<jobjectArray>(env, a)).Release(); } @@ -78,7 +75,6 @@ jclass jcaller, jlong nativePtr, jobject caller) { - TRACE_EVENT0("jni", "Ptr::Baz"); Ptr* native = reinterpret_cast<Ptr*>(nativePtr); CHECK_NATIVE_PTR(env, jcaller, native, "Baz"); return native->Baz(env, base::android::JavaParamRef<jobject>(env, caller)); @@ -88,7 +84,6 @@ JNIEnv* env, jclass jcaller, jlong nativePtr) { - TRACE_EVENT0("jni", "Ptr::FooBar"); Ptr* native = reinterpret_cast<Ptr*>(nativePtr); CHECK_NATIVE_PTR(env, jcaller, native, "FooBar"); return native->FooBar(env); @@ -98,7 +93,6 @@ JNIEnv* env, jobject jcaller, jlong nativeInstance) { - TRACE_EVENT0("jni", "Instance::InstanceMethod"); Instance* native = reinterpret_cast<Instance*>(nativeInstance); CHECK_NATIVE_PTR(env, jcaller, native, "InstanceMethod"); return native->InstanceMethod(env, base::android::JavaParamRef<jobject>(env, jcaller)); @@ -109,7 +103,6 @@ JNI_GENERATOR_EXPORT void Java_org_chromium_foo_Foo_nativeStaticMethod( JNIEnv* env, jclass jcaller) { - TRACE_EVENT0("jni", "JNI_Foo_StaticMethod"); return JNI_Foo_StaticMethod(env); }
diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py index 0b2aa0bb..163ef3f 100755 --- a/base/android/jni_generator/jni_generator.py +++ b/base/android/jni_generator/jni_generator.py
@@ -1334,7 +1334,6 @@ 'POST_CALL': post_call, 'STUB_NAME': self.helper.GetStubName(native), 'PROFILING_ENTERED_NATIVE': profiling_entered_native, - 'TRACE_EVENT': '', } namespace_qual = self.namespace + '::' if self.namespace else '' @@ -1347,15 +1346,11 @@ 'PARAM0_NAME': native.params[0].name, 'P0_TYPE': native.p0_type, }) - if self.options.enable_tracing: - values['TRACE_EVENT'] = self.GetTraceEventForNameTemplate( - namespace_qual + '${P0_TYPE}::${NAME}', values) template = Template("""\ JNI_GENERATOR_EXPORT ${RETURN} ${STUB_NAME}( JNIEnv* env, ${PARAMS_IN_STUB}) { ${PROFILING_ENTERED_NATIVE}\ -${TRACE_EVENT}\ ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME}); CHECK_NATIVE_PTR(env, jcaller, native, "${NAME}"${OPTIONAL_ERROR_RETURN}); return native->${NAME}(${PARAMS_IN_CALL})${POST_CALL}; @@ -1364,9 +1359,6 @@ else: if values['PARAMS']: values['PARAMS'] = ', ' + values['PARAMS'] - if self.options.enable_tracing: - values['TRACE_EVENT'] = self.GetTraceEventForNameTemplate( - namespace_qual + '${IMPL_METHOD_NAME}', values) template = Template("""\ static ${RETURN_DECLARATION} ${IMPL_METHOD_NAME}(JNIEnv* env${PARAMS}); @@ -1374,7 +1366,6 @@ JNIEnv* env, ${PARAMS_IN_STUB}) { ${PROFILING_ENTERED_NATIVE}\ -${TRACE_EVENT}\ return ${IMPL_METHOD_NAME}(${PARAMS_IN_CALL})${POST_CALL}; } """) @@ -1502,7 +1493,6 @@ ${JNI_SIGNATURE}, &g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}); -${TRACE_EVENT}\ ${PROFILING_LEAVING_NATIVE}\ ${RETURN_DECLARATION} ${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL}, @@ -1517,11 +1507,6 @@ function_header_with_unused_template.substitute(values)) else: values['FUNCTION_HEADER'] = function_header_template.substitute(values) - if self.options.enable_tracing: - values['TRACE_EVENT'] = self.GetTraceEventForNameTemplate( - '${JAVA_NAME_FULL}', values) - else: - values['TRACE_EVENT'] = '' return RemoveIndentedEmptyLines(template.substitute(values)) def GetTraceEventForNameTemplate(self, name_template, values): @@ -1653,10 +1638,6 @@ action='store_true', help='Add additional profiling instrumentation.') parser.add_argument( - '--enable_tracing', - action='store_true', - help='Add TRACE_EVENTs to generated functions.') - parser.add_argument( '--always_mangle', action='store_true', help='Mangle all function names') parser.add_argument('--unchecked_exceptions', action='store_true',
diff --git a/base/android/jni_generator/jni_generator_tests.py b/base/android/jni_generator/jni_generator_tests.py index 93240695..82dd56b4 100755 --- a/base/android/jni_generator/jni_generator_tests.py +++ b/base/android/jni_generator/jni_generator_tests.py
@@ -58,7 +58,6 @@ self.cpp = 'cpp' self.javap = 'mock-javap' self.enable_profiling = False - self.enable_tracing = False self.use_proxy_hash = False self.enable_jni_multiplexing = False self.always_mangle = False @@ -1323,30 +1322,6 @@ JniGeneratorOptions()) self.AssertGoldenTextEquals(jni_from_java.GetContent()) - def testTracing(self): - test_data = """ - package org.chromium.foo; - - @JNINamespace("org::chromium_foo") - class Foo { - - @CalledByNative - Foo(); - - @CalledByNative - void callbackFromNative(); - - native void nativeInstanceMethod(long nativeInstance); - - static native void nativeStaticMethod(); - } - """ - options_with_tracing = JniGeneratorOptions() - options_with_tracing.enable_tracing = True - jni_from_java = jni_generator.JNIFromJavaSource( - test_data, 'org/chromium/foo/Foo', options_with_tracing) - self.AssertGoldenTextEquals(jni_from_java.GetContent()) - def testStaticBindingCaller(self): test_data = """ package org.chromium.foo; @@ -1452,7 +1427,6 @@ } """ options_with_tracing = JniGeneratorOptions() - options_with_tracing.enable_tracing = True jni_from_java = jni_generator.JNIFromJavaSource( test_data, 'org/chromium/foo/Foo', options_with_tracing) self.AssertGoldenTextEquals(jni_from_java.GetContent())
diff --git a/base/task/thread_pool/thread_pool_instance.cc b/base/task/thread_pool/thread_pool_instance.cc index 0251b28..cd0b13d 100644 --- a/base/task/thread_pool/thread_pool_instance.cc +++ b/base/task/thread_pool/thread_pool_instance.cc
@@ -26,8 +26,9 @@ int num_of_efficient_processors = SysInfo::NumberOfEfficientProcessors(); if (num_of_efficient_processors != 0) { DCHECK_GT(num_of_efficient_processors, 0); - return base::clamp<size_t>(static_cast<size_t>(num_of_efficient_processors), - 2, max_num_foreground_threads_in); + return std::max<size_t>( + 2, std::min(max_num_foreground_threads_in, + static_cast<size_t>(num_of_efficient_processors))); } return std::max<size_t>(2, max_num_foreground_threads_in / 2); }
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/HistogramWatcher.java b/base/test/android/javatests/src/org/chromium/base/test/util/HistogramWatcher.java index 31301afe..97a4c65d7e 100644 --- a/base/test/android/javatests/src/org/chromium/base/test/util/HistogramWatcher.java +++ b/base/test/android/javatests/src/org/chromium/base/test/util/HistogramWatcher.java
@@ -173,7 +173,8 @@ * Add an expectation that {@code histogram} will not be recorded with any values. */ public Builder expectNoRecords(String histogram) { - if (mTotalRecordsExpected.getOrDefault(histogram, 0) != 0) { + Integer recordsAlreadyExpected = mTotalRecordsExpected.get(histogram); + if (recordsAlreadyExpected != null && recordsAlreadyExpected != 0) { throw new IllegalStateException( "Cannot expect no records but also expect records in previous calls."); } @@ -203,12 +204,18 @@ } private void incrementRecordsExpected(HistogramAndValue histogramAndValue, int increase) { - int previousCountExpected = mRecordsExpected.getOrDefault(histogramAndValue, 0); + Integer previousCountExpected = mRecordsExpected.get(histogramAndValue); + if (previousCountExpected == null) { + previousCountExpected = 0; + } mRecordsExpected.put(histogramAndValue, previousCountExpected + increase); } private void incrementTotalRecordsExpected(String histogram, int increase) { - int previousCountExpected = mTotalRecordsExpected.getOrDefault(histogram, 0); + Integer previousCountExpected = mTotalRecordsExpected.get(histogram); + if (previousCountExpected == null) { + previousCountExpected = 0; + } mTotalRecordsExpected.put(histogram, previousCountExpected + increase); } } @@ -323,8 +330,11 @@ } boolean allowAnyNumberOfExtraRecords = mHistogramsAllowedExtraRecords.contains(histogram); - int expectedExtraRecords = - mRecordsExpected.getOrDefault(new HistogramAndValue(histogram, ANY_VALUE), 0); + Integer expectedExtraRecords = + mRecordsExpected.get(new HistogramAndValue(histogram, ANY_VALUE)); + if (expectedExtraRecords == null) { + expectedExtraRecords = 0; + } if (!allowAnyNumberOfExtraRecords && actualExtraRecords > expectedExtraRecords || actualExtraRecords < expectedExtraRecords) { // Expected |extraRecordsExpected| records with any value, found |extraActualRecords|.
diff --git a/build/android/gyp/javac_output_processor.py b/build/android/gyp/javac_output_processor.py index c89980c..8071fc2 100755 --- a/build/android/gyp/javac_output_processor.py +++ b/build/android/gyp/javac_output_processor.py
@@ -166,14 +166,9 @@ if len(class_entries) == 1: return class_entries - # android_library_factory() targets set low_classpath_priority=true, and any - # target that is the "impl" side of a target that uses jar_excluded_patterns - # should use this as well. - # We should generally always suggest depending on the non-impl library - # target. - # TODO(crbug.com/1296711): Also use "visibility" a hint here. - low_entries = [x for x in class_entries if x.low_classpath_priority] - class_entries = low_entries or class_entries + # When some deps are preferred, ignore all other potential deps. + preferred_entries = [x for x in class_entries if x.preferred_dep] + class_entries = preferred_entries or class_entries # E.g. javax_annotation_jsr250_api_java. jsr_entries = [x for x in class_entries if 'jsr' in x.target]
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py index b6eae72..ae55744 100755 --- a/build/android/gyp/write_build_config.py +++ b/build/android/gyp/write_build_config.py
@@ -334,6 +334,12 @@ The list of paths to all `deps_info['dex_path']` entries for all libraries that comprise this APK. Valid only for debug builds. +* `deps_info['preferred_dep']`: +Whether the target should be the preferred dep. This is usually the case when we +have a java_group that depends on either the public or internal dep accordingly, +and it is better to depend on the group rather than the underlying dep. Another +case is for android_library_factory targets, the factory target should be +preferred instead of the actual implementation. ## <a name="target_robolectric_binary">Target type `robolectric_binary`</a>: @@ -1073,8 +1079,12 @@ parser.add_option('--treat-as-locale-paks', action='store_true', help='Consider the assets as locale paks in BuildConfig.java') - # java library options + # java library and group options + parser.add_option('--preferred-dep', + action='store_true', + help='Whether the target should be preferred as a dep.') + # java library options parser.add_option('--public-deps-configs', help='GN list of config files of deps which are exposed as ' 'part of the target\'s public API.') @@ -1422,6 +1432,9 @@ deps_info['is_prebuilt'] = bool(options.is_prebuilt) deps_info['gradle_treat_as_prebuilt'] = options.gradle_treat_as_prebuilt + if options.preferred_dep: + deps_info['preferred_dep'] = bool(options.preferred_dep) + if options.android_manifest: deps_info['android_manifest'] = options.android_manifest
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni index e90fd0b2..4643970 100644 --- a/build/config/android/internal_rules.gni +++ b/build/config/android/internal_rules.gni
@@ -221,6 +221,10 @@ _target_label, ] + if (defined(invoker.preferred_dep) && invoker.preferred_dep) { + args += [ "--preferred-dep" ] + } + if (defined(invoker.aar_path)) { args += [ "--aar-path", @@ -3599,6 +3603,7 @@ "base_allowlist_rtxt_path", "gradle_treat_as_prebuilt", "input_jars_paths", + "preferred_dep", "low_classpath_priority", "main_class", "mergeable_android_manifests",
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni index b76b3daa..aa302657 100644 --- a/build/config/android/rules.gni +++ b/build/config/android/rules.gni
@@ -16,10 +16,6 @@ import("//build/toolchain/toolchain.gni") assert(is_android || is_robolectric) -declare_args() { - enable_jni_tracing = false -} - # Use a dedicated include dir so that files can #include headers from other # toolchains without affecting non-JNI #includes. if (target_os == "android") { @@ -251,9 +247,6 @@ if (enable_profiling) { args += [ "--enable_profiling" ] } - if (enable_jni_tracing) { - args += [ "--enable_tracing" ] - } if (current_toolchain != default_toolchain && target_os == "android") { # Rather than regenerating .h files in secondary toolchains, re-use the # ones from the primary toolchain by depending on it and adding the @@ -1324,6 +1317,7 @@ forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) _build_config_vars = [ "input_jars_paths", + "preferred_dep", "mergeable_android_manifests", "proguard_configs", "requires_android",
diff --git a/build/sanitizers/lsan_suppressions.cc b/build/sanitizers/lsan_suppressions.cc index 05459c08..dceab4b 100644 --- a/build/sanitizers/lsan_suppressions.cc +++ b/build/sanitizers/lsan_suppressions.cc
@@ -69,9 +69,6 @@ // impossible, i.e. when enabling leak detection for the first time for a // test target with pre-existing leaks. - // https://crbug.com/755670 - "leak:third_party/yasm/\n" - // v8 leaks caused by weak ref not call "leak:blink::DOMWrapperWorld::Create\n" "leak:blink::ScriptState::Create\n"
diff --git a/build/sanitizers/tsan_suppressions.cc b/build/sanitizers/tsan_suppressions.cc index c833892..984ebbb 100644 --- a/build/sanitizers/tsan_suppressions.cc +++ b/build/sanitizers/tsan_suppressions.cc
@@ -61,14 +61,6 @@ // http://crbug.com/380554 "deadlock:g_type_add_interface_static\n" - // http://crbug.com/397022 - "deadlock:" - "base::trace_event::TraceEventTestFixture_ThreadOnceBlocking_Test::" - "TestBody\n" - - // http://crbug.com/415472 - "deadlock:base::trace_event::TraceLog::GetCategoryGroupEnabled\n" - // Lock inversion in third party code, won't fix. // https://crbug.com/455638 "deadlock:dbus::Bus::ShutdownAndBlock\n"
diff --git a/chrome/android/features/android_library_factory_tmpl.gni b/chrome/android/features/android_library_factory_tmpl.gni index c685d444..1aaaccb 100644 --- a/chrome/android/features/android_library_factory_tmpl.gni +++ b/chrome/android/features/android_library_factory_tmpl.gni
@@ -97,8 +97,14 @@ sources = get_target_outputs(":$_process_factory_target_name") jar_excluded_patterns = [ "*" ] - # Mark library as low priority so that android_library() which provides real implementation - # does not need to depend on factory. + # Mark library as low priority since this one does not contain the actual + # impl class. The real android_library() which provides the real + # implementation should be first on the classpath. low_classpath_priority = true + + # Although this factory library does not contain the actual impl class, it + # should still be the one that almost all targets depend on. This is so the + # real implementation can be provided later. + preferred_dep = true } }
diff --git a/chrome/android/features/keyboard_accessory/BUILD.gn b/chrome/android/features/keyboard_accessory/BUILD.gn index 7f05e2ef..cb8298b 100644 --- a/chrome/android/features/keyboard_accessory/BUILD.gn +++ b/chrome/android/features/keyboard_accessory/BUILD.gn
@@ -183,5 +183,6 @@ "//ui/android:ui_full_java", "//ui/android:ui_java_test_support", "//ui/android:ui_utils_java", + "//ui/base/ime/mojom:mojom_java", ] }
diff --git a/chrome/android/features/keyboard_accessory/internal/BUILD.gn b/chrome/android/features/keyboard_accessory/internal/BUILD.gn index 6dbb436..aa3d1b4 100644 --- a/chrome/android/features/keyboard_accessory/internal/BUILD.gn +++ b/chrome/android/features/keyboard_accessory/internal/BUILD.gn
@@ -48,6 +48,7 @@ "//third_party/androidx:androidx_viewpager_viewpager_java", "//ui/android:ui_java", "//ui/android:ui_utils_java", + "//ui/base/ime/mojom:mojom_java", "//url:gurl_java", ] sources = [
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java index 9d6830f..124d02f 100644 --- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java +++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
@@ -67,17 +67,17 @@ import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState; import org.chromium.components.browser_ui.bottomsheet.BottomSheetObserver; import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver; -import org.chromium.components.browser_ui.widget.InsetObserverView; -import org.chromium.components.browser_ui.widget.InsetObserverViewSupplier; import org.chromium.components.browser_ui.widget.gesture.BackPressHandler; import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContentsAccessibility; import org.chromium.ui.DropdownPopupWindow; +import org.chromium.ui.base.ApplicationViewportInsetSupplier; import org.chromium.ui.base.ViewUtils; import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.modelutil.PropertyKey; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyObservable; +import org.chromium.ui.mojom.VirtualKeyboardMode; import java.util.HashSet; @@ -95,8 +95,8 @@ private SparseArray<AccessorySheetTabCoordinator> mSheets = new SparseArray<>(); private PropertyModel mModel = ManualFillingProperties.createFillingModel(); private WindowAndroid mWindowAndroid; - private Supplier<InsetObserverView> mInsetObserverViewSupplier; - private final ObservableSupplierImpl<Integer> mViewportInsetSupplier = + private ApplicationViewportInsetSupplier mApplicationViewportInsetSupplier; + private final ObservableSupplierImpl<Integer> mBottomInsetSupplier = new ObservableSupplierImpl<>(); private final ManualFillingStateCache mStateCache = new ManualFillingStateCache(); private final HashSet<Tab> mObservedTabs = new HashSet<>(); @@ -143,7 +143,7 @@ /** Default constructor */ ManualFillingMediator() { - mViewportInsetSupplier.set(0); + mBottomInsetSupplier.set(0); } void initialize(KeyboardAccessoryCoordinator keyboardAccessory, @@ -163,7 +163,7 @@ mAccessorySheet = accessorySheet; mAccessorySheet.setOnPageChangeListener(mKeyboardAccessory.getOnPageChangeListener()); mAccessorySheet.setHeight(getIdealSheetHeight()); - setInsetObserverViewSupplier(InsetObserverViewSupplier.from(mWindowAndroid)); + mApplicationViewportInsetSupplier = mWindowAndroid.getApplicationBottomInsetSupplier(); mActivity.findViewById(android.R.id.content).addOnLayoutChangeListener(this); mBackPressManager = backPressManager; mBackPressChangedSupplier.set(shouldHideOnBackPress()); @@ -204,7 +204,7 @@ } ObservableSupplier<Integer> getBottomInsetSupplier() { - return mViewportInsetSupplier; + return mBottomInsetSupplier; } @Override @@ -388,16 +388,29 @@ if (mActivity == null) return false; WebContents webContents = mActivity.getCurrentWebContents(); if (webContents == null || webContents.isDestroyed()) return false; - float height = webContents.getHeight(); // In dip. Includes top control elements only. + float height = webContents.getHeight(); // In dip. Already insetted by top/bottom controls. - // WebContents height ignores the soft keyboard — subtract the keyboard height: - height -= mWindowAndroid.getApplicationBottomInsetSupplier().get() - / mWindowAndroid.getDisplay().getDipScale(); - - // Don't consider the impact of the accessory as shown already. If we have space for a bar, - // we continue to have it. The sheet is never bigger than the an open keyboard — so if an - // open sheet affects the inset, we can safely ignore it, too. - height += mViewportInsetSupplier.get() / mWindowAndroid.getDisplay().getDipScale(); + // TODO(https://crbug.com/1211066): This class shouldn't know about virtual keyboard mode. + // Move the browser controls into ApplicationViewportInsetSupplier and then do a simple + // inset on the CompositorViewHolder size (rather than WebContents) to remove this + // dependency. + if (mApplicationViewportInsetSupplier.getVirtualKeyboardMode() + == VirtualKeyboardMode.RESIZES_CONTENT) { + // If RESIZES_CONTENT is set, the soft keyboard and accessory *do* resize the + // webContents. Don't consider the impact of the accessory as shown already. If we have + // space for a bar, we continue to have it. The sheet is never bigger than an open + // keyboard — so if an open sheet affects the inset, we can safely ignore it, too. + height += mBottomInsetSupplier.get() / mWindowAndroid.getDisplay().getDipScale(); + } else { + // If the mode is RESIZES_VISUAL or OVERLAYS_CONTENT then WebContents is not resized by + // the soft keyboard or the accessory. Subtract the keyboard height to get the visible + // area but ignore the accessory height for the reasons in the above comment. + if (getContentView() != null && getContentView().getRootView() != null) { + height -= mSoftKeyboardDelegate.calculateSoftKeyboardHeight( + getContentView().getRootView()) + / mWindowAndroid.getDisplay().getDipScale(); + } + } return height >= MINIMAL_AVAILABLE_VERTICAL_SPACE // Allows for a bar if not shown yet. && webContents.getWidth() >= MINIMAL_AVAILABLE_HORIZONTAL_SPACE; @@ -634,7 +647,7 @@ newControlsOffset += mAccessorySheet.getHeight(); } mKeyboardAccessory.setBottomOffset(newControlsOffset); - mViewportInsetSupplier.set(newControlsHeight); + mBottomInsetSupplier.set(newControlsHeight); } /** @@ -693,16 +706,12 @@ /** * Uses the keyboard (if available) to determine the height of the accessory sheet. - * @param rootView Root view of the current content view -- used to estimate the height unless - * the more reliable InsetObserver is available. + * @param rootView Root view of the current content view. * @return The estimated keyboard height or enough space to display at least three suggestions. */ private @Px int calculateAccessorySheetHeight(View rootView) { - InsetObserverView insetObserver = mInsetObserverViewSupplier.get(); int minimalSheetHeight = getIdealSheetHeight(); - int newSheetHeight = insetObserver != null - ? insetObserver.getSystemWindowInsetsBottom() - : mSoftKeyboardDelegate.calculateSoftKeyboardHeight(rootView); + int newSheetHeight = mSoftKeyboardDelegate.calculateSoftKeyboardHeight(rootView); newSheetHeight += ChromeFeatureList.isEnabled(AUTOFILL_KEYBOARD_ACCESSORY) ? getHeaderHeight() : 0; newSheetHeight = Math.max(newSheetHeight, minimalSheetHeight); @@ -719,10 +728,28 @@ float density = mWindowAndroid.getDisplay().getDipScale(); // The maximal height for the sheet ensures a minimal amount of WebContents space. @Px - int maxHeight = Math.round(density * webContents.getHeight()); - maxHeight -= Math.round(density * MINIMAL_AVAILABLE_VERTICAL_SPACE); - if (mAccessorySheet.getHeight() <= maxHeight) return; // Sheet height needs no adjustment! - mAccessorySheet.setHeight(maxHeight); + int visibleViewportHeightPx = Math.round(density * webContents.getHeight()); + // TODO(bokan): This class shouldn't know about virtual keyboard mode. Move the browser + // controls into ApplicationViewportInsetSupplier and then do a simple inset on the + // CompositorViewHolder size (rather than WebContents) to remove this dependency. + // https://crbug.com/1211066. + if (mApplicationViewportInsetSupplier.getVirtualKeyboardMode() + != VirtualKeyboardMode.RESIZES_CONTENT) { + // If the mode is RESIZES_VISUAL or OVERLAYS_CONTENT then WebContents is not resized by + // the soft keyboard or the accessory. + if (getContentView() != null && getContentView().getRootView() != null) { + visibleViewportHeightPx -= mSoftKeyboardDelegate.calculateSoftKeyboardHeight( + getContentView().getRootView()); + visibleViewportHeightPx -= mBottomInsetSupplier.get(); + } + } + int minimumVerticalSpacePx = Math.round(density * MINIMAL_AVAILABLE_VERTICAL_SPACE); + if (visibleViewportHeightPx >= minimumVerticalSpacePx) return; // Sheet height needs no adjustment! + + // Adjust the height such that the new visible height will be exactly + // MINIMAL_AVAILABLE_VERTICAL_SPACE. + mAccessorySheet.setHeight( + visibleViewportHeightPx + mAccessorySheet.getHeight() - minimumVerticalSpacePx); changeBottomControlSpaceForState(mModel.get(KEYBOARD_EXTENSION_STATE)); } @@ -901,11 +928,6 @@ } @VisibleForTesting - void setInsetObserverViewSupplier(Supplier<InsetObserverView> insetObserverViewSupplier) { - mInsetObserverViewSupplier = insetObserverViewSupplier; - } - - @VisibleForTesting TabModelObserver getTabModelObserverForTesting() { return mTabModelObserver; }
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/tab_layout_component/KeyboardAccessoryButtonGroupView.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/tab_layout_component/KeyboardAccessoryButtonGroupView.java index 36a14091..4694a7e 100644 --- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/tab_layout_component/KeyboardAccessoryButtonGroupView.java +++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/tab_layout_component/KeyboardAccessoryButtonGroupView.java
@@ -12,6 +12,7 @@ import android.widget.LinearLayout; import androidx.annotation.VisibleForTesting; +import androidx.recyclerview.widget.RecyclerView; import org.chromium.chrome.browser.keyboard_accessory.R; import org.chromium.components.browser_ui.styles.SemanticColorUtils; @@ -73,6 +74,18 @@ addView(button); } + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + // The parent, which is KeyboardAccessoryModernView's recycler view may measure + // StickyLastItemDecoration offsets before the buttons are added. Notify the parent to + // recalculate the offset during each measurement. + // TODO(crbug.com/1424789): Find a better alternative. + if (getParent() == null || !(getParent() instanceof RecyclerView)) return; + RecyclerView parent = (RecyclerView) getParent(); + parent.post(parent::invalidateItemDecorations); + } + void removeAllButtons() { mButtons.clear(); removeAllViews();
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingIntegrationTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingIntegrationTest.java index 042e452..482e70b 100644 --- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingIntegrationTest.java +++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingIntegrationTest.java
@@ -447,7 +447,6 @@ @Test @SmallTest - @DisabledTest(message = "Broken with OSKResizesVisualViewportByDefault - crbug.com/1384433") public void testMovesUpSnackbar() throws TimeoutException { final String kSnackbarText = "snackbar";
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingTestHelper.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingTestHelper.java index 9e8f9ab..c10fe30 100644 --- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingTestHelper.java +++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingTestHelper.java
@@ -137,8 +137,6 @@ TestThreadUtils.runOnUiThreadBlocking(() -> { ChromeActivity activity = mActivityTestRule.getActivity(); mWebContentsRef.set(activity.getActivityTab().getWebContents()); - getManualFillingCoordinator().getMediatorForTesting().setInsetObserverViewSupplier( - () -> getKeyboard().createInsetObserver(activity.getApplicationContext())); // The TestInputMethodManagerWrapper intercepts showSoftInput so that a keyboard is // never brought up. final ImeAdapter imeAdapter = ImeAdapter.fromWebContents(mWebContentsRef.get());
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java index a94fb0c..e9d0ad0 100644 --- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java +++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
@@ -93,6 +93,7 @@ import org.chromium.ui.base.ApplicationViewportInsetSupplier; import org.chromium.ui.display.DisplayAndroid; import org.chromium.ui.modelutil.PropertyModel; +import org.chromium.ui.mojom.VirtualKeyboardMode; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -149,8 +150,6 @@ private final UserDataHost mUserDataHost = new UserDataHost(); private final ApplicationViewportInsetSupplier mInsetSupplier = ApplicationViewportInsetSupplier.createForTests(); - private final ObservableSupplierImpl<Integer> mKeyboardInsetSupplier = - new ObservableSupplierImpl<>(); private static class MockActivityTabProvider extends ActivityTabProvider { public Tab mTab; @@ -306,7 +305,6 @@ UmaRecorderHolder.resetForTesting(); MockitoAnnotations.initMocks(this); when(mMockWindow.getActivity()).thenReturn(new WeakReference<>(mMockActivity)); - mInsetSupplier.addStackingSupplier(mKeyboardInsetSupplier); when(mMockWindow.getApplicationBottomInsetSupplier()).thenReturn(mInsetSupplier); when(mMockSoftKeyboardDelegate.calculateSoftKeyboardHeight(any())).thenReturn(0); when(mMockActivity.getTabModelSelector()).thenReturn(mMockTabModelSelector); @@ -734,6 +732,7 @@ @Test public void testDisplaysAccessoryOnlyWhenVerticalSpaceIsSufficient() { + mInsetSupplier.setVirtualKeyboardMode(VirtualKeyboardMode.RESIZES_CONTENT); reset(mMockKeyboardAccessory); addBrowserTab(mMediator, 1234, null); SheetProviderHelper tabHelper = new SheetProviderHelper(); @@ -756,6 +755,17 @@ // Use a height that is too small but with a valid width (e.g. resized multi-window window). simulateLayoutSizeChange(2.0f, 300, 79); assertThat(mModel.get(KEYBOARD_EXTENSION_STATE), is(HIDDEN)); + + // Also test in RESIZES_VISUAL mode where the keyboard and accessory won't resize the + // WebContents. + mInsetSupplier.setVirtualKeyboardMode(VirtualKeyboardMode.RESIZES_VISUAL); + when(mMockSoftKeyboardDelegate.calculateSoftKeyboardHeight(any())) + .thenReturn(sFakeKeyboardInsetPx); + simulateLayoutSizeChange(2.0f, 300, sFakeKeyboardInsetPx / 2 + 48 + 79); + assertThat(mModel.get(KEYBOARD_EXTENSION_STATE), is(HIDDEN)); + + simulateLayoutSizeChange(2.0f, 300, sFakeKeyboardInsetPx / 2 + 48 + 80); + assertThat(mModel.get(KEYBOARD_EXTENSION_STATE), not(is(HIDDEN))); } @Test @@ -780,17 +790,28 @@ assertThat(mModel.get(KEYBOARD_EXTENSION_STATE), is(HIDDEN)); } + /** + * This tests the case where an accessory sheet is showing instead of a keyboard. The screen is + * rotated so that the amount of vertical space shrinks below the minimum allowed. Confirm that + * the accessory sheet's height is shrunken. + */ @Test public void testRestrictsSheetSizeIfVerticalSpaceChanges() { + final int density = 2; + final int accessorySheetHeightDp = 100; // The height of a large keyboard. + final int minimumVisibleHeightDp = 128; // This is a constant from ManualFillingMediator. + addBrowserTab(mMediator, 1234, null); - // Resize the screen from 300x128@2.f to 300x200@2.f. - setContentAreaDimensions(2.f, 200, 300); + + // Resize the screen from 128x300@2.f to 200x300@2.f. + setContentAreaDimensions(density, 200, 300); mMediator.onLayoutChange(mMockContentView, 0, 0, 400, 600, 0, 0, 256, 600); + // No simulate showing the accessory sheet. when(mMockKeyboardAccessory.empty()).thenReturn(false); when(mMockKeyboardAccessory.isShown()).thenReturn(true); when(mMockKeyboardAccessory.hasActiveTab()).thenReturn(true); - when(mMockAccessorySheet.getHeight()).thenReturn(200); // Return height of a large keyboard. + when(mMockAccessorySheet.getHeight()).thenReturn(accessorySheetHeightDp * density); mModel.set(SHOW_WHEN_VISIBLE, true); mModel.set(KEYBOARD_EXTENSION_STATE, FLOATING_SHEET_V2); mController.registerSheetDataProvider( @@ -801,14 +822,68 @@ when(mMockKeyboardAccessory.isShown()).thenReturn(true); when(mMockKeyboardAccessory.hasActiveTab()).thenReturn(true); when(mMockAccessorySheet.isShown()).thenReturn(true); - when(mMockAccessorySheet.getHeight()).thenReturn(200); // Return height of a large keyboard. + when(mMockAccessorySheet.getHeight()).thenReturn(accessorySheetHeightDp * density); - // Set layout as if it was rotated: 200x300@2f. - mKeyboardInsetSupplier.set(sFakeKeyboardInsetPx); // Mediator has to check the density! - setContentAreaDimensions(2.f, 300, 200); - mMediator.onLayoutChange(mMockContentView, 0, 0, 600, 104, 0, 0, 400, 600); - verify(mMockAccessorySheet).setHeight(144); // == 2f * (200dp - 128dp) - mKeyboardInsetSupplier.set(0); + // Set layout as if it was rotated: 300x200@2f. The sheet does not inset WebContents since + // we're in the default RESIZES_VISUAL VirtualKeyboardMode. Even though contentsHeightDp > + // minimumVisibleHeightDp, test that the visible area is correctly deduced to be 200 - 100 < + // minimumVisibleHeightDp so the sheet should be restricted in height. + setContentAreaDimensions(density, 300, 200); + mMediator.onLayoutChange(mMockContentView, 0, 0, 600, 400, 0, 0, 400, 600); + + // 200 - 128 = 72 + int expectedSheetHeightDp = 200 - minimumVisibleHeightDp; + verify(mMockAccessorySheet).setHeight(density * expectedSheetHeightDp); + } + + /** + * This tests the case where an accessory sheet is showing instead of a keyboard. The screen is + * rotated so that the amount of vertical space shrinks below the minimum allowed. Confirm that + * the accessory sheet's height is shrunken. + * + * This is the same test as above but with the keyboard in RESIZES_CONTENT mode, so that the + * WebContents height is insetted by the keyboard and its accessories. + */ + @Test + public void testRestrictsSheetSizeIfVerticalSpaceChangesWithResizesContent() { + final int density = 2; + final int accessorySheetHeightDp = 100; // The height of a large keyboard. + final int minimumVisibleHeightDp = 128; // This is a constant from ManualFillingMediator. + + mInsetSupplier.setVirtualKeyboardMode(VirtualKeyboardMode.RESIZES_CONTENT); + addBrowserTab(mMediator, 1234, null); + // Resize the screen from 128x300@2.f to 200x300@2.f. + setContentAreaDimensions(density, 200, 300); + mMediator.onLayoutChange(mMockContentView, 0, 0, 400, 600, 0, 0, 256, 600); + + // No simulate showing the accessory sheet. + when(mMockKeyboardAccessory.empty()).thenReturn(false); + when(mMockKeyboardAccessory.isShown()).thenReturn(true); + when(mMockKeyboardAccessory.hasActiveTab()).thenReturn(true); + when(mMockAccessorySheet.getHeight()).thenReturn(accessorySheetHeightDp * density); + mModel.set(SHOW_WHEN_VISIBLE, true); + mModel.set(KEYBOARD_EXTENSION_STATE, FLOATING_SHEET_V2); + mController.registerSheetDataProvider( + mLastMockWebContents, AccessoryTabType.PASSWORDS, new PropertyProvider<>()); + reset(mMockKeyboardAccessory, mMockAccessorySheet); + + when(mMockKeyboardAccessory.empty()).thenReturn(false); + when(mMockKeyboardAccessory.isShown()).thenReturn(true); + when(mMockKeyboardAccessory.hasActiveTab()).thenReturn(true); + when(mMockAccessorySheet.isShown()).thenReturn(true); + when(mMockAccessorySheet.getHeight()).thenReturn(accessorySheetHeightDp * density); + + // Set layout as if it was rotated: 300x200@2f. The sheet causes a resize to the web + // contents so subtract the sheet height. contentsHeightDp < minimumVisibleHeightDp so the + // sheet should be restricted in height. + int contentsHeightDp = 200 - accessorySheetHeightDp; + setContentAreaDimensions(density, 300, contentsHeightDp); + mMediator.onLayoutChange(mMockContentView, 0, 0, 600, 400, 0, 0, 400, 600); + + // 100 + 100 - 128 = 72 + int expectedSheetHeightDp = + contentsHeightDp + accessorySheetHeightDp - minimumVisibleHeightDp; + verify(mMockAccessorySheet).setHeight(density * expectedSheetHeightDp); } @Test
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewBinder.java index 28feb37..e89d234 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewBinder.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewBinder.java
@@ -65,6 +65,10 @@ model.get(TabProperties.TAB_CLOSED_LISTENER).run(tabId); }); } + } else if (TabProperties.CLOSE_BUTTON_DESCRIPTION_STRING == propertyKey) { + fastView.findViewById(R.id.end_button) + .setContentDescription( + model.get(TabProperties.CLOSE_BUTTON_DESCRIPTION_STRING)); } else if (TabProperties.IS_SELECTED == propertyKey) { int selectedTabBackground = model.get(TabProperties.SELECTED_TAB_BACKGROUND_DRAWABLE_ID);
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java index 111750a..47cbacb 100644 --- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java +++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
@@ -627,6 +627,24 @@ @Test @MediumTest @UiThreadTest + @Features.EnableFeatures(ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID) + public void testCloseButtonDescription() { + ImageView listActionButton = mTabListView.findViewById(R.id.end_button); + ImageView gridActionButton = mTabGridView.findViewById(R.id.action_button); + + Assert.assertNull(listActionButton.getContentDescription()); + Assert.assertNull(gridActionButton.getContentDescription()); + + String closeTabDescription = "Close tab"; + mGridModel.set(TabProperties.CLOSE_BUTTON_DESCRIPTION_STRING, closeTabDescription); + + Assert.assertEquals(closeTabDescription, listActionButton.getContentDescription()); + Assert.assertEquals(closeTabDescription, gridActionButton.getContentDescription()); + } + + @Test + @MediumTest + @UiThreadTest public void testClickToClose() { ImageView gridActionButton = mTabGridView.findViewById(R.id.action_button); ImageView listActionButton = mTabListView.findViewById(R.id.end_button);
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java index 3e8c4c0..65174a51 100644 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java +++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
@@ -133,7 +133,7 @@ private @Nullable PropertyModelChangeProcessor<PropertyModel, SectionHeaderView, PropertyKey> mStickySectionHeaderModelChangeProcessor; // Feed RecyclerView/xSurface fields. - private @Nullable NtpListContentManager mContentManager; + private @Nullable FeedListContentManager mContentManager; private @Nullable RecyclerView mRecyclerView; private @Nullable SurfaceScope mSurfaceScope; private @Nullable FeedSurfaceScopeDependencyProvider mDependencyProvider; @@ -676,7 +676,7 @@ } private RecyclerView setUpView() { - mContentManager = new NtpListContentManager(); + mContentManager = new FeedListContentManager(); ProcessScope processScope = FeedSurfaceTracker.getInstance().getXSurfaceProcessScope(); if (processScope != null) { mDependencyProvider = new FeedSurfaceScopeDependencyProvider( @@ -746,8 +746,8 @@ return mHybridListRenderer; } - /** @return The {@link NtpListContentManager} managing the contents of this feed. */ - NtpListContentManager getContentManager() { + /** @return The {@link FeedListContentManager} managing the contents of this feed. */ + FeedListContentManager getContentManager() { return mContentManager; } @@ -808,7 +808,7 @@ private void setHeaders(List<View> headerViews) { // Build the list of headers we want, and then replace existing headers. - List<NtpListContentManager.FeedContent> headerList = new ArrayList<>(); + List<FeedListContentManager.FeedContent> headerList = new ArrayList<>(); for (View header : headerViews) { // Feed header view in multi does not need padding added. int lateralPaddingsPx = getLateralPaddingsPx(); @@ -816,8 +816,8 @@ lateralPaddingsPx = 0; } - NtpListContentManager.NativeViewContent content = - new NtpListContentManager.NativeViewContent( + FeedListContentManager.NativeViewContent content = + new FeedListContentManager.NativeViewContent( lateralPaddingsPx, "Header" + header.hashCode(), header); headerList.add(content); }
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java index 71759b76..0aad038 100644 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java +++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
@@ -610,7 +610,7 @@ mainFeedStream.addOnContentChangedListener(new ContentChangedListener() { @Override public void onContentChanged( - List<NtpListContentManager.FeedContent> feedContents) { + List<FeedListContentManager.FeedContent> feedContents) { if (feedContents.size() > mHeaderCount + 1) { followingHeaderModel.set( SectionHeaderProperties.ANIMATION_START_KEY, true); @@ -624,7 +624,7 @@ } /** - * Binds a stream to the {@link NtpListContentManager}. Unbinds currently active stream if + * Binds a stream to the {@link FeedListContentManager}. Unbinds currently active stream if * different from new stream. Once bound, the stream can add/remove contents. */ @VisibleForTesting
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java index 426f95f..0ac14d0b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -235,6 +235,7 @@ import org.chromium.printing.PrintingControllerImpl; import org.chromium.ui.UiUtils; import org.chromium.ui.base.ActivityWindowAndroid; +import org.chromium.ui.base.ApplicationViewportInsetSupplier; import org.chromium.ui.base.Clipboard; import org.chromium.ui.base.DeviceFormFactor; import org.chromium.ui.base.PageTransition; @@ -645,8 +646,7 @@ getControlContainerHeightResource()); mBottomContainer.initialize(getBrowserControlsManager(), - getWindowAndroid().getApplicationBottomInsetSupplier(), - mManualFillingComponentSupplier.get().getBottomInsetSupplier()); + getWindowAndroid().getApplicationBottomInsetSupplier()); ShareDelegate shareDelegate = new ShareDelegateImpl(mRootUiCoordinator.getBottomSheetController(), @@ -2038,11 +2038,16 @@ compositorViewHolder.setControlContainer(controlContainer); compositorViewHolder.setBrowserControlsManager(mBrowserControlsManagerSupplier.get()); compositorViewHolder.setUrlBar(urlBar); - compositorViewHolder.setInsetObserverView(mInsetObserverViewSupplier.get()); + + ApplicationViewportInsetSupplier insetSupplier = + getWindowAndroid().getApplicationBottomInsetSupplier(); + insetSupplier.setKeyboardInsetSupplier( + mInsetObserverViewSupplier.get().getSupplierForBottomInset()); + insetSupplier.setKeyboardAccessoryInsetSupplier( + mManualFillingComponentSupplier.get().getBottomInsetSupplier()); + compositorViewHolder.setApplicationViewportInsetSupplier(insetSupplier); compositorViewHolder.setAutofillUiBottomInsetSupplier( mManualFillingComponentSupplier.get().getBottomInsetSupplier()); - compositorViewHolder.setApplicationViewportInsetSupplier( - getWindowAndroid().getApplicationBottomInsetSupplier()); compositorViewHolder.setTopUiThemeColorProvider( mRootUiCoordinator.getTopUiThemeColorProvider());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/creator/CreatorActionDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/app/creator/CreatorActionDelegateImpl.java index c2805bf..9d707dfb 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/app/creator/CreatorActionDelegateImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/app/creator/CreatorActionDelegateImpl.java
@@ -13,7 +13,9 @@ import org.chromium.chrome.browser.bookmarks.BookmarkUtils; import org.chromium.chrome.browser.creator.CreatorCoordinator; import org.chromium.chrome.browser.feed.FeedActionDelegate; +import org.chromium.chrome.browser.feed.signinbottomsheet.SigninBottomSheetCoordinator; import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.signin.SyncConsentActivityLauncherImpl; import org.chromium.chrome.browser.tab.TabLaunchType; import org.chromium.chrome.browser.tabmodel.document.TabDelegate; import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; @@ -72,12 +74,15 @@ @Override public void showSyncConsentActivity(int signinAccessPoint) { - // TODO(crbug.com/1422024) + SyncConsentActivityLauncherImpl.get().launchActivityIfAllowed( + mActivityContext, signinAccessPoint); } @Override public void showSignInInterstitial(int signinAccessPoint, BottomSheetController mBottomSheetController, WindowAndroid mWindowAndroid) { - // TODO(crbug.com/1422024) + SigninBottomSheetCoordinator signinCoordinator = + new SigninBottomSheetCoordinator(mWindowAndroid, mBottomSheetController, mProfile); + signinCoordinator.show(); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/creator/CreatorActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/creator/CreatorActivity.java index f8104f0..9e09f597 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/app/creator/CreatorActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/app/creator/CreatorActivity.java
@@ -30,6 +30,7 @@ import org.chromium.chrome.browser.tabmodel.document.TabDelegate; import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator; +import org.chromium.components.signin.metrics.SigninAccessPoint; import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.content_public.browser.WebContents; import org.chromium.ui.base.ActivityWindowAndroid; @@ -98,7 +99,8 @@ CreatorCoordinator coordinator = new CreatorCoordinator(this, mWebFeedId, getSnackbarManager(), mWindowAndroid, mProfile, url, this::createWebContents, - this::createNewTab, mTabShareDelegateSupplier, mEntryPoint, following); + this::createNewTab, mTabShareDelegateSupplier, mEntryPoint, following, + this::showSignInInterstitial); mBottomSheetController = coordinator.getBottomSheetController(); @@ -150,4 +152,10 @@ public void createNewTab(LoadUrlParams params) { new TabDelegate(false).createNewTab(params, TabLaunchType.FROM_LINK, null); } + + // This implements the SignInInterstitialInitiator interface. + public void showSignInInterstitial() { + mCreatorActionDelegate.showSignInInterstitial( + SigninAccessPoint.CREATOR_FEED_FOLLOW, mBottomSheetController, mWindowAndroid); + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java index fdf28a56..5ff0fb8 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
@@ -69,7 +69,6 @@ import org.chromium.chrome.browser.toolbar.ControlContainer; import org.chromium.chrome.browser.util.ChromeAccessibilityUtil; import org.chromium.chrome.features.start_surface.StartSurfaceUserData; -import org.chromium.components.browser_ui.widget.InsetObserverView; import org.chromium.components.browser_ui.widget.TouchEventObserver; import org.chromium.components.content_capture.OnscreenContentProvider; import org.chromium.components.embedder_support.view.ContentView; @@ -82,6 +81,7 @@ import org.chromium.ui.base.EventForwarder; import org.chromium.ui.base.EventOffsetHandler; import org.chromium.ui.base.ViewUtils; +import org.chromium.ui.base.ViewportInsets; import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.mojom.VirtualKeyboardMode; import org.chromium.ui.resources.ResourceManager; @@ -101,9 +101,8 @@ */ public class CompositorViewHolder extends FrameLayout implements ContentOffsetProvider, LayoutManagerHost, LayoutRenderHost, Invalidator.Host, - BrowserControlsStateProvider.Observer, InsetObserverView.WindowInsetObserver, - ChromeAccessibilityUtil.Observer, TabObscuringHandler.Observer, - ViewGroup.OnHierarchyChangeListener { + BrowserControlsStateProvider.Observer, ChromeAccessibilityUtil.Observer, + TabObscuringHandler.Observer, ViewGroup.OnHierarchyChangeListener { private static final long SYSTEM_UI_VIEWPORT_UPDATE_DELAY_MS = 500; private static final MutableFlagWithSafeDefault sDeferKeepScreenOnFlag = new MutableFlagWithSafeDefault( @@ -170,7 +169,6 @@ /** The toolbar control container. **/ private @Nullable ControlContainer mControlContainer; - private InsetObserverView mInsetObserverView; private ObservableSupplier<Integer> mAutofillUiBottomInsetSupplier; private boolean mShowingFullscreen; private Runnable mSystemUiFullscreenResizeRunnable; @@ -202,7 +200,14 @@ private boolean mInGesture; private boolean mContentViewScrolling; private ApplicationViewportInsetSupplier mApplicationBottomInsetSupplier; - private final Callback<Integer> mBottomInsetObserver = (inset) -> bottomInsetChanged(); + + // Handler for changes to viewport insets. + private Callback<ViewportInsets> mOnViewportInsetsChanged = (unused) -> { + // TODO(bokan): This needs to be called in non-fullscreen cases too. e.g. When showing a + // keyboard accessory sheet, the inset will change without causing a View layout so the + // WebContents size is not updated. + if (mShowingFullscreen) handleWindowInsetChanged(); + }; /** * Tracks whether geometrychange event is fired for the active tab when the keyboard @@ -561,115 +566,59 @@ } /** - * Set the InsetObserverView that can be monitored for changes to the window insets from Android - * system UI. - */ - public void setInsetObserverView(InsetObserverView view) { - if (mInsetObserverView != null) { - mInsetObserverView.removeObserver(this); - } - mInsetObserverView = view; - if (mInsetObserverView != null) { - mInsetObserverView.addObserver(this); - handleWindowInsetChanged(); - } - updateApplicationViewportInsetSuppliers(); - } - - /** * A supplier providing an inset that resizes the page in addition or instead of the keyboard. - * This is inset is used by autofill UI as addition to bottom controls. + * This is inset is used to add to browser bottom controls until the + * ApplicationViewportInsetSupplier integrates them. * @param autofillUiBottomInsetSupplier A {@link ObservableSupplier<Integer>}. */ public void setAutofillUiBottomInsetSupplier( ObservableSupplier<Integer> autofillUiBottomInsetSupplier) { mAutofillUiBottomInsetSupplier = autofillUiBottomInsetSupplier; - mAutofillUiBottomInsetSupplier.addObserver(mBottomInsetObserver); - updateApplicationViewportInsetSuppliers(); } /** - * Sets the ApplicationViewportInsetSupplier. - * This object manages inset suppliers that should inset the visual viewport - * of the current page (i.e. allow scrolling but don't affect layout). This - * class manages adding and removing observers from this object as needed. + * Sets the ApplicationViewportInsetSupplier that will notify CompositorViewHolder when the + * WebContent must be resized by viewport insets. */ public void setApplicationViewportInsetSupplier(ApplicationViewportInsetSupplier supplier) { assert mApplicationBottomInsetSupplier == null; mApplicationBottomInsetSupplier = supplier; - mApplicationBottomInsetSupplier.addObserver(mBottomInsetObserver); - updateApplicationViewportInsetSuppliers(); + mApplicationBottomInsetSupplier.setVirtualKeyboardMode(mVirtualKeyboardMode); + mApplicationBottomInsetSupplier.addObserver(mOnViewportInsetsChanged); } - // Ensures the keyboard-related inset suppliers are either registered or unregistered with the - // ApplicationViewportInsetSupplier. The ApplicationViewportInsetSupplier is responsible for - // insetting the visual viewport so the keyboard insets should only be applied to it if the - // keyboard resizes only the visual viewport (otherwise the visual viewport and web contents - // size are resized together). - private void updateApplicationViewportInsetSuppliers() { - if (mApplicationBottomInsetSupplier == null) return; - - if (mVirtualKeyboardMode == VirtualKeyboardMode.RESIZES_VISUAL) { - if (mAutofillUiBottomInsetSupplier != null) { - mApplicationBottomInsetSupplier.addStackingSupplier(mAutofillUiBottomInsetSupplier); - } - if (mInsetObserverView != null) { - mApplicationBottomInsetSupplier.addStackingSupplier( - mInsetObserverView.getSupplierForBottomInset()); - } - } else { - if (mAutofillUiBottomInsetSupplier != null) { - mApplicationBottomInsetSupplier.removeSupplier(mAutofillUiBottomInsetSupplier); - } - if (mInsetObserverView != null) { - mApplicationBottomInsetSupplier.removeSupplier( - mInsetObserverView.getSupplierForBottomInset()); - } - } - } - - @Override - public void onInsetChanged(int left, int top, int right, int bottom) { - if (mShowingFullscreen) handleWindowInsetChanged(); - } - + // This method is called when any viewport insets change but is needed to watch for keyboard + // state changes while fullscreened and is used to simulate a view resize. This is only needed + // if the page has opted in to keyboard resizes. private void handleWindowInsetChanged() { - // The InsetObserverView is used to monitor keyboard resizes while - // fullscreened to simulate a view resize. This is unneeded when the - // OSK resizes only the visual viewport. - if (mVirtualKeyboardMode == VirtualKeyboardMode.RESIZES_VISUAL) { - return; + if (mVirtualKeyboardMode == VirtualKeyboardMode.RESIZES_CONTENT) { + // Notify the WebContents that the size has changed. + updateWebContentsSize(getCurrentTab()); + + // Notify the compositor layout that the size has changed. The layout does not drive + // the WebContents sizing, so this needs to be done in addition to the above size + // update. + onViewportChanged(); + } else if (mVirtualKeyboardMode == VirtualKeyboardMode.OVERLAYS_CONTENT) { + // Call updateWebContentsSize for OVERLAYS_CONTENT to ensure the virtual-keyboard + // geometrychange event is dispatched. + // TODO(bokan): The viewport doesn't change size in OVERLAYS_CONTENT so we should factor + // the event dispatch code out of there. + updateWebContentsSize(getCurrentTab()); } - - // Notify the WebContents that the size has changed. - updateWebContentsSize(getCurrentTab()); - - // Notify the compositor layout that the size has changed. The layout does not drive - // the WebContents sizing, so this needs to be done in addition to the above size update. - onViewportChanged(); } - @Override - public void onSafeAreaChanged(Rect area) {} - /** * Should be called for cleanup when the CompositorView instance is no longer used. */ public void shutDown() { setTab(null); - if (mApplicationBottomInsetSupplier != null && mBottomInsetObserver != null) { - mApplicationBottomInsetSupplier.removeObserver(mBottomInsetObserver); - } - if (mAutofillUiBottomInsetSupplier != null && mBottomInsetObserver != null) { - mAutofillUiBottomInsetSupplier.removeObserver(mBottomInsetObserver); + if (mApplicationBottomInsetSupplier != null) { + mApplicationBottomInsetSupplier.removeObserver(mOnViewportInsetsChanged); } mCompositorView.shutDown(); if (mLayoutManager != null) mLayoutManager.destroy(); - if (mInsetObserverView != null) { - mInsetObserverView.removeObserver(this); - mInsetObserverView = null; - } if (mOnscreenContentProvider != null) mOnscreenContentProvider.destroy(); if (mContentView != null) { mContentView.removeOnHierarchyChangeListener(this); @@ -933,42 +882,61 @@ int width = viewportSize.x; int height = viewportSize.y; - // The view size takes into account of the browser controls whose height - // should be subtracted from the view if they are visible, therefore shrink - // Blink-side view size. - // TODO(https://crbug.com/1211066): Centralize the logic for calculating bottom insets. - final int totalMinHeight = getKeyboardBottomInsetForControlsPixels() - + (mBrowserControlsManager != null - ? mBrowserControlsManager.getTopControlsMinHeight() - + mBrowserControlsManager.getBottomControlsMinHeight() - : 0); - int controlsHeight = mControlsResizeView - ? getTopControlsHeightPixels() + getBottomControlsHeightPixels() - : totalMinHeight; + // The view size takes into account of the browser controls whose height should be + // subtracted from the view if they are visible, therefore shrink Blink-side view size. + // TODO(https://crbug.com/1211066): Centralize the logic for calculating bottom insets by + // merging them into ApplicationBottomInsetSupplier. + int controlsInsets = 0; + if (mBrowserControlsManager != null) { + int controlsMinHeight = mBrowserControlsManager.getTopControlsMinHeight() + + mBrowserControlsManager.getBottomControlsMinHeight(); + int controlsHeight = mBrowserControlsManager.getTopControlsHeight() + + mBrowserControlsManager.getBottomControlsHeight(); + controlsInsets = mControlsResizeView ? controlsHeight : controlsMinHeight; + } + + // The system keyboard itself has already shrunk the View so its contribution is already + // included in `height`; viewVisibleHeightInset includes only the accessory inset. + // TODO(bokan): This method currently applies logic, based on mVirtualKeyboardMode, to + // compute the right inset/outset to use for the WebContents size. Move this logic into + // ApplicationViewportInsetSupplier. + int keyboardAccessoryInset = mApplicationBottomInsetSupplier != null + ? mApplicationBottomInsetSupplier.get().viewVisibleHeightInset + : 0; + + int viewportInsets = controlsInsets + keyboardAccessoryInset; if (isAttachedToWindow(view)) { - // If overlay content flag is set and the keyboard is shown or hidden then resize the - // visual/layout viewports in WebContents to match the previous size so there - // isn't a change in size after the keyboard is raised or hidden. - // Also the geometrychange event should only fire to the foreground tab. int keyboardHeight = 0; + // If the VirtualKeyboardMode is set to OVERLAYS_CONTENT or RESIZES_VISUAL, the + // WebContents size will not match the View size when the keyboard is showing (these + // modes mean "keyboard doesn't resize web content"). In that case, add the shown + // keyboard height to the WebContents size to keep it from being resized. boolean vkModePreservesWebContentsHeight = mVirtualKeyboardMode == VirtualKeyboardMode.OVERLAYS_CONTENT || mVirtualKeyboardMode == VirtualKeyboardMode.RESIZES_VISUAL; // In fullscreen, the keyboard doesn't resize the view so there's no need to adjust the - // layout height by the keyboard height to keep it from changing in response. - if (!mShowingFullscreen && vkModePreservesWebContentsHeight) { - // During orientation changes, width of the |WebContents| changes to match the width - // of the screen and so does the keyboard. We fire geometrychange with the updated - // keyboard size as well as resize the viewport so the height resize doesn't affect - // the |WebContents|. - keyboardHeight = KeyboardVisibilityDelegate.getInstance().calculateKeyboardHeight( - this.getRootView()); - height += keyboardHeight; + // layout height by the keyboard height to keep it from changing in response (but we do + // have to add back in keyboardAccessoryInset to avoid insetting by it). + if (vkModePreservesWebContentsHeight) { + // TODO(bokan): We could probably remove this special case if we remove the + // RESIZES_CONTENT case in getViewport. Then ApplicationViewportInsetSupplier can + // compute this outset for us. + if (!mShowingFullscreen) { + keyboardHeight = + KeyboardVisibilityDelegate.getInstance().calculateKeyboardHeight( + this.getRootView()); + } + height += keyboardHeight + keyboardAccessoryInset; } - webContents.setSize(width, height - controlsHeight); + + webContents.setSize(width, height - viewportInsets); + + // Dispatch the geometrychange JavaScript event to the page. + // TODO(bokan): This doesn't belong in updateWebContentsSize. Ideally the content/ layer + // would listen to changes in keyboard state and dispatch this event itself. if (mVirtualKeyboardMode == VirtualKeyboardMode.OVERLAYS_CONTENT) { notifyVirtualKeyboardOverlayGeometryChangeEvent(width, keyboardHeight, webContents); } @@ -980,7 +948,7 @@ view.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); - webContents.setSize(view.getWidth(), view.getHeight() - controlsHeight); + webContents.setSize(view.getWidth(), view.getHeight() - viewportInsets); requestRender(); } } @@ -1141,22 +1109,6 @@ } } - /** - * Called when keyboard-related UI insets the bottom of the viewport - */ - private void bottomInsetChanged() { - // updateViewportSize only needs to be called if OSK related UI - // actually resizes the view. Otherwise the inset only resizes the - // visual viewport which is communicated to the renderer by observing - // the inset provider in TabViewAndroidDelegate. This is a no-op in - // CompositorViewHolder. - if (mVirtualKeyboardMode == VirtualKeyboardMode.RESIZES_VISUAL) { - return; - } - - updateViewportSize(); - } - // View.OnHierarchyChangeListener implementation @Override @@ -1405,27 +1357,17 @@ @Override public int getBottomControlsHeightPixels() { - return getKeyboardBottomInsetForControlsPixels() - + (mBrowserControlsManager != null - ? mBrowserControlsManager.getBottomControlsHeight() - : 0); - } - - /** - * If there is keyboard extension or replacement available, this method returns the inset that - * resizes the page in addition to the bottom controls height. - * @return The inset height in pixels. - */ - private int getKeyboardBottomInsetForControlsPixels() { - // If the OSK mode resizes only the visual viewport avoid insetting the the container. - if (mVirtualKeyboardMode == VirtualKeyboardMode.RESIZES_VISUAL) { - return 0; + int bottomControlsHeight = mBrowserControlsManager != null + ? mBrowserControlsManager.getBottomControlsHeight() + : 0; + int keyboardAccessoryHeight = 0; + // TODO(bokan): This preserves behavior but this method is used to determine UI insets so + // this shouldn't depend on VirtualKeyboardMode + if (mVirtualKeyboardMode != VirtualKeyboardMode.RESIZES_VISUAL) { + keyboardAccessoryHeight = mAutofillUiBottomInsetSupplier.get(); } - return mAutofillUiBottomInsetSupplier != null - && mAutofillUiBottomInsetSupplier.get() != null - ? mAutofillUiBottomInsetSupplier.get() - : 0; + return bottomControlsHeight + keyboardAccessoryHeight; } /** @@ -1632,16 +1574,10 @@ if (mVirtualKeyboardMode == newMode) return; - @VirtualKeyboardMode.EnumType - int oldMode = mVirtualKeyboardMode; mVirtualKeyboardMode = newMode; - // If we're going into or out of the default OSK resizes visual viewport mode - // we're changing whether the ApplicationViewportInsetSupplier needs to listen to - // the keyboard since its responsible for insetting the visual viewport. - if (oldMode == VirtualKeyboardMode.RESIZES_VISUAL - || newMode == VirtualKeyboardMode.RESIZES_VISUAL) { - updateApplicationViewportInsetSuppliers(); + if (mApplicationBottomInsetSupplier != null) { + mApplicationBottomInsetSupplier.setVirtualKeyboardMode(mVirtualKeyboardMode); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java index 4de6d07e..7a3b326 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java
@@ -5,6 +5,7 @@ package org.chromium.chrome.browser.customtabs; import android.app.Activity; +import android.app.ActivityOptions; import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; import android.content.Intent; @@ -23,11 +24,10 @@ import androidx.annotation.VisibleForTesting; import androidx.browser.customtabs.CustomTabsIntent; +import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.Callback; import org.chromium.base.Log; import org.chromium.base.metrics.RecordUserAction; -import org.chromium.base.supplier.ObservableSupplier; -import org.chromium.base.supplier.Supplier; import org.chromium.chrome.R; import org.chromium.chrome.browser.browser_controls.BrowserControlsSizer; import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider; @@ -43,6 +43,7 @@ import org.chromium.chrome.browser.tab.Tab; import org.chromium.components.browser_ui.widget.gesture.SwipeGestureListener; import org.chromium.components.browser_ui.widget.gesture.SwipeGestureListener.ScrollDirection; +import org.chromium.ui.base.ViewportInsets; import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.interpolators.BakedBezierInterpolator; @@ -62,7 +63,6 @@ private final Activity mActivity; private final WindowAndroid mWindowAndroid; private final BrowserControlsSizer mBrowserControlsSizer; - private final ObservableSupplier<Integer> mAutofillUiBottomInsetSupplier; private final BrowserServicesIntentDataProvider mDataProvider; private final CustomTabActivityTabProvider mTabProvider; private final CustomTabNightModeStateController mNightModeStateController; @@ -99,7 +99,6 @@ public CustomTabBottomBarDelegate(Activity activity, WindowAndroid windowAndroid, BrowserServicesIntentDataProvider dataProvider, BrowserControlsSizer browserControlsSizer, - ObservableSupplier<Integer> autofillUiBottomInsetSupplier, CustomTabNightModeStateController nightModeStateController, SystemNightModeMonitor systemNightModeMonitor, CustomTabActivityTabProvider tabProvider, CustomTabCompositorContentInitializer compositorContentInitializer) { @@ -107,7 +106,6 @@ mWindowAndroid = windowAndroid; mDataProvider = dataProvider; mBrowserControlsSizer = browserControlsSizer; - mAutofillUiBottomInsetSupplier = autofillUiBottomInsetSupplier; mNightModeStateController = nightModeStateController; mSystemNightModeMonitor = systemNightModeMonitor; mTabProvider = tabProvider; @@ -115,9 +113,9 @@ compositorContentInitializer.addCallback(this::addOverlayPanelManagerObserver); - Callback<Integer> insetObserver = this::onViewPortInsetChange; + Callback<ViewportInsets> insetObserver = this::onViewportInsetChange; + // TODO(REVIEW): Is it ok this doesn't remove itself? mWindowAndroid.getApplicationBottomInsetSupplier().addObserver(insetObserver); - mAutofillUiBottomInsetSupplier.addObserver(insetObserver); } /** @@ -373,7 +371,9 @@ Tab tab = tabProvider.getTab(); if (tab != null) addedIntent.setData(Uri.parse(tab.getUrl().getSpec())); try { - pendingIntent.send(activity, 0, addedIntent, null, null); + ActivityOptions options = ActivityOptions.makeBasic(); + ApiCompatibilityUtils.setActivityOptionsBackgroundActivityStartMode(options); + pendingIntent.send(activity, 0, addedIntent, null, null, null, options.toBundle()); } catch (CanceledException e) { Log.e(TAG, "CanceledException when sending pending intent."); } @@ -436,15 +436,12 @@ } } - private void onViewPortInsetChange(Integer integer) { + private void onViewportInsetChange(ViewportInsets insets) { if (mBottomBarView == null) return; - hideBottomBar(hasNonZeroInset(mAutofillUiBottomInsetSupplier) - || hasNonZeroInset(mWindowAndroid.getApplicationBottomInsetSupplier())); - } + boolean isKeyboardShowing = mWindowAndroid.getKeyboardDelegate().isKeyboardShowing( + mBottomBarView.getContext(), mBottomBarView); - private static boolean hasNonZeroInset(Supplier<Integer> insetSupplier) { - Integer inset = insetSupplier.get(); - return inset != null && inset > 0; + hideBottomBar(insets.viewVisibleHeightInset > 0 || isKeyboardShowing); } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java index 690b1fc..0171b6c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
@@ -9,6 +9,7 @@ import static androidx.browser.customtabs.CustomTabsIntent.EXTRA_TOOLBAR_CORNER_RADIUS_DP; import android.app.Activity; +import android.app.ActivityOptions; import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; import android.content.Context; @@ -36,6 +37,7 @@ import androidx.browser.trusted.sharing.ShareData; import androidx.browser.trusted.sharing.ShareTarget; +import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.IntentUtils; import org.chromium.base.Log; import org.chromium.base.metrics.RecordHistogram; @@ -642,8 +644,10 @@ // in these cases prevents the Intent from firing correctly. String menuTitle = mMenuEntries.get(menuIndex).first; PendingIntent pendingIntent = mMenuEntries.get(menuIndex).second; - pendingIntent.send( - activity, 0, isMediaViewer() ? null : addedIntent, mOnFinished, null); + ActivityOptions options = ActivityOptions.makeBasic(); + ApiCompatibilityUtils.setActivityOptionsBackgroundActivityStartMode(options); + pendingIntent.send(activity, 0, isMediaViewer() ? null : addedIntent, mOnFinished, null, + null, options.toBundle()); if (shouldEnableEmbeddedMediaExperience() && TextUtils.equals( menuTitle, activity.getString(R.string.download_manager_open_with))) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabBaseStrategy.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabBaseStrategy.java index 6e7505a..dfd412f3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabBaseStrategy.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabBaseStrategy.java
@@ -28,7 +28,6 @@ import androidx.annotation.Px; import androidx.annotation.VisibleForTesting; -import org.chromium.base.SysUtils; import org.chromium.chrome.R; import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbar; import org.chromium.chrome.browser.fullscreen.FullscreenManager; @@ -83,7 +82,6 @@ protected PartialCustomTabHandleStrategyFactory mHandleStrategyFactory; protected int mShadowOffset; - protected boolean mDrawOutlineShadow; // Note: Do not use anywhere except in |onConfigurationChanged| as it might not be up-to-date. protected boolean mIsInMultiWindowMode; @@ -119,7 +117,6 @@ mFullscreenManager = fullscreenManager; mFullscreenManager.addObserver(this); - mDrawOutlineShadow = SysUtils.isLowEndDevice(); mCachedHandleHeight = mActivity.getResources().getDimensionPixelSize(R.dimen.custom_tabs_handle_height); @@ -247,6 +244,10 @@ protected abstract boolean isMaximized(); + protected abstract void drawDividerLine(CustomTabToolbar toolbar); + + protected abstract boolean shouldDrawDividerLine(); + protected boolean canInteractWithBackground() { return mInteractWithBackground; } @@ -285,7 +286,8 @@ } protected void updateShadowOffset() { - if (isFullHeight() || isFullscreen() || mDrawOutlineShadow || shouldHaveNoShadowOffset()) { + if (isFullHeight() || isFullscreen() || shouldHaveNoShadowOffset() + || shouldDrawDividerLine()) { mShadowOffset = 0; } else { mShadowOffset = mActivity.getResources().getDimensionPixelSize( @@ -307,6 +309,9 @@ handleViewStub.inflate(); } + mCoordinatorLayout = (ViewGroup) mActivity.findViewById(R.id.coordinator); + mCoordinatorLayout.setElevation( + mActivity.getResources().getDimensionPixelSize(R.dimen.custom_tabs_elevation)); View handleView = mActivity.findViewById(R.id.custom_tabs_handle_view); handleView.setElevation( mActivity.getResources().getDimensionPixelSize(R.dimen.custom_tabs_elevation)); @@ -322,13 +327,9 @@ View dragBar = handleView.findViewById(R.id.drag_bar); GradientDrawable dragBarBackground = (GradientDrawable) dragBar.getBackground(); adjustCornerRadius(dragBarBackground, toolbarCornerRadius); - if (mDrawOutlineShadow) { - int width = mActivity.getResources().getDimensionPixelSize( - R.dimen.custom_tabs_outline_width); - cctBackground.setStroke(width, toolbar.getToolbarHairlineColor(mToolbarColor)); - // We need an inset to make the outline shadow visible. - dragBar.setBackground(new InsetDrawable(dragBarBackground, width, width, width, 0)); + if (shouldDrawDividerLine()) { + drawDividerLine(toolbar); } else { dragBar.setBackground(dragBarBackground); } @@ -340,6 +341,24 @@ mActivity.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); } + protected void drawDividerLine( + int leftInset, int topInset, int rightInset, CustomTabToolbar toolbar) { + mCoordinatorLayout = (ViewGroup) mActivity.findViewById(R.id.coordinator); + View handleView = mActivity.findViewById(R.id.custom_tabs_handle_view); + View dragBar = handleView.findViewById(R.id.drag_bar); + GradientDrawable cctBackground = (GradientDrawable) handleView.getBackground(); + GradientDrawable dragBarBackground = (GradientDrawable) dragBar.getBackground(); + int width = + mActivity.getResources().getDimensionPixelSize(R.dimen.custom_tabs_outline_width); + + cctBackground.setStroke(width, toolbar.getToolbarHairlineColor(mToolbarColor)); + + // We need an inset to make the outline shadow visible. + dragBar.setBackground(new InsetDrawable(dragBarBackground, width, width, width, 0)); + mCoordinatorLayout.setBackground( + new InsetDrawable(cctBackground, leftInset, topInset, rightInset, 0)); + } + protected boolean isFullscreen() { return mIsFullscreen.getAsBoolean(); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabBottomSheetStrategy.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabBottomSheetStrategy.java index fcf3185..e3617f1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabBottomSheetStrategy.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabBottomSheetStrategy.java
@@ -33,6 +33,7 @@ import androidx.swiperefreshlayout.widget.CircularProgressDrawable; import org.chromium.base.MathUtils; +import org.chromium.base.SysUtils; import org.chromium.base.metrics.RecordHistogram; import org.chromium.chrome.R; import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbar; @@ -436,6 +437,8 @@ protected void setTopMargins(int shadowOffset, int handleOffset) { View handleView = mActivity.findViewById(R.id.custom_tabs_handle_view); boolean isMaxWidthLandscapeBottomSheet = isMaxWidthLandscapeBottomSheet(); + int sideOffset = + mActivity.getResources().getDimensionPixelSize(R.dimen.custom_tabs_shadow_offset); if (ChromeFeatureList.sCctResizableSideSheet.isEnabled()) { float maxWidthBottomSheetEv = @@ -452,7 +455,7 @@ } } - int sideMargin = isMaxWidthLandscapeBottomSheet ? shadowOffset : 0; + int sideMargin = isMaxWidthLandscapeBottomSheet ? sideOffset : 0; if (handleView != null) { ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) handleView.getLayoutParams(); @@ -796,6 +799,21 @@ new Handler().post(this::restoreWindow); } + @Override + protected void drawDividerLine(CustomTabToolbar toolbar) { + int width = + mActivity.getResources().getDimensionPixelSize(R.dimen.custom_tabs_outline_width); + boolean maxWidthBottomSheet = isMaxWidthLandscapeBottomSheet(); + int dividerInset = maxWidthBottomSheet ? width : 0; + + drawDividerLine(dividerInset, 0, dividerInset, toolbar); + } + + @Override + protected boolean shouldDrawDividerLine() { + return SysUtils.isLowEndDevice(); + } + // Restore the window upon exiting fullscreen. private void restoreWindow() { initializeHeight();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabFullSizeStrategy.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabFullSizeStrategy.java index d75cd7d..134cd57 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabFullSizeStrategy.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabFullSizeStrategy.java
@@ -112,6 +112,14 @@ return false; } + @Override + protected boolean shouldDrawDividerLine() { + return false; + } + + @Override + protected void drawDividerLine(CustomTabToolbar toolbar) {} + private void positionOnWindow() { WindowManager.LayoutParams attrs = mActivity.getWindow().getAttributes(); attrs.height = MATCH_PARENT;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategy.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategy.java index 477505e..3222a0eb 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategy.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategy.java
@@ -24,6 +24,7 @@ import androidx.annotation.VisibleForTesting; import org.chromium.base.MathUtils; +import org.chromium.base.SysUtils; import org.chromium.chrome.R; import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider; import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbar; @@ -57,8 +58,8 @@ mUnclampedInitialWidth = initialWidth; mShowMaximizeButton = showMaximizeButton; mPositionUpdater = this::updatePosition; - mIsMaximized = startMaximized; mDecorationType = decorationType; + mIsMaximized = startMaximized; mSheetOnRight = isSheetOnRight(position); mSlideDownAnimation = slideInBehavior == CustomTabIntentDataProvider.ACTIVITY_SIDE_SHEET_SLIDE_IN_FROM_BOTTOM; @@ -328,6 +329,22 @@ } @Override + protected void drawDividerLine(CustomTabToolbar toolbar) { + int width = + mActivity.getResources().getDimensionPixelSize(R.dimen.custom_tabs_outline_width); + int leftDividerInset = mSheetOnRight ? width : 0; + int rightDividerInset = !mSheetOnRight ? width : 0; + + drawDividerLine(leftDividerInset, 0, rightDividerInset, toolbar); + } + + @Override + protected boolean shouldDrawDividerLine() { + return SysUtils.isLowEndDevice() + || mDecorationType == ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DIVIDER; + } + + @Override public void destroy() { super.destroy(); if (mShowMaximizeButton) ((CustomTabToolbar) mToolbarView).removeSideSheetMaximizeButton();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java index b8205e6..b2e61cc 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java
@@ -7,6 +7,7 @@ import static org.chromium.chrome.browser.dependency_injection.ChromeCommonQualifiers.APP_CONTEXT; import android.app.Activity; +import android.app.ActivityOptions; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; @@ -19,6 +20,7 @@ import androidx.annotation.VisibleForTesting; import androidx.browser.customtabs.CustomTabsIntent; +import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.Log; import org.chromium.base.metrics.RecordUserAction; import org.chromium.cc.input.BrowserControlsState; @@ -168,8 +170,10 @@ addedIntent.setData(Uri.parse(url.getSpec())); addedIntent.putExtra(Intent.EXTRA_SUBJECT, title); try { - params.getPendingIntent().send( - mAppContext, 0, addedIntent, mCustomButtonClickOnFinished, null); + ActivityOptions options = ActivityOptions.makeBasic(); + ApiCompatibilityUtils.setActivityOptionsBackgroundActivityStartMode(options); + params.getPendingIntent().send(mAppContext, 0, addedIntent, + mCustomButtonClickOnFinished, null, null, options.toBundle()); } catch (PendingIntent.CanceledException e) { Log.e(TAG, "CanceledException while sending pending intent in custom tab"); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegate.java index 525bd44..aeb890d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegate.java
@@ -10,12 +10,13 @@ import androidx.annotation.VisibleForTesting; import org.chromium.base.Callback; -import org.chromium.base.supplier.ObservableSupplier; import org.chromium.chrome.browser.dragdrop.ChromeDragAndDropBrowserDelegate; import org.chromium.components.embedder_support.view.ContentView; import org.chromium.content_public.browser.ContentFeatureList; import org.chromium.content_public.common.ContentFeatures; +import org.chromium.ui.base.ApplicationViewportInsetSupplier; import org.chromium.ui.base.ViewAndroidDelegate; +import org.chromium.ui.base.ViewportInsets; import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.dragdrop.DragAndDropBrowserDelegate; import org.chromium.ui.dragdrop.DragAndDropDelegate; @@ -34,10 +35,10 @@ * The inset for the bottom of the Visual Viewport in pixels, or 0 for no insetting. * This is the source of truth for the application viewport inset for this embedder. */ - private int mApplicationViewportInsetBottomPx; + private int mVisualViewportInsetBottomPx; /** The inset supplier the observer is currently attached to. */ - private ObservableSupplier<Integer> mCurrentInsetSupplier; + private ApplicationViewportInsetSupplier mCurrentInsetSupplier; TabViewAndroidDelegate(Tab tab, ContentView containerView) { super(containerView); @@ -50,7 +51,7 @@ getDragAndDropDelegate().setDragAndDropBrowserDelegate(mDragAndDropBrowserDelegate); } - Callback<Integer> insetObserver = (inset) -> updateInsetViewportBottom(); + Callback<ViewportInsets> insetObserver = (unused) -> updateVisualViewportBottomInset(); mCurrentInsetSupplier = tab.getWindowAndroid().getApplicationBottomInsetSupplier(); mCurrentInsetSupplier.addObserver(insetObserver); @@ -61,21 +62,22 @@ mCurrentInsetSupplier = tab.getWindowAndroid().getApplicationBottomInsetSupplier(); mCurrentInsetSupplier.addObserver(insetObserver); + updateVisualViewportBottomInset(); } else { mCurrentInsetSupplier.removeObserver(insetObserver); mCurrentInsetSupplier = null; - updateInsetViewportBottom(); + updateVisualViewportBottomInset(); } } @Override public void onShown(Tab tab, int type) { - updateInsetViewportBottom(); + updateVisualViewportBottomInset(); } @Override public void onHidden(Tab tab, int reason) { - updateInsetViewportBottom(); + updateVisualViewportBottomInset(); } }); } @@ -105,13 +107,14 @@ } /** Sets the Visual Viewport bottom inset. */ - private void updateInsetViewportBottom() { - int inset = - mTab.isHidden() || mCurrentInsetSupplier == null ? 0 : mCurrentInsetSupplier.get(); + private void updateVisualViewportBottomInset() { + int inset = mTab.isHidden() || mCurrentInsetSupplier == null + ? 0 + : mCurrentInsetSupplier.get().visualViewportBottomInset; - if (inset == mApplicationViewportInsetBottomPx) return; + if (inset == mVisualViewportInsetBottomPx) return; - mApplicationViewportInsetBottomPx = inset; + mVisualViewportInsetBottomPx = inset; if (mTab.getWebContents() == null || mTab.getWebContents().getRenderWidgetHostView() == null) { @@ -122,8 +125,10 @@ } @Override + // TODO(bokan): "Viewport Inset" is overloaded. Rename to make it clearer this is a "visual + // viewport" inset. Also the RenderWidgetHostView call above. protected int getViewportInsetBottom() { - return mApplicationViewportInsetBottomPx; + return mVisualViewportInsetBottomPx; } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomContainer.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomContainer.java index 1d6bff1a..da998e3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomContainer.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomContainer.java
@@ -12,9 +12,9 @@ import org.chromium.base.Callback; import org.chromium.base.lifetime.Destroyable; -import org.chromium.base.supplier.ObservableSupplier; import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider; import org.chromium.ui.base.ApplicationViewportInsetSupplier; +import org.chromium.ui.base.ViewportInsets; /** * The container that holds both infobars and snackbars. It will be translated up and down when the @@ -23,15 +23,13 @@ public class BottomContainer extends FrameLayout implements Destroyable, BrowserControlsStateProvider.Observer { /** An observer of the viewport insets to change this container's position. */ - private final Callback<Integer> mInsetObserver; + private final Callback<ViewportInsets> mInsetObserver; /** The {@link BrowserControlsStateProvider} to listen for controls offset changes. */ private BrowserControlsStateProvider mBrowserControlsStateProvider; /** {@link ApplicationViewportInsetSupplier} to listen for viewport-shrinking features. */ private ApplicationViewportInsetSupplier mViewportInsetSupplier; - /** {@link ObservableSupplier} to listen for page-shrinking features. */ - private ObservableSupplier<Integer> mAutofillUiBottomInsetSupplier; /** The desired Y offset if unaffected by other UI. */ private float mBaseYOffset; @@ -39,19 +37,16 @@ /** Constructor for XML inflation. */ public BottomContainer(Context context, AttributeSet attrs) { super(context, attrs); - mInsetObserver = (inset) -> setTranslationY(mBaseYOffset); + mInsetObserver = (unused) -> setTranslationY(mBaseYOffset); } /** Initializes this container. */ public void initialize(BrowserControlsStateProvider browserControlsStateProvider, - ApplicationViewportInsetSupplier viewportInsetSupplier, - ObservableSupplier<Integer> autofillUiBottomInsetSupplier) { + ApplicationViewportInsetSupplier viewportInsetSupplier) { mBrowserControlsStateProvider = browserControlsStateProvider; mBrowserControlsStateProvider.addObserver(this); mViewportInsetSupplier = viewportInsetSupplier; - mAutofillUiBottomInsetSupplier = autofillUiBottomInsetSupplier; mViewportInsetSupplier.addObserver(mInsetObserver); - mAutofillUiBottomInsetSupplier.addObserver(mInsetObserver); setTranslationY(mBaseYOffset); } @@ -68,8 +63,7 @@ float offsetFromControls = mBrowserControlsStateProvider.getBottomControlOffset() - mBrowserControlsStateProvider.getBottomControlsHeight(); - offsetFromControls -= mViewportInsetSupplier.get(); - offsetFromControls -= mAutofillUiBottomInsetSupplier.get(); + offsetFromControls -= mViewportInsetSupplier.get().viewVisibleHeightInset; // Sit on top of either the bottom sheet or the bottom toolbar depending on which is larger // (offsets are negative). @@ -89,6 +83,5 @@ if (mBrowserControlsStateProvider == null) return; mBrowserControlsStateProvider.removeObserver(this); mViewportInsetSupplier.removeObserver(mInsetObserver); - mAutofillUiBottomInsetSupplier.removeObserver(mInsetObserver); } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ViewTransitionPixelTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ViewTransitionPixelTest.java index 13e2dd7..681c4273 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ViewTransitionPixelTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ViewTransitionPixelTest.java
@@ -166,8 +166,6 @@ double keyboardHeight = getKeyboardHeightDp(); - // Use less than or equal since the keyboard may actually include accessories like the - // Autofill bar. if (mVirtualKeyboardMode == VirtualKeyboardMode.RESIZES_VISUAL) { assertWaitForVisualViewportHeight(mInitialVVHeight - keyboardHeight); } else if (mVirtualKeyboardMode == VirtualKeyboardMode.RESIZES_CONTENT) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninFirstRunFragmentRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninFirstRunFragmentRenderTest.java index cae9a38..79d9590 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninFirstRunFragmentRenderTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninFirstRunFragmentRenderTest.java
@@ -243,6 +243,26 @@ @MediumTest @Feature("RenderTest") @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class) + public void testFragmentWithAccountOnManagedDevice_doesNotApplyFREStringVariations( + boolean nightModeEnabled, int orientation) throws IOException { + FREMobileIdentityConsistencyFieldTrial.setFirstRunVariationsTrialGroupForTesting( + VariationsGroup.MAKE_CHROME_YOUR_OWN); + when(mPolicyLoadListenerMock.get()).thenReturn(true); + mAccountManagerTestRule.addAccount(TEST_EMAIL1); + + launchActivityWithFragment(orientation); + + CriteriaHelper.pollUiThread(() -> { + return mFragment.getView().findViewById(R.id.account_text_secondary).isShown(); + }); + mRenderTestRule.render(mFragment.getView(), + "signin_first_run_fragment_with_account_managed_and_string_variation"); + } + + @Test + @MediumTest + @Feature("RenderTest") + @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class) public void testFragmentWithAccountWhenSigninIsDisabledByPolicy( boolean nightModeEnabled, int orientation) throws IOException { when(mSigninManagerMock.isSigninDisabledByPolicy()).thenReturn(true); @@ -259,6 +279,24 @@ @MediumTest @Feature("RenderTest") @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class) + public void testFragmentWithAccountWhenSigninIsDisabledByPolicy_doesNotApplyFREStringVariation( + boolean nightModeEnabled, int orientation) throws IOException { + FREMobileIdentityConsistencyFieldTrial.setFirstRunVariationsTrialGroupForTesting( + VariationsGroup.MAKE_CHROME_YOUR_OWN); + when(mSigninManagerMock.isSigninDisabledByPolicy()).thenReturn(true); + when(mPolicyLoadListenerMock.get()).thenReturn(true); + mAccountManagerTestRule.addAccount(TEST_EMAIL1); + + launchActivityWithFragment(orientation); + + mRenderTestRule.render(mFragment.getView(), + "signin_first_run_fragment_when_signin_disabled_by_policy_and_string_variation"); + } + + @Test + @MediumTest + @Feature("RenderTest") + @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class) public void testFragmentWithoutAccount(boolean nightModeEnabled, int orientation) throws IOException { launchActivityWithFragment(orientation); @@ -299,6 +337,25 @@ @MediumTest @Feature("RenderTest") @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class) + public void testFragmentWithChildAccount_doesNotApplyFREStringVariation( + boolean nightModeEnabled, int orientation) throws IOException { + FREMobileIdentityConsistencyFieldTrial.setFirstRunVariationsTrialGroupForTesting( + VariationsGroup.MAKE_CHROME_YOUR_OWN); + mAccountManagerTestRule.addAccount(CHILD_ACCOUNT_NAME); + + launchActivityWithFragment(orientation); + + CriteriaHelper.pollUiThread(() -> { + return mFragment.getView().findViewById(R.id.account_text_secondary).isShown(); + }); + mRenderTestRule.render(mFragment.getView(), + "signin_first_run_fragment_with_child_account_and_string_variation"); + } + + @Test + @MediumTest + @Feature("RenderTest") + @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class) public void testFragmentWhenCannotUseGooglePlayService( boolean nightModeEnabled, int orientation) throws IOException { when(mExternalAuthUtilsMock.canUseGooglePlayServices()).thenReturn(false);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninFirstRunFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninFirstRunFragmentTest.java index 17e2dff..6d2db1b 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninFirstRunFragmentTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninFirstRunFragmentTest.java
@@ -1110,6 +1110,20 @@ onView(withId(R.id.subtitle)).check(matches(not(isDisplayed()))); } + @Test + @MediumTest + public void testFragmentWithChildAccount_doesNotApplyFREStringVariation() { + FREMobileIdentityConsistencyFieldTrial.setFirstRunVariationsTrialGroupForTesting( + VariationsGroup.MAKE_CHROME_YOUR_OWN); + mSigninTestRule.addAccount( + CHILD_ACCOUNT_EMAIL, CHILD_FULL_NAME, /* givenName= */ null, /* avatar= */ null); + when(mPolicyLoadListenerMock.get()).thenReturn(true); + + launchActivityWithFragment(); + checkFragmentWithChildAccount( + /* hasDisplayableFullName= */ true, /* hasDisplayableEmail= */ true); + } + private void checkFragmentWithSelectedAccount(String email, String fullName, String givenName) { CriteriaHelper.pollUiThread( mFragment.getView().findViewById(R.id.signin_fre_selected_account)::isShown);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/CompositorViewHolderUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/CompositorViewHolderUnitTest.java index 25fff8b..890a3751 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/CompositorViewHolderUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/CompositorViewHolderUnitTest.java
@@ -36,6 +36,7 @@ import org.chromium.base.ActivityState; import org.chromium.base.ApplicationStatus; +import org.chromium.base.supplier.ObservableSupplierImpl; import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.chrome.R; import org.chromium.chrome.browser.ActivityTabProvider; @@ -47,12 +48,13 @@ import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver; import org.chromium.chrome.browser.toolbar.ControlContainer; import org.chromium.chrome.test.util.browser.Features; -import org.chromium.chrome.test.util.browser.Features.EnableFeatures; import org.chromium.chrome.test.util.browser.Features.DisableFeatures; +import org.chromium.chrome.test.util.browser.Features.EnableFeatures; import org.chromium.components.browser_ui.widget.TouchEventObserver; import org.chromium.components.embedder_support.view.ContentView; import org.chromium.content_public.browser.WebContents; import org.chromium.ui.KeyboardVisibilityDelegate; +import org.chromium.ui.base.ApplicationViewportInsetSupplier; import org.chromium.ui.mojom.VirtualKeyboardMode; import java.util.ArrayList; @@ -108,6 +110,8 @@ private Context mContext; private CompositorViewHolder mCompositorViewHolder; private BrowserControlsManager mBrowserControlsManager; + private ApplicationViewportInsetSupplier mViewportInsets; + private ObservableSupplierImpl<Integer> mKeyboardInsetSupplier; @Before public void setUp() { @@ -118,6 +122,10 @@ // Setup the mock keyboard. KeyboardVisibilityDelegate.setInstance(mMockKeyboard); + mViewportInsets = ApplicationViewportInsetSupplier.createForTests(); + mKeyboardInsetSupplier = new ObservableSupplierImpl<>(); + mViewportInsets.setKeyboardInsetSupplier(mKeyboardInsetSupplier); + // Setup for BrowserControlsManager which initiates content/control offset changes // for CompositorViewHolder. when(mActivity.getResources()).thenReturn(mResources); @@ -139,6 +147,7 @@ mCompositorViewHolder = spy(new CompositorViewHolder(mContext)); mCompositorViewHolder.setCompositorViewForTesting(mCompositorView); mCompositorViewHolder.setBrowserControlsManager(mBrowserControlsManager); + mCompositorViewHolder.setApplicationViewportInsetSupplier(mViewportInsets); when(mCompositorViewHolder.getCurrentTab()).thenReturn(mTab); when(mTab.getWebContents()).thenReturn(mWebContents); when(mTab.getContentView()).thenReturn(mContentView); @@ -343,6 +352,7 @@ when(mMockKeyboard.isKeyboardShowing(any(), any())).thenReturn(true); when(mMockKeyboard.calculateKeyboardHeight(any())).thenReturn(KEYBOARD_HEIGHT); + mKeyboardInsetSupplier.set(KEYBOARD_HEIGHT); when(mCompositorViewHolder.getWidth()).thenReturn(fullViewportWidth); when(mCompositorViewHolder.getHeight()).thenReturn(adjustedHeight); @@ -360,6 +370,7 @@ // Hide the keyboard. when(mMockKeyboard.isKeyboardShowing(any(), any())).thenReturn(false); when(mMockKeyboard.calculateKeyboardHeight(any())).thenReturn(0); + mKeyboardInsetSupplier.set(0); when(mCompositorViewHolder.getWidth()).thenReturn(fullViewportWidth); when(mCompositorViewHolder.getHeight()).thenReturn(fullViewportHeight); @@ -379,6 +390,7 @@ // Simulate the keyboard being hidden when(mMockKeyboard.isKeyboardShowing(any(), any())).thenReturn(false); when(mMockKeyboard.calculateKeyboardHeight(any())).thenReturn(0); + mKeyboardInsetSupplier.set(0); when(mCompositorViewHolder.getWidth()).thenReturn(viewportWidth); when(mCompositorViewHolder.getHeight()).thenReturn(viewportHeight); @@ -404,6 +416,7 @@ when(mMockKeyboard.isKeyboardShowing(any(), any())).thenReturn(true); when(mMockKeyboard.calculateKeyboardHeight(any())).thenReturn(KEYBOARD_HEIGHT); + mKeyboardInsetSupplier.set(KEYBOARD_HEIGHT); when(mCompositorViewHolder.getWidth()).thenReturn(fullViewportWidth); when(mCompositorViewHolder.getHeight()).thenReturn(adjustedHeight); @@ -429,6 +442,7 @@ when(mMockKeyboard.isKeyboardShowing(any(), any())).thenReturn(true); when(mMockKeyboard.calculateKeyboardHeight(any())).thenReturn(KEYBOARD_HEIGHT); + mKeyboardInsetSupplier.set(KEYBOARD_HEIGHT); when(mCompositorViewHolder.getWidth()).thenReturn(fullViewportWidth); when(mCompositorViewHolder.getHeight()).thenReturn(adjustedHeight); @@ -455,6 +469,7 @@ when(mMockKeyboard.isKeyboardShowing(any(), any())).thenReturn(true); when(mMockKeyboard.calculateKeyboardHeight(any())).thenReturn(KEYBOARD_HEIGHT); + mKeyboardInsetSupplier.set(KEYBOARD_HEIGHT); when(mCompositorViewHolder.getWidth()).thenReturn(fullViewportWidth); when(mCompositorViewHolder.getHeight()).thenReturn(adjustedHeight);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegateUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegateUnitTest.java index 1593b0d..841c6488 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegateUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegateUnitTest.java
@@ -33,7 +33,6 @@ import org.mockito.MockitoAnnotations; import org.robolectric.annotation.Config; -import org.chromium.base.supplier.ObservableSupplier; import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.chrome.R; import org.chromium.chrome.browser.browser_controls.BrowserControlsSizer; @@ -58,8 +57,6 @@ @Mock private BrowserControlsSizer mBrowserControlsSizer; @Mock - private ObservableSupplier<Integer> mAutofillUiBottomInsetSupplier; - @Mock private CustomTabNightModeStateController mNightModeStateController; @Mock private SystemNightModeMonitor mSystemNightModeMonitor; @@ -103,9 +100,8 @@ mIntentDataProvider = new CustomTabIntentDataProvider( mIntent, mActivity, CustomTabsIntent.COLOR_SCHEME_LIGHT); mBottomBarDelegate = new CustomTabBottomBarDelegate(mActivity, mWindowAndroid, - mIntentDataProvider, mBrowserControlsSizer, mAutofillUiBottomInsetSupplier, - mNightModeStateController, mSystemNightModeMonitor, mTabProvider, - mCompositorContentInitializer); + mIntentDataProvider, mBrowserControlsSizer, mNightModeStateController, + mSystemNightModeMonitor, mTabProvider, mCompositorContentInitializer); when(mBottomBarView.findViewById(eq(R.id.bottombar_shadow))).thenReturn(mShadowView); mBottomBarDelegate.setBottomBarViewForTesting(mBottomBarView); } @@ -130,7 +126,8 @@ mBottomBarDelegate.onSwipeStarted( ScrollDirection.UP, MotionEvent.obtain(0, 10, MotionEvent.ACTION_MOVE, 0f, 10f, 0)); // Verify the intent is sent. - verify(mSwipeUpPendingIntent).send(eq(mActivity), anyInt(), any(), any(), any()); + verify(mSwipeUpPendingIntent) + .send(eq(mActivity), anyInt(), any(), any(), any(), any(), any()); } @Test @@ -142,7 +139,7 @@ mBottomBarDelegate.onSwipeStarted( ScrollDirection.UP, MotionEvent.obtain(0, 10, MotionEvent.ACTION_MOVE, 0f, 10f, 0)); // Verify the intent is sent. - verify(pendingIntent).send(eq(mActivity), anyInt(), any(), any(), any()); + verify(pendingIntent).send(eq(mActivity), anyInt(), any(), any(), any(), any(), any()); } @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabBottomSheetStrategyTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabBottomSheetStrategyTest.java index 129ffd7..1233238c 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabBottomSheetStrategyTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabBottomSheetStrategyTest.java
@@ -41,6 +41,8 @@ import android.view.WindowManager; import android.view.WindowMetrics; +import org.junit.After; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; @@ -51,7 +53,10 @@ import org.robolectric.annotation.LooperMode.Mode; import org.robolectric.shadows.ShadowLooper; +import org.chromium.base.BaseSwitches; +import org.chromium.base.SysUtils; import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.DisableIf; import org.chromium.base.test.util.HistogramWatcher; import org.chromium.chrome.R; @@ -108,6 +113,16 @@ return pcct; } + @Before + public void setUp() { + SysUtils.resetForTesting(); + } + + @After + public void tearDown() { + SysUtils.resetForTesting(); + } + @Test public void create_heightIsCappedToHalfOfDeviceHeight() { createPcctAtHeight(500); @@ -1083,6 +1098,27 @@ getWindowAttributes().width); } + @CommandLineFlags.Add({BaseSwitches.ENABLE_LOW_END_DEVICE_MODE}) + @Test + public void useDividerLine() { + doReturn(8) + .when(mPCCTTestRule.mResources) + .getDimensionPixelSize(eq(R.dimen.custom_tabs_shadow_offset)); + mPCCTTestRule.configPortraitMode(); + var strategy = createPcctAtHeight(1500); + + assertEquals("Top margin should be zero because there is no shadow", 0, + mPCCTTestRule.mLayoutParams.topMargin); + + // 900 dp landscape bottom sheet + mPCCTTestRule.configLandscapeMode(); + strategy = createPcctAtHeight(3000); + assertEquals("Right margin should be zero because there is no shadow", 0, + mPCCTTestRule.mLayoutParams.rightMargin); + assertEquals("Left margin should not be zero because there is no shadow", 0, + mPCCTTestRule.mLayoutParams.leftMargin); + } + @Test public void expandToFullHeightOnFindInPage() { PartialCustomTabBottomSheetStrategy strategy = createPcctAtHeight(800);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategyTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategyTest.java index 6ea038d..a2adf7cb 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategyTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategyTest.java
@@ -13,6 +13,9 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; +import static org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DEFAULT; +import static org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DIVIDER; +import static org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_NONE; import static org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider.ACTIVITY_SIDE_SHEET_POSITION_DEFAULT; import static org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider.ACTIVITY_SIDE_SHEET_POSITION_END; import static org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider.ACTIVITY_SIDE_SHEET_POSITION_START; @@ -65,20 +68,28 @@ public final PartialCustomTabTestRule mPCCTTestRule = new PartialCustomTabTestRule(); private PartialCustomTabSideSheetStrategy createPcctSideSheetStrategy(@Px int widthPx) { - return createPcctSideSheetStrategy(widthPx, ACTIVITY_SIDE_SHEET_POSITION_DEFAULT); + return createPcctSideSheetStrategy(widthPx, ACTIVITY_SIDE_SHEET_POSITION_DEFAULT, + ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DEFAULT); } private PartialCustomTabSideSheetStrategy createLeftPcctSideSheetStrategy(@Px int widthPx) { - return createPcctSideSheetStrategy(widthPx, ACTIVITY_SIDE_SHEET_POSITION_START); + return createPcctSideSheetStrategy(widthPx, ACTIVITY_SIDE_SHEET_POSITION_START, + ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DEFAULT); } private PartialCustomTabSideSheetStrategy createPcctSideSheetStrategy( @Px int widthPx, int position) { + return createPcctSideSheetStrategy( + widthPx, position, ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DEFAULT); + } + + private PartialCustomTabSideSheetStrategy createPcctSideSheetStrategy( + @Px int widthPx, int position, int decorationType) { PartialCustomTabSideSheetStrategy pcct = new PartialCustomTabSideSheetStrategy( mPCCTTestRule.mActivity, widthPx, mPCCTTestRule.mOnResizedCallback, mPCCTTestRule.mFullscreenManager, false, true, /*showMaximizedButton=*/true, /*startMaximized=*/false, position, ACTIVITY_SIDE_SHEET_SLIDE_IN_DEFAULT, - mPCCTTestRule.mHandleStrategyFactory, 0); + mPCCTTestRule.mHandleStrategyFactory, decorationType); pcct.setMockViewForTesting(mPCCTTestRule.mCoordinatorLayout, mPCCTTestRule.mToolbarView, mPCCTTestRule.mToolbarCoordinator); return pcct; @@ -260,6 +271,44 @@ } @Test + public void drawDividerLine() { + doReturn(10) + .when(mPCCTTestRule.mResources) + .getDimensionPixelSize(eq(org.chromium.chrome.R.dimen.custom_tabs_shadow_offset)); + mPCCTTestRule.configLandscapeMode(); + var strategy = createPcctSideSheetStrategy(2000, ACTIVITY_SIDE_SHEET_SLIDE_IN_DEFAULT, + ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DIVIDER); + strategy.onToolbarInitialized( + mPCCTTestRule.mToolbarCoordinator, mPCCTTestRule.mToolbarView, 5); + mPCCTTestRule.verifyWindowFlagsSet(); + + assertTrue(strategy.shouldDrawDividerLine()); + assertEquals("Right margin should zero for no shadow", 0, + mPCCTTestRule.mLayoutParams.rightMargin); + assertEquals("Left margin should be zero for no shadow", 0, + mPCCTTestRule.mLayoutParams.leftMargin); + } + + @Test + public void noDecoration() { + doReturn(10) + .when(mPCCTTestRule.mResources) + .getDimensionPixelSize(eq(org.chromium.chrome.R.dimen.custom_tabs_shadow_offset)); + mPCCTTestRule.configLandscapeMode(); + var strategy = createPcctSideSheetStrategy(2000, ACTIVITY_SIDE_SHEET_POSITION_DEFAULT, + ACTIVITY_SIDE_SHEET_DECORATION_TYPE_NONE); + strategy.onToolbarInitialized( + mPCCTTestRule.mToolbarCoordinator, mPCCTTestRule.mToolbarView, 5); + mPCCTTestRule.verifyWindowFlagsSet(); + + assertFalse(strategy.shouldDrawDividerLine()); + assertEquals("Right margin should zero for no shadow", 0, + mPCCTTestRule.mLayoutParams.rightMargin); + assertEquals("Left margin should be zero for no shadow", 0, + mPCCTTestRule.mLayoutParams.leftMargin); + } + + @Test public void enterAndExitHtmlFullscreen() { doReturn(47) .when(mPCCTTestRule.mResources)
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java index 129f8d2e..ab27f6f 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java
@@ -195,7 +195,7 @@ @Mock private HybridListRenderer mRenderer; @Captor - private ArgumentCaptor<NtpListContentManager> mContentManagerCaptor; + private ArgumentCaptor<FeedListContentManager> mContentManagerCaptor; // Mocked indirect dependencies. @Rule
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegateTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegateTest.java index f589f2c..7000be7 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegateTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegateTest.java
@@ -33,6 +33,7 @@ import org.chromium.content_public.common.ContentFeatures; import org.chromium.ui.base.ApplicationViewportInsetSupplier; import org.chromium.ui.base.WindowAndroid; +import org.chromium.ui.mojom.VirtualKeyboardMode; /** Unit tests for the TabViewAndroidDelegate. */ @RunWith(BaseRobolectricTestRunner.class) @@ -59,17 +60,20 @@ private ContentView mContentView; private ApplicationViewportInsetSupplier mApplicationInsetSupplier; - private ObservableSupplierImpl<Integer> mFeatureInsetSupplier; + private ObservableSupplierImpl<Integer> mVisualViewportInsetSupplier; private TabViewAndroidDelegate mViewAndroidDelegate; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mFeatureInsetSupplier = new ObservableSupplierImpl<>(); + mVisualViewportInsetSupplier = new ObservableSupplierImpl<>(); mApplicationInsetSupplier = ApplicationViewportInsetSupplier.createForTests(); - mApplicationInsetSupplier.addOverlappingSupplier(mFeatureInsetSupplier); + + // The the keyboard only insets the visual viewport while in RESIZES_VISUAL mode. + mApplicationInsetSupplier.setVirtualKeyboardMode(VirtualKeyboardMode.RESIZES_VISUAL); + mApplicationInsetSupplier.setKeyboardInsetSupplier(mVisualViewportInsetSupplier); when(mWindowAndroid.getApplicationBottomInsetSupplier()) .thenReturn(mApplicationInsetSupplier); @@ -82,14 +86,14 @@ @Test public void testInset() { - mFeatureInsetSupplier.set(10); + mVisualViewportInsetSupplier.set(10); assertEquals("The bottom inset for the tab should be non-zero.", 10, mViewAndroidDelegate.getViewportInsetBottom()); } @Test public void testInset_afterHidden() { - mFeatureInsetSupplier.set(10); + mVisualViewportInsetSupplier.set(10); when(mTab.isHidden()).thenReturn(true); @@ -100,7 +104,7 @@ @Test public void testInset_afterDetachAndAttach() { - mFeatureInsetSupplier.set(10); + mVisualViewportInsetSupplier.set(10); assertEquals("The bottom inset for the tab should be non-zero.", 10, mViewAndroidDelegate.getViewportInsetBottom());
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 2fb2ef6f..189b646 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -9263,6 +9263,9 @@ <message name="IDS_TAB_GROUP_HEADER_CXMENU_MOVE_GROUP_TO_NEW_WINDOW" desc="The label of the tab group header context menu item for moving the entire group to a new window."> Move group to new window </message> + <message name="IDS_TAB_GROUP_HEADER_CXMENU_OPEN_GROUP_IN_NEW_WINDOW" desc="The label of the saved tab group button context menu item for opening the entire group in a new window."> + Open group in new window + </message> <message name="IDS_TAB_GROUP_HEADER_CXMENU_SEND_FEEDBACK" desc="The label of the tab group header context menu item for sending feedback for tab groups."> Send feedback </message> @@ -9289,6 +9292,9 @@ <message name="IDS_TAB_GROUP_HEADER_CXMENU_MOVE_GROUP_TO_NEW_WINDOW" desc="In Title Case: The label of the tab group header context menu item for moving the entire group to a new window."> Move Group to New Window </message> + <message name="IDS_TAB_GROUP_HEADER_CXMENU_OPEN_GROUP_IN_NEW_WINDOW" desc="In Title Case: The label of the saved tab group button context menu item for opening the entire group in a new window."> + Open Group in New Window + </message> <message name="IDS_TAB_GROUP_HEADER_CXMENU_SEND_FEEDBACK" desc="In Title Case: The label of the tab group header context menu item for sending feedback for tab groups."> Send Feedback </message> @@ -12475,7 +12481,7 @@ </message> </if> <message name="IDS_DESKTOP_MEDIA_PICKER_EMPTY_PREVIEW" desc="Text shown instead of a preview image of a tab when the user hasn't yet selected a tab to screenshare." > - Select a tab to preview + Select a tab to share </message> <message name="IDS_DESKTOP_MEDIA_PICKER_PREVIEW_ACCESSIBLE_NAME" desc="Name used by screen readers for a preview screenshot of a tab selected for screensharing." > Preview of shared tab
diff --git a/chrome/app/generated_resources_grd/IDS_DESKTOP_MEDIA_PICKER_EMPTY_PREVIEW.png.sha1 b/chrome/app/generated_resources_grd/IDS_DESKTOP_MEDIA_PICKER_EMPTY_PREVIEW.png.sha1 index 57a2c3cc..0f16d5d 100644 --- a/chrome/app/generated_resources_grd/IDS_DESKTOP_MEDIA_PICKER_EMPTY_PREVIEW.png.sha1 +++ b/chrome/app/generated_resources_grd/IDS_DESKTOP_MEDIA_PICKER_EMPTY_PREVIEW.png.sha1
@@ -1 +1 @@ -8f6bb6a387a7e2a21d5df8b20d4bb466f1911b55 \ No newline at end of file +560165cf820e3dde1cb9353cbcc63da1de93ffb0 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_TAB_GROUP_HEADER_CXMENU_OPEN_GROUP_IN_NEW_WINDOW.png.sha1 b/chrome/app/generated_resources_grd/IDS_TAB_GROUP_HEADER_CXMENU_OPEN_GROUP_IN_NEW_WINDOW.png.sha1 new file mode 100644 index 0000000..59abfbbf --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_TAB_GROUP_HEADER_CXMENU_OPEN_GROUP_IN_NEW_WINDOW.png.sha1
@@ -0,0 +1 @@ +77be50c7724a5730911e6c092a8ed66f36413fd0 \ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 8c136f5e..a9079a0 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -3674,6 +3674,8 @@ "enterprise/connectors/enterprise_connectors_policy_handler.h", "enterprise/connectors/reporting/browser_crash_event_router.cc", "enterprise/connectors/reporting/browser_crash_event_router.h", + "enterprise/connectors/reporting/crash_reporting_context.cc", + "enterprise/connectors/reporting/crash_reporting_context.h", "enterprise/connectors/reporting/extension_install_event_router.cc", "enterprise/connectors/reporting/extension_install_event_router.h", "enterprise/connectors/reporting/realtime_reporting_client.cc", @@ -3976,6 +3978,8 @@ "performance_manager/policies/background_tab_loading_policy.h", "performance_manager/policies/background_tab_loading_policy_helpers.cc", "performance_manager/policies/background_tab_loading_policy_helpers.h", + "performance_manager/policies/heuristic_memory_saver_policy.cc", + "performance_manager/policies/heuristic_memory_saver_policy.h", "performance_manager/policies/high_efficiency_mode_policy.cc", "performance_manager/policies/high_efficiency_mode_policy.h", "performance_manager/policies/page_discarding_helper.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index b7c5b98..2c53cdc 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -1499,6 +1499,11 @@ {"OmniboxSquareSuggestIconIcons", "true"}}; const FeatureEntry::FeatureParam kOmniboxSquareSuggestionIconAnswers[] = { {"OmniboxSquareSuggestIconAnswers", "true"}}; +const FeatureEntry::FeatureParam + kOmniboxSquareSuggestionIconFaviconsAndAnswers[] = { + {"OmniboxSquareSuggestIconIcons", "true"}, + {"OmniboxSquareSuggestIconAnswers", "true"}, +}; const FeatureEntry::FeatureParam kOmniboxSquareSuggestionIconEntities[] = { {"OmniboxSquareSuggestIconEntities", "true"}}; const FeatureEntry::FeatureParam kOmniboxSquareSuggestionIconAll[] = { @@ -1519,6 +1524,8 @@ std::size(kOmniboxSquareSuggestionIconFavicons), nullptr}, {"Answers", kOmniboxSquareSuggestionIconAnswers, std::size(kOmniboxSquareSuggestionIconAnswers), nullptr}, + {"Favicons and answers", kOmniboxSquareSuggestionIconFaviconsAndAnswers, + std::size(kOmniboxSquareSuggestionIconFaviconsAndAnswers), nullptr}, {"Entities", kOmniboxSquareSuggestionIconEntities, std::size(kOmniboxSquareSuggestionIconEntities), nullptr}, {"All", kOmniboxSquareSuggestionIconAll,
diff --git a/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java b/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java index 72814cd..419e715c 100644 --- a/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java +++ b/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java
@@ -119,9 +119,22 @@ ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DIVIDER}) @Retention(RetentionPolicy.SOURCE) public @interface SideSheetDecorationType {} + /** + * Side sheet's default decoration type. Same as + * {@link ACTIVITY_SIDE_SHEET_DECORATION_TYPE_SHADOW}. + */ public static final int ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DEFAULT = 0; + /** + * Side sheet with no decorations - the activity is not bordered by any shadow or divider line. + */ public static final int ACTIVITY_SIDE_SHEET_DECORATION_TYPE_NONE = 1; + /** + * Side sheet with shadow decoration - the activity is bordered by a shadow effect. + */ public static final int ACTIVITY_SIDE_SHEET_DECORATION_TYPE_SHADOW = 2; + /** + * Side sheet with a divider line - the activity is bordered by a thin opaque line. + */ public static final int ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DIVIDER = 3; public static final int ACTIVITY_SIDE_SHEET_DECORATION_TYPE_MAX = 3;
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn index 9f9851e..264cdb1 100644 --- a/chrome/browser/ash/BUILD.gn +++ b/chrome/browser/ash/BUILD.gn
@@ -1919,6 +1919,8 @@ "login/ui/login_screen_extension_ui/web_dialog_view.h", "login/ui/login_screen_extension_ui/window.cc", "login/ui/login_screen_extension_ui/window.h", + "login/ui/login_ui_pref_controller.cc", + "login/ui/login_ui_pref_controller.h", "login/ui/login_web_dialog.cc", "login/ui/login_web_dialog.h", "login/ui/oobe_dialog_size_utils.cc",
diff --git a/chrome/browser/ash/arc/notification/arc_vm_data_migration_notifier.cc b/chrome/browser/ash/arc/notification/arc_vm_data_migration_notifier.cc index c02b988..8a0bd13 100644 --- a/chrome/browser/ash/arc/notification/arc_vm_data_migration_notifier.cc +++ b/chrome/browser/ash/arc/notification/arc_vm_data_migration_notifier.cc
@@ -12,6 +12,7 @@ #include "ash/public/cpp/notification_utils.h" #include "ash/resources/vector_icons/vector_icons.h" #include "ash/strings/grit/ash_strings.h" +#include "base/metrics/histogram_functions.h" #include "base/time/time.h" #include "chrome/browser/ash/arc/policy/arc_policy_util.h" #include "chrome/browser/lifetime/application_lifetime.h" @@ -51,6 +52,17 @@ } } +void ReportNotificationShownForTheFirstTime() { + base::UmaHistogramBoolean( + "Arc.VmDataMigration.NotificationShownForTheFirstTime", true); +} + +void ReportNotificationShown(int days_until_deadline) { + base::UmaHistogramExactLinear( + "Arc.VmDataMigration.RemainingDays.NotificationShown", + days_until_deadline, kArcVmDataMigrationNumberOfDismissibleDays); +} + } // namespace ArcVmDataMigrationNotifier::ArcVmDataMigrationNotifier(Profile* profile) @@ -75,10 +87,9 @@ return; } - // TODO(b/272151802): Report to UMA that a notification is shown (with the - // info about whether it is the first time or not). if (GetArcVmDataMigrationStatus(profile_->GetPrefs()) == ArcVmDataMigrationStatus::kUnnotified) { + ReportNotificationShownForTheFirstTime(); profile_->GetPrefs()->SetTime( prefs::kArcVmDataMigrationNotificationFirstShownTime, base::Time::Now()); @@ -93,9 +104,9 @@ } void ArcVmDataMigrationNotifier::ShowNotification() { - // TODO(b/272151802): Report the number of days until the deadline to UMA. const int days_until_deadline = GetDaysUntilArcVmDataMigrationDeadline(profile_->GetPrefs()); + ReportNotificationShown(days_until_deadline); message_center::Notification notification = ash::CreateSystemNotification( message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId, @@ -152,7 +163,6 @@ CloseNotification(); - // TODO(b/272151802): Report to UMA that the dialog is shown. ShowArcVmDataMigrationConfirmationDialog( profile_->GetPrefs(), base::BindOnce(&ArcVmDataMigrationNotifier::OnRestartAccepted, @@ -160,8 +170,6 @@ } void ArcVmDataMigrationNotifier::OnRestartAccepted(bool accepted) { - // TODO(b/272151802): Report to UMA whether the dialog is accepted or - // canceled. if (accepted) { SetArcVmDataMigrationStatus(profile_->GetPrefs(), ArcVmDataMigrationStatus::kConfirmed);
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.cc index 9b558c0..a6a6f4a 100644 --- a/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.cc +++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.cc
@@ -10,6 +10,7 @@ #include "base/json/json_writer.h" #include "base/values.h" #include "chrome/browser/ash/login/oobe_quick_start/connectivity/fido_assertion_info.h" +#include "chrome/browser/ash/login/oobe_quick_start/connectivity/random_session_id.h" #include "chrome/browser/nearby_sharing/public/cpp/nearby_connection.h" #include "chromeos/ash/services/nearby/public/mojom/quick_start_decoder.mojom.h" #include "chromeos/ash/services/nearby/public/mojom/quick_start_decoder_types.mojom.h" @@ -71,8 +72,10 @@ AuthenticatedConnection::AuthenticatedConnection( NearbyConnection* nearby_connection, - mojo::SharedRemote<mojom::QuickStartDecoder> remote) - : Connection(nearby_connection) { + mojo::SharedRemote<mojom::QuickStartDecoder> remote, + RandomSessionId session_id, + SharedSecret shared_secret) + : Connection(nearby_connection, session_id, shared_secret) { decoder_ = std::move(remote); }
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.h b/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.h index cfe8c45..3e01b63 100644 --- a/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.h +++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.h
@@ -11,6 +11,7 @@ #include "base/memory/weak_ptr.h" #include "chrome/browser/ash/login/oobe_quick_start/connectivity/connection.h" #include "chrome/browser/ash/login/oobe_quick_start/connectivity/fido_assertion_info.h" +#include "chrome/browser/ash/login/oobe_quick_start/connectivity/random_session_id.h" #include "chrome/browser/ash/login/oobe_quick_start/connectivity/wifi_credentials.h" #include "chrome/browser/nearby_sharing/public/cpp/nearby_connection.h" #include "chromeos/ash/services/nearby/public/mojom/quick_start_decoder.mojom.h" @@ -32,9 +33,10 @@ using RequestWifiCredentialsCallback = base::OnceCallback<void(absl::optional<WifiCredentials>)>; - explicit AuthenticatedConnection( - NearbyConnection* nearby_connection, - mojo::SharedRemote<mojom::QuickStartDecoder> remote); + AuthenticatedConnection(NearbyConnection* nearby_connection, + mojo::SharedRemote<mojom::QuickStartDecoder> remote, + RandomSessionId session_id, + SharedSecret shared_secret); AuthenticatedConnection(AuthenticatedConnection&) = delete; AuthenticatedConnection& operator=(AuthenticatedConnection&) = delete;
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection_unittest.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection_unittest.cc index 858d34e..e8c631d3 100644 --- a/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection_unittest.cc +++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection_unittest.cc
@@ -43,6 +43,16 @@ const char kNotifySourceOfUpdateMessageKey[] = "isForcedUpdateRequired"; constexpr uint8_t kSuccess = 0x00; +// 32 random bytes to use as the shared secret. +constexpr std::array<uint8_t, 32> kSharedSecret = { + 0x54, 0xbd, 0x40, 0xcf, 0x8a, 0x7c, 0x2f, 0x6a, 0xca, 0x15, 0x59, + 0xcf, 0xf3, 0xeb, 0x31, 0x08, 0x90, 0x73, 0xef, 0xda, 0x87, 0xd4, + 0x23, 0xc0, 0x55, 0xd5, 0x83, 0x5b, 0x04, 0x28, 0x49, 0xf2}; + +// 6 random bytes to use as the RandomSessionId. +constexpr std::array<uint8_t, 6> kRandomSessionId = {0x6b, 0xb3, 0x85, + 0x27, 0xbb, 0x28}; + } // namespace class AuthenticatedConnectionTest : public testing::Test { @@ -56,11 +66,12 @@ fake_nearby_connection_ = std::make_unique<FakeNearbyConnection>(); fake_quick_start_decoder_ = std::make_unique<FakeQuickStartDecoder>(); NearbyConnection* nearby_connection = fake_nearby_connection_.get(); - + RandomSessionId session_id(kRandomSessionId); authenticated_connection_ = std::make_unique<AuthenticatedConnection>( nearby_connection, mojo::SharedRemote<ash::quick_start::mojom::QuickStartDecoder>( - fake_quick_start_decoder_->GetRemote())); + fake_quick_start_decoder_->GetRemote()), + session_id, kSharedSecret); } std::string CreateFidoClientDataJson(url::Origin origin) {
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.cc index b490423..ba0bc9a5 100644 --- a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.cc +++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.cc
@@ -6,12 +6,24 @@ #include "base/json/json_writer.h" #include "base/values.h" +#include "chrome/browser/ash/login/oobe_quick_start/connectivity/random_session_id.h" #include "chrome/browser/nearby_sharing/public/cpp/nearby_connection.h" +#include "crypto/random.h" namespace ash::quick_start { -Connection::Connection(NearbyConnection* nearby_connection) - : nearby_connection_(nearby_connection) {} +Connection::Connection(NearbyConnection* nearby_connection, + RandomSessionId session_id, + SharedSecret shared_secret) + : nearby_connection_(nearby_connection), + random_session_id_(session_id), + shared_secret_(shared_secret) {} + +Connection::Connection(NearbyConnection* nearby_connection, + RandomSessionId session_id) + : nearby_connection_(nearby_connection), random_session_id_(session_id) { + crypto::RandBytes(shared_secret_); +} void Connection::SendPayload(const base::Value::Dict& message_payload) { std::string json_serialized_payload;
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.h b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.h index 0c2b2704..66be7a0 100644 --- a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.h +++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.h
@@ -6,6 +6,7 @@ #define CHROME_BROWSER_ASH_LOGIN_OOBE_QUICK_START_CONNECTIVITY_CONNECTION_H_ #include "base/values.h" +#include "chrome/browser/ash/login/oobe_quick_start/connectivity/random_session_id.h" #include "chrome/browser/nearby_sharing/public/cpp/nearby_connection.h" namespace ash::quick_start { @@ -14,7 +15,14 @@ // a Nearby Connection. class Connection { public: - explicit Connection(NearbyConnection* nearby_connection); + using SharedSecret = std::array<uint8_t, 32>; + + Connection(NearbyConnection* nearby_connection, RandomSessionId session_id); + + Connection(NearbyConnection* nearby_connection, + RandomSessionId session_id, + SharedSecret shared_secret); + Connection(const Connection&) = delete; Connection& operator=(const Connection&) = delete; virtual ~Connection() = default; @@ -33,6 +41,8 @@ PayloadResponseCallback callback); NearbyConnection* nearby_connection_; + RandomSessionId random_session_id_; + SharedSecret shared_secret_; }; } // namespace ash::quick_start
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection_unittest.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection_unittest.cc index 3b1a519..577fdbdaf 100644 --- a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection_unittest.cc +++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection_unittest.cc
@@ -8,6 +8,7 @@ #include "base/run_loop.h" #include "base/test/bind.h" #include "base/test/task_environment.h" +#include "chrome/browser/ash/login/oobe_quick_start/connectivity/random_session_id.h" #include "chrome/browser/nearby_sharing/fake_nearby_connection.h" #include "chrome/browser/nearby_sharing/public/cpp/nearby_connection.h" #include "testing/gtest/include/gtest/gtest.h" @@ -20,10 +21,22 @@ constexpr char kTestMessagePayloadValue[] = "testValue"; constexpr char kTestBytes[] = "testbytes"; +// 32 random bytes to use as the shared secret. +constexpr std::array<uint8_t, 32> kSharedSecret = { + 0x54, 0xbd, 0x40, 0xcf, 0x8a, 0x7c, 0x2f, 0x6a, 0xca, 0x15, 0x59, + 0xcf, 0xf3, 0xeb, 0x31, 0x08, 0x90, 0x73, 0xef, 0xda, 0x87, 0xd4, + 0x23, 0xc0, 0x55, 0xd5, 0x83, 0x5b, 0x04, 0x28, 0x49, 0xf2}; + +// 6 random bytes to use as the RandomSessionId. +constexpr std::array<uint8_t, 6> kRandomSessionId = {0x6b, 0xb3, 0x85, + 0x27, 0xbb, 0x28}; + class FakeConnection : public Connection { public: explicit FakeConnection(NearbyConnection* nearby_connection) - : Connection(nearby_connection) {} + : Connection(nearby_connection, + RandomSessionId(kRandomSessionId), + kSharedSecret) {} void SendPayloadAndReadResponseWrapperForTesting( const base::Value::Dict& message_payload,
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/fake_target_device_connection_broker.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/fake_target_device_connection_broker.cc index 7e60868..c88dd18 100644 --- a/chrome/browser/ash/login/oobe_quick_start/connectivity/fake_target_device_connection_broker.cc +++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/fake_target_device_connection_broker.cc
@@ -19,6 +19,12 @@ // Arbitrary string to use as the connection's authentication token. constexpr char kAuthenticationToken[] = "auth_token"; +// 32 random bytes to use as the shared secret. +constexpr std::array<uint8_t, 32> kSharedSecret = { + 0x54, 0xbd, 0x40, 0xcf, 0x8a, 0x7c, 0x2f, 0x6a, 0xca, 0x15, 0x59, + 0xcf, 0xf3, 0xeb, 0x31, 0x08, 0x90, 0x73, 0xef, 0xda, 0x87, 0xd4, + 0x23, 0xc0, 0x55, 0xd5, 0x83, 0x5b, 0x04, 0x28, 0x49, 0xf2}; + } // namespace FakeTargetDeviceConnectionBroker::Factory::Factory() = default; @@ -78,11 +84,13 @@ NearbyConnection* nearby_connection = fake_nearby_connection_.get(); mojo::PendingRemote<mojom::QuickStartDecoder> remote; fake_quick_start_decoder_ = std::make_unique<FakeQuickStartDecoder>(); + auto random_session_id = RandomSessionId(); auto fake_authenticated_connection = std::make_unique<FakeAuthenticatedConnection>( nearby_connection, mojo::SharedRemote<ash::quick_start::mojom::QuickStartDecoder>( - fake_quick_start_decoder_->GetRemote())); + fake_quick_start_decoder_->GetRemote()), + random_session_id, kSharedSecret); connection_lifecycle_listener_->OnConnectionAuthenticated( source_device_id, fake_authenticated_connection->AsWeakPtr());
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/incoming_connection.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/incoming_connection.cc index 98076cc43..0538e87 100644 --- a/chrome/browser/ash/login/oobe_quick_start/connectivity/incoming_connection.cc +++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/incoming_connection.cc
@@ -15,19 +15,14 @@ IncomingConnection::IncomingConnection(NearbyConnection* nearby_connection, RandomSessionId session_id, const std::string& authentication_token) - : Connection(nearby_connection), - random_session_id_(session_id), - pin_(DerivePin(authentication_token)) { - crypto::RandBytes(shared_secret_); -} + : Connection(nearby_connection, session_id), + pin_(DerivePin(authentication_token)) {} IncomingConnection::IncomingConnection(NearbyConnection* nearby_connection, RandomSessionId session_id, const std::string& authentication_token, - std::array<uint8_t, 32> shared_secret) - : Connection(nearby_connection), - random_session_id_(session_id), - shared_secret_(shared_secret), + SharedSecret shared_secret) + : Connection(nearby_connection, session_id, shared_secret), pin_(DerivePin(authentication_token)) {} IncomingConnection::~IncomingConnection() = default;
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/incoming_connection.h b/chrome/browser/ash/login/oobe_quick_start/connectivity/incoming_connection.h index 00533173..1427ea7 100644 --- a/chrome/browser/ash/login/oobe_quick_start/connectivity/incoming_connection.h +++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/incoming_connection.h
@@ -46,12 +46,6 @@ std::string GetConnectionVerificationPin() const { return pin_; } private: - RandomSessionId random_session_id_; - - // A secret represented in a 32-byte array that gets generated and sent to the - // source device so it can be used later to authenticate the connection. - std::array<uint8_t, 32> shared_secret_; - // A 4-digit decimal pin code derived from the connection's authentication // token for the alternative pin authentication flow. std::string pin_;
diff --git a/chrome/browser/ash/login/ui/login_display_host_common.cc b/chrome/browser/ash/login/ui/login_display_host_common.cc index 1d559522..122dadb 100644 --- a/chrome/browser/ash/login/ui/login_display_host_common.cc +++ b/chrome/browser/ash/login/ui/login_display_host_common.cc
@@ -189,6 +189,7 @@ LoginDisplayHostCommon::LoginDisplayHostCommon() : keep_alive_(KeepAliveOrigin::LOGIN_DISPLAY_HOST_WEBUI, KeepAliveRestartOption::DISABLED), + login_ui_pref_controller_(std::make_unique<LoginUIPrefController>()), wizard_context_(std::make_unique<WizardContext>()) { // Close the login screen on app termination (for the case where shutdown // occurs before login completes). @@ -721,6 +722,7 @@ SigninProfileHandler::Get()->ClearSigninProfile(base::DoNothing()); app_terminating_subscription_ = {}; BrowserList::RemoveObserver(this); + login_ui_pref_controller_.reset(); } void LoginDisplayHostCommon::OnAppTerminating() {
diff --git a/chrome/browser/ash/login/ui/login_display_host_common.h b/chrome/browser/ash/login/ui/login_display_host_common.h index 122e8d1b..5a81d9d 100644 --- a/chrome/browser/ash/login/ui/login_display_host_common.h +++ b/chrome/browser/ash/login/ui/login_display_host_common.h
@@ -14,6 +14,7 @@ #include "chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.h" #include "chrome/browser/ash/login/ui/kiosk_app_menu_controller.h" #include "chrome/browser/ash/login/ui/login_display_host.h" +#include "chrome/browser/ash/login/ui/login_ui_pref_controller.h" #include "chrome/browser/ash/login/ui/signin_ui.h" #include "chrome/browser/ash/tpm_firmware_update.h" #include "chrome/browser/ui/browser_list_observer.h" @@ -158,6 +159,8 @@ std::unique_ptr<LoginFeedback> login_feedback_; + std::unique_ptr<LoginUIPrefController> login_ui_pref_controller_; + std::unique_ptr<WizardContext> wizard_context_; // Callback to be executed when WebUI is started.
diff --git a/chrome/browser/ash/login/ui/login_display_host_webui.cc b/chrome/browser/ash/login/ui/login_display_host_webui.cc index 763661b..143919aa 100644 --- a/chrome/browser/ash/login/ui/login_display_host_webui.cc +++ b/chrome/browser/ash/login/ui/login_display_host_webui.cc
@@ -806,6 +806,7 @@ // Subscribe to crash events. content::WebContentsObserver::Observe(login_view_->GetWebContents()); login_view_->LoadURL(url); + login_window_->Show(); CHECK(GetOobeUI()); GetOobeUI()->AddObserver(this); } @@ -1081,21 +1082,6 @@ input_method::InputMethodManager* manager = input_method::InputMethodManager::Get(); - // Set up keyboards. For example, when `locale` is "en-US", enable US qwerty - // and US dvorak keyboard layouts. - if (g_browser_process && g_browser_process->local_state()) { - manager->GetActiveIMEState()->SetInputMethodLoginDefault(); - - PrefService* prefs = g_browser_process->local_state(); - // Apply owner preferences for tap-to-click and mouse buttons swap for - // login screen. - system::InputDeviceSettings::Get()->SetPrimaryButtonRight( - prefs->GetBoolean(prefs::kOwnerPrimaryMouseButtonRight)); - system::InputDeviceSettings::Get()->SetPointingStickPrimaryButtonRight( - prefs->GetBoolean(prefs::kOwnerPrimaryPointingStickButtonRight)); - system::InputDeviceSettings::Get()->SetTapToClick( - prefs->GetBoolean(prefs::kOwnerTapToClickEnabled)); - } system::InputDeviceSettings::Get()->SetNaturalScroll( base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kNaturalScrollDefault));
diff --git a/chrome/browser/ash/login/ui/login_ui_pref_controller.cc b/chrome/browser/ash/login/ui/login_ui_pref_controller.cc new file mode 100644 index 0000000..c8ed625b --- /dev/null +++ b/chrome/browser/ash/login/ui/login_ui_pref_controller.cc
@@ -0,0 +1,75 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ash/login/ui/login_ui_pref_controller.h" + +#include "base/functional/bind.h" +#include "base/logging.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/ash/system/input_device_settings.h" +#include "chrome/browser/browser_process.h" +#include "chrome/common/pref_names.h" +#include "components/prefs/pref_change_registrar.h" +#include "components/prefs/pref_service.h" + +namespace ash { + +LoginUIPrefController::LoginUIPrefController() { + PrefService* prefs = g_browser_process->local_state(); + pref_change_registrar_.Init(prefs); + pref_change_registrar_.Add( + prefs::kOwnerPrimaryMouseButtonRight, + base::BindRepeating(&LoginUIPrefController::UpdatePrimaryMouseButtonRight, + weak_factory_.GetWeakPtr())); + pref_change_registrar_.Add( + prefs::kOwnerPrimaryPointingStickButtonRight, + base::BindRepeating( + &LoginUIPrefController::UpdatePrimaryPointingStickButtonRight, + weak_factory_.GetWeakPtr())); + pref_change_registrar_.Add( + prefs::kOwnerTapToClickEnabled, + base::BindRepeating(&LoginUIPrefController::UpdateTapToClickEnabled, + weak_factory_.GetWeakPtr())); + if (prefs->GetAllPrefStoresInitializationStatus() == + PrefService::INITIALIZATION_STATUS_WAITING) { + prefs->AddPrefInitObserver( + base::BindOnce(&LoginUIPrefController::InitOwnerPreferences, + weak_factory_.GetWeakPtr())); + } else { + InitOwnerPreferences(prefs->GetAllPrefStoresInitializationStatus() != + PrefService::INITIALIZATION_STATUS_ERROR); + } +} + +LoginUIPrefController::~LoginUIPrefController() = default; + +void LoginUIPrefController::UpdatePrimaryMouseButtonRight() { + system::InputDeviceSettings::Get()->SetPrimaryButtonRight( + g_browser_process->local_state()->GetBoolean( + prefs::kOwnerPrimaryMouseButtonRight)); +} + +void LoginUIPrefController::UpdatePrimaryPointingStickButtonRight() { + system::InputDeviceSettings::Get()->SetPointingStickPrimaryButtonRight( + g_browser_process->local_state()->GetBoolean( + prefs::kOwnerPrimaryPointingStickButtonRight)); +} + +void LoginUIPrefController::UpdateTapToClickEnabled() { + system::InputDeviceSettings::Get()->SetTapToClick( + g_browser_process->local_state()->GetBoolean( + prefs::kOwnerTapToClickEnabled)); +} + +void LoginUIPrefController::InitOwnerPreferences(bool success) { + if (!success) { + LOG(ERROR) << "InitOwnerPreferences failed."; + return; + } + UpdatePrimaryMouseButtonRight(); + UpdatePrimaryPointingStickButtonRight(); + UpdateTapToClickEnabled(); +} + +} // namespace ash
diff --git a/chrome/browser/ash/login/ui/login_ui_pref_controller.h b/chrome/browser/ash/login/ui/login_ui_pref_controller.h new file mode 100644 index 0000000..402d2d74 --- /dev/null +++ b/chrome/browser/ash/login/ui/login_ui_pref_controller.h
@@ -0,0 +1,42 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_UI_PREF_CONTROLLER_H_ +#define CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_UI_PREF_CONTROLLER_H_ + +#include "base/memory/weak_ptr.h" +#include "components/prefs/pref_change_registrar.h" + +namespace ash { + +class LoginUIPrefController { + public: + // Applies effects of login-screen prefs, e.g., mouse and touchpad settings + // that are coming from the owner user and/or device policies. + LoginUIPrefController(); + LoginUIPrefController(const LoginUIPrefController&) = delete; + LoginUIPrefController& operator=(const LoginUIPrefController&) = delete; + ~LoginUIPrefController(); + + private: + // Apply the owner preferences after local_state prefService start. + void InitOwnerPreferences(bool success); + + // Apply "owner.mouse.primary_right" preference on the login screen. + void UpdatePrimaryMouseButtonRight(); + + // Apply "owner.pointing_stick.primary_right" preference on the login screen. + void UpdatePrimaryPointingStickButtonRight(); + + // Apply "owner.touchpad.enable_tap_to_click" preference on the login screen. + void UpdateTapToClickEnabled(); + + PrefChangeRegistrar pref_change_registrar_; + + base::WeakPtrFactory<LoginUIPrefController> weak_factory_{this}; +}; + +} // namespace ash + +#endif // CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_UI_PREF_CONTROLLER_H_
diff --git a/chrome/browser/ash/login/ui/login_ui_pref_controller_browsertest.cc b/chrome/browser/ash/login/ui/login_ui_pref_controller_browsertest.cc new file mode 100644 index 0000000..caa8ae04 --- /dev/null +++ b/chrome/browser/ash/login/ui/login_ui_pref_controller_browsertest.cc
@@ -0,0 +1,127 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ash/login/ui/login_ui_pref_controller.h" + +#include "ash/constants/ash_pref_names.h" +#include "chrome/browser/ash/login/login_manager_test.h" +#include "chrome/browser/ash/login/test/device_state_mixin.h" +#include "chrome/browser/ash/login/test/login_manager_mixin.h" +#include "chrome/browser/ash/login/test/oobe_base_test.h" +#include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h" +#include "chrome/browser/ash/system/fake_input_device_settings.h" +#include "chrome/browser/ash/system/input_device_settings.h" +#include "chrome/browser/browser_process.h" +#include "chrome/common/pref_names.h" +#include "components/policy/policy_constants.h" +#include "components/policy/proto/chrome_device_policy.pb.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/pref_test_utils.h" +#include "content/public/test/browser_test.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace ash { + +namespace { + +// Gets current mouse setting primary button right value. +bool GetSystemMousePrimaryButtonRight() { + return system::InputDeviceSettings::Get() + ->GetFakeInterface() + ->current_mouse_settings() + .GetPrimaryButtonRight(); +} + +// Gets current pointing_stick primary button right value. +bool GetSystemPointingStickPrimaryButtonRight() { + return system::InputDeviceSettings::Get() + ->GetFakeInterface() + ->current_pointing_stick_settings() + .GetPrimaryButtonRight(); +} + +// Set "owner.pointing_stick.primary_right" preference. +void SetOwnerPointingStickPrimaryRight(bool val) { + g_browser_process->local_state()->SetBoolean( + ::prefs::kOwnerPrimaryPointingStickButtonRight, val); +} + +// Gets current pointing_stick primary button right value. +bool GetSystemTouchpadTapToClick() { + return system::InputDeviceSettings::Get() + ->GetFakeInterface() + ->current_touchpad_settings() + .GetTapToClick(); +} + +// Set "owner.touchpad.enable_tap_to_click" preference. +void SetOwnerTouchpadEnableTapToClick(bool val) { + g_browser_process->local_state()->SetBoolean(::prefs::kOwnerTapToClickEnabled, + val); +} + +} // namespace + +class LoginUIPrefControllerTest : public LoginManagerTest { + public: + LoginUIPrefControllerTest() = default; + + LoginUIPrefControllerTest(const LoginUIPrefControllerTest&) = delete; + LoginUIPrefControllerTest& operator=(const LoginUIPrefControllerTest&) = + delete; + + ~LoginUIPrefControllerTest() override = default; + + void RefreshDevicePolicy() { policy_helper_.RefreshDevicePolicy(); } + + // Sets DeviceLoginScreenPrimaryMouseButtonSwitch proto value. + void SetDeviceLoginScreenPrimaryMouseButtonSwitch(bool val) { + policy_helper_.device_policy() + ->payload() + .mutable_login_screen_primary_mouse_button_switch() + ->set_value(val); + } + + private: + LoginManagerMixin login_mixin_{&mixin_host_}; + policy::DevicePolicyCrosTestHelper policy_helper_; + DeviceStateMixin device_state_{ + &mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED}; +}; + +IN_PROC_BROWSER_TEST_F(LoginUIPrefControllerTest, + DeviceLoginScreenPrimaryMouseButtonSwitch) { + EXPECT_FALSE(GetSystemMousePrimaryButtonRight()); + + SetDeviceLoginScreenPrimaryMouseButtonSwitch(true); + RefreshDevicePolicy(); + WaitForPrefValue(g_browser_process->local_state(), + ::prefs::kOwnerPrimaryMouseButtonRight, base::Value(true)); + EXPECT_TRUE(GetSystemMousePrimaryButtonRight()); + + SetDeviceLoginScreenPrimaryMouseButtonSwitch(false); + RefreshDevicePolicy(); + WaitForPrefValue(g_browser_process->local_state(), + ::prefs::kOwnerPrimaryMouseButtonRight, base::Value(false)); + EXPECT_FALSE(GetSystemMousePrimaryButtonRight()); +} + +IN_PROC_BROWSER_TEST_F(LoginUIPrefControllerTest, + OwnerPrimaryPointingStickButtonRight) { + EXPECT_FALSE(GetSystemPointingStickPrimaryButtonRight()); + SetOwnerPointingStickPrimaryRight(true); + EXPECT_TRUE(GetSystemPointingStickPrimaryButtonRight()); + SetOwnerPointingStickPrimaryRight(false); + EXPECT_FALSE(GetSystemPointingStickPrimaryButtonRight()); +} + +IN_PROC_BROWSER_TEST_F(LoginUIPrefControllerTest, OwnerTapToClickEnabled) { + EXPECT_TRUE(GetSystemTouchpadTapToClick()); + SetOwnerTouchpadEnableTapToClick(false); + EXPECT_FALSE(GetSystemTouchpadTapToClick()); + SetOwnerTouchpadEnableTapToClick(true); + EXPECT_TRUE(GetSystemTouchpadTapToClick()); +} + +} // namespace ash
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_collector.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_collector.cc index 0f01b3d..541041f 100644 --- a/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_collector.cc +++ b/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_collector.cc
@@ -4,7 +4,11 @@ #include "chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_collector.h" +#include <memory> + +#include "base/memory/ptr_util.h" #include "chrome/browser/apps/app_service/metrics/app_platform_metrics.h" +#include "chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_platform_metrics_retriever.h" #include "chrome/browser/chromeos/reporting/metric_default_utils.h" #include "chrome/browser/profiles/profile.h" #include "chromeos/ash/components/settings/cros_settings_names.h" @@ -15,21 +19,50 @@ namespace reporting { +// static +std::unique_ptr<AppUsageCollector> AppUsageCollector::Create( + Profile* profile, + const ReportingSettings* reporting_settings) { + auto app_platform_metrics_retriever = + std::make_unique<AppPlatformMetricsRetriever>(profile); + return base::WrapUnique(new AppUsageCollector( + profile, reporting_settings, std::move(app_platform_metrics_retriever))); +} + +// static +std::unique_ptr<AppUsageCollector> AppUsageCollector::CreateForTest( + Profile* profile, + const ReportingSettings* reporting_settings, + std::unique_ptr<AppPlatformMetricsRetriever> + app_platform_metrics_retriever) { + return base::WrapUnique(new AppUsageCollector( + profile, reporting_settings, std::move(app_platform_metrics_retriever))); +} + AppUsageCollector::AppUsageCollector( Profile* profile, const ReportingSettings* reporting_settings, - ::apps::AppPlatformMetrics* app_platform_metrics) + std::unique_ptr<AppPlatformMetricsRetriever> app_platform_metrics_retriever) : profile_(profile), reporting_settings_(reporting_settings), - app_platform_metrics_(app_platform_metrics) { - DCHECK(app_platform_metrics_); - app_platform_metrics_->AddObserver(this); + app_platform_metrics_retriever_( + std::move(app_platform_metrics_retriever)) { + DCHECK(app_platform_metrics_retriever_); + app_platform_metrics_retriever_->GetAppPlatformMetrics(base::BindOnce( + &AppUsageCollector::InitUsageCollector, weak_ptr_factory_.GetWeakPtr())); } -AppUsageCollector::~AppUsageCollector() { - if (IsInObserverList()) { - app_platform_metrics_->RemoveObserver(this); +AppUsageCollector::~AppUsageCollector() = default; + +void AppUsageCollector::InitUsageCollector( + ::apps::AppPlatformMetrics* app_platform_metrics) { + if (!app_platform_metrics) { + // This can happen if the `AppPlatformMetrics` component initialization + // failed (for example, component was destructed). We just abort + // initialization of the usage collector when this happens. + return; } + observer_.Observe(app_platform_metrics); } void AppUsageCollector::OnAppUsage(const std::string& app_id, @@ -52,6 +85,10 @@ CreateOrUpdateAppUsageEntry(app_id, app_type, instance_id, running_time); } +void AppUsageCollector::OnAppPlatformMetricsDestroyed() { + observer_.Reset(); +} + void AppUsageCollector::CreateOrUpdateAppUsageEntry( const std::string& app_id, ::apps::AppType app_type,
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_collector.h b/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_collector.h index 2be34a4..643e543 100644 --- a/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_collector.h +++ b/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_collector.h
@@ -5,10 +5,15 @@ #ifndef CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_APPS_APP_USAGE_COLLECTOR_H_ #define CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_APPS_APP_USAGE_COLLECTOR_H_ +#include <memory> + #include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/scoped_observation.h" #include "base/time/time.h" #include "base/unguessable_token.h" #include "chrome/browser/apps/app_service/metrics/app_platform_metrics.h" +#include "chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_platform_metrics_retriever.h" #include "chrome/browser/profiles/profile.h" #include "components/reporting/metrics/reporting_settings.h" #include "components/services/app_service/public/cpp/app_types.h" @@ -20,20 +25,44 @@ // reported subsequently. class AppUsageCollector : public ::apps::AppPlatformMetrics::Observer { public: - AppUsageCollector(Profile* profile, - const ReportingSettings* reporting_settings, - ::apps::AppPlatformMetrics* app_platform_metrics); + // Static helper that instantiates the `AppUsageCollector` for the given + // profile using the specified `ReportingSettings`. + static std::unique_ptr<AppUsageCollector> Create( + Profile* profile, + const ReportingSettings* reporting_settings); + + // Static test helper that instantiates the `AppUsageCollector` for the given + // profile using the specified `ReportingSettings` and + // `AppPlatformMetricsRetriever`. + static std::unique_ptr<AppUsageCollector> CreateForTest( + Profile* profile, + const ReportingSettings* reporting_settings, + std::unique_ptr<AppPlatformMetricsRetriever> + app_platform_metrics_retriever); + AppUsageCollector(const AppUsageCollector& other) = delete; AppUsageCollector& operator=(const AppUsageCollector& other) = delete; ~AppUsageCollector() override; private: + AppUsageCollector(Profile* profile, + const ReportingSettings* reporting_settings, + std::unique_ptr<AppPlatformMetricsRetriever> + app_platform_metrics_retriever); + + // Initializes the usage collector and starts observing app usage collection + // tracked by the `AppPlatformMetrics` component (if initialized). + void InitUsageCollector(::apps::AppPlatformMetrics* app_platform_metrics); + // ::apps::AppPlatformMetrics::Observer: void OnAppUsage(const std::string& app_id, ::apps::AppType app_type, const base::UnguessableToken& instance_id, base::TimeDelta running_time) override; + // ::apps::AppPlatformMetrics::Observer: + void OnAppPlatformMetricsDestroyed() override; + // Aggregates the app usage entry with the specified usage/running time and // persists it in the pref store. Creates a new placeholder entry if one does // not exist for the specified instance. @@ -42,9 +71,25 @@ const base::UnguessableToken& instance_id, const base::TimeDelta& running_time); + // Pointer to the user profile. Required to save usage data to the user pref + // store. const raw_ptr<Profile> profile_; + + // Pointer to the reporting settings. Used to control usage data collection. const raw_ptr<const ReportingSettings> reporting_settings_; - const raw_ptr<::apps::AppPlatformMetrics> app_platform_metrics_; + + // Retriever that retrieves the `AppPlatformMetrics` component so the usage + // collector can start tracking app usage collection. + const std::unique_ptr<AppPlatformMetricsRetriever> + app_platform_metrics_retriever_; + + // Observer for tracking app usage collection. Will be reset if the + // `AppPlatformMetrics` component gets destructed before the usage collector. + base::ScopedObservation<::apps::AppPlatformMetrics, + ::apps::AppPlatformMetrics::Observer> + observer_{this}; + + base::WeakPtrFactory<AppUsageCollector> weak_ptr_factory_{this}; }; } // namespace reporting
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_collector_unittest.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_collector_unittest.cc index 8e39bda..427f4883 100644 --- a/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_collector_unittest.cc +++ b/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_collector_unittest.cc
@@ -18,9 +18,11 @@ #include "components/services/app_service/public/cpp/app_types.h" #include "components/services/app_service/public/cpp/instance.h" #include "components/services/app_service/public/protos/app_types.pb.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/compositor/layer_type.h" +using ::testing::_; using ::testing::Eq; using ::testing::NotNull; using ::testing::StrEq; @@ -31,8 +33,24 @@ constexpr char kTestAppId[] = "TestApp"; constexpr base::TimeDelta kAppUsageCollectionInterval = base::Minutes(5); -class AppUsageCollectorTest : public ::apps::AppPlatformMetricsServiceTestBase { +// Mock retriever for the `AppPlatformMetrics` component. +class MockAppPlatformMetricsRetriever : public AppPlatformMetricsRetriever { public: + MockAppPlatformMetricsRetriever() : AppPlatformMetricsRetriever(nullptr) {} + MockAppPlatformMetricsRetriever(const MockAppPlatformMetricsRetriever&) = + delete; + MockAppPlatformMetricsRetriever& operator=( + const MockAppPlatformMetricsRetriever&) = delete; + ~MockAppPlatformMetricsRetriever() override = default; + + MOCK_METHOD(void, + GetAppPlatformMetrics, + (AppPlatformMetricsCallback callback), + (override)); +}; + +class AppUsageCollectorTest : public ::apps::AppPlatformMetricsServiceTestBase { + protected: void SetUp() override { ::apps::AppPlatformMetricsServiceTestBase::SetUp(); @@ -44,6 +62,19 @@ // Pre-install app so it can be used by tests to simulate usage. InstallOneApp(kTestAppId, ::apps::AppType::kArc, /*publisher_id=*/"", ::apps::Readiness::kReady, ::apps::InstallSource::kPlayStore); + + // Set up `AppUsageCollector` with relevant test params. + auto mock_app_platform_metrics_retriever = + std::make_unique<MockAppPlatformMetricsRetriever>(); + EXPECT_CALL(*mock_app_platform_metrics_retriever, GetAppPlatformMetrics(_)) + .WillOnce([this](AppPlatformMetricsRetriever::AppPlatformMetricsCallback + callback) { + std::move(callback).Run( + app_platform_metrics_service()->AppPlatformMetrics()); + }); + app_usage_collector_ = AppUsageCollector::CreateForTest( + profile(), &reporting_settings_, + std::move(mock_app_platform_metrics_retriever)); } void SimulateAppUsageForInstance(::aura::Window* window, @@ -77,15 +108,15 @@ Eq(expected_usage_time)); } - protected: + // Fake reporting settings component used by the test. test::FakeReportingSettings reporting_settings_; + + // App usage collector used by tests. + std::unique_ptr<AppUsageCollector> app_usage_collector_; }; TEST_F(AppUsageCollectorTest, PersistAppUsageDataInPrefStore) { reporting_settings_.SetBoolean(::ash::kReportDeviceAppInfo, true); - AppUsageCollector app_usage_collector( - profile(), &reporting_settings_, - app_platform_metrics_service()->AppPlatformMetrics()); // Create a new window for the app and simulate app usage. static constexpr base::TimeDelta kAppUsageDuration = base::Minutes(2); @@ -101,9 +132,6 @@ TEST_F(AppUsageCollectorTest, ShouldNotPersistAppUsageDataIfSettingDisabled) { reporting_settings_.SetBoolean(::ash::kReportDeviceAppInfo, false); - AppUsageCollector app_usage_collector( - profile(), &reporting_settings_, - app_platform_metrics_service()->AppPlatformMetrics()); // Create a new window for the app and simulate app usage. static constexpr base::TimeDelta kAppUsageDuration = base::Minutes(2); @@ -120,9 +148,6 @@ TEST_F(AppUsageCollectorTest, ShouldAggregateAppUsageDataOnSubsequentUsage) { reporting_settings_.SetBoolean(::ash::kReportDeviceAppInfo, true); - AppUsageCollector app_usage_collector( - profile(), &reporting_settings_, - app_platform_metrics_service()->AppPlatformMetrics()); // Create a new window for the app and simulate app usage. static constexpr base::TimeDelta kAppUsageDuration = base::Minutes(2); @@ -149,9 +174,6 @@ ShouldStopPersistingAppUsageDataWhenSettingDisabled) { // Enable reporting setting initially. reporting_settings_.SetBoolean(::ash::kReportDeviceAppInfo, true); - AppUsageCollector app_usage_collector( - profile(), &reporting_settings_, - app_platform_metrics_service()->AppPlatformMetrics()); // Create a new window for the app and simulate app usage. static constexpr base::TimeDelta kAppUsageDuration = base::Minutes(2); @@ -175,9 +197,6 @@ TEST_F(AppUsageCollectorTest, ShouldPersistAppUsageDataForNewInstance) { reporting_settings_.SetBoolean(::ash::kReportDeviceAppInfo, true); - AppUsageCollector app_usage_collector( - profile(), &reporting_settings_, - app_platform_metrics_service()->AppPlatformMetrics()); // Create a new window for the app and simulate app usage. static constexpr base::TimeDelta kAppUsageDuration = base::Minutes(2); @@ -201,5 +220,26 @@ VerifyAppUsageDataInPrefStoreForInstance(kInstanceId1, kAppUsageDuration); } +TEST_F(AppUsageCollectorTest, OnAppPlatformMetricsDestroyed) { + reporting_settings_.SetBoolean(::ash::kReportDeviceAppInfo, true); + + // Reset `AppPlatformMetricsService` to destroy the `AppPlatformMetrics` + // component. + ResetAppPlatformMetricsService(); + + // Create a new window for the app and simulate app usage to verify the + // component is no longer tracking app usage metric. + static constexpr base::TimeDelta kAppUsageDuration = base::Minutes(2); + const auto window = std::make_unique<::aura::Window>(nullptr); + window->Init(::ui::LAYER_NOT_DRAWN); + const base::UnguessableToken& kInstanceId = base::UnguessableToken::Create(); + SimulateAppUsageForInstance(window.get(), kInstanceId, kAppUsageDuration); + + // Verify there is no data persisted to the pref store. + const auto& usage_dict_pref = + GetPrefService()->GetDict(::apps::kAppUsageTime); + ASSERT_TRUE(usage_dict_pref.empty()); +} + } // namespace } // namespace reporting
diff --git a/chrome/browser/ash/sync/sync_mojo_service_ash.cc b/chrome/browser/ash/sync/sync_mojo_service_ash.cc index ef250115..c0a64f2 100644 --- a/chrome/browser/ash/sync/sync_mojo_service_ash.cc +++ b/chrome/browser/ash/sync/sync_mojo_service_ash.cc
@@ -58,11 +58,19 @@ } } -void SyncMojoServiceAsh::BindSyncedSessionClient( +void SyncMojoServiceAsh::DEPRECATED_BindSyncedSessionClient( mojo::PendingReceiver<crosapi::mojom::SyncedSessionClient> receiver) { - if (synced_session_client_) { - synced_session_client_->BindReceiver(std::move(receiver)); + NOTIMPLEMENTED(); +} + +void SyncMojoServiceAsh::CreateSyncedSessionClient( + CreateSyncedSessionClientCallback callback) { + if (!base::FeatureList::IsEnabled(syncer::kChromeOSSyncedSessionSharing)) { + std::move(callback).Run(mojo::NullRemote()); + return; } + + std::move(callback).Run(synced_session_client_->CreateRemote()); } } // namespace ash
diff --git a/chrome/browser/ash/sync/sync_mojo_service_ash.h b/chrome/browser/ash/sync/sync_mojo_service_ash.h index 977b8fd..690a2a2 100644 --- a/chrome/browser/ash/sync/sync_mojo_service_ash.h +++ b/chrome/browser/ash/sync/sync_mojo_service_ash.h
@@ -50,10 +50,14 @@ mojo::PendingReceiver<crosapi::mojom::SyncUserSettingsClient> receiver) override; - void BindSyncedSessionClient( + // TODO(b/260599791): Remove in M-114. + void DEPRECATED_BindSyncedSessionClient( mojo::PendingReceiver<crosapi::mojom::SyncedSessionClient> receiver) override; + void CreateSyncedSessionClient( + CreateSyncedSessionClientCallback callback) override; + // Returns null if kChromeOSSyncedSessionClient is disabled. SyncedSessionClientAsh* GetSyncedSessionClientAsh() { return synced_session_client_.get();
diff --git a/chrome/browser/ash/sync/sync_mojo_service_ash_unittest.cc b/chrome/browser/ash/sync/sync_mojo_service_ash_unittest.cc index 4e9a113a..8cd9f17 100644 --- a/chrome/browser/ash/sync/sync_mojo_service_ash_unittest.cc +++ b/chrome/browser/ash/sync/sync_mojo_service_ash_unittest.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ash/sync/sync_mojo_service_ash.h" +#include "base/run_loop.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "components/sync/base/features.h" @@ -38,12 +39,32 @@ void RunAllPendingTasks() { task_environment_.RunUntilIdle(); } + void CreateSyncedSessionClient(base::OnceClosure callback) { + sync_mojo_service_ash_->CreateSyncedSessionClient( + base::BindOnce(&SyncMojoServiceAshTest::OnCreateSyncedSessionClient, + base::Unretained(this), std::move(callback))); + } + + void OnCreateSyncedSessionClient( + base::OnceClosure callback, + mojo::PendingRemote<crosapi::mojom::SyncedSessionClient> pending_remote) { + synced_session_client_remote_.Bind(std::move(pending_remote)); + std::move(callback).Run(); + } + + mojo::Remote<crosapi::mojom::SyncedSessionClient>& + synced_session_client_remote() { + return synced_session_client_remote_; + } + private: base::test::SingleThreadTaskEnvironment task_environment_; base::test::ScopedFeatureList override_features_; testing::NiceMock<syncer::MockSyncService> sync_service_; std::unique_ptr<SyncMojoServiceAsh> sync_mojo_service_ash_; + mojo::Remote<crosapi::mojom::SyncedSessionClient> + synced_session_client_remote_; }; TEST_F(SyncMojoServiceAshTest, ShouldSupportMultipleRemotes) { @@ -79,11 +100,10 @@ user_settings_client_remote.BindNewPipeAndPassReceiver()); ASSERT_TRUE(user_settings_client_remote.is_connected()); - mojo::Remote<crosapi::mojom::SyncedSessionClient> - synced_session_client_remote; - sync_mojo_service_ash()->BindSyncedSessionClient( - synced_session_client_remote.BindNewPipeAndPassReceiver()); - ASSERT_TRUE(synced_session_client_remote.is_connected()); + base::RunLoop run_loop; + CreateSyncedSessionClient(run_loop.QuitClosure()); + run_loop.Run(); + ASSERT_TRUE(synced_session_client_remote().is_connected()); sync_mojo_service_ash()->Shutdown(); // Wait for the disconnect handler to be called. @@ -91,7 +111,7 @@ EXPECT_FALSE(sync_mojo_service_ash_remote.is_connected()); EXPECT_FALSE(explicit_passphrase_client_remote.is_connected()); EXPECT_FALSE(user_settings_client_remote.is_connected()); - EXPECT_FALSE(synced_session_client_remote.is_connected()); + EXPECT_FALSE(synced_session_client_remote().is_connected()); } } // namespace
diff --git a/chrome/browser/ash/sync/synced_session_client_ash.cc b/chrome/browser/ash/sync/synced_session_client_ash.cc index 4b974b3..6eed0ce9 100644 --- a/chrome/browser/ash/sync/synced_session_client_ash.cc +++ b/chrome/browser/ash/sync/synced_session_client_ash.cc
@@ -9,7 +9,8 @@ #include "chromeos/crosapi/mojom/synced_session_client.mojom.h" #include "components/sync_sessions/synced_session.h" #include "mojo/public/cpp/bindings/pending_receiver.h" -#include "mojo/public/cpp/bindings/receiver_set.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver.h" namespace ash { @@ -49,9 +50,14 @@ SyncedSessionClientAsh::SyncedSessionClientAsh() = default; SyncedSessionClientAsh::~SyncedSessionClientAsh() = default; -void SyncedSessionClientAsh::BindReceiver( - mojo::PendingReceiver<crosapi::mojom::SyncedSessionClient> receiver) { - receivers_.Add(this, std::move(receiver)); +mojo::PendingRemote<crosapi::mojom::SyncedSessionClient> +SyncedSessionClientAsh::CreateRemote() { + mojo::PendingReceiver<crosapi::mojom::SyncedSessionClient> pending_receiver; + mojo::PendingRemote<crosapi::mojom::SyncedSessionClient> pending_remote = + pending_receiver.InitWithNewPipeAndPassRemote(); + receivers_.Add(this, std::move(pending_receiver)); + return {pending_remote.PassPipe(), + crosapi::mojom::SyncedSessionClient::Version_}; } void SyncedSessionClientAsh::OnForeignSyncedPhoneSessionsUpdated(
diff --git a/chrome/browser/ash/sync/synced_session_client_ash.h b/chrome/browser/ash/sync/synced_session_client_ash.h index 2dba48a..92aad17b 100644 --- a/chrome/browser/ash/sync/synced_session_client_ash.h +++ b/chrome/browser/ash/sync/synced_session_client_ash.h
@@ -14,6 +14,7 @@ #include "base/time/time.h" #include "chromeos/crosapi/mojom/synced_session_client.mojom.h" #include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver_set.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "url/gurl.h" @@ -91,8 +92,7 @@ void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); - void BindReceiver( - mojo::PendingReceiver<crosapi::mojom::SyncedSessionClient> receiver); + mojo::PendingRemote<crosapi::mojom::SyncedSessionClient> CreateRemote(); // crosapi::mojom::SyncedSessionClient: void OnForeignSyncedPhoneSessionsUpdated(
diff --git a/chrome/browser/ash/sync/synced_session_client_ash_unittest.cc b/chrome/browser/ash/sync/synced_session_client_ash_unittest.cc index 7520ff0..7c96261 100644 --- a/chrome/browser/ash/sync/synced_session_client_ash_unittest.cc +++ b/chrome/browser/ash/sync/synced_session_client_ash_unittest.cc
@@ -84,7 +84,7 @@ void SetUp() override { client_ = std::make_unique<SyncedSessionClientAsh>(); - client_->BindReceiver(client_remote_.BindNewPipeAndPassReceiver()); + client_remote_.Bind(client_->CreateRemote()); test_observer_ = std::make_unique<TestSyncedSessionClientObserver>(); client_->AddObserver(test_observer_.get()); }
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl.cc index ebf3f8d7..c66ed38 100644 --- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl.cc +++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl.cc
@@ -80,9 +80,9 @@ &PersonalizationAppAmbientProviderImpl::OnAmbientModeEnabledChanged, base::Unretained(this))); pref_change_registrar_.Add( - ash::ambient::prefs::kAmbientTheme, + ash::ambient::prefs::kAmbientUiSettings, base::BindRepeating( - &PersonalizationAppAmbientProviderImpl::OnAnimationThemeChanged, + &PersonalizationAppAmbientProviderImpl::OnAmbientUiSettingsChanged, base::Unretained(this))); ambient_ui_model_observer_.Observe( Shell::Get()->ambient_controller()->ambient_ui_model()); @@ -128,8 +128,8 @@ // Call it once to get the current ambient mode enabled status. OnAmbientModeEnabledChanged(); - // Call it once to get the current animation theme. - OnAnimationThemeChanged(); + // Call it once to get the current ambient ui settings. + OnAmbientUiSettingsChanged(); ResetLocalSettings(); } @@ -146,8 +146,7 @@ PrefService* pref_service = profile_->GetPrefs(); DCHECK(pref_service); LogAmbientModeTheme(animation_theme); - pref_service->SetInteger(ash::ambient::prefs::kAmbientTheme, - static_cast<int>(animation_theme)); + AmbientUiSettings(animation_theme).WriteToPrefService(*pref_service); } void PersonalizationAppAmbientProviderImpl::SetTopicSource( @@ -266,11 +265,12 @@ } } -void PersonalizationAppAmbientProviderImpl::OnAnimationThemeChanged() { +void PersonalizationAppAmbientProviderImpl::OnAmbientUiSettingsChanged() { if (!ambient_observer_remote_.is_bound()) return; - ambient_observer_remote_->OnAnimationThemeChanged(GetCurrentAnimationTheme()); + ambient_observer_remote_->OnAnimationThemeChanged( + GetCurrentUiSettings().theme()); } void PersonalizationAppAmbientProviderImpl::OnTemperatureUnitChanged() { @@ -356,12 +356,11 @@ return pref_service->GetBoolean(ash::ambient::prefs::kAmbientModeEnabled); } -ash::AmbientTheme -PersonalizationAppAmbientProviderImpl::GetCurrentAnimationTheme() { +AmbientUiSettings +PersonalizationAppAmbientProviderImpl::GetCurrentUiSettings() { PrefService* pref_service = profile_->GetPrefs(); DCHECK(pref_service); - return static_cast<ash::AmbientTheme>( - pref_service->GetInteger(ash::ambient::prefs::kAmbientTheme)); + return AmbientUiSettings::ReadFromPrefService(*pref_service); } void PersonalizationAppAmbientProviderImpl::UpdateSettings() {
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl.h b/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl.h index 0a81cf0..f5dcfbf 100644 --- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl.h +++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl.h
@@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_ASH_WEB_APPLICATIONS_PERSONALIZATION_APP_PERSONALIZATION_APP_AMBIENT_PROVIDER_IMPL_H_ #define CHROME_BROWSER_ASH_WEB_APPLICATIONS_PERSONALIZATION_APP_PERSONALIZATION_APP_AMBIENT_PROVIDER_IMPL_H_ +#include "ash/ambient/ambient_ui_settings.h" #include "ash/constants/ambient_theme.h" #include "ash/public/cpp/ambient/ambient_ui_model.h" #include "ash/public/cpp/ambient/common/ambient_settings.h" @@ -65,7 +66,7 @@ // Notify WebUI the latest values. void OnAmbientModeEnabledChanged(); - void OnAnimationThemeChanged(); + void OnAmbientUiSettingsChanged(); void OnTemperatureUnitChanged(); void OnTopicSourceChanged(); void OnAlbumsChanged(); @@ -76,7 +77,7 @@ bool IsAmbientModeEnabled(); - ash::AmbientTheme GetCurrentAnimationTheme(); + AmbientUiSettings GetCurrentUiSettings(); // Update the local `settings_` to server. void UpdateSettings();
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc index 014d48e..72b6031d 100644 --- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc +++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc
@@ -15,6 +15,7 @@ #include <vector> #include "ash/public/cpp/image_util.h" +#include "ash/public/cpp/schedule_enums.h" #include "ash/public/cpp/tablet_mode.h" #include "ash/public/cpp/wallpaper/google_photos_wallpaper_params.h" #include "ash/public/cpp/wallpaper/online_wallpaper_params.h" @@ -23,6 +24,8 @@ #include "ash/public/cpp/wallpaper/wallpaper_info.h" #include "ash/public/cpp/wallpaper/wallpaper_types.h" #include "ash/public/cpp/window_backdrop.h" +#include "ash/style/dark_light_mode_controller_impl.h" +#include "ash/wallpaper/wallpaper_utils/wallpaper_online_variant_utils.h" #include "ash/webui/personalization_app/mojom/personalization_app.mojom.h" #include "ash/webui/personalization_app/mojom/personalization_app_mojom_traits.h" #include "ash/webui/personalization_app/proto/backdrop_wallpaper.pb.h" @@ -138,6 +141,15 @@ return GetJpegDataUrl(output.data(), output.size()); } +// Convenience method to get the current checkpoint. +ScheduleCheckpoint GetCurrentCheckPoint() { + auto* dark_light_mode_controller = DarkLightModeControllerImpl::Get(); + if (!dark_light_mode_controller) { + return ScheduleCheckpoint::kDisabled; + } + return dark_light_mode_controller->current_checkpoint(); +} + } // namespace PersonalizationAppWallpaperProviderImpl:: @@ -154,9 +166,9 @@ PersonalizationAppWallpaperProviderImpl:: ~PersonalizationAppWallpaperProviderImpl() { - if (!image_asset_id_map_.empty()) { + if (!image_unit_id_map_.empty()) { // User viewed wallpaper page at least once during this session because - // |image_asset_id_map_| has wallpaper asset ids saved. Check if this user + // |image_unit_id_map_| has wallpaper unit ids saved. Check if this user // should see a wallpaper HaTS. ::ash::personalization_app::PersonalizationAppManagerFactory:: GetForBrowserContext(profile_) @@ -515,13 +527,13 @@ } void PersonalizationAppWallpaperProviderImpl::SelectWallpaper( - uint64_t image_asset_id, + uint64_t unit_id, bool preview_mode, SelectWallpaperCallback callback) { - const auto& it = image_asset_id_map_.find(image_asset_id); + const auto& it = image_unit_id_map_.find(unit_id); - if (it == image_asset_id_map_.end()) { - wallpaper_receiver_.ReportBadMessage("Invalid image asset_id selected"); + if (it == image_unit_id_map_.end()) { + wallpaper_receiver_.ReportBadMessage("Invalid image unit_id selected"); return; } @@ -532,16 +544,18 @@ return; } + std::string collection_id; std::vector<ash::OnlineWallpaperVariant> variants; - for (const auto& entry : image_asset_id_map_) { - const ImageInfo& image_info = entry.second; - if (image_info.unit_id == it->second.unit_id && - image_info.collection_id == it->second.collection_id) { - variants.emplace_back(image_info.asset_id, image_info.image_url, - image_info.type); - } + for (const auto& image_info : it->second) { + variants.emplace_back(image_info.asset_id, image_info.image_url, + image_info.type); + collection_id = image_info.collection_id; } + const auto checkpoint = GetCurrentCheckPoint(); + auto* variant = FirstValidVariant(variants, checkpoint); + DCHECK(variant); + WallpaperControllerClientImpl* client = WallpaperControllerClientImpl::Get(); DCHECK(client); @@ -555,11 +569,11 @@ client->SetOnlineWallpaper( ash::OnlineWallpaperParams( - GetAccountId(profile_), image_asset_id, - GURL(it->second.image_url.spec()), it->second.collection_id, + GetAccountId(profile_), variant->asset_id, + GURL(variant->raw_url.spec()), collection_id, ash::WallpaperLayout::WALLPAPER_LAYOUT_CENTER_CROPPED, preview_mode, /*from_user=*/true, - /*daily_refresh_enabled=*/false, it->second.unit_id, variants), + /*daily_refresh_enabled=*/false, unit_id, variants), base::BindOnce( &PersonalizationAppWallpaperProviderImpl::OnOnlineWallpaperSelected, backend_weak_ptr_factory_.GetWeakPtr())); @@ -753,14 +767,14 @@ return; } - bool force_refresh = !info->asset_id.has_value(); - if (info->asset_id.has_value()) { - const auto& it = image_asset_id_map_.find(info->asset_id.value()); + bool force_refresh = !info->unit_id.has_value(); + if (info->unit_id.has_value()) { + const auto& it = image_unit_id_map_.find(info->unit_id.value()); // Only force refresh if the current wallpaper image does not belong to // this collection. - force_refresh = it == image_asset_id_map_.end() || - it->second.collection_id != collection_id; + force_refresh = it == image_unit_id_map_.end() || it->second.empty() || + it->second[0].collection_id != collection_id; } DVLOG(1) << __func__ << " info=" << info.value() << " collection_id=" << collection_id @@ -878,17 +892,16 @@ absl::optional<std::vector<backdrop::Image>> result; if (success && !images.empty()) { for (const auto& proto_image : images) { - if (!proto_image.has_asset_id() || !proto_image.has_image_url()) { + if (!proto_image.has_asset_id() || !proto_image.has_unit_id() || + !proto_image.has_image_url()) { LOG(WARNING) << "Invalid image discarded"; continue; } - image_asset_id_map_.insert( - {proto_image.asset_id(), - ImageInfo(GURL(proto_image.image_url()), collection_id, - proto_image.asset_id(), proto_image.unit_id(), - proto_image.has_image_type() - ? proto_image.image_type() - : backdrop::Image::IMAGE_TYPE_UNKNOWN)}); + image_unit_id_map_[proto_image.unit_id()].push_back(ImageInfo( + GURL(proto_image.image_url()), collection_id, proto_image.asset_id(), + proto_image.unit_id(), + proto_image.has_image_type() ? proto_image.image_type() + : backdrop::Image::IMAGE_TYPE_UNKNOWN)); } result = std::move(images); }
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.h b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.h index 4252e5d..1af6921 100644 --- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.h +++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.h
@@ -147,7 +147,7 @@ void OnWallpaperPreviewEnded() override; // ash::personalization_app::mojom::WallpaperProvider: - void SelectWallpaper(uint64_t image_asset_id, + void SelectWallpaper(uint64_t unit_id, bool preview_mode, SelectWallpaperCallback callback) override; @@ -356,10 +356,10 @@ type(in_type) {} }; - // Store a mapping of valid image asset_ids to their ImageInfo to validate - // user wallpaper selections. This is filled when a user first visits the - // Wallpaper subpage of Personalization App. - std::map<uint64_t, ImageInfo> image_asset_id_map_; + // Stores the mapping of valid image unit_ids and their image variants. This + // is filled when a user first visits the Wallpaper subpage of Personalization + // App. + std::map<uint64_t, std::vector<ImageInfo>> image_unit_id_map_; // Stores the mapping of Google Photos album id to its photos' dedup keys. // Used to determine whether an album contains the currently selected
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc index e3a28089..2e401369 100644 --- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc +++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
@@ -282,8 +282,8 @@ void AddWallpaperImage( const PersonalizationAppWallpaperProviderImpl::ImageInfo& image_info) { - wallpaper_provider_->image_asset_id_map_.insert( - {image_info.asset_id, image_info}); + wallpaper_provider_->image_unit_id_map_.insert( + {image_info.unit_id, {image_info}}); } TestWallpaperController* test_wallpaper_controller() {
diff --git a/chrome/browser/ash/web_applications/shortcut_customization_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/shortcut_customization_app_integration_browsertest.cc index 32eaa6d..9d275c70 100644 --- a/chrome/browser/ash/web_applications/shortcut_customization_app_integration_browsertest.cc +++ b/chrome/browser/ash/web_applications/shortcut_customization_app_integration_browsertest.cc
@@ -3,10 +3,6 @@ // found in the LICENSE file. #include "ash/constants/ash_features.h" -#include "ash/webui/shortcut_customization_ui/backend/search/search.mojom.h" -#include "ash/webui/shortcut_customization_ui/backend/search/search_handler.h" -#include "ash/webui/shortcut_customization_ui/shortcuts_app_manager.h" -#include "ash/webui/shortcut_customization_ui/shortcuts_app_manager_factory.h" #include "ash/webui/shortcut_customization_ui/url_constants.h" #include "base/run_loop.h" #include "base/test/bind.h" @@ -67,34 +63,5 @@ "Apps.DefaultAppLaunch.FromChromeInternal", 44, 1); } -IN_PROC_BROWSER_TEST_P(ShortcutCustomizationAppIntegrationTest, - ShortcutsAppManager) { - WaitForTestSystemAppInstall(); - - ash::LaunchSystemWebAppAsync(profile(), - ash::SystemWebAppType::SHORTCUT_CUSTOMIZATION); - - base::RunLoop run_loop; - // Test that the ShortcutsAppManagerFactory can retrieve the - // ShortcutsAppManager, and that we can retrieve the SearchHandler and perform - // a search. - ash::shortcut_ui::ShortcutsAppManagerFactory::GetForBrowserContext(profile()) - ->search_handler() - ->Search(u"verycomplicatedsearchquery", - /*max_num_results=*/2u, - base::BindLambdaForTesting( - [&](std::vector< - ash::shortcut_customization::mojom::SearchResultPtr> - search_results) { - // Assert that the number of results is equal to the - // max_num_results passed into the query. - // TODO(cambickel): Update this test when SearchHandler - // provides real data from the LocalSearchService. - EXPECT_EQ(search_results.size(), 2u); - run_loop.QuitClosure().Run(); - })); - run_loop.Run(); -} - INSTANTIATE_SYSTEM_WEB_APP_MANAGER_TEST_SUITE_REGULAR_PROFILE_P( ShortcutCustomizationAppIntegrationTest);
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index b92360e..3800b8f 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -7055,7 +7055,7 @@ #endif } -bool ChromeContentBrowserClient::ShouldAllowInsecurePrivateNetworkRequests( +bool ChromeContentBrowserClient::ShouldAllowInsecureLocalNetworkRequests( content::BrowserContext* browser_context, const url::Origin& origin) { // The host content settings map might no be null for some irregular profiles, @@ -7537,7 +7537,8 @@ } bool ChromeContentBrowserClient::IsThirdPartyStoragePartitioningAllowed( - content::BrowserContext* browser_context) { + content::BrowserContext* browser_context, + const url::Origin& top_level_origin) { const HostContentSettingsMap* const content_settings = HostContentSettingsMapFactory::GetForProfile( Profile::FromBrowserContext(browser_context)); @@ -7546,8 +7547,9 @@ // should be blocked, but isn't the final word on if it's allowed. return true; } - return content_settings->GetDefaultContentSetting( - ContentSettingsType::THIRD_PARTY_STORAGE_PARTITIONING, nullptr) == + return content_settings->GetContentSetting( + top_level_origin.GetURL(), top_level_origin.GetURL(), + ContentSettingsType::THIRD_PARTY_STORAGE_PARTITIONING) == CONTENT_SETTING_ALLOW; }
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h index 3f0c099..c51ec13 100644 --- a/chrome/browser/chrome_content_browser_client.h +++ b/chrome/browser/chrome_content_browser_client.h
@@ -786,7 +786,7 @@ const GURL& url) override; bool ShouldServiceWorkerInheritPolicyContainerFromCreator( const GURL& url) override; - bool ShouldAllowInsecurePrivateNetworkRequests( + bool ShouldAllowInsecureLocalNetworkRequests( content::BrowserContext* browser_context, const url::Origin& origin) override; bool IsJitDisabledForSite(content::BrowserContext* browser_context, @@ -866,7 +866,8 @@ content::BrowserContext* browser_context) override; bool IsThirdPartyStoragePartitioningAllowed( - content::BrowserContext* browser_context) override; + content::BrowserContext* browser_context, + const url::Origin& top_level_origin) override; protected: static bool HandleWebUI(GURL* url, content::BrowserContext* browser_context);
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_clipboard_notifier.cc b/chrome/browser/chromeos/policy/dlp/dlp_clipboard_notifier.cc index 0a475f9..5ba0797f 100644 --- a/chrome/browser/chromeos/policy/dlp/dlp_clipboard_notifier.cc +++ b/chrome/browser/chromeos/policy/dlp/dlp_clipboard_notifier.cc
@@ -25,6 +25,7 @@ #include "ash/public/cpp/system/toast_data.h" #include "ash/public/cpp/system/toast_manager.h" #include "ash/public/cpp/window_tree_host_lookup.h" +#include "ash/resources/vector_icons/vector_icons.h" #endif // BUILDFLAG(IS_CHROMEOS_ASH) #if BUILDFLAG(IS_CHROMEOS_LACROS) @@ -280,9 +281,9 @@ /*visible_on_lock_screen=*/false, /*has_dismiss_button=*/true, /*custom_dismiss_text=*/ - l10n_util::GetStringUTF16(IDS_POLICY_DLP_CLIPBOARD_BLOCK_TOAST_BUTTON)); - toast.is_managed = true; - toast.dismiss_callback = base::BindRepeating(&OnToastClicked); + l10n_util::GetStringUTF16(IDS_POLICY_DLP_CLIPBOARD_BLOCK_TOAST_BUTTON), + /*dismiss_callback=*/base::BindRepeating(&OnToastClicked), + /*leading_icon=*/ash::kSystemMenuBusinessIcon); ash::ToastManager::Get()->Show(std::move(toast)); } #endif // BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/creator/android/BUILD.gn b/chrome/browser/creator/android/BUILD.gn index 1d4a8a65..d87e429b 100644 --- a/chrome/browser/creator/android/BUILD.gn +++ b/chrome/browser/creator/android/BUILD.gn
@@ -18,6 +18,7 @@ "java/src/org/chromium/chrome/browser/creator/CreatorToolbarView.java", "java/src/org/chromium/chrome/browser/creator/CreatorToolbarViewBinder.java", "java/src/org/chromium/chrome/browser/creator/NewTabCreator.java", + "java/src/org/chromium/chrome/browser/creator/SignInInterstitialInitiator.java", "java/src/org/chromium/chrome/browser/creator/WebContentsCreator.java", ] deps = [
diff --git a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinator.java b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinator.java index 983c47c..84976cd 100644 --- a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinator.java +++ b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinator.java
@@ -24,12 +24,12 @@ import org.chromium.chrome.browser.feed.FeedActionDelegate; import org.chromium.chrome.browser.feed.FeedAutoplaySettingsDelegate; import org.chromium.chrome.browser.feed.FeedContentFirstLoadWatcher; +import org.chromium.chrome.browser.feed.FeedListContentManager; +import org.chromium.chrome.browser.feed.FeedListContentManager.FeedContent; import org.chromium.chrome.browser.feed.FeedStream; import org.chromium.chrome.browser.feed.FeedSurfaceScopeDependencyProvider; import org.chromium.chrome.browser.feed.FeedSurfaceTracker; import org.chromium.chrome.browser.feed.NativeViewListRenderer; -import org.chromium.chrome.browser.feed.NtpListContentManager; -import org.chromium.chrome.browser.feed.NtpListContentManager.FeedContent; import org.chromium.chrome.browser.feed.SingleWebFeedEntryPoint; import org.chromium.chrome.browser.feed.SingleWebFeedParameters; import org.chromium.chrome.browser.feed.Stream; @@ -86,7 +86,7 @@ private CreatorMediator mMediator; private CreatorTabMediator mTabMediator; private Activity mActivity; - private NtpListContentManager mContentManager; + private FeedListContentManager mContentManager; private RecyclerView mRecyclerView; private View mProfileView; private ViewGroup mLayoutView; @@ -146,7 +146,7 @@ WindowAndroid windowAndroid, Profile profile, String url, WebContentsCreator creatorWebContents, NewTabCreator creatorOpenTab, UnownedUserDataSupplier<ShareDelegate> bottomsheetShareDelegateSupplier, int entryPoint, - boolean isFollowing) { + boolean isFollowing, SignInInterstitialInitiator signInInterstitialInitiator) { mActivity = activity; mProfile = profile; mSnackbarManager = snackbarManager; @@ -160,8 +160,8 @@ mProfileView = (View) LayoutInflater.from(mActivity).inflate(R.layout.creator_profile, null); - List<NtpListContentManager.FeedContent> contentPreviewsList = new ArrayList<>(); - contentPreviewsList.add(new NtpListContentManager.NativeViewContent( + List<FeedListContentManager.FeedContent> contentPreviewsList = new ArrayList<>(); + contentPreviewsList.add(new FeedListContentManager.NativeViewContent( getContentPreviewsPaddingPx(), CREATOR_PROFILE_ID, mProfileView)); mContentManager.addContents(0, contentPreviewsList); mHeaderCount = 1; @@ -186,7 +186,8 @@ mCreatorModel, (CreatorToolbarView) mLayoutView, CreatorToolbarViewBinder::bind); setUpToolbarListener(); - mMediator = new CreatorMediator(mActivity, mCreatorModel, mCreatorSnackbarController); + mMediator = new CreatorMediator( + mActivity, mCreatorModel, mCreatorSnackbarController, signInInterstitialInitiator); } /** @@ -283,7 +284,7 @@ private RecyclerView setUpView() { // TODO(crbug.com/1374744): Refactor NTP naming out of the general Feed code. - mContentManager = new NtpListContentManager(); + mContentManager = new FeedListContentManager(); ProcessScope processScope = FeedSurfaceTracker.getInstance().getXSurfaceProcessScope(); if (processScope != null) { @@ -564,7 +565,7 @@ List<FeedContent> privacyList = new ArrayList<>(); View privacyView = LayoutInflater.from(mActivity).inflate(R.layout.creator_privacy, null); - privacyList.add(new NtpListContentManager.NativeViewContent( + privacyList.add(new FeedListContentManager.NativeViewContent( getContentPreviewsPaddingPx(), CREATOR_PRIVACY_ID, privacyView)); mContentManager.addContents(mHeaderCount, privacyList); mHeaderCount += privacyList.size();
diff --git a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinatorTest.java b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinatorTest.java index 44d47cb..8724c705 100644 --- a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinatorTest.java +++ b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinatorTest.java
@@ -33,13 +33,13 @@ import org.chromium.base.test.util.JniMocker; import org.chromium.chrome.browser.creator.CreatorCoordinator.ContentChangedListener; import org.chromium.chrome.browser.creator.test.R; +import org.chromium.chrome.browser.feed.FeedListContentManager.ExternalViewContent; +import org.chromium.chrome.browser.feed.FeedListContentManager.FeedContent; +import org.chromium.chrome.browser.feed.FeedListContentManager.NativeViewContent; import org.chromium.chrome.browser.feed.FeedReliabilityLoggingBridge; import org.chromium.chrome.browser.feed.FeedServiceBridge; import org.chromium.chrome.browser.feed.FeedServiceBridgeJni; import org.chromium.chrome.browser.feed.FeedStream; -import org.chromium.chrome.browser.feed.NtpListContentManager.ExternalViewContent; -import org.chromium.chrome.browser.feed.NtpListContentManager.FeedContent; -import org.chromium.chrome.browser.feed.NtpListContentManager.NativeViewContent; import org.chromium.chrome.browser.feed.SingleWebFeedEntryPoint; import org.chromium.chrome.browser.feed.webfeed.WebFeedBridge; import org.chromium.chrome.browser.profiles.Profile; @@ -85,6 +85,8 @@ private UnownedUserDataSupplier<ShareDelegate> mShareDelegateSupplier; @Mock private FeedStream mStreamMock; + @Mock + private SignInInterstitialInitiator mSignInInterstitialInitiator; @Rule public ActivityScenarioRule<TestActivity> mActivityScenarioRule = @@ -121,7 +123,7 @@ String url, byte[] webFeedId, int entryPoint, boolean following) { return new CreatorCoordinator(mActivity, webFeedId, mSnackbarManager, mWindowAndroid, mProfile, url, mCreatorWebContents, mCreatorOpenTab, mShareDelegateSupplier, - entryPoint, following); + entryPoint, following, mSignInInterstitialInitiator); } @Test
diff --git a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorMediator.java b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorMediator.java index 1a915c9..37fd60ce 100644 --- a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorMediator.java +++ b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorMediator.java
@@ -9,6 +9,7 @@ import android.content.Context; import org.chromium.chrome.browser.creator.CreatorApiBridge.Creator; +import org.chromium.chrome.browser.feed.FeedServiceBridge; import org.chromium.chrome.browser.feed.webfeed.WebFeedBridge; import org.chromium.ui.modelutil.PropertyModel; @@ -23,12 +24,15 @@ private Creator mCreator; private PropertyModel mCreatorModel; private final CreatorSnackbarController mCreatorSnackbarController; + private SignInInterstitialInitiator mSignInInterstitialInitiator; CreatorMediator(Context context, PropertyModel creatorModel, - CreatorSnackbarController creatorSnackbarController) { + CreatorSnackbarController creatorSnackbarController, + SignInInterstitialInitiator signInInterstitialInitiator) { mContext = context; mCreatorModel = creatorModel; mCreatorSnackbarController = creatorSnackbarController; + mSignInInterstitialInitiator = signInInterstitialInitiator; if (mCreatorModel.get(CreatorProperties.WEB_FEED_ID_KEY) != null) { getCreator(); } @@ -39,14 +43,18 @@ } private void followClickHandler() { - WebFeedBridge.followFromId(mCreatorModel.get(CreatorProperties.WEB_FEED_ID_KEY), - /*isDurable=*/false, WebFeedBridge.CHANGE_REASON_WEB_PAGE_MENU, (result) -> { - if (result.requestStatus == SUCCESS) { - mCreatorModel.set(CreatorProperties.IS_FOLLOWED_KEY, true); - } - mCreatorSnackbarController.showSnackbarForFollow( - result.requestStatus, mCreatorModel.get(CreatorProperties.TITLE_KEY)); - }); + if (FeedServiceBridge.isSignedIn()) { + WebFeedBridge.followFromId(mCreatorModel.get(CreatorProperties.WEB_FEED_ID_KEY), + /*isDurable=*/false, WebFeedBridge.CHANGE_REASON_WEB_PAGE_MENU, (result) -> { + if (result.requestStatus == SUCCESS) { + mCreatorModel.set(CreatorProperties.IS_FOLLOWED_KEY, true); + } + mCreatorSnackbarController.showSnackbarForFollow(result.requestStatus, + mCreatorModel.get(CreatorProperties.TITLE_KEY)); + }); + } else { + mSignInInterstitialInitiator.showSignInInterstitial(); + } } private void followingClickHandler() {
diff --git a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorMediatorTest.java b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorMediatorTest.java index adeb5f23..a79d22b 100644 --- a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorMediatorTest.java +++ b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorMediatorTest.java
@@ -83,6 +83,8 @@ private UnownedUserDataSupplier<ShareDelegate> mShareDelegateSupplier; @Mock private UrlFormatter.Natives mUrlFormatterJniMock; + @Mock + private SignInInterstitialInitiator mSignInInterstitialInitiator; @Captor private ArgumentCaptor<Callback<FollowResults>> mFollowResultsCallbackCaptor; @@ -116,14 +118,17 @@ when(mUrlFormatterJniMock.formatUrlForDisplayOmitSchemePathAndTrivialSubdomains(any())) .thenReturn(JUnitTestGURLs.URL_1); + when(mFeedServiceBridgeJniMock.isSignedIn()).thenReturn(true); + mActivityScenarioRule.getScenario().onActivity(activity -> mActivity = activity); mCreatorCoordinator = new CreatorCoordinator(mActivity, mWebFeedId, mSnackbarManager, mWindowAndroid, mProfile, mUrl, mCreatorWebContents, mCreatorOpenTab, - mShareDelegateSupplier, SingleWebFeedEntryPoint.OTHER, /* isFollowing= */ false); + mShareDelegateSupplier, SingleWebFeedEntryPoint.OTHER, /* isFollowing= */ false, + mSignInInterstitialInitiator); mCreatorModel = mCreatorCoordinator.getCreatorModel(); - mCreatorMediator = - new CreatorMediator(mActivity, mCreatorModel, mCreatorSnackbarController); + mCreatorMediator = new CreatorMediator( + mActivity, mCreatorModel, mCreatorSnackbarController, mSignInInterstitialInitiator); } @Test
diff --git a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/SignInInterstitialInitiator.java b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/SignInInterstitialInitiator.java new file mode 100644 index 0000000..9de40966 --- /dev/null +++ b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/SignInInterstitialInitiator.java
@@ -0,0 +1,13 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.creator; + +/** Interface for showing sign-in insterstitial. */ +public interface SignInInterstitialInitiator { + /** + * Shows a sign-in interstitial. + */ + void showSignInInterstitial(); +}
diff --git a/chrome/browser/enterprise/connectors/reporting/browser_crash_event_router.cc b/chrome/browser/enterprise/connectors/reporting/browser_crash_event_router.cc index 21fe8dda..8a574b39 100644 --- a/chrome/browser/enterprise/connectors/reporting/browser_crash_event_router.cc +++ b/chrome/browser/enterprise/connectors/reporting/browser_crash_event_router.cc
@@ -4,241 +4,34 @@ #include "chrome/browser/enterprise/connectors/reporting/browser_crash_event_router.h" -#include "base/command_line.h" -#include "base/files/file_util.h" -#include "base/files/important_file_writer.h" -#include "base/logging.h" -#include "base/path_service.h" -#include "base/strings/string_util.h" -#include "base/task/thread_pool.h" -#include "base/time/time.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/enterprise/connectors/common.h" -#include "chrome/browser/enterprise/connectors/connectors_prefs.h" -#include "chrome/browser/enterprise/connectors/reporting/realtime_reporting_client_factory.h" +#include "chrome/browser/enterprise/connectors/reporting/crash_reporting_context.h" #include "chrome/browser/enterprise/connectors/reporting/reporting_service_settings.h" -#include "chrome/browser/policy/chrome_browser_policy_connector.h" -#include "chrome/common/channel_info.h" -#include "chrome/common/chrome_paths.h" -#include "components/prefs/pref_service.h" -#include "components/version_info/version_info.h" - -#if !BUILDFLAG(IS_FUCHSIA) -#include "components/crash/core/app/crashpad.h" -#include "third_party/crashpad/crashpad/client/crash_report_database.h" -#endif // !BUILDFLAG(IS_FUCHSIA) namespace enterprise_connectors { -#if !BUILDFLAG(IS_FUCHSIA) -namespace { -// key names used when building the dictionary to pass to the real-time -// reporting API -constexpr char kKeyChannel[] = "channel"; -constexpr char kKeyVersion[] = "version"; -constexpr char kKeyReportId[] = "reportId"; -constexpr char kKeyPlatform[] = "platform"; -constexpr char kKeyProfileUserName[] = "profileUserName"; - -constexpr char kCrashpadPollingIntervalFlag[] = "crashpad-polling-interval"; -constexpr int kDefaultCrashpadPollingIntervalSeconds = 3600; - -void SetLatestCrashReportTime(time_t timestamp) { - PrefService* local_state = g_browser_process->local_state(); - local_state->SetInt64(enterprise_connectors::kLatestCrashReportCreationTime, - timestamp); -} - -time_t GetLatestCrashReportTime() { - PrefService* local_state = g_browser_process->local_state(); - time_t timestamp = local_state->GetInt64( - enterprise_connectors::kLatestCrashReportCreationTime); - VLOG(1) << "enterprise.crash_reporting: latest crash report time: " - << base::Time::FromTimeT(timestamp); - return timestamp; -} - -// Get polling interval for crashpad database. This is factored into a -// function to allow for a dev-only command-line option for ease of -// manual testing -base::TimeDelta GetCrashpadPollingInterval() { - base::TimeDelta result = - base::Seconds(kDefaultCrashpadPollingIntervalSeconds); - if (g_browser_process && g_browser_process->browser_policy_connector() - ->IsCommandLineSwitchSupported()) { - base::CommandLine* cmd = base::CommandLine::ForCurrentProcess(); - if (cmd->HasSwitch(kCrashpadPollingIntervalFlag)) { - int crashpad_polling_interval_seconds = 0; - if (base::StringToInt( - cmd->GetSwitchValueASCII(kCrashpadPollingIntervalFlag), - &crashpad_polling_interval_seconds) && - crashpad_polling_interval_seconds > 0) { - result = base::Seconds(crashpad_polling_interval_seconds); - } - } - } - VLOG(1) << "enterprise.crash_reporting: crashpad polling interval set to " - << result; - return result; -} - -// Copy new reports (i.e. reports that have not been sent to the -// reporting server) from `reports_to_be_copied` to `reports` -// based on the `latest_creation_time`. -void CopyNewReports( - const std::vector<crashpad::CrashReportDatabase::Report>& - reports_to_be_copied, - int64_t latest_creation_time, - std::vector<crashpad::CrashReportDatabase::Report>& reports) { - for (const crashpad::CrashReportDatabase::Report& report : - reports_to_be_copied) { - if (report.creation_time <= latest_creation_time) { - continue; - } - reports.push_back(report); - } -} - -bool GetReportsFromDatabase( - std::vector<crashpad::CrashReportDatabase::Report>& pending_reports, - std::vector<crashpad::CrashReportDatabase::Report>& completed_reports) { - std::unique_ptr<crashpad::CrashReportDatabase> database = - crashpad::CrashReportDatabase::InitializeWithoutCreating( - crash_reporter::GetCrashpadDatabasePath()); - // `database` could be null if it has not been initialized yet. - if (!database) { - VLOG(1) << "enterprise.crash_reporting: failed to fetch crashpad db"; - return false; - } - - crashpad::CrashReportDatabase::OperationStatus status; - status = database->GetPendingReports(&pending_reports); - if (status != crashpad::CrashReportDatabase::kNoError) { - return false; - } - status = database->GetCompletedReports(&completed_reports); - if (status != crashpad::CrashReportDatabase::kNoError) { - return false; - } - return true; -} - -// Return a list of new Reports that are ready to be to sent to the -// reporting server. It returns an empty list if any operation fails or -// there is no new report. -std::vector<crashpad::CrashReportDatabase::Report> GetNewReports( - time_t latest_creation_time) { - // Get the creation time of the latest report that was sent to the reporting - // server last time. - // latest_crash_report is the filepath where stores the latest report info - std::vector<crashpad::CrashReportDatabase::Report> reports; - std::vector<crashpad::CrashReportDatabase::Report> pending_reports; - std::vector<crashpad::CrashReportDatabase::Report> completed_reports; - if (!GetReportsFromDatabase(pending_reports, completed_reports)) { - return reports; - } - // Get reports that have not been sent (<= latest_creation_time) - CopyNewReports(pending_reports, latest_creation_time, reports); - CopyNewReports(completed_reports, latest_creation_time, reports); - - return reports; -} - -} // namespace - -// TODO(b/238427470): unit testing this function -void BrowserCrashEventRouter::UploadToReportingServer( - RealtimeReportingClient* reporting_client, - ReportingSettings settings, - std::vector<crashpad::CrashReportDatabase::Report> reports) { - DCHECK(reporting_client); - if (reports.empty()) { - VLOG(1) << "enterprise.crash_reporting: no new crashes"; - return; - } - VLOG(1) << "enterprise.crash_reporting: " << reports.size() - << " new crashes to report"; - const std::string version = version_info::GetVersionNumber(); - const std::string channel = - version_info::GetChannelString(chrome::GetChannel()); - const std::string platform = version_info::GetOSType(); - - int64_t latest_creation_time = -1; - - for (const auto& report : reports) { - base::Value::Dict event; - event.Set(kKeyChannel, channel); - event.Set(kKeyVersion, version); - event.Set(kKeyReportId, report.id); - event.Set(kKeyPlatform, platform); - event.Set(kKeyProfileUserName, reporting_client->GetProfileUserName()); - reporting_client->ReportPastEvent( - ReportingServiceSettings::kBrowserCrashEvent, settings, - std::move(event), base::Time::FromTimeT(report.creation_time)); - if (report.creation_time > latest_creation_time) { - latest_creation_time = report.creation_time; - } - } - SetLatestCrashReportTime(latest_creation_time); -} - -void BrowserCrashEventRouter::ReportCrashes() { - VLOG(1) << "enterprise.crash_reporting: checking for unreported crashes"; - DCHECK(reporting_client_); - const absl::optional<ReportingSettings> settings = - reporting_client_->GetReportingSettings(); - bool isBrowserCrashReportingEnabled = - settings.has_value() && - settings->enabled_event_names.count( - ReportingServiceSettings::kBrowserCrashEvent) != 0; - VLOG(1) << "enterprise.crash_reporting: crash reporting enabled: " - << isBrowserCrashReportingEnabled; - if (!isBrowserCrashReportingEnabled) { - g_browser_process->local_state()->ClearPref( - enterprise_connectors::kLatestCrashReportCreationTime); - return; - } - time_t latest_creation_time = GetLatestCrashReportTime(); - if (latest_creation_time == 0) { - latest_creation_time = base::Time::Now().ToTimeT(); - SetLatestCrashReportTime(latest_creation_time); - } - base::ThreadPool::PostTaskAndReplyWithResult( - FROM_HERE, {base::MayBlock()}, - base::BindOnce(&GetNewReports, latest_creation_time), - base::BindOnce(&BrowserCrashEventRouter::UploadToReportingServer, - AsWeakPtr(), reporting_client_, std::move(*settings))); -} - -void BrowserCrashEventRouter::OnCloudReportingLaunched( - enterprise_reporting::ReportScheduler* report_scheduler) { - VLOG(1) << "enterprise.crash_reporting: crash event reporting initializing"; - // An initial call to ReportCrashes() is required because the first call - // in the repeating callback happens after the delay. - ReportCrashes(); - repeating_crash_report_.Start(FROM_HERE, GetCrashpadPollingInterval(), this, - &BrowserCrashEventRouter::ReportCrashes); -} -#endif // !BUILDFLAG(IS_FUCHSIA) - BrowserCrashEventRouter::BrowserCrashEventRouter( content::BrowserContext* context) { - reporting_client_ = RealtimeReportingClientFactory::GetForProfile(context); - if (base::FeatureList::IsEnabled(kBrowserCrashEventsEnabled)) { -#if !BUILDFLAG(IS_CHROMEOS_ASH) - controller_ = g_browser_process->browser_policy_connector() - ->chrome_browser_cloud_management_controller(); - controller_->AddObserver(this); -#endif // !BUILDFLAG(IS_CHROMEOS_ASH) + if (!base::FeatureList::IsEnabled(kBrowserCrashEventsEnabled)) { + return; } +#if !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_CHROMEOS_ASH) + CrashReportingContext* crash_reporting_context = + CrashReportingContext::GetInstance(); + Profile* profile = Profile::FromBrowserContext(context); + crash_reporting_context->AddProfile(this, profile); + +#endif } BrowserCrashEventRouter::~BrowserCrashEventRouter() { - if (controller_) { -#if !BUILDFLAG(IS_CHROMEOS_ASH) - controller_->RemoveObserver(this); -#endif // !BUILDFLAG(IS_CHROMEOS_ASH) + if (!base::FeatureList::IsEnabled(kBrowserCrashEventsEnabled)) { + return; } +#if !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_CHROMEOS_ASH) + CrashReportingContext* crash_reporting_context = + CrashReportingContext::GetInstance(); + crash_reporting_context->RemoveProfile(this); +#endif } } // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/reporting/browser_crash_event_router.h b/chrome/browser/enterprise/connectors/reporting/browser_crash_event_router.h index 23ab03a..54387a6 100644 --- a/chrome/browser/enterprise/connectors/reporting/browser_crash_event_router.h +++ b/chrome/browser/enterprise/connectors/reporting/browser_crash_event_router.h
@@ -5,54 +5,23 @@ #ifndef CHROME_BROWSER_ENTERPRISE_CONNECTORS_REPORTING_BROWSER_CRASH_EVENT_ROUTER_H_ #define CHROME_BROWSER_ENTERPRISE_CONNECTORS_REPORTING_BROWSER_CRASH_EVENT_ROUTER_H_ -#include "base/memory/weak_ptr.h" -#include "chrome/browser/enterprise/connectors/reporting/realtime_reporting_client.h" -#include "components/enterprise/browser/controller/chrome_browser_cloud_management_controller.h" +#include "content/public/browser/browser_context.h" -#if !BUILDFLAG(IS_FUCHSIA) -#include "third_party/crashpad/crashpad/client/crash_report_database.h" -#endif // !BUILDFLAG(IS_FUCHSIA) namespace enterprise_connectors { -// This class collects crash reports from the crashpad database -// and then sends generated crash events to the reporting server. -class BrowserCrashEventRouter - : public policy::ChromeBrowserCloudManagementController::Observer, - public base::SupportsWeakPtr<BrowserCrashEventRouter> { + +// An instance of class is owned by the ConnectorsManager, we use its lifetime +// to manage which profiles are observed for the purposes of crash reporting. +// Its constructor and destructor add and remove profiles to the +// CrashReportingContext, respectively, if they are valid for crash reporting. +class BrowserCrashEventRouter { public: - // BrowserCrashEventRouter adds itself to - // ChromeBrowserCloudManagementController::observers in the constructor, so - // that once the browser launches, OnCloudReportingLaunched() will be called, - // where we can call ReportCrashes() to report crashes. explicit BrowserCrashEventRouter(content::BrowserContext* context); BrowserCrashEventRouter(const BrowserCrashEventRouter&) = delete; BrowserCrashEventRouter& operator=(const BrowserCrashEventRouter&) = delete; BrowserCrashEventRouter(BrowserCrashEventRouter&&) = delete; BrowserCrashEventRouter& operator=(BrowserCrashEventRouter&&) = delete; - ~BrowserCrashEventRouter() override; - -#if !BUILDFLAG(IS_FUCHSIA) - void OnCloudReportingLaunched( - enterprise_reporting::ReportScheduler* report_scheduler) override; - void UploadToReportingServer( - RealtimeReportingClient* reporting_client, - ReportingSettings settings, - std::vector<crashpad::CrashReportDatabase::Report> reports); -#endif // !BUILDFLAG(IS_FUCHSIA) - - private: - raw_ptr<enterprise_connectors::RealtimeReportingClient, DanglingUntriaged> - reporting_client_ = nullptr; - raw_ptr<policy::ChromeBrowserCloudManagementController, DanglingUntriaged> - controller_ = nullptr; - -#if !BUILDFLAG(IS_FUCHSIA) - base::RepeatingTimer repeating_crash_report_; - // ReportCrashes() checks the enterprise policy settings, retrieves crash - // reports from the crashpad local database and sends reports that have not - // been sent to the reporting server. - void ReportCrashes(); -#endif // !BUILDFLAG(IS_FUCHSIA) + ~BrowserCrashEventRouter(); }; } // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/reporting/crash_reporting_context.cc b/chrome/browser/enterprise/connectors/reporting/crash_reporting_context.cc new file mode 100644 index 0000000..375bcb5 --- /dev/null +++ b/chrome/browser/enterprise/connectors/reporting/crash_reporting_context.cc
@@ -0,0 +1,258 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/enterprise/connectors/reporting/crash_reporting_context.h" + +#include "base/command_line.h" +#include "base/task/thread_pool.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/enterprise/connectors/connectors_prefs.h" +#include "chrome/browser/enterprise/connectors/reporting/realtime_reporting_client_factory.h" +#include "chrome/browser/enterprise/connectors/reporting/reporting_service_settings.h" +#include "chrome/browser/policy/chrome_browser_policy_connector.h" +#include "chrome/common/channel_info.h" +#include "components/crash/core/app/crashpad.h" +#include "components/prefs/pref_service.h" +#include "components/version_info/version_info.h" + +namespace enterprise_connectors { + +#if !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_CHROMEOS_ASH) + +namespace { + +constexpr char kKeyChannel[] = "channel"; +constexpr char kKeyVersion[] = "version"; +constexpr char kKeyReportId[] = "reportId"; +constexpr char kKeyPlatform[] = "platform"; +constexpr char kCrashpadPollingIntervalFlag[] = "crashpad-polling-interval"; +constexpr int kDefaultCrashpadPollingIntervalSeconds = 3600; + +policy::ChromeBrowserCloudManagementController* GetCBCMController() { + return g_browser_process->browser_policy_connector() + ->chrome_browser_cloud_management_controller(); +} + +base::TimeDelta GetCrashpadPollingInterval() { + base::TimeDelta result = + base::Seconds(kDefaultCrashpadPollingIntervalSeconds); + if (g_browser_process && g_browser_process->browser_policy_connector() + ->IsCommandLineSwitchSupported()) { + base::CommandLine* cmd = base::CommandLine::ForCurrentProcess(); + if (cmd->HasSwitch(kCrashpadPollingIntervalFlag)) { + int crashpad_polling_interval_seconds = 0; + if (base::StringToInt( + cmd->GetSwitchValueASCII(kCrashpadPollingIntervalFlag), + &crashpad_polling_interval_seconds) && + crashpad_polling_interval_seconds > 0) { + result = base::Seconds(crashpad_polling_interval_seconds); + } + } + } + VLOG(1) << "enterprise.crash_reporting: crashpad polling interval set to " + << result; + return result; +} + +// Copy new reports (i.e. reports that have not been sent to the +// reporting server) from `reports_to_be_copied` to `reports` +// based on the `latest_creation_time`. +void CopyNewReports( + const std::vector<crashpad::CrashReportDatabase::Report>& + reports_to_be_copied, + int64_t latest_creation_time, + std::vector<crashpad::CrashReportDatabase::Report>& reports) { + for (const crashpad::CrashReportDatabase::Report& report : + reports_to_be_copied) { + if (report.creation_time <= latest_creation_time) { + continue; + } + reports.push_back(report); + } +} + +std::vector<crashpad::CrashReportDatabase::Report> GetNewReports( + time_t latest_creation_time) { + std::unique_ptr<crashpad::CrashReportDatabase> database = + crashpad::CrashReportDatabase::InitializeWithoutCreating( + crash_reporter::GetCrashpadDatabasePath()); + if (!database) { + VLOG(1) << "enterprise.crash_reporting: failed to fetch crashpad db"; + return {}; + } + return GetNewReportsFromDatabase(latest_creation_time, database.get()); +} + +void ReportCrashes() { + CrashReportingContext* context = CrashReportingContext::GetInstance(); + if (!context->HasActiveProfile()) { + return; + } + RealtimeReportingClient* reporting_client = + context->GetCrashReportingClient(); + VLOG(1) << "enterprise.crash_reporting: crash reporting enabled: " + << (reporting_client != nullptr); + if (!reporting_client) { + g_browser_process->local_state()->ClearPref( + enterprise_connectors::kLatestCrashReportCreationTime); + return; + } + VLOG(1) << "enterprise.crash_reporting: checking for unreported crashes"; + time_t latest_creation_time = + GetLatestCrashReportTime(g_browser_process->local_state()); + if (latest_creation_time == 0) { + latest_creation_time = base::Time::Now().ToTimeT(); + SetLatestCrashReportTime(g_browser_process->local_state(), + latest_creation_time); + } + base::ThreadPool::PostTaskAndReplyWithResult( + FROM_HERE, {base::MayBlock()}, + base::BindOnce(&GetNewReports, latest_creation_time), + base::BindOnce(&UploadToReportingServer, reporting_client, + g_browser_process->local_state())); +} + +} // namespace + +std::vector<crashpad::CrashReportDatabase::Report> GetNewReportsFromDatabase( + time_t latest_creation_time, + crashpad::CrashReportDatabase* database) { + std::vector<crashpad::CrashReportDatabase::Report> reports; + std::vector<crashpad::CrashReportDatabase::Report> from_db; + crashpad::CrashReportDatabase::OperationStatus status = + database->GetPendingReports(&from_db); + if (status == crashpad::CrashReportDatabase::kNoError) { + CopyNewReports(from_db, latest_creation_time, reports); + from_db.clear(); + } + status = database->GetCompletedReports(&from_db); + if (status == crashpad::CrashReportDatabase::kNoError) { + CopyNewReports(from_db, latest_creation_time, reports); + } + return reports; +} + +time_t GetLatestCrashReportTime(PrefService* local_state) { + time_t timestamp = local_state->GetInt64( + enterprise_connectors::kLatestCrashReportCreationTime); + VLOG(1) << "enterprise.crash_reporting: latest crash report time: " + << base::Time::FromTimeT(timestamp); + return timestamp; +} + +void SetLatestCrashReportTime(PrefService* local_state, time_t timestamp) { + local_state->SetInt64(enterprise_connectors::kLatestCrashReportCreationTime, + timestamp); +} + +void UploadToReportingServer( + RealtimeReportingClient* reporting_client, + PrefService* local_state, + std::vector<crashpad::CrashReportDatabase::Report> reports) { + VLOG(1) << "enterprise.crash_reporting: " << reports.size() + << " crashes to report"; + if (reports.empty()) { + return; + } + absl::optional<ReportingSettings> settings = + reporting_client->GetReportingSettings(); + const std::string version = version_info::GetVersionNumber(); + const std::string channel = + version_info::GetChannelString(chrome::GetChannel()); + const std::string platform = version_info::GetOSType(); + + int64_t latest_creation_time = -1; + + for (const auto& report : reports) { + base::Value::Dict event; + event.Set(kKeyChannel, channel); + event.Set(kKeyVersion, version); + event.Set(kKeyReportId, report.id); + event.Set(kKeyPlatform, platform); + reporting_client->ReportPastEvent( + ReportingServiceSettings::kBrowserCrashEvent, settings.value(), + std::move(event), base::Time::FromTimeT(report.creation_time)); + if (report.creation_time > latest_creation_time) { + latest_creation_time = report.creation_time; + } + } + SetLatestCrashReportTime(local_state, latest_creation_time); +} + +CrashReportingContext::CrashReportingContext() { + GetCBCMController()->AddObserver(this); +} + +void CrashReportingContext::AddProfile(BrowserCrashEventRouter* router, + Profile* profile) { + if (!profile->IsRegularProfile()) { + return; + } + active_profiles_[router] = profile; +} + +CrashReportingContext* CrashReportingContext::GetInstance() { + return base::Singleton<CrashReportingContext>::get(); +} + +RealtimeReportingClient* CrashReportingContext::GetCrashReportingClient() + const { + for (auto& it : active_profiles_) { + Profile* profile = it.second; + RealtimeReportingClient* reporting_client = + RealtimeReportingClientFactory::GetForProfile(profile); + if (!reporting_client) { + continue; + } + absl::optional<ReportingSettings> settings = + reporting_client->GetReportingSettings(); + if (settings.has_value() && + settings->enabled_event_names.count( + ReportingServiceSettings::kBrowserCrashEvent) != 0 && + !settings->per_profile) { + return reporting_client; + } + } + return nullptr; +} + +bool CrashReportingContext::HasActiveProfile() const { + return active_profiles_.size() != 0; +} + +void CrashReportingContext::OnBrowserUnenrolled(bool succeeded) { + if (succeeded && repeating_crash_report_.IsRunning()) { + VLOG(1) << "enterprise.crash_reporting: browser unenrolled"; + repeating_crash_report_.Stop(); + } +} + +void CrashReportingContext::OnCloudReportingLaunched( + enterprise_reporting::ReportScheduler* report_scheduler) { + VLOG(1) << "enterprise.crash_reporting: crash event reporting initializing"; + // An initial call to ReportCrashes() is required because the first call + // in the repeating callback happens after the delay. + ReportCrashes(); + if (!repeating_crash_report_.IsRunning()) { + repeating_crash_report_.Start(FROM_HERE, GetCrashpadPollingInterval(), + base::BindRepeating(&ReportCrashes)); + } +} + +void CrashReportingContext::OnShutdown() { + GetCBCMController()->RemoveObserver(this); +} + +void CrashReportingContext::RemoveProfile(BrowserCrashEventRouter* router) { + auto it = active_profiles_.find(router); + if (it != active_profiles_.end()) { + active_profiles_.erase(it); + } +} + +#endif // !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_CHROMEOS_ASH) + +CrashReportingContext::~CrashReportingContext() = default; + +} // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/reporting/crash_reporting_context.h b/chrome/browser/enterprise/connectors/reporting/crash_reporting_context.h new file mode 100644 index 0000000..645a6ca --- /dev/null +++ b/chrome/browser/enterprise/connectors/reporting/crash_reporting_context.h
@@ -0,0 +1,81 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_ENTERPRISE_CONNECTORS_REPORTING_CRASH_REPORTING_CONTEXT_H_ +#define CHROME_BROWSER_ENTERPRISE_CONNECTORS_REPORTING_CRASH_REPORTING_CONTEXT_H_ + +#include "base/memory/singleton.h" +#include "base/timer/timer.h" +#include "chrome/browser/enterprise/connectors/reporting/browser_crash_event_router.h" +#include "components/enterprise/browser/controller/chrome_browser_cloud_management_controller.h" + +#if !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_CHROMEOS_ASH) +#include "third_party/crashpad/crashpad/client/crash_report_database.h" +#endif + +namespace enterprise_connectors { + +class RealtimeReportingClient; + +class CrashReportingContext + : public policy::ChromeBrowserCloudManagementController::Observer { + public: + friend struct base::DefaultSingletonTraits<CrashReportingContext>; + + CrashReportingContext(const CrashReportingContext&) = delete; + CrashReportingContext(CrashReportingContext&&) = delete; + CrashReportingContext operator=(const CrashReportingContext&) = delete; + CrashReportingContext operator=(CrashReportingContext&&) = delete; + ~CrashReportingContext() override; + +#if !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_CHROMEOS_ASH) + static CrashReportingContext* GetInstance(); + + void AddProfile(BrowserCrashEventRouter* router, Profile* profile); + RealtimeReportingClient* GetCrashReportingClient() const; + bool HasActiveProfile() const; + void RemoveProfile(BrowserCrashEventRouter* router); + + // policy::Chromepolicy::ChromeBrowserCloudManagementController::Observer + void OnBrowserUnenrolled(bool succeeded) override; + void OnCloudReportingLaunched( + enterprise_reporting::ReportScheduler* report_scheduler) override; + void OnShutdown() override; + + private: + CrashReportingContext(); + + base::RepeatingTimer repeating_crash_report_; + std::unordered_map<BrowserCrashEventRouter*, Profile*> active_profiles_; +#endif +}; + +#if !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_CHROMEOS_ASH) + +// Utility function to parse reports from a crash database that were +// created past a given timestamp. Included in header for testing. +std::vector<crashpad::CrashReportDatabase::Report> GetNewReportsFromDatabase( + time_t latest_creation_time, + crashpad::CrashReportDatabase* database); + +// Update the local_state pref with the latest crash upload timestamp. +// Included in header for testing. +void SetLatestCrashReportTime(PrefService* local_state, time_t timestamp); + +// Get the current value of the latest crash upload timestamp. Included in +// header for testing. +time_t GetLatestCrashReportTime(PrefService* local_state); + +// Upload a crash event reports to the reporting server if available and +// update latest crash upload timestamp if one or more such events are uploaded. +// Included in header for testing. +void UploadToReportingServer( + RealtimeReportingClient* reporting_client, + PrefService* local_state, + std::vector<crashpad::CrashReportDatabase::Report> reports); + +#endif + +} // namespace enterprise_connectors +#endif
diff --git a/chrome/browser/enterprise/connectors/reporting/crash_reporting_context_unittest.cc b/chrome/browser/enterprise/connectors/reporting/crash_reporting_context_unittest.cc new file mode 100644 index 0000000..ae8f3ae --- /dev/null +++ b/chrome/browser/enterprise/connectors/reporting/crash_reporting_context_unittest.cc
@@ -0,0 +1,146 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "chrome/browser/enterprise/connectors/reporting/crash_reporting_context.h" + +#include "base/command_line.h" +#include "base/files/scoped_temp_dir.h" +#include "chrome/browser/enterprise/connectors/connectors_prefs.h" +#include "chrome/browser/enterprise/connectors/reporting/realtime_reporting_client.h" +#include "chrome/browser/enterprise/connectors/reporting/realtime_reporting_client_factory.h" +#include "chrome/browser/enterprise/connectors/reporting/reporting_service_settings.h" +#include "chrome/browser/policy/dm_token_utils.h" +#include "chrome/common/channel_info.h" +#include "chrome/test/base/testing_browser_process.h" +#include "chrome/test/base/testing_profile_manager.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/testing_pref_service.h" +#include "components/version_info/version_info.h" +#include "content/public/test/browser_task_environment.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::_; +using ::testing::ByMove; +using ::testing::Eq; +using ::testing::Return; + +namespace enterprise_connectors { + +#if !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_CHROMEOS_ASH) + +namespace { + +void CreateCrashReport(crashpad::CrashReportDatabase* database, + crashpad::CrashReportDatabase::Report* report) { + std::unique_ptr<crashpad::CrashReportDatabase::NewReport> new_report; + ASSERT_EQ(database->PrepareNewCrashReport(&new_report), + crashpad::CrashReportDatabase::kNoError); + static constexpr char kTest[] = "test"; + ASSERT_TRUE(new_report->Writer()->Write(kTest, sizeof(kTest))); + crashpad::UUID uuid; + EXPECT_EQ(database->FinishedWritingCrashReport(std::move(new_report), &uuid), + crashpad::CrashReportDatabase::kNoError); + EXPECT_EQ(database->LookUpCrashReport(uuid, report), + crashpad::CrashReportDatabase::kNoError); +} + +} // namespace + +class MockRealtimeCrashReportingClient : public RealtimeReportingClient { + public: + explicit MockRealtimeCrashReportingClient(content::BrowserContext* context) + : RealtimeReportingClient(context) {} + MockRealtimeCrashReportingClient(const MockRealtimeCrashReportingClient&) = + delete; + MockRealtimeCrashReportingClient& operator=( + const MockRealtimeCrashReportingClient&) = delete; + + absl::optional<enterprise_connectors::ReportingSettings> + GetReportingSettings() override { + return enterprise_connectors::ReportingSettings(); + } + + MOCK_METHOD4(ReportPastEvent, + void(const std::string&, + const enterprise_connectors::ReportingSettings& settings, + base::Value::Dict event, + const base::Time& time)); +}; + +std::unique_ptr<KeyedService> CreateMockRealtimeCrashReportingClient( + content::BrowserContext* profile) { + return std::make_unique<MockRealtimeCrashReportingClient>(profile); +} + +class CrashReportingContextTest : public testing::Test { + public: + CrashReportingContextTest() + : profile_manager_(TestingBrowserProcess::GetGlobal()) {} + + void SetUp() override { EXPECT_TRUE(profile_manager_.SetUp()); } + + content::BrowserTaskEnvironment task_environment_; + TestingProfileManager profile_manager_; +}; + +TEST_F(CrashReportingContextTest, GetNewReportsFromDB) { + base::ScopedTempDir database_dir; + ASSERT_TRUE(database_dir.CreateUniqueTempDir()); + std::unique_ptr<crashpad::CrashReportDatabase> database = + crashpad::CrashReportDatabase::InitializeWithoutCreating( + database_dir.GetPath()); + crashpad::CrashReportDatabase::Report report; + CreateCrashReport(database.get(), &report); + std::vector<crashpad::CrashReportDatabase::Report> reports = + GetNewReportsFromDatabase(report.creation_time + 1, database.get()); + EXPECT_EQ(reports.size(), 0u); + reports = GetNewReportsFromDatabase(report.creation_time - 1, database.get()); + EXPECT_EQ(reports.size(), 1u); +} + +TEST_F(CrashReportingContextTest, GetAndSetLatestCrashReportingTime) { + TestingPrefServiceSimple pref_service; + pref_service.registry()->RegisterInt64Pref( + enterprise_connectors::kLatestCrashReportCreationTime, 0); + time_t timestamp = base::Time::Now().ToTimeT(); + + enterprise_connectors::SetLatestCrashReportTime(&pref_service, timestamp); + ASSERT_EQ(timestamp, + enterprise_connectors::GetLatestCrashReportTime(&pref_service)); +} + +TEST_F(CrashReportingContextTest, UploadToReportingServer) { + TestingPrefServiceSimple pref_service; + pref_service.registry()->RegisterInt64Pref( + enterprise_connectors::kLatestCrashReportCreationTime, 0); + EXPECT_EQ(0u, enterprise_connectors::GetLatestCrashReportTime(&pref_service)); + + time_t timestamp = base::Time::Now().ToTimeT(); + std::vector<crashpad::CrashReportDatabase::Report> reports; + crashpad::CrashReportDatabase::Report report; + report.creation_time = timestamp; + reports.push_back(report); + + TestingProfile* profile = + profile_manager_.CreateTestingProfile("fake-profile"); + policy::SetDMTokenForTesting( + policy::DMToken::CreateValidTokenForTesting("fake-token")); + RealtimeReportingClientFactory::GetInstance()->SetTestingFactory( + profile, base::BindRepeating(&CreateMockRealtimeCrashReportingClient)); + MockRealtimeCrashReportingClient* reporting_client = + static_cast<MockRealtimeCrashReportingClient*>( + RealtimeReportingClientFactory::GetForProfile(profile)); + + EXPECT_CALL(*reporting_client, + ReportPastEvent(ReportingServiceSettings::kBrowserCrashEvent, _, + _, base::Time::FromTimeT(timestamp))) + .Times(1); + UploadToReportingServer(reporting_client, &pref_service, reports); + EXPECT_EQ(timestamp, + enterprise_connectors::GetLatestCrashReportTime(&pref_service)); +} + +#endif + +} // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/reporting/realtime_reporting_client.h b/chrome/browser/enterprise/connectors/reporting/realtime_reporting_client.h index 2c4f1ec..952c0ff 100644 --- a/chrome/browser/enterprise/connectors/reporting/realtime_reporting_client.h +++ b/chrome/browser/enterprise/connectors/reporting/realtime_reporting_client.h
@@ -80,8 +80,9 @@ // Determines if the real-time reporting feature is enabled. // Obtain settings to apply to a reporting event from ConnectorsService. // absl::nullopt represents that reporting should not be done. - absl::optional<enterprise_connectors::ReportingSettings> - GetReportingSettings(); + // Declared virtual for tests. + absl::optional< + enterprise_connectors::ReportingSettings> virtual GetReportingSettings(); // Returns the Gaia email address of the account signed in to the profile or // an empty string if the profile is not signed in (declared virtual for
diff --git a/chrome/browser/enterprise/idle/idle_timeout_policy_handler.cc b/chrome/browser/enterprise/idle/idle_timeout_policy_handler.cc index 3fa7afba..0408ea7 100644 --- a/chrome/browser/enterprise/idle/idle_timeout_policy_handler.cc +++ b/chrome/browser/enterprise/idle/idle_timeout_policy_handler.cc
@@ -4,12 +4,17 @@ #include "chrome/browser/enterprise/idle/idle_timeout_policy_handler.h" +#include <cstring> #include <string> +#include "base/containers/span.h" #include "base/json/values_util.h" +#include "base/ranges/algorithm.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" #include "base/time/time.h" #include "base/values.h" +#include "build/build_config.h" #include "chrome/browser/enterprise/idle/action.h" #include "chrome/browser/prefs/session_startup_pref.h" #include "chrome/common/pref_names.h" @@ -25,6 +30,21 @@ namespace { +#if !BUILDFLAG(IS_ANDROID) +const char kCloseBrowsersActionName[] = "close_browsers"; +const char kShowProfilePickerActionName[] = "show_profile_picker"; +#endif // !BUILDFLAG(IS_ANDROID) +const char kClearBrowsingHistoryActionName[] = "clear_browsing_history"; +const char kClearDownloadHistoryActionName[] = "clear_download_history"; +const char kClearCookiesAndOtherSiteDataActionName[] = + "clear_cookies_and_other_site_data"; +const char kClearCachedImagesAndFilesActionName[] = + "clear_cached_images_and_files"; +const char kClearPasswordSigninActionName[] = "clear_password_signin"; +const char kClearAutofillActionName[] = "clear_autofill"; +const char kClearSiteSettingsActionName[] = "clear_site_settings"; +const char kClearHostedAppDataActionName[] = "clear_hosted_app_data"; + // If `other_policy_name` is unset, adds an error to `errors` and returns false. bool CheckOtherPolicySet(const policy::PolicyMap& policies, const std::string& this_policy_name, @@ -38,37 +58,53 @@ return false; } +#if !BUILDFLAG(IS_ANDROID) +bool RequiresSyncDisabled(const std::string& name) { + static const char* kActionsAllowedWithSync[] = { + kCloseBrowsersActionName, + kShowProfilePickerActionName, + kClearDownloadHistoryActionName, + kClearCookiesAndOtherSiteDataActionName, + kClearCachedImagesAndFilesActionName, + kClearSiteSettingsActionName, + }; + return !base::ranges::any_of( + base::make_span(kActionsAllowedWithSync), + [&name](const char* s) { return !std::strcmp(s, name.c_str()); }); +} +#endif // !BUILDFLAG(IS_ANDROID) + absl::optional<ActionType> NameToActionType(const std::string& name) { #if !BUILDFLAG(IS_ANDROID) - if (name == "close_browsers") { + if (name == kCloseBrowsersActionName) { return ActionType::kCloseBrowsers; } - if (name == "show_profile_picker") { + if (name == kShowProfilePickerActionName) { return ActionType::kShowProfilePicker; } #endif // !BUILDFLAG(IS_ANDROID) - if (name == "clear_browsing_history") { + if (name == kClearBrowsingHistoryActionName) { return ActionType::kClearBrowsingHistory; } - if (name == "clear_download_history") { + if (name == kClearDownloadHistoryActionName) { return ActionType::kClearDownloadHistory; } - if (name == "clear_cookies_and_other_site_data") { + if (name == kClearCookiesAndOtherSiteDataActionName) { return ActionType::kClearCookiesAndOtherSiteData; } - if (name == "clear_cached_images_and_files") { + if (name == kClearCachedImagesAndFilesActionName) { return ActionType::kClearCachedImagesAndFiles; } - if (name == "clear_password_signin") { + if (name == kClearPasswordSigninActionName) { return ActionType::kClearPasswordSignin; } - if (name == "clear_autofill") { + if (name == kClearAutofillActionName) { return ActionType::kClearAutofill; } - if (name == "clear_site_settings") { + if (name == kClearSiteSettingsActionName) { return ActionType::kClearSiteSettings; } - if (name == "clear_hosted_app_data") { + if (name == kClearHostedAppDataActionName) { return ActionType::kClearHostedAppData; } return absl::nullopt; @@ -89,7 +125,7 @@ const policy::PolicyMap& policies, PrefValueMap* prefs) { const base::Value* value = - policies.GetValue(policy::key::kIdleTimeout, base::Value::Type::INTEGER); + policies.GetValue(policy_name(), base::Value::Type::INTEGER); DCHECK(value); // Apply a minimum of 1. @@ -101,15 +137,17 @@ const policy::PolicyMap& policies, policy::PolicyErrorMap* errors) { // Nothing to do if unset. - if (!policies.GetValueUnsafe(policy::key::kIdleTimeout)) + if (!policies.GetValueUnsafe(policy_name())) { return false; + } // Check that it's an integer, and that it's >= 1. - if (!policy::IntRangePolicyHandler::CheckPolicySettings(policies, errors)) + if (!policy::IntRangePolicyHandler::CheckPolicySettings(policies, errors)) { return false; + } // If IdleTimeoutActions is unset, add an error and do nothing. - if (!CheckOtherPolicySet(policies, policy::key::kIdleTimeout, + if (!CheckOtherPolicySet(policies, policy_name(), policy::key::kIdleTimeoutActions, errors)) { return false; } @@ -129,8 +167,8 @@ void IdleTimeoutActionsPolicyHandler::ApplyPolicySettings( const policy::PolicyMap& policies, PrefValueMap* prefs) { - const base::Value* policy_value = policies.GetValue( - policy::key::kIdleTimeoutActions, base::Value::Type::LIST); + const base::Value* policy_value = + policies.GetValue(policy_name(), base::Value::Type::LIST); DCHECK(policy_value); // Convert strings to integers (from the ActionType enum). @@ -152,8 +190,9 @@ const policy::PolicyMap& policies, policy::PolicyErrorMap* errors) { // Nothing to do if unset. - if (!policies.GetValueUnsafe(policy::key::kIdleTimeoutActions)) + if (!policies.GetValueUnsafe(policy_name())) { return false; + } // Check that it's a list of strings, and that they're supported enum values. // Unsupported enum values are dropped, with a warning on chrome://policy. @@ -163,11 +202,36 @@ } // If IdleTimeout is unset, add an error and do nothing. - if (!CheckOtherPolicySet(policies, policy::key::kIdleTimeoutActions, - policy::key::kIdleTimeout, errors)) { + if (!CheckOtherPolicySet(policies, policy_name(), policy::key::kIdleTimeout, + errors)) { return false; } +#if !BUILDFLAG(IS_ANDROID) + const base::Value* sync_disabled = + policies.GetValue(policy::key::kSyncDisabled, base::Value::Type::BOOLEAN); + if (!sync_disabled || !sync_disabled->GetBool()) { + // SyncDisabled is false. Check actions that require SyncDisabled=true, + // and show a user-friendly error message if needed. + std::vector<std::string> invalid_actions; + const base::Value* value = + policies.GetValue(policy_name(), base::Value::Type::LIST); + DCHECK(value); + for (const base::Value& action : value->GetList()) { + if (action.is_string() && RequiresSyncDisabled(action.GetString())) { + invalid_actions.push_back(action.GetString()); + } + } + if (!invalid_actions.empty()) { + errors->AddError( + policy_name(), IDS_POLICY_IDLE_TIMEOUT_ACTIONS_DEPENDENCY_ERROR, + std::vector<std::string>{policy::key::kSyncDisabled, "Enabled", + base::JoinString(invalid_actions, ", ")}); + return false; + } + } +#endif // !BUILDFLAG(IS_ANDROID) + return true; } } // namespace enterprise_idle
diff --git a/chrome/browser/enterprise/idle/idle_timeout_policy_handler_unittest.cc b/chrome/browser/enterprise/idle/idle_timeout_policy_handler_unittest.cc index fa7c627..e1e2df8 100644 --- a/chrome/browser/enterprise/idle/idle_timeout_policy_handler_unittest.cc +++ b/chrome/browser/enterprise/idle/idle_timeout_policy_handler_unittest.cc
@@ -4,7 +4,9 @@ #include "chrome/browser/enterprise/idle/idle_timeout_policy_handler.h" +#include <iterator> #include <string> +#include <vector> #include "base/json/values_util.h" #include "base/ranges/algorithm.h" @@ -27,9 +29,16 @@ namespace enterprise_idle { using base::UTF8ToUTF16; +using testing::IsEmpty; +using testing::UnorderedElementsAre; class IdleTimeoutPolicyHandlerTest : public testing::Test { protected: + void SetUp() override { + // Some action types require SyncDisabled=true, so set it for most tests. + SetPolicyValue(policy::key::kSyncDisabled, base::Value(true)); + } + void SetPolicyValue(const std::string& policy, base::Value value) { policies_.Set(policy, policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_PLATFORM, @@ -54,8 +63,15 @@ ApplyPolicySettings(); } - policy::PolicyErrorMap& errors() { return errors_; } PrefValueMap& prefs() { return prefs_; } + policy::PolicyMap& policies() { return policies_; } + + std::vector<std::u16string> errors() { + std::vector<std::u16string> strings; + base::ranges::transform(errors_, std::back_inserter(strings), + [](const auto& it) { return it.second.message; }); + return strings; + } private: policy::PolicyMap policies_; @@ -71,7 +87,7 @@ CheckAndApplyPolicySettings(); // Shouldn't error. - EXPECT_EQ(errors().size(), 0U); + EXPECT_THAT(errors(), IsEmpty()); // Prefs should not be set. const base::Value* pref_value; @@ -89,8 +105,7 @@ auto expected_error = l10n_util::GetStringFUTF16(IDS_POLICY_DEPENDENCY_ERROR_ANY_VALUE, UTF8ToUTF16(policy::key::kIdleTimeoutActions)); - EXPECT_EQ(errors().size(), 1U); - EXPECT_EQ(errors().begin()->second.message, expected_error); + EXPECT_THAT(errors(), UnorderedElementsAre(expected_error)); // Prefs should not be set. const base::Value* pref_value; @@ -109,8 +124,7 @@ auto expected_error = l10n_util::GetStringFUTF16(IDS_POLICY_DEPENDENCY_ERROR_ANY_VALUE, UTF8ToUTF16(policy::key::kIdleTimeout)); - EXPECT_EQ(errors().size(), 1U); - EXPECT_EQ(errors().begin()->second.message, expected_error); + EXPECT_THAT(errors(), UnorderedElementsAre(expected_error)); // Prefs should not be set. const base::Value* pref_value; @@ -130,8 +144,7 @@ auto expected_error = l10n_util::GetStringFUTF16( IDS_POLICY_TYPE_ERROR, UTF8ToUTF16(base::Value::GetTypeName(base::Value::Type::INTEGER))); - EXPECT_EQ(errors().size(), 1U); - EXPECT_EQ(errors().begin()->second.message, expected_error); + EXPECT_THAT(errors(), UnorderedElementsAre(expected_error)); // Prefs should not be set. const base::Value* pref_value; @@ -150,8 +163,7 @@ auto expected_error = l10n_util::GetStringFUTF16( IDS_POLICY_SCHEMA_VALIDATION_ERROR, u"Policy type mismatch: expected: \"list\", actual: \"string\"."); - EXPECT_EQ(errors().size(), 1U); - EXPECT_EQ(errors().begin()->second.message, expected_error); + EXPECT_THAT(errors(), UnorderedElementsAre(expected_error)); // Prefs should not be set. const base::Value* pref_value; @@ -177,8 +189,7 @@ l10n_util::GetStringFUTF16( IDS_POLICY_SCHEMA_VALIDATION_ERROR, u"Policy type mismatch: expected: \"string\", actual: \"integer\".")); - EXPECT_EQ(errors().size(), 1U); - EXPECT_EQ(errors().begin()->second.message, expected_error); + EXPECT_THAT(errors(), UnorderedElementsAre(expected_error)); // Prefs should be set. const base::Value* pref_value; @@ -198,8 +209,7 @@ CheckAndApplyPolicySettings(); - // Should have no errors. - EXPECT_TRUE(errors().empty()); + EXPECT_THAT(errors(), IsEmpty()); // Prefs should be set. const base::Value* pref_value; @@ -227,8 +237,7 @@ // Should have an error. auto expected_error = l10n_util::GetStringFUTF16(IDS_POLICY_OUT_OF_RANGE_ERROR, u"0"); - EXPECT_EQ(errors().size(), 1U); - EXPECT_EQ(errors().begin()->second.message, expected_error); + EXPECT_THAT(errors(), UnorderedElementsAre(expected_error)); // Prefs should be set. const base::Value* pref_value; @@ -256,8 +265,7 @@ UTF8ToUTF16(policy::key::kIdleTimeoutActions) + u"[2]", l10n_util::GetStringFUTF16(IDS_POLICY_SCHEMA_VALIDATION_ERROR, u"Invalid value for string")); - EXPECT_EQ(errors().size(), 1U); - EXPECT_EQ(errors().begin()->second.message, expected_error); + EXPECT_THAT(errors(), UnorderedElementsAre(expected_error)); // Prefs should not be set. const base::Value* pref_value; @@ -292,7 +300,7 @@ CheckAndApplyPolicySettings(); // Should have no errors. - EXPECT_TRUE(errors().empty()); + EXPECT_THAT(errors(), IsEmpty()); // Prefs should be set. const base::Value* pref_value; @@ -319,4 +327,70 @@ static_cast<int>(ActionType::kClearHostedAppData))); } +#if !BUILDFLAG(IS_ANDROID) +TEST_F(IdleTimeoutPolicyHandlerTest, SyncNotDisabled) { + SetPolicyValue(policy::key::kSyncDisabled, base::Value(false)); + SetPolicyValue(policy::key::kIdleTimeout, base::Value(15)); + base::Value::List list; + list.Append("close_browsers"); + list.Append("show_profile_picker"); + list.Append("clear_browsing_history"); + list.Append("clear_download_history"); + list.Append("clear_cookies_and_other_site_data"); + list.Append("clear_cached_images_and_files"); + list.Append("clear_password_signin"); + list.Append("clear_autofill"); + list.Append("clear_site_settings"); + list.Append("clear_hosted_app_data"); + SetPolicyValue(policy::key::kIdleTimeoutActions, + base::Value(std::move(list))); + + CheckAndApplyPolicySettings(); + + // Should have these errors. + EXPECT_THAT(errors(), + UnorderedElementsAre( + u"These actions require the SyncDisabled policy to be set to " + u"Enabled: clear_browsing_history, clear_password_signin, " + u"clear_autofill, clear_hosted_app_data.")); + + // Prefs should not be set. + const base::Value* pref_value; + EXPECT_FALSE(prefs().GetValue(prefs::kIdleTimeout, &pref_value)); + EXPECT_FALSE(prefs().GetValue(prefs::kIdleTimeoutActions, &pref_value)); +} + +TEST_F(IdleTimeoutPolicyHandlerTest, SyncDisabledIsFalse) { + policies().Erase(policy::key::kSyncDisabled); + SetPolicyValue(policy::key::kIdleTimeout, base::Value(15)); + base::Value::List list; + list.Append("close_browsers"); + list.Append("show_profile_picker"); + list.Append("clear_browsing_history"); + list.Append("clear_download_history"); + list.Append("clear_cookies_and_other_site_data"); + list.Append("clear_cached_images_and_files"); + list.Append("clear_password_signin"); + list.Append("clear_autofill"); + list.Append("clear_site_settings"); + list.Append("clear_hosted_app_data"); + SetPolicyValue(policy::key::kIdleTimeoutActions, + base::Value(std::move(list))); + + CheckAndApplyPolicySettings(); + + // Should have these errors. + EXPECT_THAT(errors(), + UnorderedElementsAre( + u"These actions require the SyncDisabled policy to be set to " + u"Enabled: clear_browsing_history, clear_password_signin, " + u"clear_autofill, clear_hosted_app_data.")); + + // Prefs should not be set. + const base::Value* pref_value; + EXPECT_FALSE(prefs().GetValue(prefs::kIdleTimeout, &pref_value)); + EXPECT_FALSE(prefs().GetValue(prefs::kIdleTimeoutActions, &pref_value)); +} +#endif // !BUILDFLAG(IS_ANDROID) + } // namespace enterprise_idle
diff --git a/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc b/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc index 24bb1396..a5e17191 100644 --- a/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc +++ b/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc
@@ -23,6 +23,7 @@ #include "components/autofill/core/browser/browser_autofill_manager.h" #include "components/autofill/core/browser/data_model/autofill_profile.h" #include "components/autofill/core/browser/data_model/credit_card.h" +#include "components/autofill/core/browser/data_model/iban.h" #include "components/autofill/core/browser/form_data_importer.h" #include "components/autofill/core/browser/payments/local_card_migration_manager.h" #include "components/autofill/core/browser/payments/virtual_card_enrollment_flow.h" @@ -716,6 +717,17 @@ } //////////////////////////////////////////////////////////////////////////////// +// AutofillPrivateIsValidIbanFunction + +ExtensionFunction::ResponseAction AutofillPrivateIsValidIbanFunction::Run() { + absl::optional<api::autofill_private::IsValidIban::Params> parameters = + api::autofill_private::IsValidIban::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(parameters); + return RespondNow(WithArguments( + autofill::IBAN::IsValid(base::UTF8ToUTF16(parameters->iban_value)))); +} + +//////////////////////////////////////////////////////////////////////////////// // AutofillPrivateGetUpiIdListFunction ExtensionFunction::ResponseAction AutofillPrivateGetUpiIdListFunction::Run() {
diff --git a/chrome/browser/extensions/api/autofill_private/autofill_private_api.h b/chrome/browser/extensions/api/autofill_private/autofill_private_api.h index 3d207497..1ab817f2 100644 --- a/chrome/browser/extensions/api/autofill_private/autofill_private_api.h +++ b/chrome/browser/extensions/api/autofill_private/autofill_private_api.h
@@ -268,6 +268,23 @@ ResponseAction Run() override; }; +class AutofillPrivateIsValidIbanFunction : public ExtensionFunction { + public: + AutofillPrivateIsValidIbanFunction() = default; + AutofillPrivateIsValidIbanFunction( + const AutofillPrivateIsValidIbanFunction&) = delete; + AutofillPrivateIsValidIbanFunction& operator=( + const AutofillPrivateIsValidIbanFunction&) = delete; + DECLARE_EXTENSION_FUNCTION("autofillPrivate.isValidIban", + AUTOFILLPRIVATE_ISVALIDIBAN) + + protected: + ~AutofillPrivateIsValidIbanFunction() override = default; + + // ExtensionFunction overrides. + ResponseAction Run() override; +}; + class AutofillPrivateGetUpiIdListFunction : public ExtensionFunction { public: AutofillPrivateGetUpiIdListFunction() = default;
diff --git a/chrome/browser/extensions/api/autofill_private/autofill_private_apitest.cc b/chrome/browser/extensions/api/autofill_private/autofill_private_apitest.cc index e275fba2..84bc3b6e 100644 --- a/chrome/browser/extensions/api/autofill_private/autofill_private_apitest.cc +++ b/chrome/browser/extensions/api/autofill_private/autofill_private_apitest.cc
@@ -130,4 +130,9 @@ EXPECT_EQ(1, user_action_tester.GetActionCount("AutofillIbanDeleted")); } +IN_PROC_BROWSER_TEST_P(AutofillPrivateApiTest, isValidIban) { + base::UserActionTester user_action_tester; + EXPECT_TRUE(RunAutofillSubtest("isValidIban")) << message_; +} + } // namespace extensions
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc index 6510db2..34424a7 100644 --- a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc +++ b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
@@ -15,6 +15,7 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_feature_list.h" #include "base/threading/thread_restrictions.h" #include "build/build_config.h" #include "chrome/app/chrome_command_ids.h" @@ -29,6 +30,7 @@ #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_navigator_params.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/common/chrome_features.h" #include "chrome/test/base/ui_test_utils.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/context_menu_params.h" @@ -206,6 +208,10 @@ public: explicit WebNavigationApiTest(ContextType context_type = ContextType::kNone) : ExtensionApiTest(context_type) { + // TODO(crbug.com/1394910): Use HTTPS URLs in tests to avoid having to + // disable this feature. + feature_list_.InitAndDisableFeature(features::kHttpsUpgrades); + embedded_test_server()->RegisterRequestHandler( base::BindRepeating(&HandleTestRequest)); } @@ -231,12 +237,18 @@ content::WebContents* GetWebContents() { return browser()->tab_strip_model()->GetActiveWebContents(); } + + base::test::ScopedFeatureList* feature_list() { return &feature_list_; } + + private: + base::test::ScopedFeatureList feature_list_; }; class WebNavigationApiBackForwardCacheTest : public WebNavigationApiTest { public: WebNavigationApiBackForwardCacheTest() { - feature_list_.InitWithFeaturesAndParameters( + feature_list()->Reset(); + feature_list()->InitWithFeaturesAndParameters( content::GetBasicBackForwardCacheFeatureForTesting( {{features::kBackForwardCache, {{"content_injection_supported", "true"}, @@ -244,9 +256,6 @@ content::GetDefaultDisabledBackForwardCacheFeaturesForTesting()); } ~WebNavigationApiBackForwardCacheTest() override = default; - - private: - base::test::ScopedFeatureList feature_list_; }; using ContextType = extensions::ExtensionBrowserTest::ContextType; @@ -276,14 +285,12 @@ public: WebNavigationApiPrerenderTestWithContextType() : WebNavigationApiTest(GetParam()) {} + ~WebNavigationApiPrerenderTestWithContextType() override = default; WebNavigationApiPrerenderTestWithContextType( const WebNavigationApiPrerenderTestWithContextType&) = delete; WebNavigationApiPrerenderTestWithContextType& operator=( const WebNavigationApiPrerenderTestWithContextType&) = delete; - - private: - content::test::ScopedPrerenderFeatureList prerender_feature_list_; }; IN_PROC_BROWSER_TEST_P(WebNavigationApiTestWithContextType, Api) { @@ -717,17 +724,18 @@ class WebNavigationApiFencedFrameTest : public WebNavigationApiTest { protected: WebNavigationApiFencedFrameTest() { - feature_list_.InitWithFeaturesAndParameters( + feature_list()->Reset(); + feature_list()->InitWithFeaturesAndParameters( /*enabled_features=*/{{blink::features::kFencedFrames, {}}, {features::kPrivacySandboxAdsAPIsOverride, {}}}, - /*disabled_features=*/{features::kSpareRendererForSitePerProcess}); + /*disabled_features=*/{features::kSpareRendererForSitePerProcess, + // TODO(crbug.com/1394910): Use HTTPS URLs in + // tests to avoid having to disable this feature. + features::kHttpsUpgrades}); // Fenced frames are only allowed in a secure context. UseHttpsTestServer(); } ~WebNavigationApiFencedFrameTest() override = default; - - private: - base::test::ScopedFeatureList feature_list_; }; IN_PROC_BROWSER_TEST_F(WebNavigationApiFencedFrameTest, Load) {
diff --git a/chrome/browser/feed/android/BUILD.gn b/chrome/browser/feed/android/BUILD.gn index 4f2444b..42c494e 100644 --- a/chrome/browser/feed/android/BUILD.gn +++ b/chrome/browser/feed/android/BUILD.gn
@@ -18,6 +18,7 @@ "java/src/org/chromium/chrome/browser/feed/FeedContentFirstLoadWatcher.java", "java/src/org/chromium/chrome/browser/feed/FeedFeatures.java", "java/src/org/chromium/chrome/browser/feed/FeedImageFetchClient.java", + "java/src/org/chromium/chrome/browser/feed/FeedListContentManager.java", "java/src/org/chromium/chrome/browser/feed/FeedLoggingParameters.java", "java/src/org/chromium/chrome/browser/feed/FeedPersistentKeyValueCache.java", "java/src/org/chromium/chrome/browser/feed/FeedPlaceholderLayout.java", @@ -41,7 +42,6 @@ "java/src/org/chromium/chrome/browser/feed/FeedUma.java", "java/src/org/chromium/chrome/browser/feed/HeaderIphScrollListener.java", "java/src/org/chromium/chrome/browser/feed/NativeViewListRenderer.java", - "java/src/org/chromium/chrome/browser/feed/NtpListContentManager.java", "java/src/org/chromium/chrome/browser/feed/RefreshIphScrollListener.java", "java/src/org/chromium/chrome/browser/feed/ScrollListener.java", "java/src/org/chromium/chrome/browser/feed/ScrollTracker.java", @@ -285,6 +285,7 @@ "java/src/org/chromium/chrome/browser/feed/BackToTopBubbleScrollListenerTest.java", "java/src/org/chromium/chrome/browser/feed/FakeLinearLayoutManager.java", "java/src/org/chromium/chrome/browser/feed/FeedFeaturesTest.java", + "java/src/org/chromium/chrome/browser/feed/FeedListContentManagerTest.java", "java/src/org/chromium/chrome/browser/feed/FeedLoggingParametersTest.java", "java/src/org/chromium/chrome/browser/feed/FeedPlaceholderLayoutTest.java", "java/src/org/chromium/chrome/browser/feed/FeedProcessScopeDependencyProviderTest.java", @@ -294,7 +295,6 @@ "java/src/org/chromium/chrome/browser/feed/FeedStreamViewResizerTest.java", "java/src/org/chromium/chrome/browser/feed/HeaderIphScrollListenerTest.java", "java/src/org/chromium/chrome/browser/feed/NativeViewListRendererTest.java", - "java/src/org/chromium/chrome/browser/feed/NtpListContentManagerTest.java", "java/src/org/chromium/chrome/browser/feed/RefreshIphScrollListenerTest.java", "java/src/org/chromium/chrome/browser/feed/ScrollTrackerTest.java", "java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementCoordinatorTest.java",
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/NtpListContentManager.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedListContentManager.java similarity index 99% rename from chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/NtpListContentManager.java rename to chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedListContentManager.java index 3cfbe5da..3525bc3 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/NtpListContentManager.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedListContentManager.java
@@ -30,7 +30,7 @@ * Implementation of ListContentManager that manages a list of feed contents that are supported by * either native view or external surface controlled view. */ -public class NtpListContentManager implements ListContentManager { +public class FeedListContentManager implements ListContentManager { /** * Encapsulates the content of an item stored and managed by ListContentManager. */
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/NtpListContentManagerTest.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedListContentManagerTest.java similarity index 64% rename from chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/NtpListContentManagerTest.java rename to chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedListContentManagerTest.java index 6b58c06..15276f3 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/NtpListContentManagerTest.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedListContentManagerTest.java
@@ -38,11 +38,11 @@ import java.util.List; import java.util.Map; -/** Unit tests for {@link NtpListContentManager}. */ +/** Unit tests for {@link FeedListContentManager}. */ @RunWith(BaseRobolectricTestRunner.class) @Config(manifest = Config.NONE) -public class NtpListContentManagerTest implements ListContentManagerObserver { - private NtpListContentManager mManager; +public class FeedListContentManagerTest implements ListContentManagerObserver { + private FeedListContentManager mManager; private Context mContext; private LinearLayout mParent; @@ -68,18 +68,18 @@ public void setUp() { mContext = Robolectric.buildActivity(Activity.class).get(); mParent = new LinearLayout(mContext); - mManager = new NtpListContentManager(); + mManager = new FeedListContentManager(); mManager.addObserver(this); } @Test @SmallTest public void testFindContentPositionByKey() { - NtpListContentManager.FeedContent c1 = createExternalViewContent("a"); - NtpListContentManager.FeedContent c2 = createExternalViewContent("b"); - NtpListContentManager.FeedContent c3 = createExternalViewContent("c"); + FeedListContentManager.FeedContent c1 = createExternalViewContent("a"); + FeedListContentManager.FeedContent c2 = createExternalViewContent("b"); + FeedListContentManager.FeedContent c3 = createExternalViewContent("c"); - addContents(0, Arrays.asList(new NtpListContentManager.FeedContent[] {c1, c2, c3})); + addContents(0, Arrays.asList(new FeedListContentManager.FeedContent[] {c1, c2, c3})); assertEquals(3, mManager.getItemCount()); assertEquals(0, mManager.findContentPositionByKey("a")); @@ -90,21 +90,21 @@ @Test @SmallTest public void testAddContents() { - NtpListContentManager.FeedContent c1 = createExternalViewContent("a"); - NtpListContentManager.FeedContent c2 = createExternalViewContent("b"); - NtpListContentManager.FeedContent c3 = createExternalViewContent("c"); + FeedListContentManager.FeedContent c1 = createExternalViewContent("a"); + FeedListContentManager.FeedContent c2 = createExternalViewContent("b"); + FeedListContentManager.FeedContent c3 = createExternalViewContent("c"); - addContents(0, Arrays.asList(new NtpListContentManager.FeedContent[] {c1})); + addContents(0, Arrays.asList(new FeedListContentManager.FeedContent[] {c1})); assertEquals(1, mManager.getItemCount()); assertEquals(c1, mManager.getContent(0)); - addContents(0, Arrays.asList(new NtpListContentManager.FeedContent[] {c2, c3})); + addContents(0, Arrays.asList(new FeedListContentManager.FeedContent[] {c2, c3})); assertEquals(3, mManager.getItemCount()); assertEquals(c2, mManager.getContent(0)); assertEquals(c3, mManager.getContent(1)); assertEquals(c1, mManager.getContent(2)); - addContents(3, Arrays.asList(new NtpListContentManager.FeedContent[] {c2, c3})); + addContents(3, Arrays.asList(new FeedListContentManager.FeedContent[] {c2, c3})); assertEquals(5, mManager.getItemCount()); assertEquals(c2, mManager.getContent(0)); assertEquals(c3, mManager.getContent(1)); @@ -116,13 +116,14 @@ @Test @SmallTest public void testRemoveContents() { - NtpListContentManager.FeedContent c1 = createExternalViewContent("a"); - NtpListContentManager.FeedContent c2 = createExternalViewContent("b"); - NtpListContentManager.FeedContent c3 = createExternalViewContent("c"); - NtpListContentManager.FeedContent c4 = createExternalViewContent("d"); - NtpListContentManager.FeedContent c5 = createExternalViewContent("e"); + FeedListContentManager.FeedContent c1 = createExternalViewContent("a"); + FeedListContentManager.FeedContent c2 = createExternalViewContent("b"); + FeedListContentManager.FeedContent c3 = createExternalViewContent("c"); + FeedListContentManager.FeedContent c4 = createExternalViewContent("d"); + FeedListContentManager.FeedContent c5 = createExternalViewContent("e"); - addContents(0, Arrays.asList(new NtpListContentManager.FeedContent[] {c1, c2, c3, c4, c5})); + addContents( + 0, Arrays.asList(new FeedListContentManager.FeedContent[] {c1, c2, c3, c4, c5})); assertEquals(5, mManager.getItemCount()); removeContents(0, 2); @@ -142,16 +143,16 @@ @Test @SmallTest public void testUpdateContents() { - NtpListContentManager.FeedContent c1 = createExternalViewContent("a"); - NtpListContentManager.FeedContent c2 = createExternalViewContent("b"); - NtpListContentManager.FeedContent c3 = createExternalViewContent("c"); - NtpListContentManager.FeedContent c4 = createExternalViewContent("d"); - NtpListContentManager.FeedContent c5 = createExternalViewContent("e"); + FeedListContentManager.FeedContent c1 = createExternalViewContent("a"); + FeedListContentManager.FeedContent c2 = createExternalViewContent("b"); + FeedListContentManager.FeedContent c3 = createExternalViewContent("c"); + FeedListContentManager.FeedContent c4 = createExternalViewContent("d"); + FeedListContentManager.FeedContent c5 = createExternalViewContent("e"); - addContents(0, Arrays.asList(new NtpListContentManager.FeedContent[] {c1, c2, c3})); + addContents(0, Arrays.asList(new FeedListContentManager.FeedContent[] {c1, c2, c3})); assertEquals(3, mManager.getItemCount()); - updateContents(1, Arrays.asList(new NtpListContentManager.FeedContent[] {c4, c5})); + updateContents(1, Arrays.asList(new FeedListContentManager.FeedContent[] {c4, c5})); assertEquals(3, mManager.getItemCount()); assertEquals(c1, mManager.getContent(0)); assertEquals(c4, mManager.getContent(1)); @@ -161,13 +162,14 @@ @Test @SmallTest public void testMoveContent() { - NtpListContentManager.FeedContent c1 = createExternalViewContent("a"); - NtpListContentManager.FeedContent c2 = createExternalViewContent("b"); - NtpListContentManager.FeedContent c3 = createExternalViewContent("c"); - NtpListContentManager.FeedContent c4 = createExternalViewContent("d"); - NtpListContentManager.FeedContent c5 = createExternalViewContent("e"); + FeedListContentManager.FeedContent c1 = createExternalViewContent("a"); + FeedListContentManager.FeedContent c2 = createExternalViewContent("b"); + FeedListContentManager.FeedContent c3 = createExternalViewContent("c"); + FeedListContentManager.FeedContent c4 = createExternalViewContent("d"); + FeedListContentManager.FeedContent c5 = createExternalViewContent("e"); - addContents(0, Arrays.asList(new NtpListContentManager.FeedContent[] {c1, c2, c3, c4, c5})); + addContents( + 0, Arrays.asList(new FeedListContentManager.FeedContent[] {c1, c2, c3, c4, c5})); assertEquals(5, mManager.getItemCount()); moveContent(0, 3); @@ -190,16 +192,17 @@ @Test @SmallTest public void testGetViewData() { - NtpListContentManager.FeedContent c1 = createExternalViewContent("foo"); + FeedListContentManager.FeedContent c1 = createExternalViewContent("foo"); View v2 = new View(mContext); - NtpListContentManager.FeedContent c2 = createNativeViewContent(v2); + FeedListContentManager.FeedContent c2 = createNativeViewContent(v2); View v3 = new View(mContext); - NtpListContentManager.FeedContent c3 = createNativeViewContent(v3); - NtpListContentManager.FeedContent c4 = createExternalViewContent("hello"); + FeedListContentManager.FeedContent c3 = createNativeViewContent(v3); + FeedListContentManager.FeedContent c4 = createExternalViewContent("hello"); View v5 = new View(mContext); - NtpListContentManager.FeedContent c5 = createNativeViewContent(v5); + FeedListContentManager.FeedContent c5 = createNativeViewContent(v5); - addContents(0, Arrays.asList(new NtpListContentManager.FeedContent[] {c1, c2, c3, c4, c5})); + addContents( + 0, Arrays.asList(new FeedListContentManager.FeedContent[] {c1, c2, c3, c4, c5})); assertEquals(5, mManager.getItemCount()); assertFalse(mManager.isNativeView(0)); @@ -225,9 +228,9 @@ @SmallTest public void testGetViewDataCreatesEnclosingViewOnce() { View v = new View(mContext); - NtpListContentManager.FeedContent c = createNativeViewContent(v); + FeedListContentManager.FeedContent c = createNativeViewContent(v); - addContents(0, Arrays.asList(new NtpListContentManager.FeedContent[] {c})); + addContents(0, Arrays.asList(new FeedListContentManager.FeedContent[] {c})); assertEquals(v, getNativeView(mManager.getViewType(0))); ViewParent p = v.getParent(); @@ -236,7 +239,7 @@ // This time, getNativeView() creates a new enclosing parent view. removeContents(0, 1); c = createNativeViewContent(v); - addContents(0, Arrays.asList(new NtpListContentManager.FeedContent[] {c})); + addContents(0, Arrays.asList(new FeedListContentManager.FeedContent[] {c})); assertEquals(v, getNativeView(mManager.getViewType(0))); assertNotEquals(p, v.getParent()); } @@ -245,7 +248,7 @@ @SmallTest public void testGetContextValuesReturnsLoggingParameters() { addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { + Arrays.asList(new FeedListContentManager.FeedContent[] { createExternalViewContent("A", mLoggingParametersA), createExternalViewContent("B", mLoggingParametersB)})); @@ -261,7 +264,7 @@ @SmallTest public void testGetContextValues_SetHandlersAfterAddingContent() { addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { + Arrays.asList(new FeedListContentManager.FeedContent[] { createExternalViewContent("A", mLoggingParametersA)})); mManager.setHandlers(Map.of("HKEY1", "someHandler")); @@ -274,7 +277,7 @@ public void testGetContextValues_SetHandlersBeforeAddingContent() { mManager.setHandlers(Map.of("HKEY1", "someHandler")); addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { + Arrays.asList(new FeedListContentManager.FeedContent[] { createExternalViewContent("A", mLoggingParametersA)})); assertEquals(Map.of("HKEY1", "someHandler", LoggingParameters.KEY, mLoggingParametersA), @@ -285,13 +288,13 @@ @SmallTest public void testGetNativeViewAfterMove() { View v1 = new View(mContext); - NtpListContentManager.FeedContent c1 = createNativeViewContent(v1); + FeedListContentManager.FeedContent c1 = createNativeViewContent(v1); View v0 = new View(mContext); - NtpListContentManager.FeedContent c0 = createNativeViewContent(v0); + FeedListContentManager.FeedContent c0 = createNativeViewContent(v0); - addContents(0, Arrays.asList(new NtpListContentManager.FeedContent[] {c1})); + addContents(0, Arrays.asList(new FeedListContentManager.FeedContent[] {c1})); int t1 = mManager.getViewType(0); - addContents(0, Arrays.asList(new NtpListContentManager.FeedContent[] {c0})); + addContents(0, Arrays.asList(new FeedListContentManager.FeedContent[] {c0})); int t0 = mManager.getViewType(0); assertEquals(1, t1); @@ -307,7 +310,7 @@ @SmallTest public void testReplaceRange_Empty() { boolean changed = mManager.replaceRange( - 0, 0, Arrays.asList(new NtpListContentManager.FeedContent[] {})); + 0, 0, Arrays.asList(new FeedListContentManager.FeedContent[] {})); assertThat(getContentKeys(), Matchers.empty()); assertFalse(changed); @@ -317,10 +320,10 @@ @Test @SmallTest public void testReplaceRange_twoWhileEmpty() { - NtpListContentManager.FeedContent c1 = createExternalViewContent("a"); - NtpListContentManager.FeedContent c2 = createExternalViewContent("b"); + FeedListContentManager.FeedContent c1 = createExternalViewContent("a"); + FeedListContentManager.FeedContent c2 = createExternalViewContent("b"); boolean changed = mManager.replaceRange( - 0, 0, Arrays.asList(new NtpListContentManager.FeedContent[] {c1, c2})); + 0, 0, Arrays.asList(new FeedListContentManager.FeedContent[] {c1, c2})); assertThat(getContentKeys(), contains("a", "b")); assertTrue(changed); @@ -330,17 +333,17 @@ @Test @SmallTest public void testReplaceRange_twoInMiddle() { - NtpListContentManager.FeedContent c1 = createExternalViewContent("a"); - NtpListContentManager.FeedContent c2 = createExternalViewContent("b"); - NtpListContentManager.FeedContent c3 = createExternalViewContent("c"); - NtpListContentManager.FeedContent c4 = createExternalViewContent("d"); - NtpListContentManager.FeedContent c5 = createExternalViewContent("e"); + FeedListContentManager.FeedContent c1 = createExternalViewContent("a"); + FeedListContentManager.FeedContent c2 = createExternalViewContent("b"); + FeedListContentManager.FeedContent c3 = createExternalViewContent("c"); + FeedListContentManager.FeedContent c4 = createExternalViewContent("d"); + FeedListContentManager.FeedContent c5 = createExternalViewContent("e"); mManager.replaceRange( - 0, 0, Arrays.asList(new NtpListContentManager.FeedContent[] {c1, c2, c3})); + 0, 0, Arrays.asList(new FeedListContentManager.FeedContent[] {c1, c2, c3})); mObservedChanges = ""; boolean changed = mManager.replaceRange( - 1, 1, Arrays.asList(new NtpListContentManager.FeedContent[] {c4, c5})); + 1, 1, Arrays.asList(new FeedListContentManager.FeedContent[] {c4, c5})); assertThat(getContentKeys(), contains("a", "d", "e", "c")); assertTrue(changed); @@ -352,17 +355,17 @@ @Test @SmallTest public void testReplaceRange_twoAtEnd() { - NtpListContentManager.FeedContent c1 = createExternalViewContent("a"); - NtpListContentManager.FeedContent c2 = createExternalViewContent("b"); - NtpListContentManager.FeedContent c3 = createExternalViewContent("c"); - NtpListContentManager.FeedContent c4 = createExternalViewContent("d"); - NtpListContentManager.FeedContent c5 = createExternalViewContent("e"); + FeedListContentManager.FeedContent c1 = createExternalViewContent("a"); + FeedListContentManager.FeedContent c2 = createExternalViewContent("b"); + FeedListContentManager.FeedContent c3 = createExternalViewContent("c"); + FeedListContentManager.FeedContent c4 = createExternalViewContent("d"); + FeedListContentManager.FeedContent c5 = createExternalViewContent("e"); mManager.replaceRange( - 0, 0, Arrays.asList(new NtpListContentManager.FeedContent[] {c1, c2, c3})); + 0, 0, Arrays.asList(new FeedListContentManager.FeedContent[] {c1, c2, c3})); mObservedChanges = ""; boolean changed = mManager.replaceRange( - 2, 1, Arrays.asList(new NtpListContentManager.FeedContent[] {c4, c5})); + 2, 1, Arrays.asList(new FeedListContentManager.FeedContent[] {c4, c5})); assertThat(getContentKeys(), contains("a", "b", "d", "e")); assertTrue(changed); @@ -374,17 +377,17 @@ @Test @SmallTest public void testReplaceRange_twoAtStart() { - NtpListContentManager.FeedContent c1 = createExternalViewContent("a"); - NtpListContentManager.FeedContent c2 = createExternalViewContent("b"); - NtpListContentManager.FeedContent c3 = createExternalViewContent("c"); - NtpListContentManager.FeedContent c4 = createExternalViewContent("d"); - NtpListContentManager.FeedContent c5 = createExternalViewContent("e"); + FeedListContentManager.FeedContent c1 = createExternalViewContent("a"); + FeedListContentManager.FeedContent c2 = createExternalViewContent("b"); + FeedListContentManager.FeedContent c3 = createExternalViewContent("c"); + FeedListContentManager.FeedContent c4 = createExternalViewContent("d"); + FeedListContentManager.FeedContent c5 = createExternalViewContent("e"); mManager.replaceRange( - 0, 0, Arrays.asList(new NtpListContentManager.FeedContent[] {c1, c2, c3})); + 0, 0, Arrays.asList(new FeedListContentManager.FeedContent[] {c1, c2, c3})); mObservedChanges = ""; boolean changed = mManager.replaceRange( - 0, 1, Arrays.asList(new NtpListContentManager.FeedContent[] {c4, c5})); + 0, 1, Arrays.asList(new FeedListContentManager.FeedContent[] {c4, c5})); assertThat(getContentKeys(), contains("d", "e", "b", "c")); assertTrue(changed); @@ -396,15 +399,15 @@ @Test @SmallTest public void testReplaceRange_moveFirstToLast() { - NtpListContentManager.FeedContent c1 = createExternalViewContent("a"); - NtpListContentManager.FeedContent c2 = createExternalViewContent("b"); - NtpListContentManager.FeedContent c3 = createExternalViewContent("c"); + FeedListContentManager.FeedContent c1 = createExternalViewContent("a"); + FeedListContentManager.FeedContent c2 = createExternalViewContent("b"); + FeedListContentManager.FeedContent c3 = createExternalViewContent("c"); mManager.replaceRange( - 0, 0, Arrays.asList(new NtpListContentManager.FeedContent[] {c1, c2, c3})); + 0, 0, Arrays.asList(new FeedListContentManager.FeedContent[] {c1, c2, c3})); mObservedChanges = ""; boolean changed = mManager.replaceRange( - 0, 3, Arrays.asList(new NtpListContentManager.FeedContent[] {c2, c3, c1})); + 0, 3, Arrays.asList(new FeedListContentManager.FeedContent[] {c2, c3, c1})); assertTrue(changed); assertThat(getContentKeys(), contains("b", "c", "a")); @@ -414,15 +417,15 @@ @Test @SmallTest public void testReplaceRange_reverseOrder() { - NtpListContentManager.FeedContent c1 = createExternalViewContent("a"); - NtpListContentManager.FeedContent c2 = createExternalViewContent("b"); - NtpListContentManager.FeedContent c3 = createExternalViewContent("c"); + FeedListContentManager.FeedContent c1 = createExternalViewContent("a"); + FeedListContentManager.FeedContent c2 = createExternalViewContent("b"); + FeedListContentManager.FeedContent c3 = createExternalViewContent("c"); mManager.replaceRange( - 0, 0, Arrays.asList(new NtpListContentManager.FeedContent[] {c1, c2, c3})); + 0, 0, Arrays.asList(new FeedListContentManager.FeedContent[] {c1, c2, c3})); mObservedChanges = ""; boolean changed = mManager.replaceRange( - 0, 3, Arrays.asList(new NtpListContentManager.FeedContent[] {c3, c2, c1})); + 0, 3, Arrays.asList(new FeedListContentManager.FeedContent[] {c3, c2, c1})); assertTrue(changed); assertThat(getContentKeys(), contains("c", "b", "a")); @@ -432,15 +435,15 @@ @Test @SmallTest public void testReplaceRange_removeAll() { - NtpListContentManager.FeedContent c1 = createExternalViewContent("a"); - NtpListContentManager.FeedContent c2 = createExternalViewContent("b"); - NtpListContentManager.FeedContent c3 = createExternalViewContent("c"); + FeedListContentManager.FeedContent c1 = createExternalViewContent("a"); + FeedListContentManager.FeedContent c2 = createExternalViewContent("b"); + FeedListContentManager.FeedContent c3 = createExternalViewContent("c"); mManager.replaceRange( - 0, 0, Arrays.asList(new NtpListContentManager.FeedContent[] {c1, c2, c3})); + 0, 0, Arrays.asList(new FeedListContentManager.FeedContent[] {c1, c2, c3})); mObservedChanges = ""; boolean changed = mManager.replaceRange( - 0, 3, Arrays.asList(new NtpListContentManager.FeedContent[] {})); + 0, 3, Arrays.asList(new FeedListContentManager.FeedContent[] {})); assertTrue(changed); assertThat(getContentKeys(), Matchers.empty()); @@ -450,21 +453,21 @@ @Test @SmallTest public void testReplaceRange_complexUpdate() { - NtpListContentManager.FeedContent a = createExternalViewContent("a"); - NtpListContentManager.FeedContent b = createExternalViewContent("b"); - NtpListContentManager.FeedContent c = createExternalViewContent("c"); - NtpListContentManager.FeedContent d = createExternalViewContent("d"); - NtpListContentManager.FeedContent e = createExternalViewContent("e"); - NtpListContentManager.FeedContent f = createExternalViewContent("f"); - NtpListContentManager.FeedContent g = createExternalViewContent("g"); - NtpListContentManager.FeedContent h = createExternalViewContent("h"); - NtpListContentManager.FeedContent i = createExternalViewContent("i"); + FeedListContentManager.FeedContent a = createExternalViewContent("a"); + FeedListContentManager.FeedContent b = createExternalViewContent("b"); + FeedListContentManager.FeedContent c = createExternalViewContent("c"); + FeedListContentManager.FeedContent d = createExternalViewContent("d"); + FeedListContentManager.FeedContent e = createExternalViewContent("e"); + FeedListContentManager.FeedContent f = createExternalViewContent("f"); + FeedListContentManager.FeedContent g = createExternalViewContent("g"); + FeedListContentManager.FeedContent h = createExternalViewContent("h"); + FeedListContentManager.FeedContent i = createExternalViewContent("i"); mManager.replaceRange( - 0, 0, Arrays.asList(new NtpListContentManager.FeedContent[] {a, b, c, d, e})); + 0, 0, Arrays.asList(new FeedListContentManager.FeedContent[] {a, b, c, d, e})); mObservedChanges = ""; - boolean changed = mManager.replaceRange( - 0, 5, Arrays.asList(new NtpListContentManager.FeedContent[] {f, g, a, h, c, e, i})); + boolean changed = mManager.replaceRange(0, 5, + Arrays.asList(new FeedListContentManager.FeedContent[] {f, g, a, h, c, e, i})); assertTrue(changed); assertThat(getContentKeys(), contains("f", "g", "a", "h", "c", "e", "i")); @@ -511,7 +514,7 @@ mItemMovedNewIndex = newIndex; } - private void addContents(int index, List<NtpListContentManager.FeedContent> contents) { + private void addContents(int index, List<FeedListContentManager.FeedContent> contents) { mItemRangeInserted = false; mItemRangeInsertedStartIndex = -1; mItemRangeInsertedCount = -1; @@ -531,7 +534,7 @@ assertEquals(count, mItemRangeRemovedCount); } - private void updateContents(int index, List<NtpListContentManager.FeedContent> contents) { + private void updateContents(int index, List<FeedListContentManager.FeedContent> contents) { mItemRangeChanged = false; mItemRangeChangedStartIndex = -1; mItemRangeChangedCount = -1; @@ -558,17 +561,17 @@ mObservedChanges += change; } - private NtpListContentManager.FeedContent createExternalViewContent(String s) { + private FeedListContentManager.FeedContent createExternalViewContent(String s) { return createExternalViewContent(s, mLoggingParametersA); } - private NtpListContentManager.FeedContent createExternalViewContent( + private FeedListContentManager.FeedContent createExternalViewContent( String s, FeedLoggingParameters loggingParameters) { - return new NtpListContentManager.ExternalViewContent(s, s.getBytes(), loggingParameters); + return new FeedListContentManager.ExternalViewContent(s, s.getBytes(), loggingParameters); } - private NtpListContentManager.FeedContent createNativeViewContent(View v) { - return new NtpListContentManager.NativeViewContent(123, v.toString(), v); + private FeedListContentManager.FeedContent createNativeViewContent(View v) { + return new FeedListContentManager.NativeViewContent(123, v.toString(), v); } private View getNativeView(int viewType) { @@ -579,7 +582,7 @@ } private List<String> getContentKeys() { List<String> result = new ArrayList<>(); - for (NtpListContentManager.FeedContent content : mManager.getContentList()) { + for (FeedListContentManager.FeedContent content : mManager.getContentList()) { result.add(content.getKey()); } return result;
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTracker.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTracker.java index 012eee4..b98b457 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTracker.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTracker.java
@@ -45,7 +45,7 @@ @Nullable private RecyclerView mRootView; @Nullable - private NtpListContentManager mContentManager; + private FeedListContentManager mContentManager; private ListLayoutHelper mLayoutHelper; // The set of content keys already reported as visible. private HashSet<String> mContentKeysVisible = new HashSet<String>(); @@ -72,7 +72,7 @@ } public FeedSliceViewTracker(@NonNull RecyclerView rootView, @NonNull Activity activity, - @NonNull NtpListContentManager contentManager, @Nullable ListLayoutHelper layoutHelper, + @NonNull FeedListContentManager contentManager, @Nullable ListLayoutHelper layoutHelper, @NonNull Observer observer) { mActivity = activity; mRootView = rootView;
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTrackerTest.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTrackerTest.java index dacae36..ebebdf0 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTrackerTest.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTrackerTest.java
@@ -70,7 +70,7 @@ Window mWindow; @Mock View mDecorView; - NtpListContentManager mContentManager; + FeedListContentManager mContentManager; FeedSliceViewTracker mTracker; @@ -90,7 +90,7 @@ public void setUp() { ShadowLog.stream = System.out; MockitoAnnotations.initMocks(this); - mContentManager = new NtpListContentManager(); + mContentManager = new FeedListContentManager(); doReturn(mLayoutManager).when(mParentView).getLayoutManager(); doReturn(mViewTreeObserver).when(mParentView).getViewTreeObserver(); doReturn(mWindow).when(mActivity).getWindow(); @@ -192,9 +192,9 @@ @SmallTest public void testOnPreDraw_BothVisibleAreReportedExactlyOnce() { mContentManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { - new NtpListContentManager.NativeViewContent(0, "c/key1", mChildA), - new NtpListContentManager.NativeViewContent(0, "c/key2", mChildB), + Arrays.asList(new FeedListContentManager.FeedContent[] { + new FeedListContentManager.NativeViewContent(0, "c/key1", mChildA), + new FeedListContentManager.NativeViewContent(0, "c/key2", mChildB), })); doReturn(0).when(mLayoutHelper).findFirstVisibleItemPosition(); doReturn(1).when(mLayoutHelper).findLastVisibleItemPosition(); @@ -217,9 +217,9 @@ @SmallTest public void testOnPreDraw_AfterClearReportsAgain() { mContentManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { - new NtpListContentManager.NativeViewContent(0, "c/key1", mChildA), - new NtpListContentManager.NativeViewContent(0, "c/key2", mChildB), + Arrays.asList(new FeedListContentManager.FeedContent[] { + new FeedListContentManager.NativeViewContent(0, "c/key1", mChildA), + new FeedListContentManager.NativeViewContent(0, "c/key2", mChildB), })); doReturn(0).when(mLayoutHelper).findFirstVisibleItemPosition(); doReturn(1).when(mLayoutHelper).findLastVisibleItemPosition(); @@ -242,9 +242,11 @@ @SmallTest public void testOnPreDraw_IgnoresNonContentViews() { mContentManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { - new NtpListContentManager.NativeViewContent(0, "non-content-key1", mChildA), - new NtpListContentManager.NativeViewContent(0, "non-content-key2", mChildB), + Arrays.asList(new FeedListContentManager.FeedContent[] { + new FeedListContentManager.NativeViewContent( + 0, "non-content-key1", mChildA), + new FeedListContentManager.NativeViewContent( + 0, "non-content-key2", mChildB), })); doReturn(0).when(mLayoutHelper).findFirstVisibleItemPosition(); doReturn(1).when(mLayoutHelper).findLastVisibleItemPosition(); @@ -266,9 +268,9 @@ @SmallTest public void testOnPreDraw_OnlyOneVisible() { mContentManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { - new NtpListContentManager.NativeViewContent(0, "c/key1", mChildA), - new NtpListContentManager.NativeViewContent(0, "c/key2", mChildB), + Arrays.asList(new FeedListContentManager.FeedContent[] { + new FeedListContentManager.NativeViewContent(0, "c/key1", mChildA), + new FeedListContentManager.NativeViewContent(0, "c/key2", mChildB), })); doReturn(0).when(mLayoutHelper).findFirstVisibleItemPosition(); doReturn(1).when(mLayoutHelper).findLastVisibleItemPosition(); @@ -287,9 +289,9 @@ @SmallTest public void testOnPreDraw_EmptyRecyclerView() { mContentManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { - new NtpListContentManager.NativeViewContent(0, "c/key1", mChildA), - new NtpListContentManager.NativeViewContent(0, "c/key2", mChildB), + Arrays.asList(new FeedListContentManager.FeedContent[] { + new FeedListContentManager.NativeViewContent(0, "c/key1", mChildA), + new FeedListContentManager.NativeViewContent(0, "c/key2", mChildB), })); doReturn(RecyclerView.NO_POSITION).when(mLayoutHelper).findFirstVisibleItemPosition(); doReturn(RecyclerView.NO_POSITION).when(mLayoutHelper).findLastVisibleItemPosition(); @@ -315,9 +317,9 @@ @SmallTest public void testWatchForFirstVisible() { mContentManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { - new NtpListContentManager.NativeViewContent(0, "c/key1", mChildA), - new NtpListContentManager.NativeViewContent(0, "c/key2", mChildB), + Arrays.asList(new FeedListContentManager.FeedContent[] { + new FeedListContentManager.NativeViewContent(0, "c/key1", mChildA), + new FeedListContentManager.NativeViewContent(0, "c/key2", mChildB), })); doReturn(0).when(mLayoutHelper).findFirstVisibleItemPosition(); doReturn(1).when(mLayoutHelper).findLastVisibleItemPosition(); @@ -378,9 +380,9 @@ @SmallTest public void testReportContentVisibleTime_visibleAndCovering() { mContentManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { - new NtpListContentManager.NativeViewContent(0, "c/key1", mChildA), - new NtpListContentManager.NativeViewContent(0, "c/key2", mChildB), + Arrays.asList(new FeedListContentManager.FeedContent[] { + new FeedListContentManager.NativeViewContent(0, "c/key1", mChildA), + new FeedListContentManager.NativeViewContent(0, "c/key2", mChildB), })); doReturn(0).when(mLayoutHelper).findFirstVisibleItemPosition(); doReturn(1).when(mLayoutHelper).findLastVisibleItemPosition(); @@ -424,9 +426,9 @@ @SmallTest public void testReportContentVisibleTime_testSmallCardsCoveringEnough() { mContentManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { - new NtpListContentManager.NativeViewContent(0, "c/key1", mChildA), - new NtpListContentManager.NativeViewContent(0, "c/key2", mChildB), + Arrays.asList(new FeedListContentManager.FeedContent[] { + new FeedListContentManager.NativeViewContent(0, "c/key1", mChildA), + new FeedListContentManager.NativeViewContent(0, "c/key2", mChildB), })); doReturn(0).when(mLayoutHelper).findFirstVisibleItemPosition(); doReturn(1).when(mLayoutHelper).findLastVisibleItemPosition(); @@ -450,8 +452,8 @@ @SmallTest public void testReportContentVisibleTime_testBigCardCoveringEnough() { mContentManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { - new NtpListContentManager.NativeViewContent(0, "c/key1", mChildA), + Arrays.asList(new FeedListContentManager.FeedContent[] { + new FeedListContentManager.NativeViewContent(0, "c/key1", mChildA), })); doReturn(0).when(mLayoutHelper).findFirstVisibleItemPosition(); doReturn(0).when(mLayoutHelper).findLastVisibleItemPosition(); @@ -472,8 +474,8 @@ @SmallTest public void testReportContentVisibleTime_testBigCardExposedEnough() { mContentManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { - new NtpListContentManager.NativeViewContent(0, "c/key1", mChildA), + Arrays.asList(new FeedListContentManager.FeedContent[] { + new FeedListContentManager.NativeViewContent(0, "c/key1", mChildA), })); doReturn(0).when(mLayoutHelper).findFirstVisibleItemPosition(); doReturn(0).when(mLayoutHelper).findLastVisibleItemPosition(); @@ -494,8 +496,8 @@ @SmallTest public void testReportContentVisibleTime_testReportTimeOnUnbind() { mContentManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { - new NtpListContentManager.NativeViewContent(0, "c/key1", mChildA), + Arrays.asList(new FeedListContentManager.FeedContent[] { + new FeedListContentManager.NativeViewContent(0, "c/key1", mChildA), })); doReturn(0).when(mLayoutHelper).findFirstVisibleItemPosition(); doReturn(0).when(mLayoutHelper).findLastVisibleItemPosition();
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java index 674d81a..e7778c7b 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java
@@ -606,7 +606,7 @@ // Things valid only when bound. private @Nullable RecyclerView mRecyclerView; - private @Nullable NtpListContentManager mContentManager; + private @Nullable FeedListContentManager mContentManager; private @Nullable SurfaceScope mSurfaceScope; private @Nullable HybridListRenderer mRenderer; private FeedScrollState mScrollStateToRestore; @@ -616,7 +616,7 @@ private ArrayList<SnackbarManager.SnackbarController> mSnackbarControllers = new ArrayList<>(); // Placeholder view that simply takes up space. - private NtpListContentManager.NativeViewContent mSpacerViewContent; + private FeedListContentManager.NativeViewContent mSpacerViewContent; // Bottomsheet. private final BottomSheetController mBottomSheetController; @@ -746,7 +746,7 @@ } @Override - public void bind(RecyclerView rootView, NtpListContentManager manager, + public void bind(RecyclerView rootView, FeedListContentManager manager, FeedScrollState savedInstanceState, SurfaceScope surfaceScope, HybridListRenderer renderer, FeedLaunchReliabilityLogger launchReliabilityLogger, int headerCount) { @@ -810,7 +810,7 @@ mScrollReporter.onUnbind(); // Remove Feed content from the content manager. Add spacer if needed. - ArrayList<NtpListContentManager.FeedContent> list = new ArrayList<>(); + ArrayList<FeedListContentManager.FeedContent> list = new ArrayList<>(); if (shouldPlaceSpacer) { addSpacer(list); } @@ -1005,7 +1005,7 @@ DisplayMetrics displayMetrics = new DisplayMetrics(); mActivity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); FrameLayout spacerView = new FrameLayout(mActivity); - mSpacerViewContent = new NtpListContentManager.NativeViewContent( + mSpacerViewContent = new FeedListContentManager.NativeViewContent( getLateralPaddingsPx(), SPACER_KEY, spacerView); spacerView.setLayoutParams(new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, displayMetrics.heightPixels)); @@ -1051,11 +1051,11 @@ // Builds the new list containing: // * existing headers // * both new and existing contents - ArrayList<NtpListContentManager.FeedContent> newContentList = new ArrayList<>(); + ArrayList<FeedListContentManager.FeedContent> newContentList = new ArrayList<>(); for (FeedUiProto.StreamUpdate.SliceUpdate sliceUpdate : streamUpdate.getUpdatedSlicesList()) { if (sliceUpdate.hasSlice()) { - NtpListContentManager.FeedContent content = + FeedListContentManager.FeedContent content = createContentFromSlice(sliceUpdate.getSlice(), loggingParameters); if (content != null) { newContentList.add(content); @@ -1093,11 +1093,11 @@ maybeLoadMore(/*lookaheadTrigger=*/0); } - private NtpListContentManager.FeedContent createContentFromSlice( + private FeedListContentManager.FeedContent createContentFromSlice( FeedUiProto.Slice slice, LoggingParameters loggingParameters) { String sliceId = slice.getSliceId(); if (slice.hasXsurfaceSlice()) { - return new NtpListContentManager.ExternalViewContent(sliceId, + return new FeedListContentManager.ExternalViewContent(sliceId, slice.getXsurfaceSlice().getXsurfaceFrame().toByteArray(), loggingParameters); } else if (slice.hasLoadingSpinnerSlice()) { // If the placeholder is shown, spinner is not needed. @@ -1106,15 +1106,15 @@ } if (ChromeFeatureList.isEnabled(ChromeFeatureList.FEED_LOADING_PLACEHOLDER) && slice.getLoadingSpinnerSlice().getIsAtTop()) { - return new NtpListContentManager.NativeViewContent( + return new FeedListContentManager.NativeViewContent( getLateralPaddingsPx(), sliceId, R.layout.feed_placeholder_layout); } - return new NtpListContentManager.NativeViewContent( + return new FeedListContentManager.NativeViewContent( getLateralPaddingsPx(), sliceId, R.layout.feed_spinner); } assert slice.hasZeroStateSlice(); if (mStreamKind == StreamKind.FOLLOWING) { - return new NtpListContentManager.NativeViewContent( + return new FeedListContentManager.NativeViewContent( getLateralPaddingsPx(), sliceId, R.layout.following_empty_state); } if (mStreamKind == StreamKind.SINGLE_WEB_FEED) { @@ -1138,11 +1138,11 @@ marginParams.setMargins(0, displayMetrics.heightPixels / 4, 0, mActivity.getResources().getDimensionPixelSize( R.dimen.creator_error_margin_bottom)); - return new NtpListContentManager.NativeViewContent( + return new FeedListContentManager.NativeViewContent( getLateralPaddingsPx(), sliceId, creatorErrorCard); } if (slice.getZeroStateSlice().getType() == FeedUiProto.ZeroStateSlice.Type.CANT_REFRESH) { - return new NtpListContentManager.NativeViewContent( + return new FeedListContentManager.NativeViewContent( getLateralPaddingsPx(), sliceId, R.layout.no_connection); } // TODO(crbug/1152592): Add new UI for NO_WEB_FEED_SUBSCRIPTIONS. @@ -1150,12 +1150,12 @@ == FeedUiProto.ZeroStateSlice.Type.NO_CARDS_AVAILABLE || slice.getZeroStateSlice().getType() == FeedUiProto.ZeroStateSlice.Type.NO_WEB_FEED_SUBSCRIPTIONS; - return new NtpListContentManager.NativeViewContent( + return new FeedListContentManager.NativeViewContent( getLateralPaddingsPx(), sliceId, R.layout.no_content_v2); } private void updateContentsInPlace( - ArrayList<NtpListContentManager.FeedContent> newContentList) { + ArrayList<FeedListContentManager.FeedContent> newContentList) { assert mHeaderCount <= mContentManager.getItemCount(); if (mContentManager.replaceRange( mHeaderCount, mContentManager.getItemCount() - mHeaderCount, newContentList)) {
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java index 144961c6..5c43306 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java
@@ -108,7 +108,7 @@ private RecyclerView mRecyclerView; private FakeLinearLayoutManager mLayoutManager; private FeedStream mFeedStream; - private NtpListContentManager mContentManager; + private FeedListContentManager mContentManager; @Mock private FeedStream.Natives mFeedStreamJniMock; @@ -200,7 +200,7 @@ mFeedStream.mMakeGURL = url -> JUnitTestGURLs.getGURL(url); mRecyclerView = new RecyclerView(mActivity); mRecyclerView.setAdapter(mAdapter); - mContentManager = new NtpListContentManager(); + mContentManager = new FeedListContentManager(); mLayoutManager = new FakeLinearLayoutManager(mActivity); mRecyclerView.setLayoutManager(mLayoutManager); when(mRenderer.getListLayoutHelper()).thenReturn(mLayoutManager); @@ -1025,11 +1025,11 @@ mFeedStream.onStreamUpdated(update.toByteArray()); assertEquals(2, mContentManager.getItemCount()); assertEquals("a", mContentManager.getContent(1).getKey()); - NtpListContentManager.FeedContent content = mContentManager.getContent(1); + FeedListContentManager.FeedContent content = mContentManager.getContent(1); assertThat(mContentManager.getContent(1), - instanceOf(NtpListContentManager.NativeViewContent.class)); - NtpListContentManager.NativeViewContent nativeViewContent = - (NtpListContentManager.NativeViewContent) mContentManager.getContent(1); + instanceOf(FeedListContentManager.NativeViewContent.class)); + FeedListContentManager.NativeViewContent nativeViewContent = + (FeedListContentManager.NativeViewContent) mContentManager.getContent(1); FrameLayout layout = new FrameLayout(mActivity); @@ -1050,11 +1050,11 @@ mFeedStream.onStreamUpdated(update.toByteArray()); assertEquals(2, mContentManager.getItemCount()); assertEquals("a", mContentManager.getContent(1).getKey()); - NtpListContentManager.FeedContent content = mContentManager.getContent(1); + FeedListContentManager.FeedContent content = mContentManager.getContent(1); assertThat(mContentManager.getContent(1), - instanceOf(NtpListContentManager.NativeViewContent.class)); - NtpListContentManager.NativeViewContent nativeViewContent = - (NtpListContentManager.NativeViewContent) mContentManager.getContent(1); + instanceOf(FeedListContentManager.NativeViewContent.class)); + FeedListContentManager.NativeViewContent nativeViewContent = + (FeedListContentManager.NativeViewContent) mContentManager.getContent(1); FrameLayout layout = new FrameLayout(mActivity); @@ -1215,9 +1215,9 @@ } private void createHeaderContent(int number) { - List<NtpListContentManager.FeedContent> contentList = new ArrayList<>(); + List<FeedListContentManager.FeedContent> contentList = new ArrayList<>(); for (int i = 0; i < number; i++) { - contentList.add(new NtpListContentManager.NativeViewContent( + contentList.add(new FeedListContentManager.NativeViewContent( 0, HEADER_PREFIX + i, new AppCompatTextView(mActivity))); } mContentManager.addContents(0, contentList);
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/NativeViewListRendererTest.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/NativeViewListRendererTest.java index fe63d22..554358f 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/NativeViewListRendererTest.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/NativeViewListRendererTest.java
@@ -50,7 +50,7 @@ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {} } - private NtpListContentManager mManager; + private FeedListContentManager mManager; private Context mContext; private NativeViewListRenderer mRenderer; @@ -62,7 +62,7 @@ // Note: this behaves both like a mock and a real object. // Methods calls can be mocked or tracked to validate class behavior. - mManager = Mockito.mock(NtpListContentManager.class, + mManager = Mockito.mock(FeedListContentManager.class, Mockito.withSettings().useConstructor().defaultAnswer(Mockito.CALLS_REAL_METHODS)); mRenderer = Mockito.mock(NativeViewListRenderer.class, Mockito.withSettings().useConstructor(mContext).defaultAnswer( @@ -79,7 +79,7 @@ @SmallTest public void testOnCreateViewHolder() { mManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { + Arrays.asList(new FeedListContentManager.FeedContent[] { createContent("1"), createContent("2"), createContent("3")})); mRenderer.bind(mManager); NativeViewListRenderer.ViewHolder viewHolder = mRenderer.onCreateViewHolder( @@ -94,7 +94,7 @@ @SmallTest public void testOnBindViewHolder() { mManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { + Arrays.asList(new FeedListContentManager.FeedContent[] { createContent("1"), createContent("2"), createContent("3")})); mRenderer.bind(mManager); NativeViewListRenderer.ViewHolder viewHolder = mRenderer.onCreateViewHolder( @@ -107,7 +107,7 @@ @SmallTest public void testUnbind() { mManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { + Arrays.asList(new FeedListContentManager.FeedContent[] { createContent("1"), createContent("2"), createContent("3")})); RecyclerView view = (RecyclerView) mRenderer.bind(mManager); assertNotNull(view.getAdapter()); @@ -122,7 +122,7 @@ @Test public void testObserver_itemsAddedOnBind() { mManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { + Arrays.asList(new FeedListContentManager.FeedContent[] { createContent("1"), createContent("2"), createContent("3")})); mRenderer.bind(mManager); verify(mRenderer, times(1)).onItemRangeInserted(0, 3); @@ -132,7 +132,7 @@ public void testObserver_itemsAddedLater() { mRenderer.bind(mManager); mManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { + Arrays.asList(new FeedListContentManager.FeedContent[] { createContent("1"), createContent("2"), createContent("3")})); verify(mRenderer, times(1)).onItemRangeInserted(0, 3); } @@ -140,7 +140,7 @@ @Test public void testObserver_itemsRemoved() { mManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { + Arrays.asList(new FeedListContentManager.FeedContent[] { createContent("1"), createContent("2"), createContent("3")})); mRenderer.bind(mManager); @@ -151,7 +151,7 @@ @Test public void testObserver_itemsRemovedOnUnbind() { mManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { + Arrays.asList(new FeedListContentManager.FeedContent[] { createContent("1"), createContent("2"), createContent("3")})); mRenderer.bind(mManager); @@ -162,12 +162,12 @@ @Test public void testObserver_itemsUpdated() { mManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { + Arrays.asList(new FeedListContentManager.FeedContent[] { createContent("1"), createContent("2"), createContent("3")})); mRenderer.bind(mManager); mManager.updateContents(1, - Arrays.asList(new NtpListContentManager.FeedContent[] { + Arrays.asList(new FeedListContentManager.FeedContent[] { createContent("a"), createContent("b")})); verify(mRenderer, times(1)).onItemRangeChanged(1, 2); } @@ -175,7 +175,7 @@ @Test public void testObserver_itemMoved() { mManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { + Arrays.asList(new FeedListContentManager.FeedContent[] { createContent("1"), createContent("2"), createContent("3")})); mRenderer.bind(mManager); @@ -183,16 +183,16 @@ verify(mRenderer, times(1)).onItemMoved(2, 1); } - private NtpListContentManager.FeedContent createContent(String text) { + private FeedListContentManager.FeedContent createContent(String text) { TextView v = new AppCompatTextView(mContext); v.setText(text); - return new NtpListContentManager.NativeViewContent(0, v.toString(), v); + return new FeedListContentManager.NativeViewContent(0, v.toString(), v); } @Test public void testGetListLayoutHelper() { mManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { + Arrays.asList(new FeedListContentManager.FeedContent[] { createContent("1"), createContent("2"), createContent("3")})); mRenderer.bind(mManager); @@ -208,7 +208,7 @@ @Test public void testLayoutHelperSetColumnCount() { mManager.addContents(0, - Arrays.asList(new NtpListContentManager.FeedContent[] { + Arrays.asList(new FeedListContentManager.FeedContent[] { createContent("1"), createContent("2"), createContent("3")})); mRenderer.bind(mManager);
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/Stream.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/Stream.java index 54fe5b0..07844b6 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/Stream.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/Stream.java
@@ -10,7 +10,7 @@ import org.chromium.base.Callback; import org.chromium.base.supplier.ObservableSupplier; import org.chromium.base.supplier.ObservableSupplierImpl; -import org.chromium.chrome.browser.feed.NtpListContentManager.FeedContent; +import org.chromium.chrome.browser.feed.FeedListContentManager.FeedContent; import org.chromium.chrome.browser.xsurface.FeedLaunchReliabilityLogger; import org.chromium.chrome.browser.xsurface.HybridListRenderer; import org.chromium.chrome.browser.xsurface.SurfaceScope; @@ -110,7 +110,7 @@ * When bound, the feed actively updates views and content. Assumes that whatever * views currently shown by manager are headers. * @param view The {@link RecyclerView} to which the feed is bound. - * @param manager The {@link NtpListContentManager} to which we should make updates to. + * @param manager The {@link FeedListContentManager} to which we should make updates to. * @param savedInstanceState A previously saved instance state to restore to after loading * content. * @param surfaceScope The {@link SurfaceScope} that is hosting the renderer. @@ -118,7 +118,7 @@ * @param launchReliabilityLogger Logger for timestamps and status codes related to launching * @param headerCount The number of headers in the RecyclerView that the feed shouldn't touch. */ - void bind(RecyclerView view, NtpListContentManager manager, FeedScrollState savedInstanceState, + void bind(RecyclerView view, FeedListContentManager manager, FeedScrollState savedInstanceState, SurfaceScope surfaceScope, HybridListRenderer renderer, FeedLaunchReliabilityLogger launchReliabilityLogger, int headerCount);
diff --git a/chrome/browser/lacros/sync/crosapi_session_sync_notifier.cc b/chrome/browser/lacros/sync/crosapi_session_sync_notifier.cc index a219c05..182447b 100644 --- a/chrome/browser/lacros/sync/crosapi_session_sync_notifier.cc +++ b/chrome/browser/lacros/sync/crosapi_session_sync_notifier.cc
@@ -90,14 +90,19 @@ CrosapiSessionSyncNotifier::CrosapiSessionSyncNotifier( sync_sessions::SessionSyncService* session_sync_service, - mojo::Remote<crosapi::mojom::SyncedSessionClient> synced_session_client) - : session_sync_service_(session_sync_service), + mojo::PendingRemote<crosapi::mojom::SyncedSessionClient> + synced_session_client) + : session_sync_service_(std::move(session_sync_service)), synced_session_client_(std::move(synced_session_client)) { - session_updated_subscription_ = - session_sync_service->SubscribeToForeignSessionsChanged( - base::BindRepeating( - &CrosapiSessionSyncNotifier::OnForeignSyncedSessionsUpdated, - base::Unretained(this))); + if (synced_session_client_.version() >= + static_cast<int>(crosapi::mojom::SyncedSessionClient:: + kOnForeignSyncedPhoneSessionsUpdatedMinVersion)) { + session_updated_subscription_ = + session_sync_service->SubscribeToForeignSessionsChanged( + base::BindRepeating( + &CrosapiSessionSyncNotifier::OnForeignSyncedSessionsUpdated, + base::Unretained(this))); + } } CrosapiSessionSyncNotifier::~CrosapiSessionSyncNotifier() = default; @@ -106,6 +111,10 @@ // Fetch sessions sync_sessions::OpenTabsUIDelegate* open_tabs = session_sync_service_->GetOpenTabsUIDelegate(); + if (!open_tabs) { + return; + } + std::vector<const sync_sessions::SyncedSession*> synced_sessions; open_tabs->GetAllForeignSessions(&synced_sessions); std::vector<crosapi::mojom::SyncedSessionPtr> crosapi_synced_phone_sessions =
diff --git a/chrome/browser/lacros/sync/crosapi_session_sync_notifier.h b/chrome/browser/lacros/sync/crosapi_session_sync_notifier.h index b0f5902..ddabc8e9 100644 --- a/chrome/browser/lacros/sync/crosapi_session_sync_notifier.h +++ b/chrome/browser/lacros/sync/crosapi_session_sync_notifier.h
@@ -8,6 +8,7 @@ #include "base/callback_list.h" #include "base/memory/raw_ptr.h" #include "chromeos/crosapi/mojom/synced_session_client.mojom.h" +#include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/remote.h" namespace sync_sessions { @@ -21,7 +22,8 @@ // `session_sync_service` should not be null and should outlive `this`. CrosapiSessionSyncNotifier( sync_sessions::SessionSyncService* session_sync_service, - mojo::Remote<crosapi::mojom::SyncedSessionClient> synced_session_client); + mojo::PendingRemote<crosapi::mojom::SyncedSessionClient> + synced_session_client); CrosapiSessionSyncNotifier(const CrosapiSessionSyncNotifier&) = delete; CrosapiSessionSyncNotifier& operator=(const CrosapiSessionSyncNotifier&) = delete; @@ -35,4 +37,4 @@ base::CallbackListSubscription session_updated_subscription_; }; -#endif // CHROME_BROWSER_LACROS_SYNC_CROSAPI_SESSION_SYNC_NOTIFIER_H_ \ No newline at end of file +#endif // CHROME_BROWSER_LACROS_SYNC_CROSAPI_SESSION_SYNC_NOTIFIER_H_
diff --git a/chrome/browser/lacros/sync/crosapi_session_sync_notifier_unittest.cc b/chrome/browser/lacros/sync/crosapi_session_sync_notifier_unittest.cc index 1a4b80d..1080430a 100644 --- a/chrome/browser/lacros/sync/crosapi_session_sync_notifier_unittest.cc +++ b/chrome/browser/lacros/sync/crosapi_session_sync_notifier_unittest.cc
@@ -121,24 +121,12 @@ ON_CALL(mock_sync_sessions_client_, ShouldSyncURL(_)) .WillByDefault(Return(true)); - // Create mojo pipeline between `remote_synced_session_client_` and - // `fake_synced_session_client_ash_`. - mojo::PendingRemote<crosapi::mojom::SyncedSessionClient> - pending_remote_synced_session_client; - mojo::PendingReceiver<crosapi::mojom::SyncedSessionClient> - pending_receiver_synced_session_client = - pending_remote_synced_session_client - .InitWithNewPipeAndPassReceiver(); - fake_synced_session_client_ash_.BindReceiver( - std::move(pending_receiver_synced_session_client)); - remote_synced_session_client_.Bind( - std::move(pending_remote_synced_session_client)); - // Create object under test. crosapi_session_sync_notifier_ = std::make_unique<CrosapiSessionSyncNotifier>( &mock_session_sync_service_, - std::move(remote_synced_session_client_)); + synced_session_client_receiver_ + .BindNewPipeAndPassRemoteWithVersion()); } base::CallbackListSubscription SubscribeToForeignSessionsChanged( @@ -319,11 +307,11 @@ std::unique_ptr<CrosapiSessionSyncNotifier> crosapi_session_sync_notifier_; syncer::FakeSyncedSessionClientAsh fake_synced_session_client_ash_; + mojo::Receiver<crosapi::mojom::SyncedSessionClient> + synced_session_client_receiver_{&fake_synced_session_client_ash_}; sync_sessions::SyncedSessionTracker synced_session_tracker_; - mojo::Remote<crosapi::mojom::SyncedSessionClient> - remote_synced_session_client_; testing::NiceMock<MockSessionSyncService> mock_session_sync_service_; testing::NiceMock<MockOpenTabsUIDelegate> mock_open_tabs_ui_delegate_;
diff --git a/chrome/browser/lacros/sync/sync_crosapi_manager_lacros.cc b/chrome/browser/lacros/sync/sync_crosapi_manager_lacros.cc index 6b0f93f5..caa0b5c 100644 --- a/chrome/browser/lacros/sync/sync_crosapi_manager_lacros.cc +++ b/chrome/browser/lacros/sync/sync_crosapi_manager_lacros.cc
@@ -90,42 +90,6 @@ std::move(client_remote), sync_user_settings); } -// Returns a unique ptr to a newly created CrosapiSessionSyncNotifier. -// Returns a nullptr if the: -// - ChromeOS Synced Session Sharing is disabled. -// - Crosapi version used is not high enough to include the necessary updates -// made. -// - session sync service for the user's profile cannot be found. -std::unique_ptr<CrosapiSessionSyncNotifier> -MaybeCreateCrosapiSessionSyncNotifier(chromeos::LacrosService* lacros_service, - Profile* profile) { - if (!base::FeatureList::IsEnabled(syncer::kChromeOSSyncedSessionSharing)) { - return nullptr; - } - - if (chromeos::LacrosService::Get() - ->GetInterfaceVersion<crosapi::mojom::SyncService>() < - static_cast<int>( - crosapi::mojom::SyncService::kBindSyncedSessionClientMinVersion)) { - return nullptr; - } - - mojo::Remote<crosapi::mojom::SyncedSessionClient> - synced_session_client_remote; - lacros_service->GetRemote<crosapi::mojom::SyncService>() - ->BindSyncedSessionClient( - synced_session_client_remote.BindNewPipeAndPassReceiver()); - - sync_sessions::SessionSyncService* session_sync_service = - SessionSyncServiceFactory::GetInstance()->GetForProfile(profile); - if (!session_sync_service) { - return nullptr; - } - - return std::make_unique<CrosapiSessionSyncNotifier>( - session_sync_service, std::move(synced_session_client_remote)); -} - } // namespace SyncCrosapiManagerLacros::SyncCrosapiManagerLacros() = default; @@ -146,8 +110,7 @@ DCHECK(!crosapi_session_sync_notifier_); profile->AddObserver(this); - crosapi_session_sync_notifier_ = - MaybeCreateCrosapiSessionSyncNotifier(lacros_service, profile); + MaybeCreateCrosapiSessionSyncNotifier(lacros_service, profile); DCHECK(!sync_explicit_passphrase_client_); sync_explicit_passphrase_client_ = @@ -169,3 +132,47 @@ sync_user_settings_client_.reset(); sync_service->RemoveObserver(this); } + +void SyncCrosapiManagerLacros::MaybeCreateCrosapiSessionSyncNotifier( + chromeos::LacrosService* lacros_service, + Profile* profile) { + if (!base::FeatureList::IsEnabled(syncer::kChromeOSSyncedSessionSharing)) { + return; + } + + if (chromeos::LacrosService::Get() + ->GetInterfaceVersion<crosapi::mojom::SyncService>() < + static_cast<int>( + crosapi::mojom::SyncService::kCreateSyncedSessionClientMinVersion)) { + return; + } + + sync_sessions::SessionSyncService* session_sync_service = + SessionSyncServiceFactory::GetInstance()->GetForProfile(profile); + if (!session_sync_service) { + return; + } + + lacros_service->GetRemote<crosapi::mojom::SyncService>() + ->CreateSyncedSessionClient( + base::BindOnce(&SyncCrosapiManagerLacros::OnCreateSyncedSessionClient, + weak_ptr_factory_.GetWeakPtr(), session_sync_service)); +} + +void SyncCrosapiManagerLacros::OnCreateSyncedSessionClient( + sync_sessions::SessionSyncService* session_sync_service, + mojo::PendingRemote<crosapi::mojom::SyncedSessionClient> pending_remote) { + if (!pending_remote) { + return; + } + + if (pending_remote.version() < + static_cast<int>(crosapi::mojom::SyncedSessionClient:: + kOnSessionSyncEnabledChangedMinVersion)) { + return; + } + + DCHECK(!crosapi_session_sync_notifier_); + crosapi_session_sync_notifier_ = std::make_unique<CrosapiSessionSyncNotifier>( + session_sync_service, std::move(pending_remote)); +}
diff --git a/chrome/browser/lacros/sync/sync_crosapi_manager_lacros.h b/chrome/browser/lacros/sync/sync_crosapi_manager_lacros.h index 7d5af31..a925068 100644 --- a/chrome/browser/lacros/sync/sync_crosapi_manager_lacros.h +++ b/chrome/browser/lacros/sync/sync_crosapi_manager_lacros.h
@@ -7,17 +7,28 @@ #include <memory> +#include "base/memory/weak_ptr.h" #include "chrome/browser/profiles/profile_observer.h" +#include "chromeos/crosapi/mojom/sync.mojom.h" #include "components/sync/driver/sync_service_observer.h" +#include "mojo/public/cpp/bindings/pending_remote.h" class Profile; class SyncExplicitPassphraseClientLacros; class SyncUserSettingsClientLacros; class CrosapiSessionSyncNotifier; +namespace chromeos { +class LacrosService; +} // namespace chromeos + namespace syncer { class SyncService; -} +} // namespace syncer + +namespace sync_sessions { +class SessionSyncService; +} // namespace sync_sessions // Controls lifetime of sync-related Crosapi clients. class SyncCrosapiManagerLacros : public syncer::SyncServiceObserver, @@ -37,6 +48,19 @@ // Note: |this| observes only the main profile. void OnProfileWillBeDestroyed(Profile* profile) override; + // Creates a CrosapiSessionSyncNotifier after asynchronously acquiring a + // PendingRemote from Ash. Exits early if the: + // - ChromeOS Synced Session Sharing is disabled. + // - Crosapi version used is not high enough to include the necessary updates + // made. + // - session sync service for the user's profile cannot be found. + void MaybeCreateCrosapiSessionSyncNotifier( + chromeos::LacrosService* lacros_service, + Profile* profile); + void OnCreateSyncedSessionClient( + sync_sessions::SessionSyncService* session_sync_service, + mojo::PendingRemote<crosapi::mojom::SyncedSessionClient> pending_remote); + // The objects below are created for main profile PostProfileInit() and // destroyed upon main profile SyncService shutdown. std::unique_ptr<SyncExplicitPassphraseClientLacros> @@ -45,6 +69,8 @@ // This object will be destroyed on `OnProfileWillBeDestroyed()` call. std::unique_ptr<CrosapiSessionSyncNotifier> crosapi_session_sync_notifier_; + + base::WeakPtrFactory<SyncCrosapiManagerLacros> weak_ptr_factory_{this}; }; #endif // CHROME_BROWSER_LACROS_SYNC_SYNC_CROSAPI_MANAGER_LACROS_H_
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.cc b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.cc index 022babe..5aa70548 100644 --- a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.cc +++ b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.cc
@@ -100,6 +100,7 @@ history::VisitRow visit_row; visit_row.visit_id = visit_id; visit_row.visit_time = base::Time::Now(); + visit_row.is_known_to_sync = true; auto content_annotations = history::VisitContentAnnotations(); content_annotations.has_url_keyed_image = has_url_keyed_image; history::AnnotatedVisit annotated_visit;
diff --git a/chrome/browser/notifications/win/notification_metrics.cc b/chrome/browser/notifications/win/notification_metrics.cc index 0240306..1485d7f 100644 --- a/chrome/browser/notifications/win/notification_metrics.cc +++ b/chrome/browser/notifications/win/notification_metrics.cc
@@ -70,11 +70,6 @@ status); } -void LogOnDismissedStatus(OnDismissedStatus status) { - base::UmaHistogramEnumeration("Notifications.Windows.OnDismissedStatus", - status); -} - void LogOnFailedStatus(OnFailedStatus status) { base::UmaHistogramEnumeration("Notifications.Windows.OnFailedStatus", status); }
diff --git a/chrome/browser/notifications/win/notification_metrics.h b/chrome/browser/notifications/win/notification_metrics.h index 4186f70c..8dfbaea 100644 --- a/chrome/browser/notifications/win/notification_metrics.h +++ b/chrome/browser/notifications/win/notification_metrics.h
@@ -143,14 +143,6 @@ // These values are persisted to logs. Entries should not be renumbered and // numeric values should never be reused. -enum class OnDismissedStatus { - kSuccess = 0, - kGetDismissalReasonFailed = 1, - kMaxValue = kGetDismissalReasonFailed, -}; - -// These values are persisted to logs. Entries should not be renumbered and -// numeric values should never be reused. enum class OnFailedStatus { kSuccess = 0, kGetErrorCodeFailed = 1, @@ -172,7 +164,6 @@ void LogHandleEventStatus(HandleEventStatus status); void LogActivationStatus(ActivationStatus status); void LogSetReadyCallbackStatus(SetReadyCallbackStatus status); -void LogOnDismissedStatus(OnDismissedStatus status); void LogOnFailedStatus(OnFailedStatus status); } // namespace notifications_uma
diff --git a/chrome/browser/page_load_metrics/integration_tests/metric_integration_test.cc b/chrome/browser/page_load_metrics/integration_tests/metric_integration_test.cc index 1c2f16e3..9d1f824 100644 --- a/chrome/browser/page_load_metrics/integration_tests/metric_integration_test.cc +++ b/chrome/browser/page_load_metrics/integration_tests/metric_integration_test.cc
@@ -7,6 +7,7 @@ #include "base/strings/stringprintf.h" #include "base/test/trace_event_analyzer.h" #include "chrome/browser/ui/browser.h" +#include "chrome/common/chrome_features.h" #include "chrome/common/chrome_switches.h" #include "chrome/test/base/ui_test_utils.h" #include "content/public/browser/render_widget_host_view.h" @@ -35,7 +36,12 @@ using ukm::builders::PageLoad; using ukm::mojom::UkmEntry; -MetricIntegrationTest::MetricIntegrationTest() = default; +MetricIntegrationTest::MetricIntegrationTest() { + // TODO(crbug.com/1394910): Use HTTPS URLs in tests to avoid having to + // disable this feature. + feature_list_.InitAndDisableFeature(features::kHttpsUpgrades); +} + MetricIntegrationTest::~MetricIntegrationTest() = default; void MetricIntegrationTest::SetUpOnMainThread() {
diff --git a/chrome/browser/page_load_metrics/integration_tests/metric_integration_test.h b/chrome/browser/page_load_metrics/integration_tests/metric_integration_test.h index 6bab12cc..65f2a93 100644 --- a/chrome/browser/page_load_metrics/integration_tests/metric_integration_test.h +++ b/chrome/browser/page_load_metrics/integration_tests/metric_integration_test.h
@@ -9,6 +9,7 @@ #include "chrome/test/base/in_process_browser_test.h" #include "base/test/metrics/histogram_tester.h" +#include "base/test/scoped_feature_list.h" #include "components/ukm/test_ukm_recorder.h" namespace base { @@ -165,6 +166,7 @@ const ukm::mojom::UkmEntryPtr GetEntry(); + base::test::ScopedFeatureList feature_list_; absl::optional<ukm::TestAutoSetUkmRecorder> ukm_recorder_; absl::optional<base::HistogramTester> histogram_tester_; };
diff --git a/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer_browsertest.cc index 40a1390..5355bc5 100644 --- a/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer_browsertest.cc +++ b/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer_browsertest.cc
@@ -25,6 +25,7 @@ #include "components/ukm/test_ukm_recorder.h" #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" +#include "content/public/test/content_mock_cert_verifier.h" #include "content/public/test/fenced_frame_test_util.h" #include "content/public/test/prerender_test_util.h" #include "content/public/test/test_utils.h" @@ -78,6 +79,7 @@ void SetUpOnMainThread() override { host_resolver()->AddRule("accounts-google.com", "127.0.0.1"); + LookalikeTestHelper::SetUpLookalikeTestParams(); } @@ -85,8 +87,24 @@ LookalikeTestHelper::TearDownLookalikeTestParams(); } + void SetUpCommandLine(base::CommandLine* command_line) override { + mock_cert_verifier_.SetUpCommandLine(command_line); + } + + void SetUpInProcessBrowserTestFixture() override { + mock_cert_verifier_.SetUpInProcessBrowserTestFixture(); + } + + void TearDownInProcessBrowserTestFixture() override { + mock_cert_verifier_.TearDownInProcessBrowserTestFixture(); + } + protected: void StartHttpsServer(net::EmbeddedTestServer::ServerCertificate cert) { + if (cert == net::EmbeddedTestServer::CERT_OK) { + mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK); + } + https_test_server_ = std::make_unique<net::EmbeddedTestServer>( net::EmbeddedTestServer::TYPE_HTTPS); https_test_server_->SetSSLConfig(cert); @@ -152,6 +170,7 @@ std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder_; std::unique_ptr<net::EmbeddedTestServer> https_test_server_; std::unique_ptr<net::EmbeddedTestServer> http_test_server_; + content::ContentMockCertVerifier mock_cert_verifier_; }; IN_PROC_BROWSER_TEST_F(SecurityStatePageLoadMetricsBrowserTest, Simple_Https) { @@ -239,8 +258,7 @@ IN_PROC_BROWSER_TEST_F(SecurityStatePageLoadMetricsBrowserTest, SafetyTipSiteEngagement) { const std::string kSiteEngagementHistogramPrefix = "Security.SiteEngagement."; - - StartHttpServer(); + StartHttpsServer(net::EmbeddedTestServer::CERT_OK); struct TestCase { // The URL to navigate to. @@ -248,8 +266,9 @@ // If true, url is expected to show a safety tip. bool expect_safety_tip; } kTestCases[] = { - {http_test_server()->GetURL("/simple.html"), false}, - {http_test_server()->GetURL("accounts-google.com", "/simple.html"), true}, + {https_test_server()->GetURL("/simple.html"), false}, + {https_test_server()->GetURL("accounts-google.com", "/simple.html"), + true}, }; // The histogram should be recorded regardless of whether the page is flagged
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc index 2156ff1..15c3a09dd 100644 --- a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc +++ b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
@@ -185,7 +185,9 @@ scoped_feature_list_.InitWithFeatures( {ukm::kUkmFeature, blink::features::kPortals, blink::features::kPortalsCrossOrigin}, - {}); + // TODO(crbug.com/1394910): Use HTTPS URLs in tests to avoid having to + // disable this feature. + {features::kHttpsUpgrades}); } PageLoadMetricsBrowserTest(const PageLoadMetricsBrowserTest&) = delete;
diff --git a/chrome/browser/performance_manager/chrome_browser_main_extra_parts_performance_manager.cc b/chrome/browser/performance_manager/chrome_browser_main_extra_parts_performance_manager.cc index 4399ab2..8c554c0 100644 --- a/chrome/browser/performance_manager/chrome_browser_main_extra_parts_performance_manager.cc +++ b/chrome/browser/performance_manager/chrome_browser_main_extra_parts_performance_manager.cc
@@ -67,6 +67,7 @@ #if !BUILDFLAG(IS_ANDROID) #include "base/power_monitor/battery_state_sampler.h" #include "chrome/browser/performance_manager/mechanisms/page_freezer.h" +#include "chrome/browser/performance_manager/policies/heuristic_memory_saver_policy.h" #include "chrome/browser/performance_manager/policies/high_efficiency_mode_policy.h" #include "chrome/browser/performance_manager/policies/page_discarding_helper.h" #include "chrome/browser/performance_manager/policies/page_freezing_policy.h" @@ -178,9 +179,23 @@ if (base::FeatureList::IsEnabled( performance_manager::features::kHighEfficiencyModeAvailable)) { - graph->PassToGraph( - std::make_unique< - performance_manager::policies::HighEfficiencyModePolicy>()); + if (base::FeatureList::IsEnabled( + performance_manager::features::kHeuristicMemorySaver)) { + graph->PassToGraph( + std::make_unique< + performance_manager::policies::HeuristicMemorySaverPolicy>( + performance_manager::features:: + kHeuristicMemorySaverAvailableMemoryThresholdPercent.Get(), + base::Seconds(performance_manager::features:: + kHeuristicMemorySaverHeartbeatSeconds.Get()), + base::Minutes( + performance_manager::features:: + kHeuristicMemorySaverMinimumMinutesInBackground.Get()))); + } else { + graph->PassToGraph( + std::make_unique< + performance_manager::policies::HighEfficiencyModePolicy>()); + } } #endif // !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/performance_manager/metrics/page_timeline_monitor.cc b/chrome/browser/performance_manager/metrics/page_timeline_monitor.cc index 8bc8ca0..641d5e5 100644 --- a/chrome/browser/performance_manager/metrics/page_timeline_monitor.cc +++ b/chrome/browser/performance_manager/metrics/page_timeline_monitor.cc
@@ -22,6 +22,7 @@ #include "services/metrics/public/cpp/ukm_recorder.h" #if !BUILDFLAG(IS_ANDROID) +#include "chrome/browser/performance_manager/policies/heuristic_memory_saver_policy.h" #include "chrome/browser/performance_manager/policies/high_efficiency_mode_policy.h" #endif // !BUILDFLAG(IS_ANDROID) @@ -131,16 +132,9 @@ page_live_state_data->IsConnectedToBluetoothDevice(); } - ukm::builders::PerformanceManager_PageTimelineState(source_id) - .SetSliceId(slice_id) -#if !BUILDFLAG(IS_ANDROID) - .SetHighEfficiencyMode(performance_manager::policies:: - HighEfficiencyModePolicy::GetInstance() && - performance_manager::policies:: - HighEfficiencyModePolicy::GetInstance() - ->IsHighEfficiencyDiscardingEnabled()) - .SetBatterySaverMode(battery_saver_enabled_) -#endif // !BUILDFLAG(IS_ANDROID) + ukm::builders::PerformanceManager_PageTimelineState builder(source_id); + + builder.SetSliceId(slice_id) .SetIsActiveTab(is_active_tab) .SetTimeSinceLastSlice(ukm::GetSemanticBucketMinForDurationTiming( time_since_last_slice.InMilliseconds())) @@ -159,8 +153,21 @@ .SetIsConnectedToDevice(is_connected_to_device) .SetIsPlayingAudio(page_node->IsAudible()) .SetResidentSetSize(page_node->EstimateResidentSetSize()) - .SetTabId(curr_info->tab_id) - .Record(ukm::UkmRecorder::Get()); + .SetTabId(curr_info->tab_id); + +#if !BUILDFLAG(IS_ANDROID) + bool high_efficiency_mode_active = + (policies::HighEfficiencyModePolicy::GetInstance() && + policies::HighEfficiencyModePolicy::GetInstance() + ->IsHighEfficiencyDiscardingEnabled()) || + (policies::HeuristicMemorySaverPolicy::GetInstance() && + policies::HeuristicMemorySaverPolicy::GetInstance()->IsActive()); + + builder.SetHighEfficiencyMode(high_efficiency_mode_active) + .SetBatterySaverMode(battery_saver_enabled_); +#endif // !BUILDFLAG(IS_ANDROID) + + builder.Record(ukm::UkmRecorder::Get()); } }
diff --git a/chrome/browser/performance_manager/policies/heuristic_memory_saver_policy.cc b/chrome/browser/performance_manager/policies/heuristic_memory_saver_policy.cc new file mode 100644 index 0000000..9d26997 --- /dev/null +++ b/chrome/browser/performance_manager/policies/heuristic_memory_saver_policy.cc
@@ -0,0 +1,92 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/performance_manager/policies/heuristic_memory_saver_policy.h" + +#include "chrome/browser/performance_manager/policies/page_discarding_helper.h" + +namespace performance_manager::policies { + +namespace { +HeuristicMemorySaverPolicy* g_heuristic_memory_saver_policy = nullptr; +} // namespace + +HeuristicMemorySaverPolicy::HeuristicMemorySaverPolicy( + uint64_t pmf_threshold_percent, + base::TimeDelta heartbeat_interval, + base::TimeDelta minimum_time_in_background, + AvailableMemoryCallback available_memory_cb, + TotalMemoryCallback total_memory_cb) + : pmf_threshold_percent_(pmf_threshold_percent), + heartbeat_interval_(heartbeat_interval), + minimum_time_in_background_(minimum_time_in_background), + available_memory_cb_(available_memory_cb), + total_memory_cb_(total_memory_cb) { + CHECK(!g_heuristic_memory_saver_policy); + g_heuristic_memory_saver_policy = this; +} + +HeuristicMemorySaverPolicy::~HeuristicMemorySaverPolicy() { + CHECK_EQ(this, g_heuristic_memory_saver_policy); + g_heuristic_memory_saver_policy = nullptr; +} + +// static +HeuristicMemorySaverPolicy* HeuristicMemorySaverPolicy::GetInstance() { + return g_heuristic_memory_saver_policy; +} + +// GraphOwned: +void HeuristicMemorySaverPolicy::OnPassedToGraph(Graph* graph) { + graph_ = graph; +} + +void HeuristicMemorySaverPolicy::OnTakenFromGraph(Graph* graph) { + SetActive(false); + graph_ = nullptr; +} + +void HeuristicMemorySaverPolicy::SetActive(bool active) { + is_active_ = active; + + if (is_active_) { + heartbeat_timer_.Start( + FROM_HERE, heartbeat_interval_, + base::BindRepeating(&HeuristicMemorySaverPolicy::OnHeartbeatCallback, + base::Unretained(this))); + } else { + heartbeat_timer_.Stop(); + } +} + +bool HeuristicMemorySaverPolicy::IsActive() const { + return is_active_; +} + +void HeuristicMemorySaverPolicy::OnHeartbeatCallback() { + uint64_t available_memory = available_memory_cb_.Run(); + uint64_t total_physical_memory = total_memory_cb_.Run(); + + if (static_cast<float>(available_memory) / + static_cast<float>(total_physical_memory) * 100.f < + static_cast<float>(pmf_threshold_percent_)) { + PageDiscardingHelper::GetFromGraph(graph_)->DiscardAPage( + /*post_discard_cb=*/base::DoNothing(), + /*discard_reason=*/::mojom::LifecycleUnitDiscardReason::PROACTIVE, + /*minimum_time_in_background=*/minimum_time_in_background_); + } +} + +// static +uint64_t +HeuristicMemorySaverPolicy::DefaultGetAmountOfAvailablePhysicalMemory() { + return base::SysInfo::AmountOfAvailablePhysicalMemory(); +} + +// static +uint64_t HeuristicMemorySaverPolicy::DefaultGetAmountOfPhysicalMemory() { + return base::SysInfo::AmountOfPhysicalMemory(); +} + +} // namespace performance_manager::policies
diff --git a/chrome/browser/performance_manager/policies/heuristic_memory_saver_policy.h b/chrome/browser/performance_manager/policies/heuristic_memory_saver_policy.h new file mode 100644 index 0000000..ebc4b7a --- /dev/null +++ b/chrome/browser/performance_manager/policies/heuristic_memory_saver_policy.h
@@ -0,0 +1,72 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_POLICIES_HEURISTIC_MEMORY_SAVER_POLICY_H_ +#define CHROME_BROWSER_PERFORMANCE_MANAGER_POLICIES_HEURISTIC_MEMORY_SAVER_POLICY_H_ + +#include "base/system/sys_info.h" +#include "base/timer/timer.h" +#include "components/performance_manager/public/graph/graph.h" + +namespace performance_manager::policies { + +// A memory saver policy that discards a tab that has been in the background for +// at least X amount of time, as long as the percentage of available system +// memory is smaller than Y, checking at a frequency of Z. X, Y, and Z being +// parameters to the policy. +class HeuristicMemorySaverPolicy : public GraphOwned { + public: + using AvailableMemoryCallback = base::RepeatingCallback<uint64_t()>; + using TotalMemoryCallback = base::RepeatingCallback<uint64_t()>; + // `pmf_threshold_percent`: the amount of free memory this policy tries to + // maintain, i.e. it will start discarding when the percentage available + // memory < pmf_threshold_percent + // `heartbeat_interval`: the time interval at which this policy will check + // whether a tab should be discarded. + // `minimum_time_in_background`: the minimum amount of time a page must spend + // in the background before being considered eligible for discarding. + // `available_memory_cb` and `total_memory_cb` allow mocking memory + // measurements for testing. + HeuristicMemorySaverPolicy( + uint64_t pmf_threshold_percent, + base::TimeDelta heartbeat_interval, + base::TimeDelta minimum_time_in_background, + AvailableMemoryCallback available_memory_cb = + base::BindRepeating(&HeuristicMemorySaverPolicy:: + DefaultGetAmountOfAvailablePhysicalMemory), + TotalMemoryCallback total_memory_cb = base::BindRepeating( + &HeuristicMemorySaverPolicy::DefaultGetAmountOfPhysicalMemory)); + ~HeuristicMemorySaverPolicy() override; + + static HeuristicMemorySaverPolicy* GetInstance(); + + // GraphOwned: + void OnPassedToGraph(Graph* graph) override; + void OnTakenFromGraph(Graph* graph) override; + + void SetActive(bool enabled); + bool IsActive() const; + + private: + void OnHeartbeatCallback(); + + static uint64_t DefaultGetAmountOfAvailablePhysicalMemory(); + static uint64_t DefaultGetAmountOfPhysicalMemory(); + + uint64_t pmf_threshold_percent_; + base::TimeDelta heartbeat_interval_; + base::TimeDelta minimum_time_in_background_; + + bool is_active_ = false; + base::RepeatingTimer heartbeat_timer_; + + AvailableMemoryCallback available_memory_cb_; + TotalMemoryCallback total_memory_cb_; + + raw_ptr<Graph> graph_ = nullptr; +}; + +} // namespace performance_manager::policies + +#endif // CHROME_BROWSER_PERFORMANCE_MANAGER_POLICIES_HEURISTIC_MEMORY_SAVER_POLICY_H_
diff --git a/chrome/browser/performance_manager/policies/heuristic_memory_saver_policy_unittest.cc b/chrome/browser/performance_manager/policies/heuristic_memory_saver_policy_unittest.cc new file mode 100644 index 0000000..c666eb0 --- /dev/null +++ b/chrome/browser/performance_manager/policies/heuristic_memory_saver_policy_unittest.cc
@@ -0,0 +1,159 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/performance_manager/policies/heuristic_memory_saver_policy.h" + +#include "base/test/scoped_feature_list.h" +#include "chrome/browser/performance_manager/test_support/page_discarding_utils.h" +#include "components/performance_manager/public/features.h" + +namespace performance_manager::policies { + +const uint64_t kDefaultAvailableMemoryValue = 60; +const uint64_t kDefaultTotalMemoryValue = 100; + +const base::TimeDelta kDefaultHeartbeatInterval = base::Seconds(10); +const base::TimeDelta kDefaultMinimumTimeInBackground = base::Seconds(11); + +class HeuristicMemorySaverPolicyTest + : public testing::GraphTestHarnessWithMockDiscarder { + protected: + void SetUp() override { + testing::GraphTestHarnessWithMockDiscarder::SetUp(); + + feature_list_.InitWithFeatures( + /*enabled_features=*/ + {performance_manager::features::kHighEfficiencyModeAvailable, + performance_manager::features::kHeuristicMemorySaver}, + /*disabled_features=*/{}); + // This is usually called when the profile is created. Fake it here since it + // doesn't happen in tests. + PageDiscardingHelper::GetFromGraph(graph())->SetNoDiscardPatternsForProfile( + static_cast<PageNode*>(page_node())->GetBrowserContextID(), {}); + } + + void TearDown() override { + graph()->TakeFromGraph(policy_); + testing::GraphTestHarnessWithMockDiscarder::TearDown(); + } + + // Creates the policy by forwarding it the arguments and passing it to the + // graph, with a default set of functions for memory measurements that + // always return 60 and 100. + void CreatePolicy( + uint64_t pmf_threshold_percent, + base::TimeDelta heartbeat_interval, + base::TimeDelta minimum_time_in_background, + HeuristicMemorySaverPolicy::AvailableMemoryCallback + available_memory_callback = base::BindRepeating([]() { + return kDefaultAvailableMemoryValue; + }), + HeuristicMemorySaverPolicy::TotalMemoryCallback total_memory_callback = + base::BindRepeating([]() { return kDefaultTotalMemoryValue; })) { + auto policy = std::make_unique<HeuristicMemorySaverPolicy>( + pmf_threshold_percent, heartbeat_interval, minimum_time_in_background, + available_memory_callback, total_memory_callback); + policy_ = policy.get(); + graph()->PassToGraph(std::move(policy)); + } + + PageNodeImpl* CreateOtherPageNode() { + other_process_node_ = CreateNode<performance_manager::ProcessNodeImpl>(); + other_page_node_ = CreateNode<performance_manager::PageNodeImpl>(); + other_main_frame_node_ = CreateFrameNodeAutoId(other_process_node_.get(), + other_page_node_.get()); + other_main_frame_node_->SetIsCurrent(true); + testing::MakePageNodeDiscardable(other_page_node_.get(), task_env()); + + return other_page_node_.get(); + } + + HeuristicMemorySaverPolicy* policy() { return policy_; } + + private: + // Owned by the graph. + raw_ptr<HeuristicMemorySaverPolicy> policy_; + + performance_manager::TestNodeWrapper<performance_manager::PageNodeImpl> + other_page_node_; + performance_manager::TestNodeWrapper<performance_manager::ProcessNodeImpl> + other_process_node_; + performance_manager::TestNodeWrapper<performance_manager::FrameNodeImpl> + other_main_frame_node_; + + base::test::ScopedFeatureList feature_list_; +}; + +TEST_F(HeuristicMemorySaverPolicyTest, NoDiscardIfPolicyInactive) { + CreatePolicy(/*pmf_threshold_percent=*/100, + /*heartbeat_interval=*/kDefaultHeartbeatInterval, + /*minimum_time_in_background=*/kDefaultMinimumTimeInBackground); + + policy()->SetActive(false); + + page_node()->SetType(PageType::kTab); + // Toggle visibility so that the page node updates its last visibility timing + // information. + page_node()->SetIsVisible(true); + page_node()->SetIsVisible(false); + + // Advance the time by at least `minimum_time_in_background` + + // `heartbeat_interval`. If a tab is to be discarded, it will be at this + // point. + task_env().FastForwardBy(kDefaultHeartbeatInterval + + kDefaultMinimumTimeInBackground); + // No discard. + ::testing::Mock::VerifyAndClearExpectations(discarder()); +} + +TEST_F(HeuristicMemorySaverPolicyTest, DiscardIfPolicyActive) { + CreatePolicy(/*pmf_threshold_percent=*/100, + /*heartbeat_interval=*/kDefaultHeartbeatInterval, + /*minimum_time_in_background=*/kDefaultMinimumTimeInBackground); + + policy()->SetActive(true); + + page_node()->SetType(PageType::kTab); + // Toggle visibility so that the page node updates its last visibility timing + // information. + page_node()->SetIsVisible(true); + page_node()->SetIsVisible(false); + + // Advance the time by at least `minimum_time_in_background` + task_env().FastForwardBy(kDefaultMinimumTimeInBackground); + // No discard yet. + ::testing::Mock::VerifyAndClearExpectations(discarder()); + + // Advance by at least the heartbeat interval, this should discard the + // now-eligible tab. + EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node())) + .WillOnce(::testing::Return(true)); + task_env().FastForwardBy(kDefaultHeartbeatInterval); + + ::testing::Mock::VerifyAndClearExpectations(discarder()); +} + +TEST_F(HeuristicMemorySaverPolicyTest, NoDiscardIfUnderThreshold) { + CreatePolicy(/*pmf_threshold_percent=*/30, + /*heartbeat_interval=*/kDefaultHeartbeatInterval, + /*minimum_time_in_background=*/kDefaultMinimumTimeInBackground); + + policy()->SetActive(true); + + page_node()->SetType(PageType::kTab); + // Toggle visibility so that the page node updates its last visibility timing + // information. + page_node()->SetIsVisible(true); + page_node()->SetIsVisible(false); + + // Advance the time by at least `minimum_time_in_background` + + // `heartbeat_interval`. If a tab is to be discarded, it will be at this + // point. + task_env().FastForwardBy(kDefaultHeartbeatInterval + + kDefaultMinimumTimeInBackground); + // No discard. + ::testing::Mock::VerifyAndClearExpectations(discarder()); +} + +} // namespace performance_manager::policies
diff --git a/chrome/browser/performance_manager/user_tuning/user_performance_tuning_manager.cc b/chrome/browser/performance_manager/user_tuning/user_performance_tuning_manager.cc index 58c17e7..b47a357 100644 --- a/chrome/browser/performance_manager/user_tuning/user_performance_tuning_manager.cc +++ b/chrome/browser/performance_manager/user_tuning/user_performance_tuning_manager.cc
@@ -10,6 +10,7 @@ #include "base/time/time.h" #include "base/values.h" #include "chrome/browser/performance_manager/metrics/page_timeline_monitor.h" +#include "chrome/browser/performance_manager/policies/heuristic_memory_saver_policy.h" #include "chrome/browser/performance_manager/policies/high_efficiency_mode_policy.h" #include "chrome/browser/performance_manager/policies/page_discarding_helper.h" #include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom-shared.h" @@ -68,12 +69,21 @@ public: void ToggleHighEfficiencyMode(bool enabled) override { performance_manager::PerformanceManager::CallOnGraph( - FROM_HERE, base::BindOnce( - [](bool enabled, performance_manager::Graph* graph) { - policies::HighEfficiencyModePolicy::GetInstance() - ->OnHighEfficiencyModeChanged(enabled); - }, - enabled)); + FROM_HERE, + base::BindOnce( + [](bool enabled, performance_manager::Graph* graph) { + if (base::FeatureList::IsEnabled( + performance_manager::features::kHeuristicMemorySaver)) { + CHECK(policies::HeuristicMemorySaverPolicy::GetInstance()); + policies::HeuristicMemorySaverPolicy::GetInstance()->SetActive( + enabled); + } else { + CHECK(policies::HighEfficiencyModePolicy::GetInstance()); + policies::HighEfficiencyModePolicy::GetInstance() + ->OnHighEfficiencyModeChanged(enabled); + } + }, + enabled)); } ~HighEfficiencyModeToggleDelegateImpl() override = default;
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc index 90c13e61..d6688b9 100644 --- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc +++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -306,6 +306,9 @@ { key::kDefaultThirdPartyStoragePartitioningSetting, prefs::kManagedDefaultThirdPartyStoragePartitioningSetting, base::Value::Type::INTEGER }, + { key::kThirdPartyStoragePartitioningBlockedForOrigins, + prefs::kManagedThirdPartyStoragePartitioningBlockedForOrigins, + base::Value::Type::LIST }, // Policies for all platforms - End #if BUILDFLAG(IS_ANDROID) { key::kAuthAndroidNegotiateAccountType, @@ -1004,7 +1007,7 @@ nullptr, base::Value::Type::BOOLEAN }, { key::kDeviceLoginScreenPrimaryMouseButtonSwitch, - nullptr, + ::prefs::kOwnerPrimaryMouseButtonRight, base::Value::Type::BOOLEAN }, { key::kDeviceLoginScreenDefaultSpokenFeedbackEnabled, nullptr,
diff --git a/chrome/browser/predictors/loading_predictor_browsertest.cc b/chrome/browser/predictors/loading_predictor_browsertest.cc index 5f49d2e..3ae25b1 100644 --- a/chrome/browser/predictors/loading_predictor_browsertest.cc +++ b/chrome/browser/predictors/loading_predictor_browsertest.cc
@@ -38,6 +38,7 @@ #include "chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_manager_factory.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" +#include "chrome/common/chrome_features.h" #include "chrome/common/chrome_switches.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" @@ -376,7 +377,9 @@ {features::kLoadingOnlyLearnHighPriorityResources, features::kLoadingPreconnectToRedirectTarget, features::kNavigationPredictorPreconnectHoldback}, - {}); + // TODO(crbug.com/1394910): Use HTTPS URLs in tests to avoid having to + // disable this feature. + {features::kHttpsUpgrades}); } LoadingPredictorBrowserTest(const LoadingPredictorBrowserTest&) = delete;
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index 89a7fb97..2aeb117f 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc
@@ -307,6 +307,7 @@ #if BUILDFLAG(IS_CHROMEOS_ASH) #include "ash/components/arc/arc_prefs.h" #include "ash/constants/ash_pref_names.h" +#include "ash/public/cpp/ambient/ambient_prefs.h" #include "ash/public/cpp/ash_prefs.h" #include "chrome/browser/apps/app_deduplication_service/app_deduplication_service.h" #include "chrome/browser/apps/app_preload_service/app_preload_service.h" @@ -2148,6 +2149,11 @@ profile_prefs->ClearPref(kHasSeenSmartLockSignInRemovedNotification); #endif // BUILDFLAG(IS_CHROMEOS_ASH) +// Added 03/2023. +#if BUILDFLAG(IS_CHROMEOS_ASH) + ash::ambient::prefs::MigrateDeprecatedPrefs(*profile_prefs); +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + // Added 03/2023 profile_prefs->ClearPref( kGoogleSearchDomainMixingMetricsEmitterLastMetricsTime);
diff --git a/chrome/browser/resources/new_tab_page/modules/history_clusters/page_favicon.ts b/chrome/browser/resources/new_tab_page/modules/history_clusters/page_favicon.ts index c9b8d804..33a4d65 100644 --- a/chrome/browser/resources/new_tab_page/modules/history_clusters/page_favicon.ts +++ b/chrome/browser/resources/new_tab_page/modules/history_clusters/page_favicon.ts
@@ -46,6 +46,11 @@ * fetching higher quality favicons in that case. */ isKnownToSync: Boolean, + + size: { + type: Number, + value: 16, + }, }; } @@ -55,6 +60,7 @@ url: Url; isKnownToSync: boolean; + size: number; //============================================================================ // Helper methods @@ -66,7 +72,8 @@ } return `background-image:${ getFaviconForPageURL( - this.url.url, this.isKnownToSync, '', /** --favicon-size */ 16)}`; + this.url.url, this.isKnownToSync, '', + /** --favicon-size */ this.size)}`; } }
diff --git a/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.html b/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.html index d7d79bf..35753968 100644 --- a/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.html +++ b/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.html
@@ -26,9 +26,10 @@ } #image { - background: red; + background: var(--color-new-tab-page-module-background); border-radius: 25px; overflow: hidden; + position: relative; } :host([large-format]) #image { @@ -53,6 +54,13 @@ width: 100%; } + #image page-favicon { + left: 50%; + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + } + #icon { height: 16px; margin-inline-end: 8px; @@ -125,17 +133,22 @@ </style> <a id="content" href="[[visit.normalizedUrl.url]]"> <div id="image"> - <template is="dom-if" if="[[hasImageUrl_(imageUrl_)]]"> + <template is="dom-if" if="[[hasImageUrl_(imageUrl_)]]" restamp> <img is="cr-auto-img" auto-src="[[imageUrl_.url]]" draggable="false"> </img> </template> + <template is="dom-if" if="[[!hasImageUrl_(imageUrl_)]]" restamp> + <page-favicon url="[[visit.normalizedUrl]]" + is-known-to-sync="[[visit.isKnownToSync]]" size="24"> + </page-favicon> + </template> </div> <div id="text-container"> <div id="title" class="truncate">[[visit.pageTitle]]</div> <div id="info-container"> <page-favicon id="icon" url="[[visit.normalizedUrl]]" - is-known-to-sync="[[visit.isKnownToSync]]"> + is-known-to-sync="[[visit.isKnownToSync]]"> </page-favicon> <div id="label" class="truncate"> [[label_]]</div> <span id="dot"> • </span>
diff --git a/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.ts b/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.ts index a90013dd..aeab617 100644 --- a/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.ts +++ b/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.ts
@@ -41,7 +41,10 @@ }, /* The image url for the tile. */ - imageUrl_: Object, + imageUrl_: { + type: Object, + value: null, + }, smallFormat: { type: Boolean, @@ -64,17 +67,18 @@ private async onVisitUpdated_(): Promise<void> { const visitUrl = this.visit.normalizedUrl; if (visitUrl && this.visit.hasUrlKeyedImage && !this.smallFormat && - loadTimeData.getBoolean('isHistoryClustersImagesEnabled')) { + loadTimeData.getBoolean('isHistoryClustersImagesEnabled') && + this.visit.isKnownToSync) { const result = await ImageServiceBrowserProxy.getInstance().handler.getPageImageUrl( ImageServiceClientId.NtpQuests, visitUrl, {suggestImages: true, optimizationGuideImages: true}); if (result && result.result) { this.imageUrl_ = result.result.imageUrl; + return; } - } else { - this.imageUrl_ = null; } + this.imageUrl_ = null; } private hasImageUrl_(): boolean {
diff --git a/chrome/browser/resources/settings/BUILD.gn b/chrome/browser/resources/settings/BUILD.gn index 854b8f5..411c80b 100644 --- a/chrome/browser/resources/settings/BUILD.gn +++ b/chrome/browser/resources/settings/BUILD.gn
@@ -139,7 +139,6 @@ "privacy_page/do_not_track_toggle.ts", "privacy_page/personalization_options.ts", "privacy_page/preloading_page.ts", - "privacy_page/privacy_guide_promo.ts", "privacy_page/privacy_guide/privacy_guide_completion_fragment.ts", "privacy_page/privacy_guide/privacy_guide_completion_link_row.ts", "privacy_page/privacy_guide/privacy_guide_cookies_fragment.ts", @@ -148,6 +147,7 @@ "privacy_page/privacy_guide/privacy_guide_history_sync_fragment.ts", "privacy_page/privacy_guide/privacy_guide_msbb_fragment.ts", "privacy_page/privacy_guide/privacy_guide_page.ts", + "privacy_page/privacy_guide/privacy_guide_promo.ts", "privacy_page/privacy_guide/privacy_guide_safe_browsing_fragment.ts", "privacy_page/privacy_guide/privacy_guide_welcome_fragment.ts", "privacy_page/privacy_guide/step_indicator.ts", @@ -314,8 +314,8 @@ "prefs/prefs.ts", "prefs/prefs_types.ts", "prefs/pref_util.ts", - "privacy_page/privacy_guide_availability_mixin.ts", "privacy_page/privacy_guide/constants.ts", + "privacy_page/privacy_guide/privacy_guide_availability_mixin.ts", "privacy_page/privacy_guide/privacy_guide_browser_proxy.ts", "privacy_page/privacy_page_browser_proxy.ts", "privacy_page/privacy_sandbox/privacy_sandbox_browser_proxy.ts",
diff --git a/chrome/browser/resources/settings/autofill_page/iban_edit_dialog.html b/chrome/browser/resources/settings/autofill_page/iban_edit_dialog.html index 7e6233cf..34024c9 100644 --- a/chrome/browser/resources/settings/autofill_page/iban_edit_dialog.html +++ b/chrome/browser/resources/settings/autofill_page/iban_edit_dialog.html
@@ -35,6 +35,7 @@ <div slot="title">[[title_]]</div> <div slot="body"> <cr-input id="valueInput" label="$i18n{addPaymentMethodIban}" + on-input="updateSaveIbanButtonEnablement_" value="{{value_}}" autofocus> </cr-input> <cr-input id="nicknameInput" label="$i18n{ibanNickname}" @@ -51,8 +52,7 @@ <cr-button id="cancelButton" class="cancel-button" on-click="onCancelButtonClick_">$i18n{cancel}</cr-button> <cr-button id="saveButton" class="action-button" - on-click="onIbanSaveButtonClick_" - disabled="[[!saveIbanEnabled_(value_)]]"> + on-click="onIbanSaveButtonClick_"> $i18n{save} </cr-button> </div>
diff --git a/chrome/browser/resources/settings/autofill_page/iban_edit_dialog.ts b/chrome/browser/resources/settings/autofill_page/iban_edit_dialog.ts index 145bc941..c677f116 100644 --- a/chrome/browser/resources/settings/autofill_page/iban_edit_dialog.ts +++ b/chrome/browser/resources/settings/autofill_page/iban_edit_dialog.ts
@@ -23,12 +23,7 @@ import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {getTemplate} from './iban_edit_dialog.html.js'; - -/** - * Regular expression for valid IBAN value. - */ -const IBAN_VALID_REGEX: RegExp = new RegExp( - '^[a-zA-Z]{2}[0-9]{2}[a-zA-Z0-9]{4}[0-9]{7}([a-zA-Z0-9]?){0,16}$'); +import {PaymentsManagerImpl, PaymentsManagerProxy} from './payments_manager_proxy.js'; declare global { interface HTMLElementEventMap { @@ -85,6 +80,8 @@ private value_?: string; private nickname_?: string; private title_: string; + private paymentsManager_: PaymentsManagerProxy = + PaymentsManagerImpl.getInstance(); override connectedCallback() { super.connectedCallback(); @@ -94,6 +91,7 @@ this.value_ = this.iban.value; this.nickname_ = this.iban.nickname; } + this.$.saveButton.disabled = true; this.$.dialog.showModal(); } @@ -123,16 +121,24 @@ this.close(); } - private saveIbanEnabled_(): boolean { + private updateSaveIbanButtonEnablement_() { + this.isValidIban().then(isValid => { + this.$.saveButton.disabled = !isValid; + }); + } + + private async isValidIban(): Promise<boolean> { if (!this.value_) { - return false; + return Promise.resolve(false); } // The save button is enabled if the value of the IBAN is invalid (after // removing all whitespace from it). - const ibanWithoutWhitespace = this.value_.replace(/\s/g, ''); - return !!IBAN_VALID_REGEX.test(ibanWithoutWhitespace!); + const isValid = await this.paymentsManager_.isValidIban( + this.value_!.replace(/\s/g, '')); + return isValid; } + /** * @param nickname of the IBAN, undefined when not set. * @return nickname character length.
diff --git a/chrome/browser/resources/settings/autofill_page/payments_manager_proxy.ts b/chrome/browser/resources/settings/autofill_page/payments_manager_proxy.ts index ba07592..444e657c 100644 --- a/chrome/browser/resources/settings/autofill_page/payments_manager_proxy.ts +++ b/chrome/browser/resources/settings/autofill_page/payments_manager_proxy.ts
@@ -29,6 +29,9 @@ */ getIbanList(): Promise<chrome.autofillPrivate.IbanEntry[]>; + /** @param ibanValue Returns true if the given ibanValue is valid. */ + isValidIban(ibanValue: string): Promise<boolean>; + /** @param guid The GUID of the credit card to remove. */ removeCreditCard(guid: string): void; @@ -106,6 +109,10 @@ return chrome.autofillPrivate.getIbanList(); } + isValidIban(ibanValue: string) { + return chrome.autofillPrivate.isValidIban(ibanValue); + } + removeCreditCard(guid: string) { chrome.autofillPrivate.removeEntry(guid); }
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.ts b/chrome/browser/resources/settings/basic_page/basic_page.ts index 86d6b7a..6ee8055 100644 --- a/chrome/browser/resources/settings/basic_page/basic_page.ts +++ b/chrome/browser/resources/settings/basic_page/basic_page.ts
@@ -11,7 +11,7 @@ import 'chrome://resources/cr_elements/cr_shared_vars.css.js'; import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js'; import '../appearance_page/appearance_page.js'; -import '../privacy_page/privacy_guide_promo.js'; +import '../privacy_page/privacy_guide/privacy_guide_promo.js'; import '../privacy_page/privacy_page.js'; import '../safety_check_page/safety_check_page.js'; import '../autofill_page/autofill_page.js'; @@ -44,8 +44,8 @@ import {PageVisibility} from '../page_visibility.js'; import {PerformanceBrowserProxy, PerformanceBrowserProxyImpl} from '../performance_page/performance_browser_proxy.js'; import {PrefsMixin} from '../prefs/prefs_mixin.js'; +import {PrivacyGuideAvailabilityMixin} from '../privacy_page/privacy_guide/privacy_guide_availability_mixin.js'; import {MAX_PRIVACY_GUIDE_PROMO_IMPRESSION, PrivacyGuideBrowserProxy, PrivacyGuideBrowserProxyImpl} from '../privacy_page/privacy_guide/privacy_guide_browser_proxy.js'; -import {PrivacyGuideAvailabilityMixin} from '../privacy_page/privacy_guide_availability_mixin.js'; import {routes} from '../route.js'; import {Route, RouteObserverMixin, Router} from '../router.js'; import {getSearchManager, SearchResult} from '../search_settings.js';
diff --git a/chrome/browser/resources/settings/chromeos/lazy_load.ts b/chrome/browser/resources/settings/chromeos/lazy_load.ts index 1883a698..0760b420 100644 --- a/chrome/browser/resources/settings/chromeos/lazy_load.ts +++ b/chrome/browser/resources/settings/chromeos/lazy_load.ts
@@ -103,6 +103,7 @@ export {CrostiniBrowserProxy, CrostiniBrowserProxyImpl} from './crostini_page/crostini_browser_proxy.js'; export {TimeZoneAutoDetectMethod} from './date_time_page/date_time_types.js'; export {TimeZoneBrowserProxyImpl} from './date_time_page/timezone_browser_proxy.js'; +export {TimezoneSelectorElement} from './date_time_page/timezone_selector.js'; export {CROSTINI_TYPE, GuestOsBrowserProxy, GuestOsBrowserProxyImpl, GuestOsSharedUsbDevice, PLUGIN_VM_TYPE} from './guest_os/guest_os_browser_proxy.js'; export {SettingsAudioAndCaptionsPageElement} from './os_a11y_page/audio_and_captions_page.js'; export {SettingsTtsSubpageElement} from './os_a11y_page/tts_subpage.js';
diff --git a/chrome/browser/resources/settings/privacy_page/OWNERS b/chrome/browser/resources/settings/privacy_page/OWNERS index 5ac5a48..f50b84e 100644 --- a/chrome/browser/resources/settings/privacy_page/OWNERS +++ b/chrome/browser/resources/settings/privacy_page/OWNERS
@@ -1,4 +1,3 @@ sauski@google.com per-file cookies_page*=rainhard@chromium.org -per-file privacy_guide_promo*=rainhard@chromium.org
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_guide_availability_mixin.ts b/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_availability_mixin.ts similarity index 95% rename from chrome/browser/resources/settings/privacy_page/privacy_guide_availability_mixin.ts rename to chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_availability_mixin.ts index bcfa02c3..1edab65 100644 --- a/chrome/browser/resources/settings/privacy_page/privacy_guide_availability_mixin.ts +++ b/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_availability_mixin.ts
@@ -10,8 +10,8 @@ import {WebUiListenerMixin, WebUiListenerMixinInterface} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js'; import {dedupingMixin, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; -import {loadTimeData} from '../i18n_setup.js'; -import {SyncStatus} from '../people_page/sync_browser_proxy.js'; +import {loadTimeData} from '../../i18n_setup.js'; +import {SyncStatus} from '../../people_page/sync_browser_proxy.js'; type Constructor<T> = new (...args: any[]) => T;
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_page.ts b/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_page.ts index 83994f1..ff5d4d8 100644 --- a/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_page.ts +++ b/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_page.ts
@@ -32,13 +32,13 @@ import {SyncBrowserProxy, SyncBrowserProxyImpl, SyncStatus} from '../../people_page/sync_browser_proxy.js'; import {PrefsMixin} from '../../prefs/prefs_mixin.js'; import {CrSettingsPrefs} from '../../prefs/prefs_types.js'; -import {PrivacyGuideAvailabilityMixin} from '../../privacy_page/privacy_guide_availability_mixin.js'; import {SafeBrowsingSetting} from '../../privacy_page/security_page.js'; import {routes} from '../../route.js'; import {Route, RouteObserverMixin, Router} from '../../router.js'; import {CookiePrimarySetting} from '../../site_settings/site_settings_prefs_browser_proxy.js'; import {PrivacyGuideStep} from './constants.js'; +import {PrivacyGuideAvailabilityMixin} from './privacy_guide_availability_mixin.js'; import {getTemplate} from './privacy_guide_page.html.js'; import {StepIndicatorModel} from './step_indicator.js';
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_guide_promo.html b/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_promo.html similarity index 100% rename from chrome/browser/resources/settings/privacy_page/privacy_guide_promo.html rename to chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_promo.html
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_guide_promo.ts b/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_promo.ts similarity index 88% rename from chrome/browser/resources/settings/privacy_page/privacy_guide_promo.ts rename to chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_promo.ts index 764b669f..5bc397c 100644 --- a/chrome/browser/resources/settings/privacy_page/privacy_guide_promo.ts +++ b/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_promo.ts
@@ -9,10 +9,10 @@ */ import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; -import {MetricsBrowserProxy, MetricsBrowserProxyImpl, PrivacyGuideInteractions} from '../metrics_browser_proxy.js'; -import {PrefsMixin} from '../prefs/prefs_mixin.js'; -import {routes} from '../route.js'; -import {Router} from '../router.js'; +import {MetricsBrowserProxy, MetricsBrowserProxyImpl, PrivacyGuideInteractions} from '../../metrics_browser_proxy.js'; +import {PrefsMixin} from '../../prefs/prefs_mixin.js'; +import {routes} from '../../route.js'; +import {Router} from '../../router.js'; import {getTemplate} from './privacy_guide_promo.html.js';
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.ts b/chrome/browser/resources/settings/privacy_page/privacy_page.ts index e303f97..646b7a97 100644 --- a/chrome/browser/resources/settings/privacy_page/privacy_page.ts +++ b/chrome/browser/resources/settings/privacy_page/privacy_page.ts
@@ -39,7 +39,7 @@ import {ChooserType, ContentSettingsTypes, CookieControlsMode, NotificationSetting} from '../site_settings/constants.js'; import {NotificationPermission, SiteSettingsPrefsBrowserProxy, SiteSettingsPrefsBrowserProxyImpl} from '../site_settings/site_settings_prefs_browser_proxy.js'; -import {PrivacyGuideAvailabilityMixin} from './privacy_guide_availability_mixin.js'; +import {PrivacyGuideAvailabilityMixin} from './privacy_guide/privacy_guide_availability_mixin.js'; import {getTemplate} from './privacy_page.html.js'; import {PrivacyPageBrowserProxy, PrivacyPageBrowserProxyImpl} from './privacy_page_browser_proxy.js';
diff --git a/chrome/browser/storage_access_api/api_browsertest.cc b/chrome/browser/storage_access_api/api_browsertest.cc index b68e234..4b200383 100644 --- a/chrome/browser/storage_access_api/api_browsertest.cc +++ b/chrome/browser/storage_access_api/api_browsertest.cc
@@ -55,6 +55,8 @@ namespace { constexpr char kHostA[] = "a.test"; +constexpr char kOriginA[] = "https://a.test"; +constexpr char kUrlA[] = "https://a.test/random.path"; constexpr char kHostASubdomain[] = "subdomain.a.test"; constexpr char kHostB[] = "b.test"; constexpr char kHostBSubdomain[] = "subdomain.b.test"; @@ -1263,10 +1265,11 @@ class StorageAccessAPIEnterprisePolicyBrowserTest : public StorageAccessAPIBaseBrowserTest, - public testing::WithParamInterface<std::tuple<ContentSetting, bool>> { + public testing::WithParamInterface< + std::tuple<const char*, ContentSetting, bool>> { public: StorageAccessAPIEnterprisePolicyBrowserTest() - : StorageAccessAPIBaseBrowserTest(std::get<1>(GetParam())) {} + : StorageAccessAPIBaseBrowserTest(std::get<2>(GetParam())) {} void SetUpInProcessBrowserTestFixture() override { policy::PolicyTest::SetUpInProcessBrowserTestFixture(); @@ -1274,25 +1277,38 @@ SetPolicy(&policies, policy::key::kDefaultThirdPartyStoragePartitioningSetting, base::Value(GetContentSetting())); + base::Value::List origins; + origins.Append(base::Value(GetContentOrigin())); + SetPolicy(&policies, + policy::key::kThirdPartyStoragePartitioningBlockedForOrigins, + base::Value(std::move(origins))); UpdateProviderPolicy(policies); } bool ExpectPartitionedStorage() const { + // We only expect storage to be partitioned if the base::Feature is enabled + // and the default content setting isn't BLOCK and the origin block list + // doesn't match a.test (paths are ignored) return IsStoragePartitioned() && - GetContentSetting() != CONTENT_SETTING_BLOCK; + GetContentSetting() != CONTENT_SETTING_BLOCK && + GetContentOrigin() != kHostA && GetContentOrigin() != kOriginA && + GetContentOrigin() != kUrlA; } private: - ContentSetting GetContentSetting() const { return std::get<0>(GetParam()); } + ContentSetting GetContentSetting() const { return std::get<1>(GetParam()); } + const char* GetContentOrigin() const { return std::get<0>(GetParam()); } }; INSTANTIATE_TEST_SUITE_P( /*no prefix*/, StorageAccessAPIEnterprisePolicyBrowserTest, - testing::Combine(testing::Values(CONTENT_SETTING_DEFAULT, - CONTENT_SETTING_ALLOW, - CONTENT_SETTING_BLOCK), - testing::Bool())); + testing::Combine( + testing::Values(kHostA, kOriginA, kUrlA, kHostASubdomain, kHostB, ""), + testing::Values(CONTENT_SETTING_DEFAULT, + CONTENT_SETTING_ALLOW, + CONTENT_SETTING_BLOCK), + testing::Bool())); IN_PROC_BROWSER_TEST_P(StorageAccessAPIEnterprisePolicyBrowserTest, PartitionedStorage) {
diff --git a/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc b/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc index 0dd98d5..54a94c7 100644 --- a/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc +++ b/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc
@@ -34,6 +34,10 @@ namespace { // Not an enum class to ease cast to int. namespace syncable_prefs_ids { +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. +// Please also add new entries to `SyncablePref` enum in +// tools/metrics/histograms/enums.xml. enum { // Starts with 100000 to avoid clash with prefs listed in // common_syncable_prefs_database.cc and
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fre/SigninFirstRunView.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fre/SigninFirstRunView.java index cce55fd..49db448 100644 --- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fre/SigninFirstRunView.java +++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fre/SigninFirstRunView.java
@@ -104,9 +104,14 @@ } /** Updates the title and the subtitle for UI variations on native and policy load. **/ - void applyVariationsExperiment() { - Pair<Integer, Integer> titleAndSubtitleId = - FREMobileIdentityConsistencyFieldTrial.getVariationTitleAndSubtitle(); + void applyVariationsExperiment(boolean useExperimentalStrings) { + Pair<Integer, Integer> titleAndSubtitleId; + if (useExperimentalStrings) { + titleAndSubtitleId = + FREMobileIdentityConsistencyFieldTrial.getVariationTitleAndSubtitle(); + } else { + titleAndSubtitleId = new Pair(R.string.fre_welcome, 0); + } mTitle.setText(titleAndSubtitleId.first); mTitle.setVisibility(VISIBLE); if (titleAndSubtitleId.second != 0) {
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fre/SigninFirstRunViewBinder.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fre/SigninFirstRunViewBinder.java index dd5be93f..e11de26 100644 --- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fre/SigninFirstRunViewBinder.java +++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fre/SigninFirstRunViewBinder.java
@@ -122,7 +122,12 @@ private static void updateVisibility(SigninFirstRunView view, PropertyModel model) { final boolean showInitialLoadProgressSpinner = model.get(SigninFirstRunProperties.SHOW_INITIAL_LOAD_PROGRESS_SPINNER); - if (!showInitialLoadProgressSpinner) view.applyVariationsExperiment(); + final boolean isSelectedAccountSupervised = + model.get(SigninFirstRunProperties.IS_SELECTED_ACCOUNT_SUPERVISED); + final boolean hasPolicy = model.get(SigninFirstRunProperties.FRE_POLICY) != null; + if (!showInitialLoadProgressSpinner) { + view.applyVariationsExperiment(!isSelectedAccountSupervised && !hasPolicy); + } final int selectedAccountVisibility = !showInitialLoadProgressSpinner && model.get(SigninFirstRunProperties.SELECTED_ACCOUNT_DATA) != null @@ -130,8 +135,6 @@ ? View.VISIBLE : View.GONE; view.getSelectedAccountView().setVisibility(selectedAccountVisibility); - final boolean isSelectedAccountSupervised = - model.get(SigninFirstRunProperties.IS_SELECTED_ACCOUNT_SUPERVISED); view.getExpandIconView().setVisibility( selectedAccountVisibility == View.VISIBLE && isSelectedAccountSupervised ? View.INVISIBLE
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinator.java index e48e9af..8412b338 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinator.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinator.java
@@ -149,6 +149,8 @@ } } + // Reset background alpha, in case the IPH onDismiss callback doesn't fire. + mMediator.setBackgroundAlpha(255); mMediator.updateButton(buttonData); } @@ -252,12 +254,23 @@ } }); - ViewRectProvider viewRectProvider = new ViewRectProvider(mView); + // We want this IPH highlight to be on the same position as the button's background which is + // an ImageView separate from the button's ListMenuButton. IPH highlights are implemented as + // a drawable set to the view's background (something like: + // backgroundImageView.setBackground(drawable)). If we try to highlight the background's + // ImageView nothing will be shown, because the highlight is obstructed by the image. Set + // callbacks to make the background image transparent so the highlight is visible. This gets + // reset once the IPH is dismissed. + iphCommandBuilder.setOnShowCallback(() -> mMediator.setBackgroundAlpha(0)); + iphCommandBuilder.setOnDismissCallback(() -> mMediator.setBackgroundAlpha(255)); + + ViewRectProvider viewRectProvider = new ViewRectProvider( + mView.getBackgroundView() == null ? mView : mView.getBackgroundView()); viewRectProvider.setIncludePadding(false); - highlightParams.setBoundsRespectPadding(false); + highlightParams.setBoundsRespectPadding(true); iphCommandBuilder.setAnchorView( - mView.getButtonView() == null ? mView : mView.getButtonView()); + mView.getBackgroundView() == null ? mView : mView.getBackgroundView()); iphCommandBuilder.setViewRectProvider(viewRectProvider); iphCommandBuilder.setHighlightParams(highlightParams); }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinatorTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinatorTest.java index 540f2be9..36ce619 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinatorTest.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinatorTest.java
@@ -7,6 +7,7 @@ import static junit.framework.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -203,12 +204,42 @@ verify(mockIphCommandBuilder).setAnchorView(any()); verify(mockIphCommandBuilder).setViewRectProvider(any()); verify(mockIphCommandBuilder).setHighlightParams(any()); + verify(mockIphCommandBuilder).setOnShowCallback(any()); + verify(mockIphCommandBuilder).setOnDismissCallback(any()); verifyNoMoreInteractions(mockIphCommandBuilder); verify(mMockOptionalButtonView).updateButtonWithAnimation(buttonData); } @Test + public void testUpdateButton_showingIphChangesBackgroundAlpha() { + Drawable iconDrawable = mock(Drawable.class); + OnClickListener clickListener = view -> {}; + IPHCommandBuilder mockIphCommandBuilder = mock(IPHCommandBuilder.class); + String contentDescription = "description"; + boolean isEnabled = true; + ButtonData buttonData = new ButtonDataImpl(/* canShow= */ true, iconDrawable, clickListener, + contentDescription, /* supportsTinting= */ true, mockIphCommandBuilder, + /* isEnabled= */ isEnabled, AdaptiveToolbarButtonVariant.UNKNOWN); + + ArgumentCaptor<Runnable> onShowCallbackCaptor = ArgumentCaptor.forClass(Runnable.class); + ArgumentCaptor<Runnable> onDismissCallbackCaptor = ArgumentCaptor.forClass(Runnable.class); + + mOptionalButtonCoordinator.updateButton(buttonData); + + verify(mockIphCommandBuilder).setOnShowCallback(onShowCallbackCaptor.capture()); + verify(mockIphCommandBuilder).setOnDismissCallback(onDismissCallbackCaptor.capture()); + + // Showing an IPH should make the background transparent to be able to see the highlight. + onShowCallbackCaptor.getValue().run(); + verify(mMockOptionalButtonView).setBackgroundAlpha(0); + + // Dismissing the IPH should bring back the background to normal. + onDismissCallbackCaptor.getValue().run(); + verify(mMockOptionalButtonView, atLeastOnce()).setBackgroundAlpha(255); + } + + @Test public void testUpdateButton_actionChipResourceIdGetsRemovedWhenNotInVariant() { TestValues testValues = new TestValues(); testValues.addFieldTrialParamOverride(
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonMediator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonMediator.java index 85ed65b..39c0d77 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonMediator.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonMediator.java
@@ -48,6 +48,10 @@ mModel.set(OptionalButtonProperties.ICON_BACKGROUND_COLOR, backgroundColor); } + void setBackgroundAlpha(int alpha) { + mModel.set(OptionalButtonProperties.ICON_BACKGROUND_ALPHA, alpha); + } + public void setOnBeforeHideTransitionCallback(Runnable onBeforeHideTransitionCallback) { mModel.set(OptionalButtonProperties.ON_BEFORE_HIDE_TRANSITION_CALLBACK, onBeforeHideTransitionCallback);
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonProperties.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonProperties.java index 2d70a18..e5d86d5b 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonProperties.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonProperties.java
@@ -35,6 +35,7 @@ public static final WritableObjectPropertyKey<ColorStateList> ICON_TINT_LIST = new WritableObjectPropertyKey<>(); public static final WritableIntPropertyKey ICON_BACKGROUND_COLOR = new WritableIntPropertyKey(); + public static final WritableIntPropertyKey ICON_BACKGROUND_ALPHA = new WritableIntPropertyKey(); public static final WritableIntPropertyKey PADDING_START = new WritableIntPropertyKey(); public static final WritableBooleanPropertyKey TRANSITION_CANCELLATION_REQUESTED = new WritableBooleanPropertyKey(); @@ -44,6 +45,6 @@ public static final PropertyKey[] ALL_KEYS = {BUTTON_DATA, IS_ENABLED, TRANSITION_STARTED_CALLBACK, TRANSITION_FINISHED_CALLBACK, ON_BEFORE_HIDE_TRANSITION_CALLBACK, TRANSITION_ROOT, ICON_TINT_LIST, - ICON_BACKGROUND_COLOR, PADDING_START, TRANSITION_CANCELLATION_REQUESTED, - IS_ANIMATION_ALLOWED_PREDICATE}; + ICON_BACKGROUND_COLOR, ICON_BACKGROUND_ALPHA, PADDING_START, + TRANSITION_CANCELLATION_REQUESTED, IS_ANIMATION_ALLOWED_PREDICATE}; }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonView.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonView.java index 60b24d55..583497e 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonView.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonView.java
@@ -263,6 +263,13 @@ mBackground.setColorFilter(color); } + void setBackgroundAlpha(int alpha) { + mBackground.setImageAlpha(alpha); + } + + View getBackgroundView() { + return mBackground; + } void setColorStateList(ColorStateList colorStateList) { ImageViewCompat.setImageTintList(mButton, colorStateList); ImageViewCompat.setImageTintList(mAnimationImage, colorStateList);
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonViewBinder.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonViewBinder.java index 71367f8de..70ae9f4 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonViewBinder.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonViewBinder.java
@@ -32,6 +32,8 @@ } else if (OptionalButtonProperties.ICON_BACKGROUND_COLOR.equals(propertyKey)) { view.setBackgroundColorFilter( model.get(OptionalButtonProperties.ICON_BACKGROUND_COLOR)); + } else if (OptionalButtonProperties.ICON_BACKGROUND_ALPHA.equals(propertyKey)) { + view.setBackgroundAlpha(model.get(OptionalButtonProperties.ICON_BACKGROUND_ALPHA)); } else if (OptionalButtonProperties.PADDING_START.equals(propertyKey)) { view.setPaddingStart(model.get(OptionalButtonProperties.PADDING_START)); } else if (OptionalButtonProperties.TRANSITION_CANCELLATION_REQUESTED.equals(propertyKey)) {
diff --git a/chrome/browser/ui/android/webid/account_selection_view_android.cc b/chrome/browser/ui/android/webid/account_selection_view_android.cc index 2987757..d4a5970 100644 --- a/chrome/browser/ui/android/webid/account_selection_view_android.cc +++ b/chrome/browser/ui/android/webid/account_selection_view_android.cc
@@ -121,7 +121,7 @@ // It's possible that the constructor cannot access the bottom sheet clank // component. That case may be temporary but we can't let users in a // waiting state so report that AccountSelectionView is dismissed instead. - delegate_->OnDismiss(DismissReason::OTHER); + delegate_->OnDismiss(DismissReason::kOther); return; }
diff --git a/chrome/browser/ui/ash/arc_vm_data_migration_confirmation_dialog.cc b/chrome/browser/ui/ash/arc_vm_data_migration_confirmation_dialog.cc index 3fff5ecf6..5655541 100644 --- a/chrome/browser/ui/ash/arc_vm_data_migration_confirmation_dialog.cc +++ b/chrome/browser/ui/ash/arc_vm_data_migration_confirmation_dialog.cc
@@ -9,6 +9,8 @@ #include "ash/components/arc/vector_icons/vector_icons.h" #include "ash/style/ash_color_id.h" #include "ash/style/ash_color_provider.h" +#include "base/metrics/histogram_functions.h" +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" @@ -36,6 +38,21 @@ constexpr int kDialogCornerRadius = 12; +void ReportConfirmationDialogShown(int days_until_deadline) { + base::UmaHistogramExactLinear( + "Arc.VmDataMigration.RemainingDays.ConfirmationDialogShown", + days_until_deadline, kArcVmDataMigrationNumberOfDismissibleDays); +} + +void ReportConfirmationDialogButtonClicked(int days_until_deadline, + bool accepted) { + base::UmaHistogramExactLinear( + base::StringPrintf("Arc.VmDataMigration.RemainingDays.%s", + accepted ? "ConfirmationDialogAccepted" + : "ConfirmationDialogCanceled"), + days_until_deadline, kArcVmDataMigrationNumberOfDismissibleDays); +} + } // namespace ArcVmDataMigrationConfirmationDialog::ArcVmDataMigrationConfirmationDialog( @@ -46,6 +63,8 @@ set_internal_name(kInternalName); const int days_until_deadline = GetDaysUntilArcVmDataMigrationDeadline(prefs); + ReportConfirmationDialogShown(days_until_deadline); + if (ArcVmDataMigrationShouldBeDismissible(days_until_deadline)) { SetButtons(ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL); SetButtonLabel( @@ -57,10 +76,12 @@ IDS_ARC_VM_DATA_MIGRATION_DIALOG_SKIP_BUTTON_LABEL)); SetAcceptCallback( base::BindOnce(&ArcVmDataMigrationConfirmationDialog::OnButtonClicked, - weak_ptr_factory_.GetWeakPtr(), true /* accepted */)); + weak_ptr_factory_.GetWeakPtr(), days_until_deadline, + true /* accepted */)); SetCancelCallback( base::BindOnce(&ArcVmDataMigrationConfirmationDialog::OnButtonClicked, - weak_ptr_factory_.GetWeakPtr(), false /* accepted */)); + weak_ptr_factory_.GetWeakPtr(), days_until_deadline, + false /* accepted */)); } else { SetButtons(ui::DIALOG_BUTTON_OK); SetButtonLabel(ui::DIALOG_BUTTON_OK, @@ -68,7 +89,8 @@ IDS_ARC_VM_DATA_MIGRATION_DIALOG_UPDATE_BUTTON_LABEL)); SetAcceptCallback( base::BindOnce(&ArcVmDataMigrationConfirmationDialog::OnButtonClicked, - weak_ptr_factory_.GetWeakPtr(), true /* accepted */)); + weak_ptr_factory_.GetWeakPtr(), days_until_deadline, + true /* accepted */)); } InitializeView(days_until_deadline); @@ -157,8 +179,11 @@ SetContentsView(std::move(view)); } -void ArcVmDataMigrationConfirmationDialog::OnButtonClicked(bool accepted) { +void ArcVmDataMigrationConfirmationDialog::OnButtonClicked( + int days_until_deadline, + bool accepted) { DCHECK(!callback_.is_null()); + ReportConfirmationDialogButtonClicked(days_until_deadline, accepted); std::move(callback_).Run(accepted); }
diff --git a/chrome/browser/ui/ash/arc_vm_data_migration_confirmation_dialog.h b/chrome/browser/ui/ash/arc_vm_data_migration_confirmation_dialog.h index 27871f6..4abb930 100644 --- a/chrome/browser/ui/ash/arc_vm_data_migration_confirmation_dialog.h +++ b/chrome/browser/ui/ash/arc_vm_data_migration_confirmation_dialog.h
@@ -33,7 +33,7 @@ private: void InitializeView(int days_until_deadline); - void OnButtonClicked(bool accepted); + void OnButtonClicked(int days_until_deadline, bool accepted); ArcVmDataMigrationConfirmationCallback callback_;
diff --git a/chrome/browser/ui/ash/in_session_auth_dialog_client.cc b/chrome/browser/ui/ash/in_session_auth_dialog_client.cc index 030f27e..6af4838 100644 --- a/chrome/browser/ui/ash/in_session_auth_dialog_client.cc +++ b/chrome/browser/ui/ash/in_session_auth_dialog_client.cc
@@ -75,14 +75,6 @@ bool InSessionAuthDialogClient::IsFingerprintAuthAvailable( const AccountId& account_id) { - if (!ash::features::IsUseAuthsessionForWebAuthNEnabled()) { - ash::quick_unlock::QuickUnlockStorage* quick_unlock_storage = - ash::quick_unlock::QuickUnlockFactory::GetForAccountId(account_id); - return quick_unlock_storage && - quick_unlock_storage->fingerprint_storage()->IsFingerprintAvailable( - ash::quick_unlock::Purpose::kWebAuthn); - } - return legacy_fingerprint_engine_->IsFingerprintAvailable( ash::LegacyFingerprintEngine::Purpose::kWebAuthn, user_context_->GetAccountId()); @@ -99,12 +91,6 @@ void InSessionAuthDialogClient::StartFingerprintAuthSession( const AccountId& account_id, base::OnceCallback<void(bool)> callback) { - if (!ash::features::IsUseAuthsessionForWebAuthNEnabled()) { - GetExtendedAuthenticator()->StartFingerprintAuthSession( - account_id, std::move(callback)); - return; - } - legacy_fingerprint_engine_->PrepareLegacyFingerprintFactor( std::move(user_context_), base::BindOnce( @@ -131,13 +117,6 @@ void InSessionAuthDialogClient::EndFingerprintAuthSession( base::OnceClosure callback) { - if (!ash::features::IsUseAuthsessionForWebAuthNEnabled()) { - DCHECK(extended_authenticator_); - extended_authenticator_->EndFingerprintAuthSession(); - std::move(callback).Run(); - return; - } - if (legacy_fingerprint_engine_.has_value()) { legacy_fingerprint_engine_->TerminateLegacyFingerprintFactor( std::move(user_context_), @@ -165,13 +144,6 @@ void InSessionAuthDialogClient::CheckPinAuthAvailability( const AccountId& account_id, base::OnceCallback<void(bool)> callback) { - if (!ash::features::IsUseAuthsessionForWebAuthNEnabled()) { - // PinBackend may be using cryptohome backend or prefs backend. - ash::quick_unlock::PinBackend::GetInstance()->CanAuthenticate( - account_id, ash::quick_unlock::Purpose::kWebAuthn, std::move(callback)); - return; - } - auto on_pin_availability_checked = base::BindOnce(&InSessionAuthDialogClient::OnCheckPinAuthAvailability, weak_factory_.GetWeakPtr(), std::move(callback)); @@ -247,15 +219,6 @@ return; } - if (!ash::features::IsUseAuthsessionForWebAuthNEnabled()) { - ash::quick_unlock::PinBackend::GetInstance()->TryAuthenticate( - std::move(user_context), std::move(key), - ash::quick_unlock::Purpose::kWebAuthn, - base::BindOnce(&InSessionAuthDialogClient::OnPinAttemptDone, - weak_factory_.GetWeakPtr())); - return; - } - pin_engine_->Authenticate( cryptohome::RawPin(secret), std::move(user_context_), base::BindOnce(&InSessionAuthDialogClient::OnAuthVerified, @@ -280,20 +243,6 @@ void InSessionAuthDialogClient::AuthenticateWithPassword( std::unique_ptr<UserContext> user_context, const std::string& password) { - if (!ash::features::IsUseAuthsessionForWebAuthNEnabled()) { - // TODO(crbug.com/1115120): Don't post to UI thread if it turns out to be - // unnecessary. - auto context = std::move(*user_context); - content::GetUIThreadTaskRunner({})->PostTask( - FROM_HERE, - base::BindOnce( - &ExtendedAuthenticator::AuthenticateToUnlockWebAuthnSecret, - GetExtendedAuthenticator(), context, - base::BindOnce(&InSessionAuthDialogClient::OnPasswordAuthSuccess, - weak_factory_.GetWeakPtr(), context))); - return; - } - // Check that we have a valid `user_context_`, provided by a prior // `StartAuthSession`, this also nicely asserts that we are not waiting // on other UserDataAuth dbus calls that involve the auth_session_id stored @@ -377,20 +326,6 @@ void InSessionAuthDialogClient::AuthenticateUserWithFingerprint( base::OnceCallback<void(bool, ash::FingerprintState)> callback) { - if (!ash::features::IsUseAuthsessionForWebAuthNEnabled()) { - const user_manager::User* const user = - user_manager::UserManager::Get()->GetActiveUser(); - DCHECK(user); - UserContext user_context(*user); - - DCHECK(extended_authenticator_); - extended_authenticator_->AuthenticateWithFingerprint( - user_context, - base::BindOnce(&InSessionAuthDialogClient::OnFingerprintAuthDone, - weak_factory_.GetWeakPtr(), std::move(callback))); - return; - } - DCHECK(!fingerprint_scan_done_callback_); fingerprint_scan_done_callback_ = std::move(callback); } @@ -423,26 +358,6 @@ } } -void InSessionAuthDialogClient::OnFingerprintAuthDone( - base::OnceCallback<void(bool, ash::FingerprintState)> callback, - user_data_auth::CryptohomeErrorCode error) { - switch (error) { - case user_data_auth::CRYPTOHOME_ERROR_NOT_SET: - std::move(callback).Run(true, ash::FingerprintState::AVAILABLE_DEFAULT); - break; - case user_data_auth::CRYPTOHOME_ERROR_FINGERPRINT_RETRY_REQUIRED: - std::move(callback).Run(false, ash::FingerprintState::AVAILABLE_DEFAULT); - break; - case user_data_auth::CRYPTOHOME_ERROR_FINGERPRINT_DENIED: - std::move(callback).Run(false, - ash::FingerprintState::DISABLED_FROM_ATTEMPTS); - break; - default: - // Internal error. - std::move(callback).Run(false, ash::FingerprintState::UNAVAILABLE); - } -} - aura::Window* InSessionAuthDialogClient::OpenInSessionAuthHelpPage() const { // TODO(b/156258540): Use the profile of the source browser window. Profile* profile = ProfileManager::GetActiveUserProfile();
diff --git a/chrome/browser/ui/ash/in_session_auth_dialog_client.h b/chrome/browser/ui/ash/in_session_auth_dialog_client.h index 45253be7..cbc64b7 100644 --- a/chrome/browser/ui/ash/in_session_auth_dialog_client.h +++ b/chrome/browser/ui/ash/in_session_auth_dialog_client.h
@@ -141,10 +141,6 @@ void OnPasswordAuthSuccess(const ash::UserContext& user_context); - void OnFingerprintAuthDone( - base::OnceCallback<void(bool, ash::FingerprintState)> callback, - user_data_auth::CryptohomeErrorCode error); - // Passed as a callback to `CryptohomePinEngine::InPinAuthAvailable` // Takes back ownership of the `user_context` that was borrowed by // `CryptohomePinEngine` and notifies callers of pin availability status.
diff --git a/chrome/browser/ui/ash/in_session_auth_dialog_client_unittest.cc b/chrome/browser/ui/ash/in_session_auth_dialog_client_unittest.cc index fa9553e..0e83bd42 100644 --- a/chrome/browser/ui/ash/in_session_auth_dialog_client_unittest.cc +++ b/chrome/browser/ui/ash/in_session_auth_dialog_client_unittest.cc
@@ -101,10 +101,6 @@ std::move(callback)); } - bool GetLastUnlockWebAuthnSecret() const { - return fake_authenticator_->last_unlock_webauthn_secret(); - } - void ConfigureExistingUserWithPassword(const AccountId& user, const std::string& password) { Key key(Key::KEY_TYPE_PASSWORD_PLAIN, std::string(), password); @@ -155,10 +151,8 @@ expected_user_context.SetKey(key); SetExpectedContext(expected_user_context); - if (ash::features::IsUseAuthsessionForWebAuthNEnabled()) { - ConfigureExistingUserWithPassword(user->GetAccountId(), kPassword); - StartAuthSessionForActiveUser(); - }; + ConfigureExistingUserWithPassword(user->GetAccountId(), kPassword); + StartAuthSessionForActiveUser(); base::RunLoop run_loop; @@ -183,10 +177,8 @@ SetExpectedContext(expected_user_context); - if (ash::features::IsUseAuthsessionForWebAuthNEnabled()) { - ConfigureExistingUserWithPassword(user->GetAccountId(), kPassword); - StartAuthSessionForActiveUser(); - }; + ConfigureExistingUserWithPassword(user->GetAccountId(), kPassword); + StartAuthSessionForActiveUser(); base::RunLoop run_loop; @@ -199,9 +191,6 @@ run_loop.RunUntilIdle(); EXPECT_TRUE(result); - if (!ash::features::IsUseAuthsessionForWebAuthNEnabled()) { - EXPECT_TRUE(GetLastUnlockWebAuthnSecret()); - } } } // namespace
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.cc b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.cc index a6baf3e1..658315f8 100644 --- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.cc +++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.cc
@@ -103,10 +103,12 @@ base::Token token = listener_.GetOrCreateTrackedIDForWebContents(browser, created_contents); - model_.Get(saved_tab.saved_group_guid()) - ->GetTab(saved_tab.saved_tab_guid()) - ->SetLocalTabID(token); + SavedTabGroupTab tab(*model_.Get(saved_tab.saved_group_guid()) + ->GetTab(saved_tab.saved_tab_guid())); + tab.SetLocalTabID(token); + model_.ReplaceTabInGroupAt(saved_tab.saved_group_guid(), + saved_tab.saved_tab_guid(), std::move(tab)); opened_web_contents.emplace_back(created_contents); }
diff --git a/chrome/browser/ui/views/autofill/payments/payments_view_util.cc b/chrome/browser/ui/views/autofill/payments/payments_view_util.cc index 568bbccd..6bd4897f 100644 --- a/chrome/browser/ui/views/autofill/payments/payments_view_util.cc +++ b/chrome/browser/ui/views/autofill/payments/payments_view_util.cc
@@ -86,11 +86,6 @@ icon, kIconHeight, GetColorProvider()->GetColor(ui::kColorIcon)); break; } - case TitleWithIconAndSeparatorView::Icon::PRODUCT_LOGO: - image = - gfx::CreateVectorIcon(vector_icons::kProductIcon, kIconHeight, - GetColorProvider()->GetColor(ui::kColorIcon)); - break; } SetImage(image); }
diff --git a/chrome/browser/ui/views/autofill/payments/payments_view_util.h b/chrome/browser/ui/views/autofill/payments/payments_view_util.h index c36f8cde..fdcd9e72 100644 --- a/chrome/browser/ui/views/autofill/payments/payments_view_util.h +++ b/chrome/browser/ui/views/autofill/payments/payments_view_util.h
@@ -40,8 +40,6 @@ GOOGLE_PAY, // Google super G. GOOGLE_G, - // Product logo (Logo that represents the user agent). - PRODUCT_LOGO, }; TitleWithIconAndSeparatorView(const std::u16string& window_title,
diff --git a/chrome/browser/ui/views/autofill/payments/save_iban_bubble_view.cc b/chrome/browser/ui/views/autofill/payments/save_iban_bubble_view.cc index f7e6b3b..5a62d82 100644 --- a/chrome/browser/ui/views/autofill/payments/save_iban_bubble_view.cc +++ b/chrome/browser/ui/views/autofill/payments/save_iban_bubble_view.cc
@@ -69,10 +69,6 @@ *bundle.GetImageSkiaNamed(IDR_SAVE_CARD_DARK), base::BindRepeating(&views::BubbleDialogDelegate::GetBackgroundColor, base::Unretained(this)))); - - GetBubbleFrameView()->SetTitleView( - std::make_unique<TitleWithIconAndSeparatorView>( - GetWindowTitle(), TitleWithIconAndSeparatorView::Icon::PRODUCT_LOGO)); } std::u16string SaveIbanBubbleView::GetWindowTitle() const {
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc index 627d303..730ab6e 100644 --- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc +++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc
@@ -341,7 +341,7 @@ base::Unretained(this)), base::BindRepeating(&SavedTabGroupBar::OnTabGroupButtonPressed, base::Unretained(this), group.saved_guid()), - animations_enabled_), + browser_, animations_enabled_), index); if (children().size() > (kMaxVisibleButtons + 1)) { @@ -505,7 +505,7 @@ base::Unretained(this)), base::BindRepeating(&SavedTabGroupBar::OnTabGroupButtonPressed, base::Unretained(this), group->saved_guid()), - animations_enabled_)); + browser_, animations_enabled_)); } auto* widget =
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar_unittest.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar_unittest.cc index 6f457ae5..d7cee6d6 100644 --- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar_unittest.cc +++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar_unittest.cc
@@ -4,10 +4,15 @@ #include "chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.h" +#include <memory> + #include "base/guid.h" +#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/layout_constants.h" #include "chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.h" #include "chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_overflow_button.h" +#include "chrome/test/base/test_browser_window.h" +#include "chrome/test/base/testing_profile.h" #include "chrome/test/views/chrome_views_test_base.h" #include "components/tab_groups/tab_group_visual_data.h" #include "testing/gmock/include/gmock/gmock.h" @@ -56,15 +61,21 @@ void SetUp() override { ChromeViewsTestBase::SetUp(); + CreateBrowser(); saved_tab_group_model_ = std::make_unique<SavedTabGroupModel>(); saved_tab_group_bar_ = std::make_unique<SavedTabGroupBar>( - nullptr, saved_tab_group_model(), false); + browser(), saved_tab_group_model(), false); + + saved_tab_group_bar_->SetPageNavigator(nullptr); } void TearDown() override { saved_tab_group_bar_.reset(); saved_tab_group_model_.reset(); + browser_window_.reset(); + browser_.reset(); + profile_.reset(); ChromeViewsTestBase::TearDown(); } @@ -89,10 +100,26 @@ return size; } + void CreateBrowser() { + TestingProfile::Builder profile_builder; + profile_ = profile_builder.Build(); + browser_window_ = std::make_unique<TestBrowserWindow>(); + Browser::CreateParams params(profile_.get(), /*user_gesture*/ true); + params.type = Browser::TYPE_NORMAL; + params.window = browser_window_.get(); + browser_.reset(Browser::Create(params)); + } + + Browser* browser() { return browser_.get(); } + private: std::unique_ptr<SavedTabGroupBar> saved_tab_group_bar_; std::unique_ptr<SavedTabGroupModel> saved_tab_group_model_; + std::unique_ptr<TestingProfile> profile_; + std::unique_ptr<TestBrowserWindow> browser_window_; + std::unique_ptr<Browser> browser_; + const int button_padding_; const int button_height_; }; @@ -181,7 +208,7 @@ saved_tab_group_model()->Add(kSavedTabGroup1); SavedTabGroupBar another_tab_group_bar_on_same_model( - nullptr, saved_tab_group_model(), false); + browser(), saved_tab_group_model(), false); EXPECT_EQ(saved_tab_group_bar()->children().size(), another_tab_group_bar_on_same_model.children().size());
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc index 6941773..90492a6e 100644 --- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc +++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc
@@ -8,10 +8,19 @@ #include <string> #include <vector> +#include "base/check.h" #include "base/functional/bind.h" +#include "base/functional/callback_forward.h" +#include "chrome/app/vector_icons/vector_icons.h" #include "chrome/browser/favicon/favicon_utils.h" +#include "chrome/browser/ui/bookmarks/bookmark_utils_desktop.h" +#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/layout_constants.h" +#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.h" +#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_service_factory.h" +#include "chrome/browser/ui/tabs/tab_group_model.h" #include "chrome/browser/ui/tabs/tab_group_theme.h" +#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h" #include "chrome/browser/ui/view_ids.h" #include "chrome/browser/ui/views/bookmarks/bookmark_button_util.h" #include "chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_drag_data.h" @@ -19,7 +28,9 @@ #include "chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h" #include "chrome/grit/generated_resources.h" #include "components/saved_tab_groups/saved_tab_group.h" +#include "components/tab_groups/tab_group_id.h" #include "content/public/browser/page_navigator.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_node_data.h" #include "ui/base/dragdrop/drag_drop_types.h" @@ -28,6 +39,7 @@ #include "ui/base/models/dialog_model_menu_model_adapter.h" #include "ui/base/models/image_model.h" #include "ui/base/theme_provider.h" +#include "ui/base/ui_base_types.h" #include "ui/gfx/animation/slide_animation.h" #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/point_f.h" @@ -43,8 +55,8 @@ constexpr float kBorderThickness = 2.0f; // This value comes from tab_group_style.cc (kEmptyChipSize). Since this -// button and the tab_group_header are rendered on different surfaces, keep the -// value here in case we want to change one but not the other. +// button and the tab_group_header are rendered on different surfaces, keep +// the value here in case we want to change one but not the other. constexpr float kCircleRadius = 20.0f; } // namespace @@ -52,12 +64,16 @@ const SavedTabGroup& group, base::RepeatingCallback<content::PageNavigator*()> page_navigator, PressedCallback callback, + Browser* browser, bool animations_enabled) : MenuButton(std::move(callback), group.title()), tab_group_color_id_(group.color()), - is_group_in_tabstrip_(group.local_group_id().has_value()), guid_(group.saved_guid()), + local_group_id_(group.local_group_id()), tabs_(group.saved_tabs()), + browser_(*browser), + service_( + *SavedTabGroupServiceFactory::GetForProfile(browser_->profile())), page_navigator_callback_(std::move(page_navigator)), context_menu_controller_( this, @@ -109,9 +125,10 @@ void SavedTabGroupButton::UpdateButtonData(const SavedTabGroup& group) { SetText(group.title()); + SetTooltipText(group.title()); SetAccessibleName(group.title()); tab_group_color_id_ = group.color(); - is_group_in_tabstrip_ = group.local_group_id().has_value(); + local_group_id_ = group.local_group_id(); guid_ = group.saved_guid(); tabs_.clear(); tabs_ = group.saved_tabs(); @@ -182,8 +199,9 @@ // Draw border. flags.setStyle(cc::PaintFlags::kStroke_Style); flags.setStrokeWidth(kBorderThickness); - if (is_group_in_tabstrip_) + if (local_group_id_.has_value()) { canvas->DrawRoundRect(rect_f, kBorderRadius, flags); + } if (GetState() == STATE_HOVERED) { // TODO: Draw a box shadow on hover. @@ -243,30 +261,87 @@ return View::ExceededDragThreshold(move_offset); } +void SavedTabGroupButton::TabMenuItemPressed(const GURL& url, int event_flags) { + CHECK(page_navigator_callback_); + + content::OpenURLParams params(url, content::Referrer(), + WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui::PAGE_TRANSITION_AUTO_BOOKMARK, + /*is_renderer_initiated=*/false, + /*started_from_context_menu=*/true); + page_navigator_callback_.Run()->OpenURL(params); +} + +void SavedTabGroupButton::MoveGroupToNewWindowPressed(int event_flags) { + if (!local_group_id_.has_value()) { + service_->OpenSavedTabGroupInBrowser(base::to_address(browser_), guid_); + } + + const SavedTabGroup* group = service_->model()->Get(guid_); + browser_->tab_strip_model()->delegate()->MoveGroupToNewWindow( + group->local_group_id().value()); +} + +void SavedTabGroupButton::DeleteGroupPressed(int event_flags) { + if (local_group_id_.has_value()) { + // Keep the opened tab group in the tabstrip but remove the SavedTabGroup + // data from the model. + TabGroup* tab_group = + browser_->tab_strip_model()->group_model()->GetTabGroup( + local_group_id_.value()); + + service_->UnsaveGroup(local_group_id_.value()); + + // Notify observers to update the tab group header. + // TODO(dljames): Find a way to move this into + // SavedTabGroupKeyedService::DisconnectLocalTabGroup. The goal is to + // abstract this logic from the button in case we need to do similar + // functionality elsewhere in the future. Ensure this change works when + // dragging a Saved group out of the window. + tab_group->SetVisualData(*tab_group->visual_data()); + + } else { + // Remove the SavedTabGroup from the model. No need to worry about updating + // tabstrip, since this group is not open. + service_->model()->Remove(guid_); + } +} + std::unique_ptr<ui::DialogModel> SavedTabGroupButton::CreateDialogModelForContextMenu() { ui::DialogModel::Builder dialog_model = ui::DialogModel::Builder(); + const std::u16string move_or_open_group_text = + local_group_id_.has_value() + ? l10n_util::GetStringUTF16( + IDS_TAB_GROUP_HEADER_CXMENU_MOVE_GROUP_TO_NEW_WINDOW) + : l10n_util::GetStringUTF16( + IDS_TAB_GROUP_HEADER_CXMENU_OPEN_GROUP_IN_NEW_WINDOW); + + dialog_model + .AddMenuItem( + ui::ImageModel::FromVectorIcon(kMoveGroupToNewWindowIcon), + move_or_open_group_text, + base::BindRepeating(&SavedTabGroupButton::MoveGroupToNewWindowPressed, + base::Unretained(this))) + .AddMenuItem( + ui::ImageModel::FromVectorIcon(kCloseGroupIcon), + l10n_util::GetStringUTF16(IDS_TAB_GROUP_HEADER_CXMENU_DELETE_GROUP), + base::BindRepeating(&SavedTabGroupButton::DeleteGroupPressed, + base::Unretained(this))) + .AddSeparator(); + for (const SavedTabGroupTab& tab : tabs_) { - dialog_model.AddMenuItem( + const ui::ImageModel& image = tab.favicon().has_value() ? ui::ImageModel::FromImage(tab.favicon().value()) - : ui::ImageModel::FromImage(favicon::GetDefaultFavicon()), - tab.title().empty() ? base::UTF8ToUTF16(tab.url().spec()) : tab.title(), - base::BindRepeating( - [](GURL url, - base::RepeatingCallback<content::PageNavigator*()> - page_navigator_callback, - int event_flags) { - content::OpenURLParams params( - url, content::Referrer(), - WindowOpenDisposition::NEW_FOREGROUND_TAB, - ui::PAGE_TRANSITION_AUTO_BOOKMARK, - /*is_renderer_initiated=*/false, - /*started_from_context_menu=*/true); - page_navigator_callback.Run()->OpenURL(params); - }, - tab.url(), page_navigator_callback_)); + : ui::ImageModel::FromImage(favicon::GetDefaultFavicon()); + const std::u16string title = + tab.title().empty() ? base::UTF8ToUTF16(tab.url().spec()) : tab.title(); + dialog_model.AddMenuItem( + image, title, + base::BindRepeating(&SavedTabGroupButton::TabMenuItemPressed, + base::Unretained(this), tab.url())); } return dialog_model.Build();
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.h b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.h index acb854d..8b1987e 100644 --- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.h +++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.h
@@ -11,14 +11,19 @@ #include "components/saved_tab_groups/saved_tab_group.h" #include "components/tab_groups/tab_group_color.h" +#include "components/tab_groups/tab_group_id.h" #include "content/public/browser/page.h" #include "content/public/browser/page_navigator.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/base/metadata/metadata_header_macros.h" #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/views/controls/button/menu_button.h" #include "ui/views/dialog_model_context_menu_controller.h" #include "ui/views/drag_controller.h" +class Browser; +class SavedTabGroupKeyedService; + namespace gfx { class Canvas; } @@ -35,6 +40,7 @@ const SavedTabGroup& group, base::RepeatingCallback<content::PageNavigator*()> page_navigator, PressedCallback callback, + Browser* browser, bool animations_enabled = true); SavedTabGroupButton(const SavedTabGroupButton&) = delete; @@ -71,6 +77,10 @@ const base::GUID guid() const { return guid_; } private: + void TabMenuItemPressed(const GURL& url, int event_flags); + void MoveGroupToNewWindowPressed(int event_flags); + void DeleteGroupPressed(int event_flags); + std::unique_ptr<ui::DialogModel> CreateDialogModelForContextMenu(); // The animations for button movement. @@ -79,16 +89,20 @@ // The color of the TabGroup this button is associated with. tab_groups::TabGroupColorId tab_group_color_id_; - // Denotes if the tabgroup is currently open in the tabstrip. - bool is_group_in_tabstrip_; - // The guid used to identify the group this button represents. base::GUID guid_; + // The local guid used to identify the group in the tabstrip if it is open. + absl::optional<tab_groups::TabGroupId> local_group_id_; + // The tabs to be displayed in the context menu. Currently supports tab title, // url, and favicon. std::vector<SavedTabGroupTab> tabs_; + const raw_ref<Browser> browser_; + + const raw_ref<SavedTabGroupKeyedService> service_; + // A callback used to fetch the current PageNavigator used to open URLs. const base::RepeatingCallback<content::PageNavigator*()> page_navigator_callback_;
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc index 55e9338..8b34826a 100644 --- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc +++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -1096,14 +1096,8 @@ // Creates a browser with four tabs with third and fourth in a group. // Selecting and dragging the second and third tabs towards left out // of the group will result in the tabs being ungrouped. -// TODO(crbug.com/1342795): Flaky on Lacros. -#if BUILDFLAG(IS_CHROMEOS_LACROS) -#define MAYBE_DragUngroupedTabGroupedTabOutsideGroup DISABLED_DragUngroupedTabGroupedTabOutsideGroup -#else -#define MAYBE_DragUngroupedTabGroupedTabOutsideGroup DragUngroupedTabGroupedTabOutsideGroup -#endif IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest, - MAYBE_DragUngroupedTabGroupedTabOutsideGroup) { + DragUngroupedTabGroupedTabOutsideGroup) { ASSERT_TRUE(browser()->tab_strip_model()->SupportsTabGroups()); TabStrip* tab_strip = GetTabStripForBrowser(browser());
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc b/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc index 686eee6..939405d 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc +++ b/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc
@@ -30,6 +30,7 @@ #include "components/bookmarks/browser/bookmark_model.h" #include "components/bookmarks/browser/bookmark_utils.h" #include "content/public/test/browser_test.h" +#include "content/public/test/test_navigation_observer.h" #include "ui/views/focus/focus_manager.h" #include "ui/views/test/widget_test.h" #include "ui/views/view.h" @@ -132,20 +133,43 @@ IN_PROC_BROWSER_TEST_F(ToolbarViewTest, BackButtonUpdate) { ToolbarButtonProvider* toolbar_button_provider = BrowserView::GetBrowserViewForBrowser(browser())->toolbar(); - EXPECT_FALSE(toolbar_button_provider->GetBackButton()->GetEnabled()); + ToolbarButton* back_button = toolbar_button_provider->GetBackButton(); + EXPECT_FALSE(back_button->GetEnabled()); // Navigate to title1.html. Back button should be enabled. GURL url = ui_test_utils::GetTestUrl( base::FilePath(), base::FilePath(FILE_PATH_LITERAL("title1.html"))); ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - EXPECT_TRUE(toolbar_button_provider->GetBackButton()->GetEnabled()); + EXPECT_TRUE(back_button->GetEnabled()); // Delete old navigations. Back button will be disabled. auto& controller = browser()->tab_strip_model()->GetActiveWebContents()->GetController(); controller.DeleteNavigationEntries(base::BindRepeating( [&](content::NavigationEntry* entry) { return true; })); - EXPECT_FALSE(toolbar_button_provider->GetBackButton()->GetEnabled()); + EXPECT_FALSE(back_button->GetEnabled()); +} + +IN_PROC_BROWSER_TEST_F(ToolbarViewTest, BackButtonHoverThenClick) { + ToolbarButtonProvider* toolbar_button_provider = + BrowserView::GetBrowserViewForBrowser(browser())->toolbar(); + ToolbarButton* back_button = toolbar_button_provider->GetBackButton(); + EXPECT_FALSE(back_button->GetEnabled()); + + // Navigate to title1.html. Back button should be enabled. + GURL url = ui_test_utils::GetTestUrl( + base::FilePath(), base::FilePath(FILE_PATH_LITERAL("title1.html"))); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); + EXPECT_TRUE(back_button->GetEnabled()); + + // Mouse over and click on the back button. This should navigate back in + // session history. + content::TestNavigationObserver back_nav_observer( + browser()->tab_strip_model()->GetActiveWebContents()); + ui_test_utils::ClickOnView(back_button); + back_nav_observer.Wait(); + + EXPECT_FALSE(back_button->GetEnabled()); } IN_PROC_BROWSER_TEST_F(ToolbarViewTest,
diff --git a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.cc b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.cc index 34c8457..4730d8b9 100644 --- a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.cc +++ b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.cc
@@ -247,8 +247,8 @@ DismissReason dismiss_reason = (bubble_widget_->closed_reason() == views::Widget::ClosedReason::kCloseButtonClicked) - ? DismissReason::CLOSE_BUTTON - : DismissReason::OTHER; + ? DismissReason::kCloseButton + : DismissReason::kOther; OnDismiss(dismiss_reason); } @@ -370,7 +370,7 @@ return; bubble_widget_->Close(); - OnDismiss(DismissReason::OTHER); + OnDismiss(DismissReason::kOther); } void FedCmAccountSelectionView::OnDismiss(DismissReason dismiss_reason) {
diff --git a/chrome/browser/ui/webui/signin/profile_picker_handler.cc b/chrome/browser/ui/webui/signin/profile_picker_handler.cc index f78733e..e9a49593 100644 --- a/chrome/browser/ui/webui/signin/profile_picker_handler.cc +++ b/chrome/browser/ui/webui/signin/profile_picker_handler.cc
@@ -109,20 +109,6 @@ base::UmaHistogramBoolean("ProfilePicker.AskOnStartupChanged", value); } -void RecordNewProfileSpec(absl::optional<SkColor> profile_color, - bool create_shortcut) { - int theme_id = - profile_color.has_value() - ? chrome_colors::ChromeColorsService::GetColorId(*profile_color) - : chrome_colors::kDefaultColorId; - base::UmaHistogramSparse("ProfilePicker.NewProfileTheme", theme_id); - - if (ProfileShortcutManager::IsFeatureEnabled()) { - base::UmaHistogramBoolean("ProfilePicker.NewProfileCreateShortcut", - create_shortcut); - } -} - base::Value::Dict GetAutogeneratedProfileThemeInfoValue( int color_id, absl::optional<SkColor> color, @@ -813,7 +799,6 @@ // Skip the FRE for this profile as sign-in was offered as part of the flow. profile->GetPrefs()->SetBoolean(prefs::kHasSeenWelcomePage, true); - RecordNewProfileSpec(profile_color, create_shortcut); // Launch profile and close the picker. profiles::OpenBrowserWindowForProfile( base::BindOnce(&ProfilePickerHandler::OnSwitchToProfileComplete,
diff --git a/chrome/browser/web_applications/commands/externally_managed_install_command_browsertest.cc b/chrome/browser/web_applications/commands/externally_managed_install_command_browsertest.cc index acad771..8c9eabd 100644 --- a/chrome/browser/web_applications/commands/externally_managed_install_command_browsertest.cc +++ b/chrome/browser/web_applications/commands/externally_managed_install_command_browsertest.cc
@@ -219,8 +219,9 @@ EXPECT_FALSE(provider().registrar_unsafe().IsLocallyInstalled(app_id)); } -IN_PROC_BROWSER_TEST_F(ExternallyManagedInstallCommandBrowserTest, - ExternalInstallSourceReinstallOverrideManifestData) { +IN_PROC_BROWSER_TEST_F( + ExternallyManagedInstallCommandBrowserTest, + DISABLED_ExternalInstallSourceReinstallOverrideManifestData) { const GURL kWebAppUrl = https_server()->GetURL( "/banners/" "manifest_test_page.html");
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index 420afe1..1f3daec3 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1678887821-b947ff96adbd776d047ef6744244f6ab084119e6.profdata +chrome-mac-arm-main-1678895971-1214fe270224c81e28b6baeb31033f3a6f5f5a4c.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index a4adef4..a304d7a9 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1678870782-7bc4fad5994e06c55a1668dce403b85ae26d2a51.profdata +chrome-win64-main-1678892392-e79812c915f912383a525681016a71d95bc6e26b.profdata
diff --git a/chrome/common/extensions/api/autofill_private.idl b/chrome/common/extensions/api/autofill_private.idl index 807c875..b839b8e 100644 --- a/chrome/common/extensions/api/autofill_private.idl +++ b/chrome/common/extensions/api/autofill_private.idl
@@ -245,6 +245,7 @@ callback GetCreditCardListCallback = void(CreditCardEntry[] entries); callback GetIbanListCallback = void(IbanEntry[] entries); callback GetUpiIdListCallback = void(DOMString[] entries); + callback IsValidIbanCallback = void(boolean isValid); interface Functions { // Gets currently signed-in user profile info, no value is returned if @@ -310,6 +311,12 @@ [supportsPromises] static void getIbanList( GetIbanListCallback callback); + // Returns true if the given `ibanValue` is a valid IBAN. + // `callback`: Callback which will be called with the result of IBAN + // validation. + [supportsPromises] static void isValidIban( + DOMString ibanValue, IsValidIbanCallback callback); + // Clears the data associated with a wallet card which was saved // locally so that the saved copy is masked (e.g., "Card ending // in 1234").
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.cc b/chrome/renderer/accessibility/read_anything_app_controller.cc index 19a30ace..659247c 100644 --- a/chrome/renderer/accessibility/read_anything_app_controller.cc +++ b/chrome/renderer/accessibility/read_anything_app_controller.cc
@@ -345,7 +345,6 @@ model_.ContainsTree(model_.active_tree_id())) { Distill(); } - OnAXTreeDestroyed(previous_active_tree_id); } void ReadAnythingAppController::OnAXTreeDestroyed(const ui::AXTreeID& tree_id) { @@ -361,11 +360,6 @@ model_.SetActiveTreeId(ui::AXTreeIDUnknown()); model_.SetActiveUkmSourceId(ukm::kInvalidSourceId); } - std::set<ui::AXTreeID> child_tree_ids = - model_.GetTreeFromId(tree_id)->GetAllChildTreeIds(); - for (const auto& child_tree_id : child_tree_ids) { - OnAXTreeDestroyed(child_tree_id); - } model_.EraseTree(tree_id); }
diff --git a/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc b/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc index 5339c2a..30c6ab5f 100644 --- a/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc +++ b/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
@@ -647,44 +647,6 @@ OnActiveAXTreeIDChanged(tree_ids[2]); } -TEST_F(ReadAnythingAppControllerTest, - OnActiveAXTreeIDChanged_PreviousActiveTreeDestroyed) { - ui::AXTreeID tree_id_2 = ui::AXTreeID::CreateNewAXTreeID(); - ui::AXTreeUpdate update_2; - SetUpdateTreeID(&update_2, tree_id_2); - update_2.root_id = 1; - update_2.nodes.resize(1); - update_2.nodes[0].id = 1; - - ASSERT_EQ(1u, GetNumTrees()); - ASSERT_TRUE(HasTree(tree_id_)); - - // Add update 2. - AccessibilityEventReceived({update_2}); - ASSERT_EQ(2u, GetNumTrees()); - ASSERT_TRUE(HasTree(tree_id_)); - ASSERT_TRUE(HasTree(tree_id_2)); - OnAXTreeDistilled({1}); - EXPECT_CALL(*distiller_, Distill).Times(1); - - // Change the active tree id to the same id. Nothing happens. - OnActiveAXTreeIDChanged(tree_id_); - ASSERT_EQ(2u, GetNumTrees()); - ASSERT_TRUE(HasTree(tree_id_)); - ASSERT_TRUE(HasTree(tree_id_2)); - - // Change the active tree id to tree id 2. The previously active tree id's - // tree is deleted. - OnActiveAXTreeIDChanged(tree_id_2); - ASSERT_EQ(1u, GetNumTrees()); - ASSERT_TRUE(HasTree(tree_id_2)); - - // Change the active tree id to the same id. The previously active tree id's - // tree is deleted. - OnActiveAXTreeIDChanged(tree_id_); - ASSERT_EQ(0u, GetNumTrees()); -} - TEST_F(ReadAnythingAppControllerTest, ModelUpdatesTreeState) { // Set up trees. ui::AXTreeID tree_id_2 = ui::AXTreeID::CreateNewAXTreeID(); @@ -775,53 +737,6 @@ ASSERT_EQ(0u, GetNumTrees()); } -TEST_F(ReadAnythingAppControllerTest, OnAXTreeDestroyed_ChildTree) { - // Create three AXTreeUpdates. The first has tree ID tree_id_ and the others - // have two new tree IDs. - std::vector<ui::AXTreeID> tree_ids = {tree_id_, - ui::AXTreeID::CreateNewAXTreeID(), - ui::AXTreeID::CreateNewAXTreeID()}; - std::vector<ui::AXTreeUpdate> updates; - for (int i = 0; i < 3; i++) { - ui::AXTreeUpdate update; - SetUpdateTreeID(&update, tree_ids[i]); - update.root_id = 1; - update.nodes.resize(1); - update.nodes[0].id = 1; - updates.push_back(update); - } - - // Tree_ids[0] is the parent of tree_ids[1]. Tree_ids[1] is the parent of - // tree_ids[2]. - updates[1].tree_data.parent_tree_id = tree_ids[0]; - updates[2].tree_data.parent_tree_id = tree_ids[1]; - updates[0].nodes[0].AddChildTreeId(tree_ids[1]); - updates[1].nodes[0].AddChildTreeId(tree_ids[2]); - - // Start with 1 tree (the tree created in SetUp). - ASSERT_EQ(1u, GetNumTrees()); - ASSERT_TRUE(HasTree(tree_id_)); - AccessibilityEventReceived({updates[0]}); - ASSERT_EQ(1u, GetNumTrees()); - ASSERT_TRUE(HasTree(tree_id_)); - - // Add two trees. - AccessibilityEventReceived({updates[1]}); - ASSERT_EQ(2u, GetNumTrees()); - ASSERT_TRUE(HasTree(tree_id_)); - ASSERT_TRUE(HasTree(tree_ids[0])); - AccessibilityEventReceived({updates[2]}); - ASSERT_EQ(3u, GetNumTrees()); - ASSERT_TRUE(HasTree(tree_id_)); - ASSERT_TRUE(HasTree(tree_ids[0])); - ASSERT_TRUE(HasTree(tree_ids[1])); - - // Remove the grandparent tree (tree_ids[0]. When it is removed, its - // child (tree_ids[1]) and its child's child (tree_ids[2]) are both removed. - OnAXTreeDestroyed(tree_ids[0]); - ASSERT_EQ(0u, GetNumTrees()); -} - TEST_F(ReadAnythingAppControllerTest, DistillationInProgress_TreeUpdateReceivedOnActiveTree) { // Set the name of each node to be its id.
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index caedc8cf..e83b207 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -4000,6 +4000,7 @@ "../browser/ash/login/ui/captive_portal_dialog_delegate_browsertest.cc", "../browser/ash/login/ui/captive_portal_window_browsertest.cc", "../browser/ash/login/ui/login_feedback_browsertest.cc", + "../browser/ash/login/ui/login_ui_pref_controller_browsertest.cc", "../browser/ash/login/ui/login_web_dialog_browsertest.cc", "../browser/ash/login/ui/simple_web_view_dialog_browsertest.cc", "../browser/ash/login/ui/user_adding_screen_browsertest.cc", @@ -6820,6 +6821,7 @@ "../browser/enterprise/connectors/connectors_manager_unittest.cc", "../browser/enterprise/connectors/connectors_service_unittest.cc", "../browser/enterprise/connectors/enterprise_connectors_policy_handler_unittest.cc", + "../browser/enterprise/connectors/reporting/crash_reporting_context_unittest.cc", "../browser/enterprise/connectors/reporting/extension_install_event_router_unittest.cc", "../browser/enterprise/connectors/reporting/realtime_reporting_client_unittest.cc", "../browser/enterprise/connectors/reporting/reporting_service_settings_unittest.cc", @@ -7237,6 +7239,7 @@ sources += [ # Urgent discarding from performance_manager, high-efficiency mode, and # the High-PMF discard policy aren't supported on Android. + "../browser/performance_manager/policies/heuristic_memory_saver_policy_unittest.cc", "../browser/performance_manager/policies/high_efficiency_mode_policy_unittest.cc", "../browser/performance_manager/policies/page_discarding_helper_unittest.cc", "../browser/performance_manager/policies/urgent_page_discarding_policy_unittest.cc",
diff --git a/chrome/test/data/extensions/api_test/autofill_private/test.js b/chrome/test/data/extensions/api_test/autofill_private/test.js index 5f98f5e..54878e4 100644 --- a/chrome/test/data/extensions/api_test/autofill_private/test.js +++ b/chrome/test/data/extensions/api_test/autofill_private/test.js
@@ -22,6 +22,7 @@ var EXP_MONTH = '02'; var EXP_YEAR = '2999'; var IBAN_VALUE = 'AD1400080001001234567890'; +var INVALID_IBAN_VALUE = 'AD14000800010012345678900'; var failOnceCalled = function() { chrome.test.fail(); @@ -524,6 +525,22 @@ countryCode: COUNTRY_CODE }, handler1); }, + + function isValidIban() { + var handler1 = function(isValidIban) { + // IBAN_VALUE should be valid, then follow up with invalid value. + chrome.test.assertTrue(isValidIban); + chrome.autofillPrivate.isValidIban(INVALID_IBAN_VALUE, handler2); + } + + var handler2 = function(isValidIban) { + // INVALID_IBAN_VALUE is not valid. + chrome.test.assertFalse(isValidIban); + chrome.test.succeed(); + } + + chrome.autofillPrivate.isValidIban(IBAN_VALUE, handler1); + }, ]; /** @const */ @@ -535,13 +552,14 @@ ], 'addNewIbanNoNickname': ['addNewIbanNoNickname'], 'addNewIbanWithNickname': ['addNewIbanWithNickname'], - 'noChangesToExistingIban': ['addNewIbanNoNickname','noChangesToExistingIban'], - 'updateExistingIbanNoNickname': [ - 'addNewIbanNoNickname', 'updateExistingIban_NoNickname'], - 'updateExistingIbanWithNickname': [ - 'addNewIbanNoNickname', 'updateExistingIban_WithNickname'], - 'removeExistingIban': [ - 'addNewIbanNoNickname', 'removeExistingIban'], + 'noChangesToExistingIban': + ['addNewIbanNoNickname', 'noChangesToExistingIban'], + 'updateExistingIbanNoNickname': + ['addNewIbanNoNickname', 'updateExistingIban_NoNickname'], + 'updateExistingIbanWithNickname': + ['addNewIbanNoNickname', 'updateExistingIban_WithNickname'], + 'removeExistingIban': ['addNewIbanNoNickname', 'removeExistingIban'], + 'isValidIban': ['isValidIban'], }; chrome.test.getConfig(function(config) {
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.ts b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.ts index f12bf6b2..0ce685e3 100644 --- a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.ts +++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.ts
@@ -656,10 +656,10 @@ const selectWallpaperPromise = selectWallpaper( wallpaperProvider.images![0]!, wallpaperProvider, personalizationStore); - const [assetId, previewMode] = + const [unitId, previewMode] = await wallpaperProvider.whenCalled('selectWallpaper'); assertTrue(previewMode); - assertEquals(wallpaperProvider.images![0]!.assetId, assetId); + assertEquals(wallpaperProvider.images![0]!.unitId, unitId); await selectWallpaperPromise; assertEquals(
diff --git a/chrome/test/data/webui/chromeos/personalization_app/user_preview_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/user_preview_element_test.ts index 3421954..499b326 100644 --- a/chrome/test/data/webui/chromeos/personalization_app/user_preview_element_test.ts +++ b/chrome/test/data/webui/chromeos/personalization_app/user_preview_element_test.ts
@@ -5,7 +5,7 @@ import 'chrome://personalization/strings.m.js'; import 'chrome://webui-test/mojo_webui_test_support.js'; -import {DefaultUserImage, getSanitizedDefaultImageUrl, Paths, UserImage, UserPreview} from 'chrome://personalization/js/personalization_app.js'; +import {DefaultUserImage, Paths, UserImage, UserPreview} from 'chrome://personalization/js/personalization_app.js'; import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js'; @@ -74,8 +74,8 @@ const avatarImage = userPreviewElement!.shadowRoot!.getElementById( 'avatar') as HTMLImageElement; assertEquals( - getSanitizedDefaultImageUrl(userProvider.image.defaultImage?.url!).url, - avatarImage.src, 'correct image url is shown for default image'); + userProvider.image.defaultImage?.url!.url, avatarImage.src, + 'correct image url is shown for default image'); }); test('displays user image from profile image', async () => { @@ -89,6 +89,11 @@ assertEquals( userProvider.profileImage.url, avatarImage.src, 'correct image url is shown for profile image'); + assertTrue( + avatarImage.src.startsWith('data:'), 'data url is not sanitized'); + assertTrue( + !avatarImage.style.backgroundImage, + 'data url does not have a background image'); }); test('displays user image from external image', async () => { @@ -112,6 +117,33 @@ assertTrue( avatarImage.src.startsWith('blob:'), 'blob url is shown for external image'); + assertTrue( + !avatarImage.style.backgroundImage, + 'blob url does not have a background image'); + }); + + test('sanitizes gstatic image', async () => { + personalizationStore.data.user.image = { + 'defaultImage': { + url: { + url: 'https://www.gstatic.com/', + }, + title: toString16('the remains of the day'), + index: 1, + }, + }; + + userPreviewElement = initElement(UserPreview, {path: Paths.ROOT}); + await waitAfterNextRender(userPreviewElement); + + const avatarImage = userPreviewElement!.shadowRoot!.getElementById( + 'avatar') as HTMLImageElement; + + assertTrue( + avatarImage.src.startsWith('chrome://image'), 'url was sanitized'); + assertTrue( + avatarImage.style.backgroundImage.startsWith('url("chrome://image'), + 'background-url was sanitized'); }); test('do not display image if user image is not ready yet', async () => { @@ -145,8 +177,8 @@ const avatarImage = userPreviewElement!.shadowRoot!.getElementById( 'avatar2') as HTMLImageElement; assertEquals( - getSanitizedDefaultImageUrl(userProvider.image.defaultImage?.url!).url, - avatarImage.src, 'default image url is shown on non-clickable image'); + userProvider.image.defaultImage?.url!.url, avatarImage.src, + 'default image url is shown on non-clickable image'); }); test('displays enterprise logo on avatar image', async () => {
diff --git a/chrome/test/data/webui/settings/chromeos/BUILD.gn b/chrome/test/data/webui/settings/chromeos/BUILD.gn index 569c322..eb5ee1c 100644 --- a/chrome/test/data/webui/settings/chromeos/BUILD.gn +++ b/chrome/test/data/webui/settings/chromeos/BUILD.gn
@@ -173,7 +173,6 @@ "tether_connection_dialog_test.js", "text_to_speech_page_tests.js", "text_to_speech_subpage_tests.js", - "timezone_selector_test.js", "timezone_subpage_test.js", "utils.ts", @@ -213,6 +212,8 @@ "app_management/test_util.js", "app_management/toggle_row_test.js", + "date_time_page/timezone_selector_test.ts", + "os_a11y_page/audio_and_captions_page_tests.ts", "os_a11y_page/tts_subpage_test.ts",
diff --git a/chrome/test/data/webui/settings/chromeos/chromevox_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/chromevox_subpage_tests.js index 1643a1f..ef2dad4 100644 --- a/chrome/test/data/webui/settings/chromeos/chromevox_subpage_tests.js +++ b/chrome/test/data/webui/settings/chromeos/chromevox_subpage_tests.js
@@ -16,6 +16,15 @@ import {TestChromeVoxSubpageBrowserProxy} from './test_chromevox_subpage_browser_proxy.js'; +/** + * Control types for pref-based settings. + * @enum {string} + */ +export const ControlType = { + DROPDOWN: 'dropdown', + TOGGLE: 'toggle', +}; + suite('ChromeVoxSubpageTests', function() { /** @type {SettingsChromeVoxSubpageElement} */ let page = null; @@ -41,6 +50,55 @@ page.remove(); }); + const settingsControls = [ + { + id: 'voiceDropdown', + prefKey: 'settings.a11y.chromevox.voice_name', + defaultValue: 'chromeos_system_voice', + secondaryValue: 'Chrome OS US English', + type: ControlType.DROPDOWN, + }, + { + id: 'languageSwitchingToggle', + prefKey: 'settings.a11y.chromevox.language_switching', + defaultValue: false, + secondaryValue: true, + type: ControlType.TOGGLE, + }, + ]; + + settingsControls.forEach(control => { + const {id, prefKey, defaultValue, secondaryValue, type} = control; + + test(`ChromeVox ${type} ${id} syncs to Pref: ${prefKey}`, async () => { + // Make sure control exists. + const control = page.shadowRoot.querySelector(`#${id}`); + assertTrue(!!control); + + // Make sure pref is set to the default value. + let pref = page.getPref(prefKey); + assertEquals(defaultValue, pref.value); + + // Update control to secondary value. + switch (type) { + case ControlType.TOGGLE: + control.click(); + break; + case ControlType.DROPDOWN: + await waitAfterNextRender(control); + const controlElement = control.shadowRoot.querySelector('select'); + controlElement.value = secondaryValue; + controlElement.dispatchEvent( + new CustomEvent('change', {bubbles: true, composed: true})); + break; + } + + // Make sure pref is set to secondary value. + pref = page.getPref(prefKey); + assertEquals(secondaryValue, pref.value); + }); + }); + test('voices are ordered', async function() { // Make sure voices are ordered with the system default voice first, then // Google voices, then eSpeak, then local, then remote. @@ -56,33 +114,4 @@ ]; assertDeepEquals(expectedMenuOptions, voiceDropdown.menuOptions); }); - - test('voice pref and dropdown synced', async function() { - // Make sure voice dropdown is system voice, matching default pref state. - const voiceDropdown = page.shadowRoot.querySelector('#voiceDropdown'); - await waitAfterNextRender(voiceDropdown); - const voiceSelectElement = voiceDropdown.shadowRoot.querySelector('select'); - assertEquals('chromeos_system_voice', voiceSelectElement.value); - - // Change voice to Chrome OS US English, and verify pref is also changed. - voiceSelectElement.value = 'Chrome OS US English'; - voiceSelectElement.dispatchEvent(new CustomEvent('change')); - flush(); - const voicePref = page.getPref('settings.a11y.chromevox.voice_name'); - assertEquals('Chrome OS US English', voicePref.value); - }); - - test('language switching pref and toggle synced', function() { - // Make sure language switching toggle is off, matching default pref state. - const languageSwitchingToggle = - page.shadowRoot.querySelector('#languageSwitchingToggle'); - assertFalse(languageSwitchingToggle.checked); - - // Toggle language switching on, and verify language_switching pref is - // enabled. - languageSwitchingToggle.click(); - const languageSwitchingPref = - page.getPref('settings.a11y.chromevox.language_switching'); - assertTrue(languageSwitchingPref.value); - }); });
diff --git a/chrome/test/data/webui/settings/chromeos/timezone_selector_test.js b/chrome/test/data/webui/settings/chromeos/date_time_page/timezone_selector_test.ts similarity index 65% rename from chrome/test/data/webui/settings/chromeos/timezone_selector_test.js rename to chrome/test/data/webui/settings/chromeos/date_time_page/timezone_selector_test.ts index 333f92c..34df2c52 100644 --- a/chrome/test/data/webui/settings/chromeos/timezone_selector_test.js +++ b/chrome/test/data/webui/settings/chromeos/date_time_page/timezone_selector_test.ts
@@ -4,18 +4,13 @@ import 'chrome://os-settings/chromeos/lazy_load.js'; -import {assert} from 'chrome://resources/ash/common/assert.js'; +import {TimezoneSelectorElement} from 'chrome://os-settings/chromeos/lazy_load.js'; +import {assert} from 'chrome://resources/js/assert_ts.js'; import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; - import {assertEquals} from 'chrome://webui-test/chai_assert.js'; -suite('TimezoneSelectorTests', function() { - /** @type {TimezoneSelector} */ - let timezoneSelector = null; - - setup(function() { - PolymerTest.clearBody(); - }); +suite('<timezone-selector>', function() { + let timezoneSelector: TimezoneSelectorElement; teardown(function() { timezoneSelector.remove(); @@ -38,10 +33,10 @@ assertEquals( null, - timezoneSelector.shadowRoot.querySelector('#userTimeZoneSelector')); + timezoneSelector.shadowRoot!.querySelector('#userTimeZoneSelector')); assertEquals( null, - timezoneSelector.shadowRoot.querySelector('#systemTimezoneSelector')); + timezoneSelector.shadowRoot!.querySelector('#systemTimezoneSelector')); }); test('Per-user timezone enabled', async () => { @@ -59,9 +54,8 @@ flush(); - const userTimezoneSelector = assert( - timezoneSelector.shadowRoot.querySelector('#userTimeZoneSelector')); - const systemTimezoneSelector = assert( - timezoneSelector.shadowRoot.querySelector('#systemTimezoneSelector')); + assert(timezoneSelector.shadowRoot!.querySelector('#userTimeZoneSelector')); + assert( + timezoneSelector.shadowRoot!.querySelector('#systemTimezoneSelector')); }); });
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js index 80092ec..aa9bf5b 100644 --- a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js +++ b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
@@ -259,6 +259,7 @@ 'cursor_and_touchpad_page_tests.js', ], ['DateTimePage', 'date_time_page_tests.js'], + ['DateTimePageTimezoneSelector', 'date_time_page/timezone_selector_test.js'], [ 'DisplayAndMagnificationPage', 'display_and_magnification_page_tests.js', @@ -477,7 +478,6 @@ 'text_to_speech_page_tests.js', ], ['TextToSpeechSubpage', 'text_to_speech_subpage_tests.js'], - ['TimezoneSelector', 'timezone_selector_test.js'], ['TimezoneSubpage', 'timezone_subpage_test.js'], ].forEach(test => registerTest(...test));
diff --git a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.ts b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.ts index 24df134b6..40bd6e8 100644 --- a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.ts +++ b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.ts
@@ -506,6 +506,7 @@ addedVirtualCards: number = 0; requestedIbans: number = 0; removedIbans: number = 0; + isValidIban: number = 0; } /** @@ -535,6 +536,7 @@ 'removeCreditCard', 'removeIban', 'addVirtualCard', + 'isValidIban', ]); // Set these to have non-empty data. @@ -602,6 +604,11 @@ return Promise.resolve(this.data.ibans); } + isValidIban(_ibanValue: string) { + this.methodCalled('isValidIban'); + return Promise.resolve(true); + } + setIsUserVerifyingPlatformAuthenticatorAvailable(available: boolean|null) { this.isUserVerifyingPlatformAuthenticatorAvailable_ = available;
diff --git a/chrome/test/data/webui/settings/payments_section_iban_test.ts b/chrome/test/data/webui/settings/payments_section_iban_test.ts index ae4c04a..cb6d5f3d 100644 --- a/chrome/test/data/webui/settings/payments_section_iban_test.ts +++ b/chrome/test/data/webui/settings/payments_section_iban_test.ts
@@ -4,7 +4,7 @@ // clang-format off import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; -import {PaymentsManagerImpl, SettingsIbanEditDialogElement} from 'chrome://settings/lazy_load.js'; +import {CrInputElement, PaymentsManagerImpl, SettingsIbanEditDialogElement} from 'chrome://settings/lazy_load.js'; import {CrButtonElement, loadTimeData} from 'chrome://settings/settings.js'; import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; import {eventToPromise, whenAttributeIs} from 'chrome://webui-test/test_util.js'; @@ -14,6 +14,16 @@ // clang-format on +/** + * Helper function to update IBAN value in the IBAN field. + */ +function updateIbanTextboxValue(valueInput: CrInputElement, value: string) { + valueInput.value = value; + valueInput.dispatchEvent( + new CustomEvent('input', {bubbles: true, composed: true})); +} + + suite('PaymentsSectionIban', function() { setup(function() { document.body.innerHTML = window.trustedTypes!.emptyHTML; @@ -26,7 +36,7 @@ }); /** - * Creates the Edit IBAN dialog. + * Creates the Add or Edit IBAN dialog. */ function createIbanDialog(ibanItem: chrome.autofillPrivate.IbanEntry): SettingsIbanEditDialogElement { @@ -34,6 +44,7 @@ dialog.iban = ibanItem; document.body.appendChild(dialog); flush(); + dialog.$.saveButton.disabled = false; return dialog; } @@ -135,29 +146,28 @@ const saveButton = ibanDialog.$.saveButton; assertTrue(!!saveButton); - // Can't be saved, because there's no value. - assertTrue(saveButton.disabled); - // Add invalid IBAN value. + // Add a valid IBAN value. const valueInput = ibanDialog.$.valueInput; - valueInput.value = '11112222333344445678'; - flush(); - // Can't be saved, because the value of IBAN is invalid. - assertTrue(saveButton.disabled); + updateIbanTextboxValue(valueInput, 'FI1410093000123458'); - // Add valid IBAN value. - valueInput.value = 'FI1410093000123458'; - flush(); - // Can be saved, because the value of IBAN is valid. - assertFalse(saveButton.disabled); + // Type in another valid IBAN value. + updateIbanTextboxValue(valueInput, 'IT60X0542811101000000123456'); const savePromise = eventToPromise('save-iban', ibanDialog); saveButton.click(); const event = await savePromise; assertEquals(undefined, event.detail.guid); - assertEquals('FI1410093000123458', event.detail.value); + assertEquals('IT60X0542811101000000123456', event.detail.value); assertEquals('', event.detail.nickname); + + const paymentsManager = + PaymentsManagerImpl.getInstance() as TestPaymentsManager; + const expectations = getDefaultExpectations(); + expectations.isValidIban = 2; + expectations.listeningCreditCards = 0; + paymentsManager.assertExpectations(expectations); }); test('verifyIbanEntryIsNotEditedAfterCancel', async function() {
diff --git a/chrome/test/data/webui/settings/payments_section_interactive_test.ts b/chrome/test/data/webui/settings/payments_section_interactive_test.ts index dab4e7d..f3dc00e 100644 --- a/chrome/test/data/webui/settings/payments_section_interactive_test.ts +++ b/chrome/test/data/webui/settings/payments_section_interactive_test.ts
@@ -131,6 +131,7 @@ const ibanDialog = section.shadowRoot!.querySelector('settings-iban-edit-dialog'); assertTrue(!!ibanDialog); + ibanDialog.$.saveButton.disabled = false; return ibanDialog!; } @@ -191,6 +192,7 @@ const ibanDialog = section.shadowRoot!.querySelector('settings-iban-edit-dialog'); assertTrue(!!ibanDialog); + ibanDialog.$.saveButton.disabled = false; return ibanDialog; }
diff --git a/chrome/test/data/webui/settings/payments_section_utils.ts b/chrome/test/data/webui/settings/payments_section_utils.ts index 53fe215c..5446dbc 100644 --- a/chrome/test/data/webui/settings/payments_section_utils.ts +++ b/chrome/test/data/webui/settings/payments_section_utils.ts
@@ -47,6 +47,7 @@ expected.addedVirtualCards = 0; expected.requestedIbans = 1; expected.removedIbans = 0; + expected.isValidIban = 0; return expected; }
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc index ecaf1969..bf785484 100644 --- a/chromecast/browser/cast_content_browser_client.cc +++ b/chromecast/browser/cast_content_browser_client.cc
@@ -859,10 +859,10 @@ return false; } -bool CastContentBrowserClient::ShouldAllowInsecurePrivateNetworkRequests( +bool CastContentBrowserClient::ShouldAllowInsecureLocalNetworkRequests( content::BrowserContext* browser_context, const url::Origin& origin) { - // Some Cast apps hosted over HTTP needs to access the private network so that + // Some Cast apps hosted over HTTP needs to access the local network so that // media can be streamed from a local media server. return true; }
diff --git a/chromecast/browser/cast_content_browser_client.h b/chromecast/browser/cast_content_browser_client.h index 1b1834ea..62325e33 100644 --- a/chromecast/browser/cast_content_browser_client.h +++ b/chromecast/browser/cast_content_browser_client.h
@@ -262,7 +262,7 @@ bool DoesSiteRequireDedicatedProcess(content::BrowserContext* browser_context, const GURL& effective_site_url) override; bool IsWebUIAllowedToMakeNetworkRequests(const url::Origin& origin) override; - bool ShouldAllowInsecurePrivateNetworkRequests( + bool ShouldAllowInsecureLocalNetworkRequests( content::BrowserContext* browser_context, const url::Origin& origin) override; std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM index d9028e0..3003ccf 100644 --- a/chromeos/CHROMEOS_LKGM +++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@ -15380.0.0 \ No newline at end of file +15382.0.0 \ No newline at end of file
diff --git a/chromeos/ash/components/cryptohome/cryptohome_parameters.h b/chromeos/ash/components/cryptohome/cryptohome_parameters.h index d07e289c7..15bf1e4 100644 --- a/chromeos/ash/components/cryptohome/cryptohome_parameters.h +++ b/chromeos/ash/components/cryptohome/cryptohome_parameters.h
@@ -71,10 +71,11 @@ // challenged is stored in |challenge_response_keys|, while |secret| should // be empty. TYPE_CHALLENGE_RESPONSE = 1, + // DEPRECATED: // Fingerprint-based key. It doesn't carry secrets but indicates that // cryptohome needs to query fingerprint scan results from biod and // compare with the identity passed along with the key. - TYPE_FINGERPRINT = 2, + // TYPE_FINGERPRINT = 2, // Public mount is used by Kiosk sessions. This type of key does not have // any secret, instead crypotohomed would generate a key based on user // identity.
diff --git a/chromeos/ash/components/cryptohome/cryptohome_util.cc b/chromeos/ash/components/cryptohome/cryptohome_util.cc index 3e755023..f42d16aa 100644 --- a/chromeos/ash/components/cryptohome/cryptohome_util.cc +++ b/chromeos/ash/components/cryptohome/cryptohome_util.cc
@@ -167,8 +167,6 @@ auth_request.mutable_key_delegate()->set_dbus_object_path( cryptohome::kCryptohomeKeyDelegateServicePath); break; - case KeyDefinition::TYPE_FINGERPRINT: - break; case KeyDefinition::TYPE_PUBLIC_MOUNT: break; } @@ -198,9 +196,6 @@ } break; - case KeyDefinition::TYPE_FINGERPRINT: - data->set_type(KeyData::KEY_TYPE_FINGERPRINT); - break; case KeyDefinition::TYPE_PUBLIC_MOUNT: data->set_type(KeyData::KEY_TYPE_KIOSK); break;
diff --git a/chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.cc b/chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.cc index fa34d7c9..fc3353d 100644 --- a/chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.cc +++ b/chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.cc
@@ -718,18 +718,6 @@ } } -void FakeUserDataAuthClient::StartFingerprintAuthSession( - const ::user_data_auth::StartFingerprintAuthSessionRequest& request, - StartFingerprintAuthSessionCallback callback) { - ::user_data_auth::StartFingerprintAuthSessionReply reply; - ReplyOnReturn auto_reply(&reply, std::move(callback)); -} -void FakeUserDataAuthClient::EndFingerprintAuthSession( - const ::user_data_auth::EndFingerprintAuthSessionRequest& request, - EndFingerprintAuthSessionCallback callback) { - ::user_data_auth::EndFingerprintAuthSessionReply reply; - ReplyOnReturn auto_reply(&reply, std::move(callback)); -} void FakeUserDataAuthClient::StartMigrateToDircrypto( const ::user_data_auth::StartMigrateToDircryptoRequest& request, StartMigrateToDircryptoCallback callback) {
diff --git a/chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.h b/chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.h index 1ef26c6..aa64892 100644 --- a/chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.h +++ b/chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.h
@@ -200,12 +200,6 @@ RemoveCallback callback) override; void CheckKey(const ::user_data_auth::CheckKeyRequest& request, CheckKeyCallback callback) override; - void StartFingerprintAuthSession( - const ::user_data_auth::StartFingerprintAuthSessionRequest& request, - StartFingerprintAuthSessionCallback callback) override; - void EndFingerprintAuthSession( - const ::user_data_auth::EndFingerprintAuthSessionRequest& request, - EndFingerprintAuthSessionCallback callback) override; void StartMigrateToDircrypto( const ::user_data_auth::StartMigrateToDircryptoRequest& request, StartMigrateToDircryptoCallback callback) override;
diff --git a/chromeos/ash/components/dbus/userdataauth/mock_userdataauth_client.h b/chromeos/ash/components/dbus/userdataauth/mock_userdataauth_client.h index 0c13fc0..cde2ae4 100644 --- a/chromeos/ash/components/dbus/userdataauth/mock_userdataauth_client.h +++ b/chromeos/ash/components/dbus/userdataauth/mock_userdataauth_client.h
@@ -46,18 +46,6 @@ (const ::user_data_auth::CheckKeyRequest& request, CheckKeyCallback callback), (override)); - MOCK_METHOD( - void, - StartFingerprintAuthSession, - (const ::user_data_auth::StartFingerprintAuthSessionRequest& request, - StartFingerprintAuthSessionCallback callback), - (override)); - MOCK_METHOD( - void, - EndFingerprintAuthSession, - (const ::user_data_auth::EndFingerprintAuthSessionRequest& request, - EndFingerprintAuthSessionCallback callback), - (override)); MOCK_METHOD(void, StartMigrateToDircrypto, (const ::user_data_auth::StartMigrateToDircryptoRequest& request,
diff --git a/chromeos/ash/components/dbus/userdataauth/userdataauth_client.cc b/chromeos/ash/components/dbus/userdataauth/userdataauth_client.cc index 535bd81..1d15405 100644 --- a/chromeos/ash/components/dbus/userdataauth/userdataauth_client.cc +++ b/chromeos/ash/components/dbus/userdataauth/userdataauth_client.cc
@@ -131,22 +131,6 @@ std::move(callback)); } - void StartFingerprintAuthSession( - const ::user_data_auth::StartFingerprintAuthSessionRequest& request, - StartFingerprintAuthSessionCallback callback) override { - CallProtoMethod(::user_data_auth::kStartFingerprintAuthSession, - ::user_data_auth::kUserDataAuthInterface, request, - std::move(callback)); - } - - void EndFingerprintAuthSession( - const ::user_data_auth::EndFingerprintAuthSessionRequest& request, - EndFingerprintAuthSessionCallback callback) override { - CallProtoMethod(::user_data_auth::kEndFingerprintAuthSession, - ::user_data_auth::kUserDataAuthInterface, request, - std::move(callback)); - } - void StartMigrateToDircrypto( const ::user_data_auth::StartMigrateToDircryptoRequest& request, StartMigrateToDircryptoCallback callback) override {
diff --git a/chromeos/ash/components/dbus/userdataauth/userdataauth_client.h b/chromeos/ash/components/dbus/userdataauth/userdataauth_client.h index 44a923d6..0ce926c 100644 --- a/chromeos/ash/components/dbus/userdataauth/userdataauth_client.h +++ b/chromeos/ash/components/dbus/userdataauth/userdataauth_client.h
@@ -59,10 +59,6 @@ using CheckKeyCallback = chromeos::DBusMethodCallback<::user_data_auth::CheckKeyReply>; - using StartFingerprintAuthSessionCallback = chromeos::DBusMethodCallback< - ::user_data_auth::StartFingerprintAuthSessionReply>; - using EndFingerprintAuthSessionCallback = chromeos::DBusMethodCallback< - ::user_data_auth::EndFingerprintAuthSessionReply>; using GetSupportedKeyPoliciesCallback = chromeos::DBusMethodCallback< ::user_data_auth::GetSupportedKeyPoliciesReply>; using GetAccountDiskUsageCallback = @@ -179,16 +175,6 @@ virtual void CheckKey(const ::user_data_auth::CheckKeyRequest& request, CheckKeyCallback callback) = 0; - // Starts a fingerprint auth session. - virtual void StartFingerprintAuthSession( - const ::user_data_auth::StartFingerprintAuthSessionRequest& request, - StartFingerprintAuthSessionCallback callback) = 0; - - // Ends a fingerprint auth session. - virtual void EndFingerprintAuthSession( - const ::user_data_auth::EndFingerprintAuthSessionRequest& request, - EndFingerprintAuthSessionCallback callback) = 0; - // Instructs cryptohome to migrate the vault from eCryptfs to Dircrypto. virtual void StartMigrateToDircrypto( const ::user_data_auth::StartMigrateToDircryptoRequest& request,
diff --git a/chromeos/ash/components/dbus/userdataauth/userdataauth_client_unittest.cc b/chromeos/ash/components/dbus/userdataauth/userdataauth_client_unittest.cc index a82c9fb..6128a72 100644 --- a/chromeos/ash/components/dbus/userdataauth/userdataauth_client_unittest.cc +++ b/chromeos/ash/components/dbus/userdataauth/userdataauth_client_unittest.cc
@@ -185,10 +185,6 @@ ::user_data_auth::UnmountReply expected_unmount_reply_; ::user_data_auth::RemoveReply expected_remove_reply_; ::user_data_auth::CheckKeyReply expected_check_key_reply_; - ::user_data_auth::StartFingerprintAuthSessionReply - expected_start_fingerprint_auth_session_reply_; - ::user_data_auth::EndFingerprintAuthSessionReply - expected_end_fingerprint_auth_session_reply_; ::user_data_auth::StartMigrateToDircryptoReply expected_start_migrate_to_dircrypto_reply_; ::user_data_auth::NeedsDircryptoMigrationReply @@ -228,14 +224,6 @@ } else if (method_call->GetMember() == ::user_data_auth::kCheckKey) { writer.AppendProtoAsArrayOfBytes(expected_check_key_reply_); } else if (method_call->GetMember() == - ::user_data_auth::kStartFingerprintAuthSession) { - writer.AppendProtoAsArrayOfBytes( - expected_start_fingerprint_auth_session_reply_); - } else if (method_call->GetMember() == - ::user_data_auth::kEndFingerprintAuthSession) { - writer.AppendProtoAsArrayOfBytes( - expected_end_fingerprint_auth_session_reply_); - } else if (method_call->GetMember() == ::user_data_auth::kStartMigrateToDircrypto) { writer.AppendProtoAsArrayOfBytes( expected_start_migrate_to_dircrypto_reply_); @@ -347,35 +335,6 @@ EXPECT_TRUE(ProtobufEquals(result_reply.value(), expected_check_key_reply_)); } -TEST_F(UserDataAuthClientTest, StartFingerprintAuthSession) { - expected_start_fingerprint_auth_session_reply_.set_error( - user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_TPM_DEFEND_LOCK); - absl::optional<::user_data_auth::StartFingerprintAuthSessionReply> - result_reply; - - client_->StartFingerprintAuthSession( - ::user_data_auth::StartFingerprintAuthSessionRequest(), - CreateCopyCallback(&result_reply)); - base::RunLoop().RunUntilIdle(); - ASSERT_NE(result_reply, absl::nullopt); - EXPECT_TRUE(ProtobufEquals(result_reply.value(), - expected_start_fingerprint_auth_session_reply_)); -} - -TEST_F(UserDataAuthClientTest, EndFingerprintAuthSession) { - expected_end_fingerprint_auth_session_reply_.set_error( - user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_TPM_DEFEND_LOCK); - absl::optional<::user_data_auth::EndFingerprintAuthSessionReply> result_reply; - - client_->EndFingerprintAuthSession( - ::user_data_auth::EndFingerprintAuthSessionRequest(), - CreateCopyCallback(&result_reply)); - base::RunLoop().RunUntilIdle(); - ASSERT_NE(result_reply, absl::nullopt); - EXPECT_TRUE(ProtobufEquals(result_reply.value(), - expected_end_fingerprint_auth_session_reply_)); -} - TEST_F(UserDataAuthClientTest, StartMigrateToDircrypto) { expected_start_migrate_to_dircrypto_reply_.set_error( user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_TPM_DEFEND_LOCK);
diff --git a/chromeos/ash/components/login/auth/cryptohome_parameter_utils.cc b/chromeos/ash/components/login/auth/cryptohome_parameter_utils.cc index 5753e62..da11e67 100644 --- a/chromeos/ash/components/login/auth/cryptohome_parameter_utils.cc +++ b/chromeos/ash/components/login/auth/cryptohome_parameter_utils.cc
@@ -56,8 +56,6 @@ break; case KeyDefinition::TYPE_CHALLENGE_RESPONSE: break; - case KeyDefinition::TYPE_FINGERPRINT: - break; case KeyDefinition::TYPE_PUBLIC_MOUNT: break; }
diff --git a/chromeos/ash/components/login/auth/extended_authenticator.h b/chromeos/ash/components/login/auth/extended_authenticator.h index 69196920..0185468 100644 --- a/chromeos/ash/components/login/auth/extended_authenticator.h +++ b/chromeos/ash/components/login/auth/extended_authenticator.h
@@ -53,33 +53,6 @@ virtual void AuthenticateToCheck(const UserContext& context, base::OnceClosure success_callback) = 0; - // This call will attempt to authenticate the user with the key (and key - // label) in |context|, and unlock the WebAuthn secret using key if - // authentication succeeds. - virtual void AuthenticateToUnlockWebAuthnSecret( - const UserContext& context, - base::OnceClosure success_callback) = 0; - - // Attempts to start fingerprint auth session (prepare biometrics daemon for - // upcoming fingerprint scan) for the user with |account_id|. |callback| will - // be invoked with whether the fingerprint auth session is successfully - // started. - virtual void StartFingerprintAuthSession( - const AccountId& account_id, - base::OnceCallback<void(bool)> callback) = 0; - - // Attempts to end the current fingerprint auth session. Logs an error if no - // response. - virtual void EndFingerprintAuthSession() = 0; - - // Waits for a fingerprint scan from the user in |context|, and calls - // |callback| with a fingerprint-specific CryptohomeErrorCode. No further - // actions are taken after authentication. - virtual void AuthenticateWithFingerprint( - const UserContext& context, - base::OnceCallback<void(user_data_auth::CryptohomeErrorCode)> - callback) = 0; - // Hashes the key in |user_context| with the system salt it its type is // KEY_TYPE_PASSWORD_PLAIN and passes the resulting UserContext to the // |callback|.
diff --git a/chromeos/ash/components/login/auth/extended_authenticator_impl.cc b/chromeos/ash/components/login/auth/extended_authenticator_impl.cc index 66f4f4c7..a6a8a2fa 100644 --- a/chromeos/ash/components/login/auth/extended_authenticator_impl.cc +++ b/chromeos/ash/components/login/auth/extended_authenticator_impl.cc
@@ -53,81 +53,6 @@ /*unlock_webauthn_secret=*/false)); } -void ExtendedAuthenticatorImpl::AuthenticateToUnlockWebAuthnSecret( - const UserContext& context, - base::OnceClosure success_callback) { - TransformKeyIfNeeded( - context, base::BindOnce(&ExtendedAuthenticatorImpl::DoAuthenticateToCheck, - this, std::move(success_callback), - /*unlock_webauthn_secret=*/true)); -} - -void ExtendedAuthenticatorImpl::StartFingerprintAuthSession( - const AccountId& account_id, - base::OnceCallback<void(bool)> callback) { - user_data_auth::StartFingerprintAuthSessionRequest request; - *request.mutable_account_id() = - cryptohome::CreateAccountIdentifierFromAccountId(account_id); - UserDataAuthClient::Get()->StartFingerprintAuthSession( - request, - base::BindOnce( - &ExtendedAuthenticatorImpl::OnStartFingerprintAuthSessionComplete, - this, std::move(callback))); -} - -void ExtendedAuthenticatorImpl::OnStartFingerprintAuthSessionComplete( - base::OnceCallback<void(bool)> callback, - absl::optional<user_data_auth::StartFingerprintAuthSessionReply> reply) { - std::move(callback).Run( - reply.has_value() && - reply->error() == - user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET); -} - -void ExtendedAuthenticatorImpl::EndFingerprintAuthSession() { - UserDataAuthClient::Get()->EndFingerprintAuthSession( - user_data_auth::EndFingerprintAuthSessionRequest(), - base::BindOnce([](absl::optional< - user_data_auth::EndFingerprintAuthSessionReply> - reply) { - if (!reply || - reply->error() != - user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET) { - LOG(ERROR) << "EndFingerprintAuthSession call failed with error: " - << (reply.has_value() ? static_cast<int>(reply->error()) - : -1); - } - })); -} - -void ExtendedAuthenticatorImpl::AuthenticateWithFingerprint( - const UserContext& context, - base::OnceCallback<void(user_data_auth::CryptohomeErrorCode)> callback) { - cryptohome::KeyDefinition key_def; - key_def.type = cryptohome::KeyDefinition::TYPE_FINGERPRINT; - user_data_auth::CheckKeyRequest request; - *request.mutable_account_id() = - cryptohome::CreateAccountIdentifierFromAccountId(context.GetAccountId()); - *request.mutable_authorization_request() = - cryptohome::CreateAuthorizationRequestFromKeyDef(key_def); - UserDataAuthClient::Get()->CheckKey( - request, - base::BindOnce(&ExtendedAuthenticatorImpl::OnFingerprintScanComplete, - this, std::move(callback))); -} - -void ExtendedAuthenticatorImpl::OnFingerprintScanComplete( - base::OnceCallback<void(user_data_auth::CryptohomeErrorCode)> callback, - absl::optional<user_data_auth::CheckKeyReply> reply) { - if (!reply) { - std::move(callback).Run(user_data_auth::CryptohomeErrorCode:: - CRYPTOHOME_ERROR_FINGERPRINT_ERROR_INTERNAL); - return; - } - - std::move(callback).Run(reply->error()); -} - void ExtendedAuthenticatorImpl::TransformKeyIfNeeded( const UserContext& user_context, ContextCallback callback) {
diff --git a/chromeos/ash/components/login/auth/extended_authenticator_impl.h b/chromeos/ash/components/login/auth/extended_authenticator_impl.h index f79a90b..6f22964 100644 --- a/chromeos/ash/components/login/auth/extended_authenticator_impl.h +++ b/chromeos/ash/components/login/auth/extended_authenticator_impl.h
@@ -36,17 +36,6 @@ void SetConsumer(AuthStatusConsumer* consumer) override; void AuthenticateToCheck(const UserContext& context, base::OnceClosure success_callback) override; - void AuthenticateToUnlockWebAuthnSecret( - const UserContext& context, - base::OnceClosure success_callback) override; - void StartFingerprintAuthSession( - const AccountId& account_id, - base::OnceCallback<void(bool)> callback) override; - void EndFingerprintAuthSession() override; - void AuthenticateWithFingerprint( - const UserContext& context, - base::OnceCallback<void(user_data_auth::CryptohomeErrorCode)> callback) - override; void TransformKeyIfNeeded(const UserContext& user_context, ContextCallback callback) override; @@ -69,13 +58,6 @@ base::OnceClosure success_callback, absl::optional<ReplyType> reply); - void OnStartFingerprintAuthSessionComplete( - base::OnceCallback<void(bool)> callback, - absl::optional<user_data_auth::StartFingerprintAuthSessionReply> reply); - void OnFingerprintScanComplete( - base::OnceCallback<void(user_data_auth::CryptohomeErrorCode)> callback, - absl::optional<user_data_auth::CheckKeyReply> reply); - bool salt_obtained_; std::string system_salt_; std::vector<base::OnceClosure> system_salt_callbacks_;
diff --git a/chromeos/ash/components/login/auth/fake_extended_authenticator.cc b/chromeos/ash/components/login/auth/fake_extended_authenticator.cc index 2dc36ea..e71d695 100644 --- a/chromeos/ash/components/login/auth/fake_extended_authenticator.cc +++ b/chromeos/ash/components/login/auth/fake_extended_authenticator.cc
@@ -29,34 +29,6 @@ std::move(success_callback)); } -void FakeExtendedAuthenticator::AuthenticateToUnlockWebAuthnSecret( - const UserContext& context, - base::OnceClosure success_callback) { - DoAuthenticateToCheck(context, /*unlock_webauthn_secret=*/true, - std::move(success_callback)); -} - -void FakeExtendedAuthenticator::StartFingerprintAuthSession( - const AccountId& account_id, - base::OnceCallback<void(bool)> callback) { - std::move(callback).Run(expected_user_context_.GetAccountId() == account_id); -} - -void FakeExtendedAuthenticator::EndFingerprintAuthSession() {} - -void FakeExtendedAuthenticator::AuthenticateWithFingerprint( - const UserContext& context, - base::OnceCallback<void(::user_data_auth::CryptohomeErrorCode)> callback) { - if (expected_user_context_ != context) { - std::move(callback).Run(::user_data_auth::CryptohomeErrorCode:: - CRYPTOHOME_ERROR_FINGERPRINT_RETRY_REQUIRED); - return; - } - - std::move(callback).Run( - ::user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET); -} - void FakeExtendedAuthenticator::TransformKeyIfNeeded( const UserContext& user_context, ContextCallback callback) { @@ -68,7 +40,6 @@ const UserContext& context, bool unlock_webauthn_secret, base::OnceClosure success_callback) { - last_unlock_webauthn_secret_ = unlock_webauthn_secret; if (expected_user_context_ == context) { if (success_callback) std::move(success_callback).Run();
diff --git a/chromeos/ash/components/login/auth/fake_extended_authenticator.h b/chromeos/ash/components/login/auth/fake_extended_authenticator.h index 2ad69bff..ee528cb 100644 --- a/chromeos/ash/components/login/auth/fake_extended_authenticator.h +++ b/chromeos/ash/components/login/auth/fake_extended_authenticator.h
@@ -27,17 +27,6 @@ void SetConsumer(AuthStatusConsumer* consumer) override; void AuthenticateToCheck(const UserContext& context, base::OnceClosure success_callback) override; - void AuthenticateToUnlockWebAuthnSecret( - const UserContext& context, - base::OnceClosure success_callback) override; - void StartFingerprintAuthSession( - const AccountId& account_id, - base::OnceCallback<void(bool)> callback) override; - void EndFingerprintAuthSession() override; - void AuthenticateWithFingerprint( - const UserContext& context, - base::OnceCallback<void(::user_data_auth::CryptohomeErrorCode)> callback) - override; void TransformKeyIfNeeded(const UserContext& user_context, ContextCallback callback) override;
diff --git a/chromeos/ash/services/assistant/public/cpp/features.cc b/chromeos/ash/services/assistant/public/cpp/features.cc index 571dc5f..20632a0 100644 --- a/chromeos/ash/services/assistant/public/cpp/features.cc +++ b/chromeos/ash/services/assistant/public/cpp/features.cc
@@ -50,7 +50,7 @@ BASE_FEATURE(kEnableLibAssistantV2, "LibAssistantV2", - base::FEATURE_ENABLED_BY_DEFAULT); + base::FEATURE_DISABLED_BY_DEFAULT); BASE_FEATURE(kEnableLibAssistantDLC, "LibAssistantDLC",
diff --git a/chromeos/crosapi/mojom/sync.mojom b/chromeos/crosapi/mojom/sync.mojom index 2deaa45a..0be5e2b 100644 --- a/chromeos/crosapi/mojom/sync.mojom +++ b/chromeos/crosapi/mojom/sync.mojom
@@ -94,5 +94,12 @@ // Binds SyncedSessionClient, that lives in ash-chrome. [MinVersion = 2] - BindSyncedSessionClient@2(pending_receiver<SyncedSessionClient> receiver); + DEPRECATED_BindSyncedSessionClient@2( + pending_receiver<SyncedSessionClient> receiver); + + // Creates a SyncedSessionClient which lives in ash-chrome. With this + // method Lacros can inspect the version info of the Ash implementation. + [MinVersion = 3] + CreateSyncedSessionClient@3() + => (pending_remote<SyncedSessionClient>? sync_session_client); };
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni index e1a07e3..b5d0082e 100644 --- a/chromeos/tast_control.gni +++ b/chromeos/tast_control.gni
@@ -327,6 +327,9 @@ "crostini.AppVscode.bullseye_clamshell_stable", "crostini.AppVscode.clamshell_stable", + # https://crbug.com/1424388 + "health.ProbeFanInfo", + # READ COMMENT AT TOP BEFORE ADDING NEW TESTS HERE. ]
diff --git a/components/BUILD.gn b/components/BUILD.gn index 8fc8e86d..42a1417 100644 --- a/components/BUILD.gn +++ b/components/BUILD.gn
@@ -175,6 +175,7 @@ "//components/open_from_clipboard:unit_tests", "//components/openscreen_platform:unittests", "//components/optimization_guide/core:unit_tests", + "//components/os_crypt/async/browser:unit_tests", "//components/os_crypt/sync:unit_tests", "//components/password_manager/core/browser:unit_tests", "//components/payments/core:unit_tests",
diff --git a/components/content_settings/core/browser/content_settings_policy_provider.cc b/components/content_settings/core/browser/content_settings_policy_provider.cc index 856709c..2a742f9 100644 --- a/components/content_settings/core/browser/content_settings_policy_provider.cc +++ b/components/content_settings/core/browser/content_settings_policy_provider.cc
@@ -107,6 +107,9 @@ ContentSettingsType::LOCAL_FONTS, CONTENT_SETTING_ALLOW}, {prefs::kManagedLocalFontsBlockedForUrls, ContentSettingsType::LOCAL_FONTS, CONTENT_SETTING_BLOCK}, + {prefs::kManagedThirdPartyStoragePartitioningBlockedForOrigins, + ContentSettingsType::THIRD_PARTY_STORAGE_PARTITIONING, + CONTENT_SETTING_BLOCK}, }; constexpr const char* kManagedPrefs[] = { @@ -148,6 +151,7 @@ prefs::kManagedWindowManagementBlockedForUrls, prefs::kManagedLocalFontsAllowedForUrls, prefs::kManagedLocalFontsBlockedForUrls, + prefs::kManagedThirdPartyStoragePartitioningBlockedForOrigins, }; // The following preferences are only used to indicate if a default content
diff --git a/components/content_settings/core/common/pref_names.cc b/components/content_settings/core/common/pref_names.cc index 8decd1c..102cd74e 100644 --- a/components/content_settings/core/common/pref_names.cc +++ b/components/content_settings/core/common/pref_names.cc
@@ -143,6 +143,8 @@ "profile.managed_local_fonts_allowed_for_urls"; const char kManagedLocalFontsBlockedForUrls[] = "profile.managed_local_fonts_blocked_for_urls"; +const char kManagedThirdPartyStoragePartitioningBlockedForOrigins[] = + "profile.managed_third_party_storage_partitioning_blocked_for_origins"; // Boolean indicating whether the quiet UI is enabled for notification // permission requests.
diff --git a/components/content_settings/core/common/pref_names.h b/components/content_settings/core/common/pref_names.h index 090ccbe..f4ca7fe 100644 --- a/components/content_settings/core/common/pref_names.h +++ b/components/content_settings/core/common/pref_names.h
@@ -79,6 +79,7 @@ extern const char kManagedWindowManagementBlockedForUrls[]; extern const char kManagedLocalFontsAllowedForUrls[]; extern const char kManagedLocalFontsBlockedForUrls[]; +extern const char kManagedThirdPartyStoragePartitioningBlockedForOrigins[]; extern const char kEnableQuietNotificationPermissionUi[]; extern const char kQuietNotificationPermissionUiEnablingMethod[];
diff --git a/components/enterprise/browser/controller/chrome_browser_cloud_management_controller.cc b/components/enterprise/browser/controller/chrome_browser_cloud_management_controller.cc index f30de17b..7b9ee9b 100644 --- a/components/enterprise/browser/controller/chrome_browser_cloud_management_controller.cc +++ b/components/enterprise/browser/controller/chrome_browser_cloud_management_controller.cc
@@ -375,6 +375,7 @@ } void ChromeBrowserCloudManagementController::ShutDown() { + NotifyShutdown(); delegate_->ShutDown(); if (report_scheduler_) report_scheduler_.reset(); @@ -412,6 +413,12 @@ } } +void ChromeBrowserCloudManagementController::NotifyShutdown() { + for (auto& observer : observers_) { + observer.OnShutdown(); + } +} + bool ChromeBrowserCloudManagementController::GetEnrollmentTokenAndClientId( std::string* enrollment_token, std::string* client_id) {
diff --git a/components/enterprise/browser/controller/chrome_browser_cloud_management_controller.h b/components/enterprise/browser/controller/chrome_browser_cloud_management_controller.h index be342e8..210f17a 100644 --- a/components/enterprise/browser/controller/chrome_browser_cloud_management_controller.h +++ b/components/enterprise/browser/controller/chrome_browser_cloud_management_controller.h
@@ -181,6 +181,9 @@ // Called when enrollment result is recorded. virtual void OnEnrollmentResultRecorded() {} + + // Called when shutting down. + virtual void OnShutdown() {} }; // Directory name under the user-data-dir where the policy data is stored. @@ -264,6 +267,7 @@ void NotifyPolicyRegisterFinished(bool succeeded); void NotifyBrowserUnenrolled(bool succeeded); void NotifyCloudReportingLaunched(); + void NotifyShutdown(); private: bool GetEnrollmentTokenAndClientId(std::string* enrollment_token,
diff --git a/components/os_crypt/sync/DIR_METADATA b/components/os_crypt/DIR_METADATA similarity index 100% rename from components/os_crypt/sync/DIR_METADATA rename to components/os_crypt/DIR_METADATA
diff --git a/components/os_crypt/sync/OWNERS b/components/os_crypt/OWNERS similarity index 100% rename from components/os_crypt/sync/OWNERS rename to components/os_crypt/OWNERS
diff --git a/components/os_crypt/README.md b/components/os_crypt/README.md new file mode 100644 index 0000000..84ed9149 --- /dev/null +++ b/components/os_crypt/README.md
@@ -0,0 +1,13 @@ +***OS Crypt*** + +This directory contains `OSCrypt` implementations that support cryptographic +primitives that allow binding data to the OS user. + +There are two implementations, a [sync](sync) interface which can be called on +any thread, and an [async](async) interface that is instance based. + +The [async](async) interface is currently under construction and so all current +usage should go via the [sync](sync) interface. + +Please see the README.md in those directories for more information on each +implementation.
diff --git a/components/os_crypt/async/OWNERS b/components/os_crypt/async/OWNERS new file mode 100644 index 0000000..e5e1667 --- /dev/null +++ b/components/os_crypt/async/OWNERS
@@ -0,0 +1,2 @@ +# Also, feel free to use components/os_crypt/OWNERS. +wfh@chromium.org
diff --git a/components/os_crypt/async/README.md b/components/os_crypt/async/README.md new file mode 100644 index 0000000..69cf8d29 --- /dev/null +++ b/components/os_crypt/async/README.md
@@ -0,0 +1,27 @@ +***OS Crypt Async*** + +This directory contains the new version of `OSCrypt` that supports asynchronous +initialization and pluggable providers. + +**Main interfaces** + +`browser/` should only be included by code that lives in the browser process. An +instance of `OSCryptAsync` should be constructed and held in browser and is +responsible for minting `Encryptor` instances. + +`GetInstance` can be called as many times as necessary to obtain instances of +`Encryptor` that should be used for encryption operations. Note that +`GetInstance` returns a `base::CallbackListSubscription` whose destruction will +cause the callback to never run. This should be stored with the same lifetime as +the callback to ensure correct function. See documentation for +`base::CallbackList` for more on this. + +`common/` can be included by any code in any process and allows `Encryptor` +instances to perform encrypt/decrypt operations. These `EncryptString` and +`DecryptString` operations are sync and can be called on any thread, the same as +with legacy `os_crypt::OSCrypt`. + +It is preferred to use the `base::span` `EncryptData` and `DecryptData` APIs, +however the `EncryptString` and `DecryptString` APIs are provided for ease of +compatibility with existing callers of `os_crypt::OSCrypt`. The string and span +APIs are compatible with one another.
diff --git a/components/os_crypt/async/browser/BUILD.gn b/components/os_crypt/async/browser/BUILD.gn new file mode 100644 index 0000000..828f9291 --- /dev/null +++ b/components/os_crypt/async/browser/BUILD.gn
@@ -0,0 +1,35 @@ +# Copyright 2023 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +component("browser") { + sources = [ + "os_crypt_async.cc", + "os_crypt_async.h", + ] + + public_deps = [ + "//base", + "//components/os_crypt/async/common", + ] + + defines = [ "IS_OS_CRYPT_ASYNC_IMPL" ] +} + +source_set("unit_tests") { + testonly = true + sources = [ "os_crypt_async_unittest.cc" ] + deps = [ + ":browser", + "//base", + "//base/test:test_support", + "//components/os_crypt/async/common", + "//components/os_crypt/sync", + "//components/os_crypt/sync:test_support", + "//testing/gtest", + ] + + if (is_win) { + libs = [ "crypt32.lib" ] + } +}
diff --git a/components/os_crypt/async/browser/DEPS b/components/os_crypt/async/browser/DEPS new file mode 100644 index 0000000..094b26b --- /dev/null +++ b/components/os_crypt/async/browser/DEPS
@@ -0,0 +1,3 @@ +include_rules = [ + "+components/os_crypt/sync", +]
diff --git a/components/os_crypt/async/browser/os_crypt_async.cc b/components/os_crypt/async/browser/os_crypt_async.cc new file mode 100644 index 0000000..0188169 --- /dev/null +++ b/components/os_crypt/async/browser/os_crypt_async.cc
@@ -0,0 +1,34 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/os_crypt/async/browser/os_crypt_async.h" + +#include "base/callback_list.h" +#include "base/sequence_checker.h" +#include "base/types/pass_key.h" +#include "components/os_crypt/async/common/encryptor.h" + +namespace os_crypt_async { + +OSCryptAsync::OSCryptAsync() = default; +OSCryptAsync::~OSCryptAsync() = default; + +OSCryptAsync::OSCryptAsync(OSCryptAsync&& other) = default; +OSCryptAsync& OSCryptAsync::operator=(OSCryptAsync&& other) = default; + +base::CallbackListSubscription OSCryptAsync::GetInstance( + InitCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (!is_initialized_) { + encryptor_instance_ = + std::make_unique<Encryptor>(/*passkey=*/base::PassKey<OSCryptAsync>()); + is_initialized_ = true; + } + + std::move(callback).Run(encryptor_instance_->Clone(), true); + return base::CallbackListSubscription(); +} + +} // namespace os_crypt_async
diff --git a/components/os_crypt/async/browser/os_crypt_async.h b/components/os_crypt/async/browser/os_crypt_async.h new file mode 100644 index 0000000..be17231 --- /dev/null +++ b/components/os_crypt/async/browser/os_crypt_async.h
@@ -0,0 +1,54 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_OS_CRYPT_ASYNC_BROWSER_OS_CRYPT_ASYNC_H_ +#define COMPONENTS_OS_CRYPT_ASYNC_BROWSER_OS_CRYPT_ASYNC_H_ + +#include "base/callback_list.h" +#include "base/component_export.h" +#include "base/functional/callback.h" +#include "base/sequence_checker.h" +#include "components/os_crypt/async/common/encryptor.h" + +namespace os_crypt_async { + +// This class is responsible for vending Encryptor instances. +class COMPONENT_EXPORT(OS_CRYPT_ASYNC) OSCryptAsync { + public: + using InitCallback = base::OnceCallback<void(Encryptor, bool result)>; + + // TODO(crbug.com/1373092): add configuration parameters here, and a + // UIThreadRunner parameter. + OSCryptAsync(); + ~OSCryptAsync(); + + // Moveable, not copyable. + OSCryptAsync(OSCryptAsync&& other); + OSCryptAsync& operator=(OSCryptAsync&& other); + OSCryptAsync(const OSCryptAsync&) = delete; + OSCryptAsync& operator=(const OSCryptAsync&) = delete; + + // Obtain an Encryptor instance. Can be called multiple times, each one will + // get a valid instance once the initialization has completed, on the + // `callback`. Must be called on the same sequence that the OSCryptAsync + // object was created on. Destruction of the `base::CallbackListSubscription` + // will cause the callback not to run, see `base/callback_list.h`. + // + // TODO(crbug.com/1373092): This function is currently sync, but will be made + // async in a future CL. + [[nodiscard]] base::CallbackListSubscription GetInstance( + InitCallback callback); + + private: + std::unique_ptr<Encryptor> GUARDED_BY_CONTEXT(sequence_checker_) + encryptor_instance_; + + bool is_initialized_ GUARDED_BY_CONTEXT(sequence_checker_) = false; + + SEQUENCE_CHECKER(sequence_checker_); +}; + +} // namespace os_crypt_async + +#endif // COMPONENTS_OS_CRYPT_ASYNC_BROWSER_OS_CRYPT_ASYNC_H_
diff --git a/components/os_crypt/async/browser/os_crypt_async_unittest.cc b/components/os_crypt/async/browser/os_crypt_async_unittest.cc new file mode 100644 index 0000000..dfbadc1 --- /dev/null +++ b/components/os_crypt/async/browser/os_crypt_async_unittest.cc
@@ -0,0 +1,244 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/os_crypt/async/browser/os_crypt_async.h" + +#include <algorithm> +#include <vector> + +#include "base/containers/span.h" +#include "base/run_loop.h" +#include "base/test/bind.h" +#include "base/test/gtest_util.h" +#include "base/test/task_environment.h" +#include "build/build_config.h" +#include "components/os_crypt/async/common/encryptor.h" +#include "components/os_crypt/sync/os_crypt.h" +#include "components/os_crypt/sync/os_crypt_mocker.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if BUILDFLAG(IS_WIN) +#include <windows.h> + +#include <wincrypt.h> + +#include "base/win/scoped_localalloc.h" +#endif // BUILDFLAG(IS_WIN) + +namespace os_crypt_async { + +namespace { + +#if BUILDFLAG(IS_WIN) +// Utility function to encrypt data using the raw DPAPI interface. +bool EncryptStringWithDPAPI(const std::string& plaintext, + std::string& ciphertext) { + DATA_BLOB input = {}; + input.pbData = + const_cast<BYTE*>(reinterpret_cast<const BYTE*>(plaintext.data())); + input.cbData = static_cast<DWORD>(plaintext.length()); + + BOOL result = FALSE; + DATA_BLOB output = {}; + result = + ::CryptProtectData(&input, /*szDataDescr=*/L"", + /*pOptionalEntropy=*/nullptr, /*pvReserved=*/nullptr, + /*pPromptStruct=*/nullptr, /*dwFlags=*/0, &output); + if (!result) { + return false; + } + base::win::ScopedLocalAlloc scoped_memory(output.pbData); + + ciphertext.assign(reinterpret_cast<std::string::value_type*>(output.pbData), + output.cbData); + + return true; +} +#endif // BUILDFLAG(IS_WIN) + +} // namespace + +class OSCryptAsyncTest : public ::testing::Test { + protected: + void SetUp() override { OSCryptMocker::SetUp(); } + + void TearDown() override { + OSCryptMocker::TearDown(); +#if BUILDFLAG(IS_WIN) + OSCrypt::ResetStateForTesting(); +#endif // BUILDFLAG(IS_WIN) + } + + std::unique_ptr<Encryptor> GetInstanceSync() { + base::RunLoop run_loop; + std::unique_ptr<Encryptor> encryptor; + auto sub = factory_.GetInstance(base::BindLambdaForTesting( + [&](Encryptor encryptor_param, bool success) { + EXPECT_TRUE(success); + encryptor = std::make_unique<Encryptor>(std::move(encryptor_param)); + run_loop.Quit(); + })); + run_loop.Run(); + return encryptor; + } + + OSCryptAsync factory_; + + private: + base::test::TaskEnvironment task_environment_; +}; + +TEST_F(OSCryptAsyncTest, StringInterface) { + std::unique_ptr<Encryptor> encryptor = GetInstanceSync(); + ASSERT_TRUE(encryptor); + std::string plaintext = "secrets"; + std::string ciphertext; + ASSERT_TRUE(encryptor->EncryptString(plaintext, &ciphertext)); + std::string decrypted; + ASSERT_TRUE(encryptor->DecryptString(ciphertext, &decrypted)); + EXPECT_EQ(plaintext, decrypted); +} + +TEST_F(OSCryptAsyncTest, SpanInterface) { + std::unique_ptr<Encryptor> encryptor = GetInstanceSync(); + ASSERT_TRUE(encryptor); + std::string plaintext = "secrets"; + + auto ciphertext = encryptor->EncryptString(plaintext); + ASSERT_TRUE(ciphertext.has_value()); + + auto decrypted = encryptor->DecryptData(*ciphertext); + + ASSERT_TRUE(decrypted.has_value()); + + EXPECT_EQ(plaintext, *decrypted); +} + +TEST_F(OSCryptAsyncTest, EncryptStringDecryptSpan) { + std::unique_ptr<Encryptor> encryptor = GetInstanceSync(); + ASSERT_TRUE(encryptor); + std::string plaintext = "secrets"; + std::string ciphertext; + ASSERT_TRUE(encryptor->EncryptString(plaintext, &ciphertext)); + + auto decrypted = + encryptor->DecryptData(base::as_bytes(base::make_span(ciphertext))); + + ASSERT_TRUE(decrypted.has_value()); + + ASSERT_EQ(plaintext.size(), decrypted->size()); + + ASSERT_TRUE( + std::equal(plaintext.cbegin(), plaintext.cend(), decrypted->cbegin())); +} + +TEST_F(OSCryptAsyncTest, EncryptSpanDecryptString) { + std::unique_ptr<Encryptor> encryptor = GetInstanceSync(); + ASSERT_TRUE(encryptor); + std::string plaintext = "secrets"; + + auto ciphertext = encryptor->EncryptString(plaintext); + ASSERT_TRUE(ciphertext.has_value()); + + std::string decrypted; + ASSERT_TRUE(encryptor->DecryptString( + std::string(ciphertext->begin(), ciphertext->end()), &decrypted)); + ASSERT_EQ(plaintext.size(), decrypted.size()); + + ASSERT_TRUE( + std::equal(plaintext.cbegin(), plaintext.cend(), decrypted.cbegin())); +} + +TEST_F(OSCryptAsyncTest, EncryptEmpty) { + std::unique_ptr<Encryptor> encryptor = GetInstanceSync(); + ASSERT_TRUE(encryptor); + + auto ciphertext = encryptor->EncryptString(std::string()); + ASSERT_TRUE(ciphertext.has_value()); + + auto decrypted = encryptor->DecryptData(*ciphertext); + ASSERT_TRUE(decrypted); + ASSERT_TRUE(decrypted->empty()); +} + +// In a behavior change on Windows, Decrypt/Encrypt of empty data results in a +// success and an empty buffer. This was already the behavior on non-Windows so +// this change makes it consistent. +TEST_F(OSCryptAsyncTest, DecryptEmpty) { + std::unique_ptr<Encryptor> encryptor = GetInstanceSync(); + ASSERT_TRUE(encryptor); + + auto plaintext = encryptor->DecryptData({}); + ASSERT_TRUE(plaintext); + ASSERT_TRUE(plaintext->empty()); +} + +// Non-Windows platforms can decrypt random data fine. +#if BUILDFLAG(IS_WIN) +TEST_F(OSCryptAsyncTest, DecryptInvalid) { + std::unique_ptr<Encryptor> encryptor = GetInstanceSync(); + ASSERT_TRUE(encryptor); + + std::vector<uint8_t> invalid_cipher(100); + for (auto c : invalid_cipher) { + invalid_cipher[c] = c; + } + + auto plaintext = encryptor->DecryptData(invalid_cipher); + ASSERT_FALSE(plaintext.has_value()); +} +#endif // BUILDFLAG(IS_WIN) + +// This test verifies that GetInstanceAsync can correctly handle multiple queued +// requests for an instance for a slow init. +TEST_F(OSCryptAsyncTest, MultipleCalls) { + size_t calls = 0; + const size_t kExpectedCalls = 10; + base::RunLoop run_loop; + std::list<base::CallbackListSubscription> subs; + for (size_t call = 0; call < kExpectedCalls; call++) { + subs.push_back(factory_.GetInstance(base::BindLambdaForTesting( + [&calls, &run_loop](Encryptor encryptor, bool success) { + calls++; + if (calls == kExpectedCalls) { + run_loop.Quit(); + } + }))); + } + run_loop.Run(); + EXPECT_EQ(calls, kExpectedCalls); +} + +// Encryptor can decrypt data encrypted with OSCrypt. +TEST_F(OSCryptAsyncTest, DecryptFallback) { + std::string ciphertext; + EXPECT_TRUE(OSCrypt::EncryptString("secret", &ciphertext)); + + std::unique_ptr<Encryptor> encryptor = GetInstanceSync(); + + std::string decrypted; + // Fallback to OSCrypt takes place. + EXPECT_TRUE(encryptor->DecryptString(ciphertext, &decrypted)); + + EXPECT_EQ("secret", decrypted); +} + +#if BUILDFLAG(IS_WIN) +// Encryptor should still decrypt data encrypted using DPAPI (pre-m79) by fall +// back to OSCrypt. +TEST_F(OSCryptAsyncTest, AncientFallback) { + std::unique_ptr<Encryptor> encryptor = GetInstanceSync(); + + std::string ciphertext; + EXPECT_TRUE(EncryptStringWithDPAPI("secret", ciphertext)); + + std::string decrypted; + // Encryptor can still decrypt very old DPAPI data. + EXPECT_TRUE(encryptor->DecryptString(ciphertext, &decrypted)); + + EXPECT_EQ("secret", decrypted); +} +#endif // BUILDFLAG(IS_WIN) + +} // namespace os_crypt_async
diff --git a/components/os_crypt/async/common/BUILD.gn b/components/os_crypt/async/common/BUILD.gn new file mode 100644 index 0000000..d17bef6 --- /dev/null +++ b/components/os_crypt/async/common/BUILD.gn
@@ -0,0 +1,15 @@ +# Copyright 2023 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("common") { + sources = [ + "encryptor.cc", + "encryptor.h", + ] + + deps = [ + "//base", + "//components/os_crypt/sync", + ] +}
diff --git a/components/os_crypt/async/common/DEPS b/components/os_crypt/async/common/DEPS new file mode 100644 index 0000000..094b26b --- /dev/null +++ b/components/os_crypt/async/common/DEPS
@@ -0,0 +1,3 @@ +include_rules = [ + "+components/os_crypt/sync", +]
diff --git a/components/os_crypt/async/common/encryptor.cc b/components/os_crypt/async/common/encryptor.cc new file mode 100644 index 0000000..df06c67 --- /dev/null +++ b/components/os_crypt/async/common/encryptor.cc
@@ -0,0 +1,85 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/os_crypt/async/common/encryptor.h" + +#include <string> +#include <vector> + +#include "base/containers/span.h" +#include "base/types/pass_key.h" +#include "components/os_crypt/sync/os_crypt.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace os_crypt_async { + +Encryptor::Encryptor(Encryptor&& other) = default; +Encryptor& Encryptor::operator=(Encryptor&& other) = default; + +Encryptor::Encryptor() = default; +Encryptor::Encryptor(base::PassKey<OSCryptAsync> passkey) {} + +Encryptor::~Encryptor() = default; + +bool Encryptor::EncryptString(const std::string& plaintext, + std::string* ciphertext) const { + auto encrypted = EncryptString(plaintext); + + if (!encrypted.has_value()) { + return false; + } + + *ciphertext = std::string(encrypted->begin(), encrypted->end()); + + return true; +} + +bool Encryptor::DecryptString(const std::string& ciphertext, + std::string* plaintext) const { + auto decrypted = DecryptData(base::as_bytes(base::make_span(ciphertext))); + + if (!decrypted.has_value()) { + return false; + } + + *plaintext = std::string(decrypted->begin(), decrypted->end()); + + return true; +} + +absl::optional<std::vector<uint8_t>> Encryptor::EncryptString( + const std::string& data) const { + if (data.empty()) { + return std::vector<uint8_t>(); + } + + std::string string_data(data.begin(), data.end()); + std::string ciphertext; + if (OSCrypt::EncryptString(string_data, &ciphertext)) { + return std::vector<uint8_t>(ciphertext.cbegin(), ciphertext.cend()); + } + + return absl::nullopt; +} + +absl::optional<std::string> Encryptor::DecryptData( + base::span<const uint8_t> data) const { + if (data.empty()) { + return std::string(); + } + + std::string string_data(data.begin(), data.end()); + std::string plaintext; + if (OSCrypt::DecryptString(string_data, &plaintext)) { + return plaintext; + } + + return absl::nullopt; +} + +Encryptor Encryptor::Clone() const { + return Encryptor(); +} + +} // namespace os_crypt_async
diff --git a/components/os_crypt/async/common/encryptor.h b/components/os_crypt/async/common/encryptor.h new file mode 100644 index 0000000..148f129 --- /dev/null +++ b/components/os_crypt/async/common/encryptor.h
@@ -0,0 +1,64 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_OS_CRYPT_ASYNC_COMMON_ENCRYPTOR_H_ +#define COMPONENTS_OS_CRYPT_ASYNC_COMMON_ENCRYPTOR_H_ + +#include <string> +#include <vector> + +#include "base/containers/span.h" +#include "base/functional/callback.h" +#include "base/gtest_prod_util.h" +#include "base/types/pass_key.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace os_crypt_async { + +class OSCryptAsync; + +// This class is used for data encryption. A thread-safe instance can be +// obtained by calling `os_crypt_async::OSCryptAsync::GetInstance`. +class Encryptor { + public: + explicit Encryptor(base::PassKey<OSCryptAsync> passkey); + ~Encryptor(); + + // Moveable, not copyable. + Encryptor(Encryptor&& other); + Encryptor& operator=(Encryptor&& other); + Encryptor(const Encryptor&) = delete; + Encryptor& operator=(const Encryptor&) = delete; + + // Encrypt a string with the current Encryptor configuration. This can be + // called on any thread. + [[nodiscard]] absl::optional<std::vector<uint8_t>> EncryptString( + const std::string& data) const; + + // Decrypt data previously encrypted using `EncryptData`. This can be called + // on any thread. + [[nodiscard]] absl::optional<std::string> DecryptData( + base::span<const uint8_t> data) const; + + // These two APIs are provided for backwards compatibility with OSCrypt. They + // just call the above functions. The two sets of functions are compatible + // with each other. + [[nodiscard]] bool EncryptString(const std::string& plaintext, + std::string* ciphertext) const; + [[nodiscard]] bool DecryptString(const std::string& ciphertext, + std::string* plaintext) const; + + private: + friend class OSCryptAsync; + + // Used for cloning. + Encryptor(); + + // Clone is used by the factory to vend instances. + Encryptor Clone() const; +}; + +} // namespace os_crypt_async + +#endif // COMPONENTS_OS_CRYPT_ASYNC_COMMON_ENCRYPTOR_H_
diff --git a/components/os_crypt/sync/BUILD.gn b/components/os_crypt/sync/BUILD.gn index 84afa83..3314c2a 100644 --- a/components/os_crypt/sync/BUILD.gn +++ b/components/os_crypt/sync/BUILD.gn
@@ -74,6 +74,7 @@ if (is_win) { sources += [ "os_crypt_win.cc" ] + deps += [ "//components/version_info" ] libs = [ "crypt32.lib" ] }
diff --git a/components/os_crypt/sync/DEPS b/components/os_crypt/sync/DEPS index 30eb83d..a6e42d2 100644 --- a/components/os_crypt/sync/DEPS +++ b/components/os_crypt/sync/DEPS
@@ -1,5 +1,6 @@ include_rules = [ "+components/prefs", + "+components/version_info", "+crypto", "+dbus", ]
diff --git a/components/os_crypt/sync/README.md b/components/os_crypt/sync/README.md new file mode 100644 index 0000000..50613814 --- /dev/null +++ b/components/os_crypt/sync/README.md
@@ -0,0 +1,30 @@ +***OS Crypt (Sync)*** + +This directory contains an `OSCrypt` implementation that supports cryptographic +primitives that allow binding data to the OS user. + +[os_crypt.h](os_crypt.h) contains the main interface. + +The interface supports both instance based and a singleton interface, most +callers will use the singleton interface via convenience functions that handle +obtaining the singleton and calling directly into it. Advanced usage can +directly create an `OSCryptImpl` if needed, or access the singleton via +`GetInstance`. + +Initialization is done per-process and can is usually done by calling the +platform-specific initialization function, which should take place before any +calls to encrypt or decrypt data occur. + +* Linux - `SetConfig` +* Windows - `Init` + +Alternatively, `OSCrypt` can be initialized with a key directly by using +`SetRawEncryptionKey` (or `InitWithExistingKey` - Windows only). This can also +be used to initialize OSCrypt in a non-browser process using a key supplied by +the browser. + +The main functions are `EncryptString` and `DecryptString`. These can be called +on any thread and will return a user-bound encrypted string. It is guaranteed +that a string encrypted with `EncryptString` will be able to successfully +decrypt if `DecryptString` is called in the same user context. The exact +definition of user context is OS defined.
diff --git a/components/os_crypt/sync/os_crypt_win.cc b/components/os_crypt/sync/os_crypt_win.cc index 413d058..7fac2ca 100644 --- a/components/os_crypt/sync/os_crypt_win.cc +++ b/components/os_crypt/sync/os_crypt_win.cc
@@ -5,16 +5,19 @@ #include <windows.h> #include "base/base64.h" +#include "base/feature_list.h" #include "base/logging.h" #include "base/memory/singleton.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/strings/string_util.h" +#include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/win/wincrypt_shim.h" #include "components/os_crypt/sync/os_crypt.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" +#include "components/version_info/version_info.h" #include "crypto/aead.h" #include "crypto/hkdf.h" #include "crypto/random.h" @@ -36,6 +39,9 @@ // Key prefix for a key encrypted with DPAPI. constexpr char kDPAPIKeyPrefix[] = "DPAPI"; +// Name used for a feature to used named encryption source in DPAPI. +constexpr char kNamedEncryptionSourceName[] = "NamedEncryptionSource"; + bool EncryptStringWithDPAPI(const std::string& plaintext, std::string* ciphertext) { DATA_BLOB input; @@ -47,8 +53,21 @@ DATA_BLOB output; { SCOPED_UMA_HISTOGRAM_TIMER("OSCrypt.Win.Encrypt.Time"); - result = - CryptProtectData(&input, L"", nullptr, nullptr, nullptr, 0, &output); + static BASE_FEATURE(kNamedEncyptionSource, kNamedEncryptionSourceName, + base::FEATURE_ENABLED_BY_DEFAULT); + DWORD flags = 0; + std::string data_description; + if (base::FeatureList::IsEnabled(kNamedEncyptionSource)) { + data_description = version_info::GetProductName(); + flags = CRYPTPROTECT_AUDIT; + } + result = ::CryptProtectData( + /*pDataIn=*/&input, + /*szDataDescr=*/base::SysUTF8ToWide(data_description).c_str(), + /*pOptionalEntropy=*/nullptr, + /*pvReserved=*/nullptr, + /*pPromptStruct=*/nullptr, /*dwFlags=*/flags, + /*pDataOut=*/&output); } base::UmaHistogramBoolean("OSCrypt.Win.Encrypt.Result", result); if (!result) {
diff --git a/components/performance_manager/features.cc b/components/performance_manager/features.cc index 1e5cadb..2fdb4b3 100644 --- a/components/performance_manager/features.cc +++ b/components/performance_manager/features.cc
@@ -83,6 +83,21 @@ 0, #endif // BUILDFLAG(IS_CHROMEOS_ASH) }; + +BASE_FEATURE(kHeuristicMemorySaver, + "HeuristicMemorySaver", + base::FEATURE_DISABLED_BY_DEFAULT); + +const base::FeatureParam<int> kHeuristicMemorySaverHeartbeatSeconds{ + &kHeuristicMemorySaver, "heartbeat_seconds", 10}; + +const base::FeatureParam<int> + kHeuristicMemorySaverAvailableMemoryThresholdPercent{ + &kHeuristicMemorySaver, "threshold_percent", 5}; + +const base::FeatureParam<int> kHeuristicMemorySaverMinimumMinutesInBackground{ + &kHeuristicMemorySaver, "minimum_minutes_in_background", 120}; + #endif BASE_FEATURE(kBFCachePerformanceManagerPolicy,
diff --git a/components/performance_manager/public/features.h b/components/performance_manager/public/features.h index 950e472..0782799 100644 --- a/components/performance_manager/public/features.h +++ b/components/performance_manager/public/features.h
@@ -75,6 +75,26 @@ // 23% actual battery level). extern const base::FeatureParam<int> kBatterySaverModeThresholdAdjustmentForDisplayLevel; + +// When enabled, the memory saver policy used is HeuristicMemorySaverPolicy. +BASE_DECLARE_FEATURE(kHeuristicMemorySaver); + +// Controls the interval at which HeuristicMemorySaverPolicy checks whether the +// amount of available memory is smaller than the discarding threshold. +extern const base::FeatureParam<int> kHeuristicMemorySaverHeartbeatSeconds; + +// The percentage of available physical memory at which +// HeuristicMemorySaverPolicy will start discarding tabs. For example, setting +// this param to 10 will cause HeuristicMemorySaverPolicy to discard tabs +// periodically as long as the available system memory is under 10%. +extern const base::FeatureParam<int> + kHeuristicMemorySaverAvailableMemoryThresholdPercent; + +// The minimum amount of minutes a tab has to spend in the background before +// HeuristicMemorySaverPolicy will consider it eligible for discarding. +extern const base::FeatureParam<int> + kHeuristicMemorySaverMinimumMinutesInBackground; + #endif // Policy that evicts the BFCache of pages that become non visible or the
diff --git a/components/policy/resources/templates/policies.yaml b/components/policy/resources/templates/policies.yaml index 92a04e9..e59c4bc2 100644 --- a/components/policy/resources/templates/policies.yaml +++ b/components/policy/resources/templates/policies.yaml
@@ -1089,6 +1089,7 @@ 1088: ClientSidePhishingProtectionAllowed 1089: UserAvatarCustomizationSelectorsEnabled 1090: DefaultThirdPartyStoragePartitioningSetting + 1091: ThirdPartyStoragePartitioningBlockedForOrigins atomic_groups: 1: Homepage 2: RemoteAccess @@ -1136,3 +1137,4 @@ 44: BrowserIdle 45: PrivacySandbox 46: KerberosPrefilledConfig + 47: ThirdPartyStoragePartitioningSettings
diff --git a/components/policy/resources/templates/policy_definitions/BrowserIdle/IdleTimeoutActions.yaml b/components/policy/resources/templates/policy_definitions/BrowserIdle/IdleTimeoutActions.yaml index 3416564..d141c076 100644 --- a/components/policy/resources/templates/policy_definitions/BrowserIdle/IdleTimeoutActions.yaml +++ b/components/policy/resources/templates/policy_definitions/BrowserIdle/IdleTimeoutActions.yaml
@@ -14,7 +14,9 @@ '<ph name="SHOW_PROFILE_PICKER_ACTION">show_profile_picker</ph>': show the Profile Picker window. Not supported on Android. - '<ph name="CLEAR_BROWSING_HISTORY_ACTION">clear_browsing_history</ph>', '<ph name="CLEAR_DOWNLOAD_HISTORY_ACTION">clear_download_history</ph>', '<ph name="CLEAR_COOKIES_AND_OTHER_SITE_DATA_ACTION">clear_cookies_and_other_site_data</ph>', '<ph name="CLEAR_CACHED_IMAGES_AND_FILES_ACTION">clear_cached_images_and_files</ph>', '<ph name="CLEAR_PASSWORD_SIGNIN_ACTION">clear_password_signing</ph>', '<ph name="CLEAR_AUTOFILL_ACTION">clear_autofill</ph>' , '<ph name="CLEAR_SITE_SETTINGS_ACTION">clear_site_settings</ph>' , '<ph name="CLEAR_HOSTED_APP_DATA_ACTION">clear_hosted_app_data</ph>': clear the corresponding browsing data. See the <ph name="CLEAR_BROWSING_DATA_ON_EXIT_LIST_POLICY_NAME">ClearBrowsingDataOnExitList</ph> policy for more details. + '<ph name="CLEAR_BROWSING_HISTORY_ACTION">clear_browsing_history</ph>', '<ph name="CLEAR_DOWNLOAD_HISTORY_ACTION">clear_download_history</ph>', '<ph name="CLEAR_COOKIES_AND_OTHER_SITE_DATA_ACTION">clear_cookies_and_other_site_data</ph>', '<ph name="CLEAR_CACHED_IMAGES_AND_FILES_ACTION">clear_cached_images_and_files</ph>', '<ph name="CLEAR_PASSWORD_SIGNIN_ACTION">clear_password_signing</ph>', '<ph name="CLEAR_AUTOFILL_ACTION">clear_autofill</ph>', '<ph name="CLEAR_SITE_SETTINGS_ACTION">clear_site_settings</ph>', '<ph name="CLEAR_HOSTED_APP_DATA_ACTION">clear_hosted_app_data</ph>': clear the corresponding browsing data. See the <ph name="CLEAR_BROWSING_DATA_ON_EXIT_LIST_POLICY_NAME">ClearBrowsingDataOnExitList</ph> policy for more details. + + '<ph name="CLEAR_BROWSING_HISTORY_ACTION">clear_browsing_history</ph>', '<ph name="CLEAR_PASSWORD_SIGNIN_ACTION">clear_password_signing</ph>', '<ph name="CLEAR_AUTOFILL_ACTION">clear_autofill</ph>', and '<ph name="CLEAR_HOSTED_APP_DATA_ACTION">clear_hosted_app_data</ph>' require the <ph name="SYNC_DISABLED_POLICY_NAME">SyncDisabled</ph> policy to be set to true. example_value: - close_browsers - show_profile_picker
diff --git a/components/policy/resources/templates/policy_definitions/ContentSettings/DefaultThirdPartyStoragePartitioningSetting.yaml b/components/policy/resources/templates/policy_definitions/ContentSettings/DefaultThirdPartyStoragePartitioningSetting.yaml index 7c08dda..caa9a8c 100644 --- a/components/policy/resources/templates/policy_definitions/ContentSettings/DefaultThirdPartyStoragePartitioningSetting.yaml +++ b/components/policy/resources/templates/policy_definitions/ContentSettings/DefaultThirdPartyStoragePartitioningSetting.yaml
@@ -4,6 +4,7 @@ Third-party storage partitioning is on by default for some users as of M113, but can be disabled via Chrome flag. If this policy is set to <ph name="ALLOW_PARTITIONING_VALUE">AllowPartitioning</ph> or unset, third-party storage partitioning may be enabled. If this policy is set to <ph name="BLOCK_PARTITIONING_VALUE">BlockPartitioning</ph>, third-party storage partitioning cannot be enabled. + For detailed information on third-party storage partitioning, please see https://developer.chrome.com/docs/privacy-sandbox/storage-partitioning/. example_value: 1 features: dynamic_refresh: true
diff --git a/components/policy/resources/templates/policy_definitions/ContentSettings/ThirdPartyStoragePartitioningBlockedForOrigins.yaml b/components/policy/resources/templates/policy_definitions/ContentSettings/ThirdPartyStoragePartitioningBlockedForOrigins.yaml new file mode 100644 index 0000000..5e9a150 --- /dev/null +++ b/components/policy/resources/templates/policy_definitions/ContentSettings/ThirdPartyStoragePartitioningBlockedForOrigins.yaml
@@ -0,0 +1,29 @@ +caption: Block third-party storage partitioning for these origins +desc: |- + Allows you to set a list of url patterns that specify top-level (the url in the tab's address bar) origins which block third-party storage partitioning (partitioning of cross-origin iframe storage). + If this policy is left not set or a top-level origin doesn't match then the value from <ph name="DEFAULT_THIRD_PARTY_STORAGE_PARTITIONING_SETTING_POLICY_NAME">DefaultThirdPartyStoragePartitioningSetting</ph> will be used. + For detailed information on valid patterns, please see https://cloud.google.com/docs/chrome-enterprise/policies/url-patterns. + Note that patterns you list here are treated as origins, not URLs, so you should not specify a path. + For detailed information on third-party storage partitioning, please see https://developer.chrome.com/docs/privacy-sandbox/storage-partitioning/. +example_value: +- www.example.com +- '[*.]example.edu' +features: + dynamic_refresh: true + per_profile: true +future_on: +- fuchsia +owners: +- arichiv@chromium.org +- potassium-katabolism@google.com +schema: + items: + type: string + type: array +supported_on: +# TODO(crbug.com/1422357): Deprecate this when origin trial ends (likely M123) +- android:113- +- chrome.*:113- +- chrome_os:113- +tags: [] +type: list
diff --git a/components/policy/resources/templates/policy_definitions/ContentSettings/policy_atomic_groups.yaml b/components/policy/resources/templates/policy_definitions/ContentSettings/policy_atomic_groups.yaml index b2362a16..53b59cf9 100644 --- a/components/policy/resources/templates/policy_definitions/ContentSettings/policy_atomic_groups.yaml +++ b/components/policy/resources/templates/policy_definitions/ContentSettings/policy_atomic_groups.yaml
@@ -59,6 +59,11 @@ - DefaultSensorsSetting - SensorsAllowedForUrls - SensorsBlockedForUrls +ThirdPartyStoragePartitioningSettings: + caption: Third-party storage partitioning settings + policies: + - DefaultThirdPartyStoragePartitioningSetting + - ThirdPartyStoragePartitioningBlockedForOrigins WebUsbSettings: caption: Web USB settings policies:
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/DeviceLoginScreenPrimaryMouseButtonSwitch.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/DeviceLoginScreenPrimaryMouseButtonSwitch.yaml index 7162b6d..ec4b737 100644 --- a/components/policy/resources/templates/policy_definitions/Miscellaneous/DeviceLoginScreenPrimaryMouseButtonSwitch.yaml +++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/DeviceLoginScreenPrimaryMouseButtonSwitch.yaml
@@ -28,6 +28,6 @@ schema: type: boolean supported_on: -- chrome_os:81- +- chrome_os:113- tags: [] type: main
diff --git a/components/policy/test/data/policy_test_cases.json b/components/policy/test/data/policy_test_cases.json index 8fc6c88..0cf1638 100644 --- a/components/policy/test/data/policy_test_cases.json +++ b/components/policy/test/data/policy_test_cases.json
@@ -21224,12 +21224,12 @@ { "policies": { "IdleTimeout": 10, - "IdleTimeoutActions": ["clear_browsing_history", - "clear_download_history"] + "IdleTimeoutActions": ["clear_cookies_and_other_site_data", + "clear_cached_images_and_files"] }, "prefs": { "idle_timeout_actions": { - "value": [2, 3] + "value": [4, 5] } } } @@ -22723,5 +22723,42 @@ } } ] + }, + "ThirdPartyStoragePartitioningBlockedForOrigins": { + "os": [ + "win", + "linux", + "mac", + "chromeos_ash", + "chromeos_lacros", + "android", + "fuchsia" + ], + "policy_pref_mapping_tests": [ + { + "policies": {}, + "prefs": { + "profile.managed_third_party_storage_partitioning_blocked_for_origins": { + "default_value": [] + } + } + }, + { + "policies": { + "ThirdPartyStoragePartitioningBlockedForOrigins": [ + "google.com", + "[*.]example.edu" + ] + }, + "prefs": { + "profile.managed_third_party_storage_partitioning_blocked_for_origins": { + "value": [ + "google.com", + "[*.]example.edu" + ] + } + } + } + ] } }
diff --git a/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc b/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc index 39868b3..9cddad4 100644 --- a/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc +++ b/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc
@@ -497,6 +497,13 @@ store_info_list.Append("State: " + state_base64); } + for (const auto& prefix_set : store_info.prefix_sets()) { + std::string size = base::UTF16ToUTF8(base::FormatNumber(prefix_set.size())); + std::string count = + base::UTF16ToUTF8(base::FormatNumber(prefix_set.count())); + store_info_list.Append(count + " prefixes of size " + size); + } + database_info_list.Append(std::move(store_info_list)); }
diff --git a/components/safe_browsing/core/browser/db/hash_prefix_map.cc b/components/safe_browsing/core/browser/db/hash_prefix_map.cc index 4efaa2e6..4999ca6 100644 --- a/components/safe_browsing/core/browser/db/hash_prefix_map.cc +++ b/components/safe_browsing/core/browser/db/hash_prefix_map.cc
@@ -198,6 +198,20 @@ return MigrateResult::kSuccess; } +void InMemoryHashPrefixMap::GetPrefixInfo( + google::protobuf::RepeatedPtrField< + DatabaseManagerInfo::DatabaseInfo::StoreInfo::PrefixSet>* prefix_sets) { + for (const auto& size_and_prefixes : map_) { + const PrefixSize& size = size_and_prefixes.first; + const HashPrefixes& hash_prefixes = size_and_prefixes.second; + + DatabaseManagerInfo::DatabaseInfo::StoreInfo::PrefixSet* prefix_set = + prefix_sets->Add(); + prefix_set->set_size(size); + prefix_set->set_count(hash_prefixes.size() / size); + } +} + // Writes a hash prefix file, and buffers writes to avoid a write call for each // hash prefix. The file will be deleted if Finish() is never called. class MmapHashPrefixMap::BufferedFileWriter { @@ -384,6 +398,20 @@ return MigrateResult::kSuccess; } +void MmapHashPrefixMap::GetPrefixInfo( + google::protobuf::RepeatedPtrField< + DatabaseManagerInfo::DatabaseInfo::StoreInfo::PrefixSet>* prefix_sets) { + for (const auto& size_and_info : map_) { + const PrefixSize& size = size_and_info.first; + const FileInfo& info = size_and_info.second; + + DatabaseManagerInfo::DatabaseInfo::StoreInfo::PrefixSet* prefix_set = + prefix_sets->Add(); + prefix_set->set_size(size); + prefix_set->set_count(info.GetView().size() / size); + } +} + // static base::FilePath MmapHashPrefixMap::GetPath(const base::FilePath& store_path, const std::string& extension) {
diff --git a/components/safe_browsing/core/browser/db/hash_prefix_map.h b/components/safe_browsing/core/browser/db/hash_prefix_map.h index 5ef6fc6..28b3ff6 100644 --- a/components/safe_browsing/core/browser/db/hash_prefix_map.h +++ b/components/safe_browsing/core/browser/db/hash_prefix_map.h
@@ -11,6 +11,7 @@ #include "base/files/memory_mapped_file.h" #include "components/safe_browsing/core/browser/db/v4_protocol_manager_util.h" #include "components/safe_browsing/core/browser/db/v4_store.pb.h" +#include "components/safe_browsing/core/common/proto/webui.pb.h" namespace safe_browsing { @@ -108,6 +109,12 @@ enum class MigrateResult { kSuccess, kFailure, kNotNeeded }; virtual MigrateResult MigrateFileFormat(const base::FilePath& store_path, V4StoreFileFormat* file_format) = 0; + + // Collects debug information about the prefixes in the map. + virtual void GetPrefixInfo( + google::protobuf::RepeatedPtrField< + DatabaseManagerInfo::DatabaseInfo::StoreInfo::PrefixSet>* + prefix_sets) = 0; }; // An in-memory implementation of HashPrefixMap. @@ -127,6 +134,9 @@ HashPrefixStr GetMatchingHashPrefix(base::StringPiece full_hash) override; MigrateResult MigrateFileFormat(const base::FilePath& store_path, V4StoreFileFormat* file_format) override; + void GetPrefixInfo(google::protobuf::RepeatedPtrField< + DatabaseManagerInfo::DatabaseInfo::StoreInfo::PrefixSet>* + prefix_sets) override; private: std::unordered_map<PrefixSize, HashPrefixes> map_; @@ -151,6 +161,9 @@ HashPrefixStr GetMatchingHashPrefix(base::StringPiece full_hash) override; MigrateResult MigrateFileFormat(const base::FilePath& store_path, V4StoreFileFormat* file_format) override; + void GetPrefixInfo(google::protobuf::RepeatedPtrField< + DatabaseManagerInfo::DatabaseInfo::StoreInfo::PrefixSet>* + prefix_sets) override; static base::FilePath GetPath(const base::FilePath& store_path, const std::string& extension);
diff --git a/components/safe_browsing/core/browser/db/v4_store.cc b/components/safe_browsing/core/browser/db/v4_store.cc index 5040e2e..2fb225c 100644 --- a/components/safe_browsing/core/browser/db/v4_store.cc +++ b/components/safe_browsing/core/browser/db/v4_store.cc
@@ -963,6 +963,8 @@ store_info->set_last_apply_update_time_millis( last_apply_update_time_millis_.ToJavaTime()); } + + hash_prefix_map_->GetPrefixInfo(store_info->mutable_prefix_sets()); } } // namespace safe_browsing
diff --git a/components/safe_browsing/core/common/proto/webui.proto b/components/safe_browsing/core/common/proto/webui.proto index a044ec7..df1962df 100644 --- a/components/safe_browsing/core/common/proto/webui.proto +++ b/components/safe_browsing/core/common/proto/webui.proto
@@ -58,6 +58,19 @@ // The current state of the client for the requested store (the encrypted // ClientState that was sent to the client from the previous request). optional string state = 6; + + // A message encapsulating the number of prefixes of a given size. + message PrefixSet { + // Size of the prefixes in the set, in bytes. + optional int64 size = 1; + + // Number of prefixes in the set. + optional int64 count = 2; + } + + // Has an entry for each prefix size found within the store as well as the + // corresponding count of prefixes of that size. + repeated PrefixSet prefix_sets = 7; } // Information about each of the stores managed by the database.
diff --git a/components/saved_tab_groups/saved_tab_group.cc b/components/saved_tab_groups/saved_tab_group.cc index 87cea699..6f0c037 100644 --- a/components/saved_tab_groups/saved_tab_group.cc +++ b/components/saved_tab_groups/saved_tab_group.cc
@@ -190,7 +190,6 @@ CHECK(index.has_value()); CHECK_GE(index.value(), 0u); CHECK_LT(index.value(), saved_tabs_.size()); - CHECK(!ContainsTab(tab.saved_tab_guid())); saved_tabs_.erase(saved_tabs_.begin() + index.value()); saved_tabs_.insert(saved_tabs_.begin() + index.value(), std::move(tab)); UpdateTabPositionsImpl(); @@ -265,7 +264,7 @@ } bool SavedTabGroup::ShouldMergeGroup( - const sync_pb::SavedTabGroupSpecifics& sync_specific) { + const sync_pb::SavedTabGroupSpecifics& sync_specific) const { bool sync_update_is_latest = sync_specific.update_time_windows_epoch_micros() >= update_time_windows_epoch_micros()
diff --git a/components/saved_tab_groups/saved_tab_group.h b/components/saved_tab_groups/saved_tab_group.h index 9aa6da07..482358b 100644 --- a/components/saved_tab_groups/saved_tab_group.h +++ b/components/saved_tab_groups/saved_tab_group.h
@@ -121,7 +121,8 @@ // We should merge a group if one of the following is true: // 1. The data from `sync_specific` has the most recent (larger) update time. // 2. The `sync_specific` has the oldest (smallest) creation time. - bool ShouldMergeGroup(const sync_pb::SavedTabGroupSpecifics& sync_specific); + bool ShouldMergeGroup( + const sync_pb::SavedTabGroupSpecifics& sync_specific) const; // Insert `tab` into sorted order based on its position compared to already // stored tabs in its group. It should be noted that the list of tabs in each
diff --git a/components/saved_tab_groups/saved_tab_group_model.cc b/components/saved_tab_groups/saved_tab_group_model.cc index c98d455..fbc2790 100644 --- a/components/saved_tab_groups/saved_tab_group_model.cc +++ b/components/saved_tab_groups/saved_tab_group_model.cc
@@ -51,14 +51,6 @@ return &saved_tab_groups_[index.value()]; } -SavedTabGroup* SavedTabGroupModel::Get(const base::GUID& id) { - absl::optional<int> index = GetIndexOf(id); - if (!index.has_value()) - return nullptr; - - return &saved_tab_groups_[index.value()]; -} - const SavedTabGroup* SavedTabGroupModel::Get( const tab_groups::TabGroupId local_group_id) const { absl::optional<int> index = GetIndexOf(local_group_id); @@ -68,15 +60,6 @@ return &saved_tab_groups_[index.value()]; } -SavedTabGroup* SavedTabGroupModel::Get( - const tab_groups::TabGroupId local_group_id) { - absl::optional<int> index = GetIndexOf(local_group_id); - if (!index.has_value()) - return nullptr; - - return &saved_tab_groups_[index.value()]; -} - void SavedTabGroupModel::Add(SavedTabGroup saved_group) { base::GUID group_guid = saved_group.saved_guid(); if (Contains(group_guid)) @@ -355,20 +338,24 @@ std::unique_ptr<sync_pb::SavedTabGroupSpecifics> SavedTabGroupModel::MergeTab( const sync_pb::SavedTabGroupSpecifics& sync_specific) { - const base::GUID& group_id = + const base::GUID& group_guid = base::GUID::ParseLowercase(sync_specific.tab().group_guid()); - const base::GUID& tab_id = base::GUID::ParseLowercase(sync_specific.guid()); - DCHECK(Contains(group_id)); - DCHECK(Get(group_id)->ContainsTab(tab_id)); + absl::optional<int> group_index = GetIndexOf(group_guid); + CHECK(group_index.has_value()); - SavedTabGroupTab* tab = Get(group_id)->GetTab(tab_id); - tab->MergeTab(std::move(sync_specific)); + const base::GUID& tab_guid = base::GUID::ParseLowercase(sync_specific.guid()); + CHECK(saved_tab_groups_[group_index.value()].ContainsTab(tab_guid)); + + SavedTabGroupTab merged_tab(*Get(group_guid)->GetTab(tab_guid)); + merged_tab.MergeTab(std::move(sync_specific)); + saved_tab_groups_[group_index.value()].ReplaceTabAt(tab_guid, merged_tab); for (auto& observer : observers_) { - observer.SavedTabGroupUpdatedFromSync(group_id, tab->saved_group_guid()); + observer.SavedTabGroupUpdatedFromSync(group_guid, + merged_tab.saved_group_guid()); } - return tab->ToSpecifics(); + return merged_tab.ToSpecifics(); } void SavedTabGroupModel::Reorder(const base::GUID& id, int new_index) {
diff --git a/components/saved_tab_groups/saved_tab_group_model.h b/components/saved_tab_groups/saved_tab_group_model.h index 615d7e0..17af91fc 100644 --- a/components/saved_tab_groups/saved_tab_group_model.h +++ b/components/saved_tab_groups/saved_tab_group_model.h
@@ -47,10 +47,6 @@ const SavedTabGroup* Get(const tab_groups::TabGroupId local_group_id) const; const SavedTabGroup* Get(const base::GUID& id) const; - // TODO(crbug/1372503): Remove non-const accessor functions. - SavedTabGroup* Get(const tab_groups::TabGroupId local_group_id); - SavedTabGroup* Get(const base::GUID& id); - // Methods for checking if a group is in the SavedTabGroupModel. bool Contains(const tab_groups::TabGroupId& local_group_id) const { return GetIndexOf(local_group_id).has_value();
diff --git a/components/saved_tab_groups/saved_tab_group_model_unittest.cc b/components/saved_tab_groups/saved_tab_group_model_unittest.cc index 91acebe8..0f746d2 100644 --- a/components/saved_tab_groups/saved_tab_group_model_unittest.cc +++ b/components/saved_tab_groups/saved_tab_group_model_unittest.cc
@@ -368,13 +368,6 @@ saved_tab_group_model_->UpdateVisualData(id_1_, &change_color_visual_data); EXPECT_EQ(group->title(), original_title); EXPECT_EQ(group->color(), random_color); - - // Update update time - base::Time time = base::Time::Now(); - saved_tab_group_model_->Get(id_1_)->SetUpdateTimeWindowsEpochMicros(time); - EXPECT_EQ( - time, - saved_tab_group_model_->Get(id_1_)->update_time_windows_epoch_micros()); } // Tests that the correct tabs are added to the correct position in group 1. @@ -384,7 +377,7 @@ SavedTabGroupTab tab2 = CreateSavedTabGroupTab("2nd link", u"Second Tab 4th Group", id_1_, 2); - SavedTabGroup* group = saved_tab_group_model_->Get(id_1_); + const SavedTabGroup* group = saved_tab_group_model_->Get(id_1_); ASSERT_EQ(group->saved_tabs().size(), size_t(1)); saved_tab_group_model_->AddTabToGroup(group->saved_guid(), tab1, @@ -413,7 +406,7 @@ SavedTabGroupTab tab2 = CreateSavedTabGroupTab("2nd link", u"Second Tab 4th Group", id_1_, 2); - SavedTabGroup* group = saved_tab_group_model_->Get(id_1_); + const SavedTabGroup* group = saved_tab_group_model_->Get(id_1_); ASSERT_EQ(group->saved_tabs().size(), size_t(1)); saved_tab_group_model_->AddTabToGroup(group->saved_guid(), tab1, @@ -438,7 +431,7 @@ // Tests that a group is removed from the model when the last tab is removed // from it. TEST_F(SavedTabGroupModelTest, RemoveLastTabFromGroup) { - SavedTabGroup* group = saved_tab_group_model_->Get(id_1_); + const SavedTabGroup* group = saved_tab_group_model_->Get(id_1_); ASSERT_EQ(group->saved_tabs().size(), size_t(1)); saved_tab_group_model_->RemoveTabFromGroup( @@ -457,7 +450,7 @@ SavedTabGroupTab tab3 = CreateSavedTabGroupTab("third", u"Third Tab", id_1_, absl::nullopt); - SavedTabGroup* group = saved_tab_group_model_->Get(id_1_); + const SavedTabGroup* group = saved_tab_group_model_->Get(id_1_); ASSERT_EQ(group->saved_tabs().size(), size_t(1)); saved_tab_group_model_->AddTabToGroup(group->saved_guid(), tab1, @@ -488,7 +481,7 @@ SavedTabGroupTab tab2 = CreateSavedTabGroupTab("2nd link", u"Second Tab 4th Group", id_1_, 2); - SavedTabGroup* group = saved_tab_group_model_->Get(id_1_); + const SavedTabGroup* group = saved_tab_group_model_->Get(id_1_); ASSERT_EQ(group->saved_tabs().size(), size_t(1)); saved_tab_group_model_->AddTabToGroup(group->saved_guid(), tab1, @@ -558,7 +551,7 @@ // Tests that merging a group with the same group_id changes the state of the // object correctly. TEST_F(SavedTabGroupModelTest, MergeGroupsFromModel) { - SavedTabGroup* group1 = saved_tab_group_model_->Get(id_1_); + const SavedTabGroup* group1 = saved_tab_group_model_->Get(id_1_); SavedTabGroup group2 = SavedTabGroup::FromSpecifics(*group1->ToSpecifics()); group2.SetColor(tab_groups::TabGroupColorId::kPink); group2.SetTitle(u"Updated title");
diff --git a/components/saved_tab_groups/saved_tab_group_sync_bridge_unittest.cc b/components/saved_tab_groups/saved_tab_group_sync_bridge_unittest.cc index 4e10eea..4bb3a46 100644 --- a/components/saved_tab_groups/saved_tab_group_sync_bridge_unittest.cc +++ b/components/saved_tab_groups/saved_tab_group_sync_bridge_unittest.cc
@@ -173,7 +173,7 @@ EXPECT_TRUE(saved_tab_group_model_.Contains(group.saved_guid())); EXPECT_EQ(saved_tab_group_model_.saved_tab_groups().size(), 1u); - SavedTabGroup* group_from_model = + const SavedTabGroup* group_from_model = saved_tab_group_model_.Get(group.saved_guid()); EXPECT_EQ(group_from_model->saved_tabs().size(), 2u); @@ -207,7 +207,8 @@ saved_tab_group_model_.Add(std::move(group)); - SavedTabGroup* group_from_model = saved_tab_group_model_.Get(group_guid); + const SavedTabGroup* group_from_model = + saved_tab_group_model_.Get(group_guid); // Create an updated version of `group` using the same creation time and 1 // less tab. @@ -288,7 +289,7 @@ EXPECT_TRUE(saved_tab_group_model_.Contains(orphaned_guid)); EXPECT_EQ(saved_tab_group_model_.saved_tab_groups().size(), 1u); - SavedTabGroup* orphaned_group_from_model = + const SavedTabGroup* orphaned_group_from_model = saved_tab_group_model_.Get(orphaned_guid); EXPECT_EQ(orphaned_group_from_model->saved_tabs().size(), 1u); @@ -337,7 +338,7 @@ EXPECT_TRUE(saved_tab_group_model_.Contains(orphaned_guid)); EXPECT_EQ(saved_tab_group_model_.saved_tab_groups().size(), 1u); - SavedTabGroup* orphaned_group_from_model = + const SavedTabGroup* orphaned_group_from_model = saved_tab_group_model_.Get(orphaned_guid); EXPECT_TRUE(orphaned_group_from_model->saved_tabs().empty()); EXPECT_FALSE( @@ -379,7 +380,7 @@ EXPECT_TRUE(saved_tab_group_model_.Contains(orphaned_guid)); EXPECT_EQ(saved_tab_group_model_.saved_tab_groups().size(), 1u); - SavedTabGroup* orphaned_group_from_model = + const SavedTabGroup* orphaned_group_from_model = saved_tab_group_model_.Get(orphaned_guid); EXPECT_EQ(orphaned_group_from_model->saved_tabs().size(), 1u); EXPECT_TRUE( @@ -414,7 +415,7 @@ // Ensure all data passed by the bridge is the same. ASSERT_TRUE(saved_tab_group_model_.Contains(group.saved_guid())); - SavedTabGroup* group_from_model = + const SavedTabGroup* group_from_model = saved_tab_group_model_.Get(group.saved_guid()); EXPECT_TRUE(AreGroupSpecificsEqual(*group.ToSpecifics(), @@ -478,7 +479,7 @@ CreateEntityChangeListFromGroup( group, syncer::EntityChange::ChangeType::ACTION_ADD)); - SavedTabGroup* group_from_model = + const SavedTabGroup* group_from_model = saved_tab_group_model_.Get(group.saved_guid()); group.SetTitle(u"A new title"); @@ -522,7 +523,7 @@ group, syncer::EntityChange::ChangeType::ACTION_ADD)); ASSERT_TRUE(saved_tab_group_model_.Contains(group.saved_guid())); - SavedTabGroup* group_from_model = + const SavedTabGroup* group_from_model = saved_tab_group_model_.Get(group.saved_guid()); // Ensure a deleted tab is deleted from the group correctly in the model.
diff --git a/components/segmentation_platform/public/features.cc b/components/segmentation_platform/public/features.cc index 0bf9bda..e074e70 100644 --- a/components/segmentation_platform/public/features.cc +++ b/components/segmentation_platform/public/features.cc
@@ -32,6 +32,10 @@ "SegmentationPlatformSearchUser", base::FEATURE_ENABLED_BY_DEFAULT); +BASE_FEATURE(kSegmentationPlatformDeviceSwitcher, + "SegmentationPlatformDeviceSwitcher", + base::FEATURE_ENABLED_BY_DEFAULT); + BASE_FEATURE(kSegmentationPlatformFeedSegmentFeature, "SegmentationPlatformFeedSegmentFeature", #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
diff --git a/components/segmentation_platform/public/features.h b/components/segmentation_platform/public/features.h index 98fabdd..c418e7b 100644 --- a/components/segmentation_platform/public/features.h +++ b/components/segmentation_platform/public/features.h
@@ -41,6 +41,9 @@ // Feature flag for enabling search user segment. BASE_DECLARE_FEATURE(kSegmentationPlatformSearchUser); +// Feature flag for device switcher segment. +BASE_DECLARE_FEATURE(kSegmentationPlatformDeviceSwitcher); + // Feature flag for enabling price tracking action feature. BASE_DECLARE_FEATURE(kContextualPageActionPriceTracking);
diff --git a/components/signin/public/base/signin_metrics.cc b/components/signin/public/base/signin_metrics.cc index 013245a..0c5aca7e1 100644 --- a/components/signin/public/base/signin_metrics.cc +++ b/components/signin/public/base/signin_metrics.cc
@@ -454,6 +454,10 @@ base::RecordAction( base::UserMetricsAction("Signin_Signin_FromForYouFre")); break; + case AccessPoint::ACCESS_POINT_CREATOR_FEED_FOLLOW: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromCreatorFeedFollow")); + break; case AccessPoint::ACCESS_POINT_MAX: NOTREACHED(); break; @@ -581,6 +585,10 @@ << " are not recorded for access point " << static_cast<int>(access_point); break; + case AccessPoint::ACCESS_POINT_CREATOR_FEED_FOLLOW: + base::RecordAction( + base::UserMetricsAction("Signin_Impression_FromCreatorFeedFollow")); + break; case AccessPoint::ACCESS_POINT_MAX: NOTREACHED(); break;
diff --git a/components/signin/public/base/signin_metrics.h b/components/signin/public/base/signin_metrics.h index 0bbe71d..22388608 100644 --- a/components/signin/public/base/signin_metrics.h +++ b/components/signin/public/base/signin_metrics.h
@@ -208,6 +208,10 @@ // go/for-you-fre or launch/4223982 for more info. ACCESS_POINT_FOR_YOU_FRE = 45, + // Access point for Cormorant (Creator Feed) on Android only when the "Follow" + // button is tapped while in a signed-out state. + ACCESS_POINT_CREATOR_FEED_FOLLOW = 46, + // Add values above this line with a corresponding label to the // "SigninAccessPoint" enum in tools/metrics/histograms/enums.xml ACCESS_POINT_MAX, // This must be last.
diff --git a/components/signin/public/base/signin_metrics_unittest.cc b/components/signin/public/base/signin_metrics_unittest.cc index d66047a4..726c53b 100644 --- a/components/signin/public/base/signin_metrics_unittest.cc +++ b/components/signin/public/base/signin_metrics_unittest.cc
@@ -41,7 +41,8 @@ AccessPoint::ACCESS_POINT_NTP_FEED_TOP_PROMO, AccessPoint::ACCESS_POINT_POST_DEVICE_RESTORE_SIGNIN_PROMO, AccessPoint::ACCESS_POINT_NTP_FEED_CARD_MENU_PROMO, - AccessPoint::ACCESS_POINT_NTP_FEED_BOTTOM_PROMO}; + AccessPoint::ACCESS_POINT_NTP_FEED_BOTTOM_PROMO, + AccessPoint::ACCESS_POINT_CREATOR_FEED_FOLLOW}; const AccessPoint kAccessPointsThatSupportImpression[] = { AccessPoint::ACCESS_POINT_START_PAGE, @@ -64,7 +65,8 @@ AccessPoint::ACCESS_POINT_NTP_FEED_TOP_PROMO, AccessPoint::ACCESS_POINT_POST_DEVICE_RESTORE_SIGNIN_PROMO, AccessPoint::ACCESS_POINT_NTP_FEED_CARD_MENU_PROMO, - AccessPoint::ACCESS_POINT_NTP_FEED_BOTTOM_PROMO}; + AccessPoint::ACCESS_POINT_NTP_FEED_BOTTOM_PROMO, + AccessPoint::ACCESS_POINT_CREATOR_FEED_FOLLOW}; class SigninMetricsTest : public ::testing::Test { public: @@ -154,6 +156,8 @@ return "DesktopSigninManager"; case AccessPoint::ACCESS_POINT_FOR_YOU_FRE: return "ForYouFre"; + case AccessPoint::ACCESS_POINT_CREATOR_FEED_FOLLOW: + return "CreatorFeedFollow"; case AccessPoint::ACCESS_POINT_MAX: NOTREACHED(); return "";
diff --git a/components/sync/test/fake_sync_mojo_service.cc b/components/sync/test/fake_sync_mojo_service.cc index 97caf06f9..1dd6c66d 100644 --- a/components/sync/test/fake_sync_mojo_service.cc +++ b/components/sync/test/fake_sync_mojo_service.cc
@@ -28,11 +28,16 @@ fake_sync_user_settings_client_ash_.BindReceiver(std::move(receiver)); } -void FakeSyncMojoService::BindSyncedSessionClient( +void FakeSyncMojoService::DEPRECATED_BindSyncedSessionClient( mojo::PendingReceiver<crosapi::mojom::SyncedSessionClient> receiver) { fake_synced_session_client_ash_.BindReceiver(std::move(receiver)); } +void FakeSyncMojoService::CreateSyncedSessionClient( + CreateSyncedSessionClientCallback callback) { + NOTIMPLEMENTED(); +} + void FakeSyncMojoService::BindReceiver( mojo::PendingReceiver<crosapi::mojom::SyncService> receiver) { receivers_.Add(this, std::move(receiver));
diff --git a/components/sync/test/fake_sync_mojo_service.h b/components/sync/test/fake_sync_mojo_service.h index 4b426a9..31dfbd6 100644 --- a/components/sync/test/fake_sync_mojo_service.h +++ b/components/sync/test/fake_sync_mojo_service.h
@@ -32,9 +32,11 @@ void BindUserSettingsClient( mojo::PendingReceiver<crosapi::mojom::SyncUserSettingsClient> receiver) override; - void BindSyncedSessionClient( + void DEPRECATED_BindSyncedSessionClient( mojo::PendingReceiver<crosapi::mojom::SyncedSessionClient> receiver) override; + void CreateSyncedSessionClient( + CreateSyncedSessionClientCallback callback) override; // Own methods. void BindReceiver(
diff --git a/components/sync_preferences/common_syncable_prefs_database.cc b/components/sync_preferences/common_syncable_prefs_database.cc index 2cc046e..4f456c47 100644 --- a/components/sync_preferences/common_syncable_prefs_database.cc +++ b/components/sync_preferences/common_syncable_prefs_database.cc
@@ -32,6 +32,10 @@ namespace { // Not an enum class to ease cast to int. namespace syncable_prefs_ids { +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. +// Please also add new entries to `SyncablePref` enum in +// tools/metrics/histograms/enums.xml. enum { kSyncablePrefForTesting = 0, kAutofillCreditCardEnabled = 1,
diff --git a/components/sync_preferences/pref_model_associator.cc b/components/sync_preferences/pref_model_associator.cc index 6816a82..473ede9 100644 --- a/components/sync_preferences/pref_model_associator.cc +++ b/components/sync_preferences/pref_model_associator.cc
@@ -14,6 +14,7 @@ #include "base/json/json_string_value_serializer.h" #include "base/location.h" #include "base/logging.h" +#include "base/metrics/histogram_functions.h" #include "base/observer_list.h" #include "base/values.h" #include "build/chromeos_buildflags.h" @@ -533,6 +534,15 @@ } } + if (client_ && + // Only log if there's actually something to sync. + !changes.empty()) { + base::UmaHistogramSparse("Sync.SyncablePrefValueChanged", + client_->GetSyncablePrefsDatabase() + .GetSyncablePrefMetadata(name) + ->syncable_pref_id_); + } + sync_processor_->ProcessSyncChanges(FROM_HERE, changes); }
diff --git a/components/viz/service/display/display_resource_provider_software_unittest.cc b/components/viz/service/display/display_resource_provider_software_unittest.cc index 4ad7952..d509e86 100644 --- a/components/viz/service/display/display_resource_provider_software_unittest.cc +++ b/components/viz/service/display/display_resource_provider_software_unittest.cc
@@ -69,14 +69,11 @@ class DisplayResourceProviderSoftwareTest : public testing::Test { public: - DisplayResourceProviderSoftwareTest() { - shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>(); - - resource_provider_ = std::make_unique<DisplayResourceProviderSoftware>( - shared_bitmap_manager_.get()); - - child_resource_provider_ = std::make_unique<ClientResourceProvider>(); - } + DisplayResourceProviderSoftwareTest() + : shared_bitmap_manager_(std::make_unique<TestSharedBitmapManager>()), + resource_provider_(std::make_unique<DisplayResourceProviderSoftware>( + shared_bitmap_manager_.get())), + child_resource_provider_(std::make_unique<ClientResourceProvider>()) {} ~DisplayResourceProviderSoftwareTest() override { child_resource_provider_->ShutdownAndReleaseAllResources(); @@ -91,9 +88,9 @@ } protected: - std::unique_ptr<DisplayResourceProviderSoftware> resource_provider_; - std::unique_ptr<ClientResourceProvider> child_resource_provider_; - std::unique_ptr<TestSharedBitmapManager> shared_bitmap_manager_; + const std::unique_ptr<TestSharedBitmapManager> shared_bitmap_manager_; + const std::unique_ptr<DisplayResourceProviderSoftware> resource_provider_; + const std::unique_ptr<ClientResourceProvider> child_resource_provider_; }; TEST_F(DisplayResourceProviderSoftwareTest, ReadSoftwareResources) {
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc index c1ba4221..73ad33b 100644 --- a/content/browser/accessibility/browser_accessibility_manager.cc +++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -401,7 +401,6 @@ CHECK(!ax_tree()->error().empty()) << "A failed serialization didn't supply the error via " "AXTree::RecordError()."; - // Crash immediately in unit tests. if (!delegate_) CHECK(false) << ax_tree()->error(); return false;
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc index 01ce929..12e2c7b 100644 --- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc +++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -1931,6 +1931,10 @@ RunHtmlTest(FILE_PATH_LITERAL("header.html")); } +IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityHGroup) { + RunHtmlTest(FILE_PATH_LITERAL("hgroup.html")); +} + IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityHeaderInsideOtherSection) { RunHtmlTest(FILE_PATH_LITERAL("header-inside-other-section.html"));
diff --git a/content/browser/attribution_reporting/attribution_host.cc b/content/browser/attribution_reporting/attribution_host.cc index 1ab761f..3eb223d 100644 --- a/content/browser/attribution_reporting/attribution_host.cc +++ b/content/browser/attribution_reporting/attribution_host.cc
@@ -90,8 +90,7 @@ : WebContentsObserver(web_contents), WebContentsUserData<AttributionHost>(*web_contents), receivers_(web_contents, this) { - // TODO(csharrison): When https://crbug.com/1051334 is resolved, add a DCHECK - // that the kConversionMeasurement feature is enabled. + DCHECK(base::FeatureList::IsEnabled(blink::features::kConversionMeasurement)); #if BUILDFLAG(IS_ANDROID) if (base::FeatureList::IsEnabled(
diff --git a/content/browser/direct_sockets/direct_sockets_service_impl.cc b/content/browser/direct_sockets/direct_sockets_service_impl.cc index 78e7d44..25adf39 100644 --- a/content/browser/direct_sockets/direct_sockets_service_impl.cc +++ b/content/browser/direct_sockets/direct_sockets_service_impl.cc
@@ -134,6 +134,15 @@ host_port_pair.port(), protocol); } +#if BUILDFLAG(IS_CHROMEOS) +bool ShouldOpenFirewallHole(const net::IPAddress& address) { + if (g_always_open_firewall_hole_for_testing) { + return true; + } + return !address.IsLoopback(); +} +#endif // BUILDFLAG(IS_CHROMEOS) + } // namespace #if BUILDFLAG(IS_CHROMEOS) @@ -152,8 +161,7 @@ std::move(callback).Run(result, /*local_addr=*/absl::nullopt); return; } - if (local_addr->address().IsLoopback() && - !g_always_open_firewall_hole_for_testing) { + if (!ShouldOpenFirewallHole(local_addr->address())) { std::move(callback).Run(net::OK, *local_addr); return; } @@ -171,6 +179,34 @@ net::ERR_NETWORK_ACCESS_DENIED, absl::nullopt))); } + void OpenUDPFirewallHole( + mojo::PendingReceiver<network::mojom::SocketConnectionTracker> + connection_tracker, + OpenBoundUDPSocketCallback callback, + int32_t result, + const absl::optional<net::IPEndPoint>& local_addr) { + if (result != net::OK) { + std::move(callback).Run(result, /*local_addr=*/absl::nullopt); + return; + } + if (!ShouldOpenFirewallHole(local_addr->address())) { + std::move(callback).Run(net::OK, *local_addr); + return; + } + auto [callback_a, callback_b] = + base::SplitOnceCallback(std::move(callback)); + content::OpenUDPFirewallHole( + "" /*all interfaces*/, local_addr->port(), + base::BindOnce( + &FirewallHoleDelegate::OnFirewallHoleOpened, GetWeakPtr(), + std::move(connection_tracker), + /*on_success=*/ + base::BindOnce(std::move(callback_a), net::OK, *local_addr), + /*on_failure=*/ + base::BindOnce(std::move(callback_b), + net::ERR_NETWORK_ACCESS_DENIED, absl::nullopt))); + } + base::WeakPtr<FirewallHoleDelegate> GetWeakPtr() { return weak_factory_.GetWeakPtr(); } @@ -317,13 +353,29 @@ auto params = network::mojom::RestrictedUDPSocketParams::New(); params->socket_options = std::move(socket_options); +#if BUILDFLAG(IS_CHROMEOS) + mojo::PendingReceiver<network::mojom::SocketConnectionTracker> + connection_tracker; + params->connection_tracker = + connection_tracker.InitWithNewPipeAndPassRemote(); +#endif // BUILDFLAG(IS_CHROMEOS) + GetNetworkContext()->CreateRestrictedUDPSocket( options->local_addr, /*mode=*/network::mojom::RestrictedUDPSocketMode::BOUND, /*traffic_annotation=*/ net::MutableNetworkTrafficAnnotationTag(kDirectSocketsTrafficAnnotation), - std::move(params), std::move(receiver), std::move(listener), - std::move(callback)); + /*params=*/std::move(params), std::move(receiver), std::move(listener), +#if !BUILDFLAG(IS_CHROMEOS) + std::move(callback) +#else // BUILDFLAG(IS_CHROMEOS) + // On ChromeOS the original callback will be invoked after punching a + // firewall hole. + base::BindOnce(&FirewallHoleDelegate::OpenUDPFirewallHole, + firewall_hole_delegate_->GetWeakPtr(), + std::move(connection_tracker), std::move(callback)) +#endif // BUILDFLAG(IS_CHROMEOS) + ); } void DirectSocketsServiceImpl::OpenTCPServerSocket(
diff --git a/content/browser/direct_sockets/direct_sockets_udp_browsertest.cc b/content/browser/direct_sockets/direct_sockets_udp_browsertest.cc index d799bd4..b7e2db6 100644 --- a/content/browser/direct_sockets/direct_sockets_udp_browsertest.cc +++ b/content/browser/direct_sockets/direct_sockets_udp_browsertest.cc
@@ -33,6 +33,10 @@ #include "testing/gmock/include/gmock/gmock-matchers.h" #include "url/gurl.h" +#if BUILDFLAG(IS_CHROMEOS_ASH) +#include "chromeos/dbus/permission_broker/fake_permission_broker_client.h" // nogncheck +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + // The tests in this file use the Network Service implementation of // NetworkContext, to test sending and receiving of data over UDP sockets. @@ -261,12 +265,87 @@ ::testing::HasSubstr("readWriteUdpOnError succeeded")); } -IN_PROC_BROWSER_TEST_F(DirectSocketsUdpBrowserTest, ExchangeUdp) { +class DirectSocketsBoundUdpBrowserTest : public DirectSocketsUdpBrowserTest { + public: +#if BUILDFLAG(IS_CHROMEOS_ASH) + DirectSocketsBoundUdpBrowserTest() { + chromeos::PermissionBrokerClient::InitializeFake(); + DirectSocketsServiceImpl::SetAlwaysOpenFirewallHoleForTesting(true); + } + + ~DirectSocketsBoundUdpBrowserTest() override { + chromeos::PermissionBrokerClient::Shutdown(); + } +#endif // BUILDFLAG(IS_CHROMEOS_ASH) +}; + +IN_PROC_BROWSER_TEST_F(DirectSocketsBoundUdpBrowserTest, ExchangeUdp) { ASSERT_THAT(EvalJs(shell(), "exchangeUdpPacketsBetweenClientAndServer()") .ExtractString(), testing::HasSubstr("succeeded")); } +#if BUILDFLAG(IS_CHROMEOS_ASH) +IN_PROC_BROWSER_TEST_F(DirectSocketsBoundUdpBrowserTest, HasFirewallHole) { + class DelegateImpl : public chromeos::FakePermissionBrokerClient::Delegate { + public: + DelegateImpl(uint16_t port, base::OnceClosure quit_closure) + : port_(port), quit_closure_(std::move(quit_closure)) {} + + void OnUdpPortReleased(uint16_t port, + const std::string& interface) override { + if (port == port_) { + ASSERT_EQ(interface, ""); + ASSERT_TRUE(quit_closure_); + std::move(quit_closure_).Run(); + } + } + + private: + uint16_t port_; + base::OnceClosure quit_closure_; + }; + + auto* client = static_cast<chromeos::FakePermissionBrokerClient*>( + chromeos::PermissionBrokerClient::Get()); + + const std::string open_script = R"( + (async () => { + socket = new UDPSocket({ localAddress: '127.0.0.1' }); + const { localPort } = await socket.opened; + return localPort; + })(); + )"; + + const int32_t local_port = EvalJs(shell(), open_script).ExtractInt(); + ASSERT_TRUE(client->HasUdpHole(local_port, "" /* all interfaces */)); + + base::RunLoop run_loop; + auto delegate = + std::make_unique<DelegateImpl>(local_port, run_loop.QuitClosure()); + client->AttachDelegate(delegate.get()); + + EXPECT_TRUE(EvalJs(shell(), content::test::WrapAsync("socket.close()")) + .error.empty()); + run_loop.Run(); +} + +IN_PROC_BROWSER_TEST_F(DirectSocketsBoundUdpBrowserTest, FirewallHoleDenied) { + auto* client = chromeos::FakePermissionBrokerClient::Get(); + client->SetUdpDenyAll(); + + const std::string open_script = R"( + (async () => { + socket = new UDPSocket({ localAddress: '127.0.0.1' }); + return await socket.opened.catch(err => err.message); + })(); + )"; + + EXPECT_THAT(EvalJs(shell(), open_script).ExtractString(), + testing::HasSubstr("Firewall")); +} +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + IN_PROC_BROWSER_TEST_F(DirectSocketsUdpBrowserTest, UdpMessageConfigurations) { { const std::string script = R"(
diff --git a/content/browser/interest_group/interest_group_auction.cc b/content/browser/interest_group/interest_group_auction.cc index 2303de7..a0392a22 100644 --- a/content/browser/interest_group/interest_group_auction.cc +++ b/content/browser/interest_group/interest_group_auction.cc
@@ -2481,7 +2481,7 @@ // // TODO(https://crbug.com/1394777): We should probably add new states for // whether the result was used, reports sent, etc, so either the - // InterestGroupAuction or the InterestGroupReporter logs a single result. + // InterestGroupAuction or the InterestGroupAuctionReporter logs a single result. // Alternatively, we could add a separate histogram just for the reporter // stuff, which should have exactly as many entries as the historam // `final_auction_result_` is logged to.
diff --git a/content/browser/interest_group/interest_group_auction.h b/content/browser/interest_group/interest_group_auction.h index dd2b962..fda43e7 100644 --- a/content/browser/interest_group/interest_group_auction.h +++ b/content/browser/interest_group/interest_group_auction.h
@@ -181,7 +181,7 @@ void EndTracingKAnonScoring(); // Use a unique pointer so this can be more safely moved to the - // InterestGroupReporter. Doing so both preserves pointers, and make sure + // InterestGroupAuctionReporter. Doing so both preserves pointers, and make sure // there's a crash if this is dereferenced after move. std::unique_ptr<StorageInterestGroup> bidder;
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc index ef0cd9f4..b0aa79cf 100644 --- a/content/browser/interest_group/interest_group_browsertest.cc +++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -1681,9 +1681,9 @@ }; // Make sure that FLEDGE has protections against making local network requests.. -class InterestGroupPrivateNetworkBrowserTest : public InterestGroupBrowserTest { +class InterestGroupLocalNetworkBrowserTest : public InterestGroupBrowserTest { protected: - InterestGroupPrivateNetworkBrowserTest() + InterestGroupLocalNetworkBrowserTest() : remote_test_server_(net::test_server::EmbeddedTestServer::TYPE_HTTPS) { feature_list_.InitAndEnableFeature( features::kPrivateNetworkAccessRespectPreflightResults); @@ -9964,8 +9964,8 @@ // The bidder worklet is served from a private network, everything else from a // public network. The auction should fail. -IN_PROC_BROWSER_TEST_F(InterestGroupPrivateNetworkBrowserTest, - BidderOnPrivateNetwork) { +IN_PROC_BROWSER_TEST_F(InterestGroupLocalNetworkBrowserTest, + BidderOnLocalNetwork) { URLLoaderMonitor url_loader_monitor; // Learn the bidder IG, served from the local server. @@ -10019,8 +10019,8 @@ network::mojom::IPAddressSpace::kUnknown))); } -IN_PROC_BROWSER_TEST_F(InterestGroupPrivateNetworkBrowserTest, - SellerOnPrivateNetwork) { +IN_PROC_BROWSER_TEST_F(InterestGroupLocalNetworkBrowserTest, + SellerOnLocalNetwork) { GURL seller_url = https_server_->GetURL("b.test", "/interest_group/decision_logic.js"); @@ -10076,9 +10076,9 @@ } // Have the auction and worklets server from public IPs, but send reports to a -// private network. The reports should be blocked. -IN_PROC_BROWSER_TEST_F(InterestGroupPrivateNetworkBrowserTest, - ReportToPrivateNetwork) { +// local network. The reports should be blocked. +IN_PROC_BROWSER_TEST_F(InterestGroupLocalNetworkBrowserTest, + ReportToLocalNetwork) { // Use `remote_test_server_` exclusively with hostname "a.test" for root page // and script URLs. GURL test_url = @@ -10156,7 +10156,7 @@ // Have all requests for an auction served from a public network, and all // reports send there as well. The auction should succeed, and all reports // should be sent. -IN_PROC_BROWSER_TEST_F(InterestGroupPrivateNetworkBrowserTest, +IN_PROC_BROWSER_TEST_F(InterestGroupLocalNetworkBrowserTest, ReportToPublicNetwork) { // Use `remote_test_server_` exclusively with hostname "a.test" for root page // and script URLs. @@ -10259,10 +10259,10 @@ // respected for the update request. Does this by adding an interest group, // trying to update it from a public page, and expecting the request to be // blocked, and then adding another interest group and updating it from a -// private page, which should succeed. Have to use two interest groups to avoid +// local page, which should succeed. Have to use two interest groups to avoid // the delay between updates. -IN_PROC_BROWSER_TEST_F(InterestGroupPrivateNetworkBrowserTest, - UpdatePublicVsPrivateNetwork) { +IN_PROC_BROWSER_TEST_F(InterestGroupLocalNetworkBrowserTest, + UpdatePublicVsLocalNetwork) { const char kPubliclyUpdateGroupName[] = "Publicly updated group"; const char kLocallyUpdateGroupName[] = "Locally updated group"; @@ -10289,7 +10289,7 @@ GURL test_url; std::string group_name; if (public_address_space) { - // This header treats a response from a server on a private IP as if the + // This header treats a response from a server on a local IP as if the // server were on public address space. test_url = https_server_->GetURL( "a.test", @@ -10370,14 +10370,14 @@ } // Create three interest groups, each belonging to different origins. Update one -// on a private network, but delay its server response. Update the second on a +// on a local network, but delay its server response. Update the second on a // public network (thus expecting the request to be blocked). Update the final -// interest group on a private interest group -- it should be updated after the +// interest group on a local interest group -- it should be updated after the // first two. After the server responds to the first update request, all updates // should proceed -- the first should succeed, and the second should be blocked // since the page is on a public network, and the third should succeed. -IN_PROC_BROWSER_TEST_F(InterestGroupPrivateNetworkBrowserTest, - PrivateNetProtectionsApplyToSubsequentUpdates) { +IN_PROC_BROWSER_TEST_F(InterestGroupLocalNetworkBrowserTest, + LocalNetProtectionsApplyToSubsequentUpdates) { constexpr char kLocallyUpdateGroupName[] = "Locally updated group"; constexpr char kPubliclyUpdateGroupName[] = "Publicly updated group"; @@ -10422,7 +10422,7 @@ JsReplace(kUpdateContentTemplate, new_bidding_url_c)); // First, create an interest group in a.test and start updating it from a - // private site. The update doesn't finish yet because the network response + // local site. The update doesn't finish yet because the network response // is delayed. ASSERT_TRUE(NavigateToURL(shell(), https_server_->GetURL("a.test", "/echo"))); @@ -10462,7 +10462,7 @@ EXPECT_EQ("done", UpdateInterestGroupsInJS()); - // Finally, create and update the last interest group on a private network -- + // Finally, create and update the last interest group on a local network -- // this update shouldn't be blocked. ASSERT_TRUE(NavigateToURL(shell(), https_server_->GetURL("c.test", "/echo"))); @@ -10510,20 +10510,20 @@ EXPECT_EQ(b_groups[0].interest_group.bidding_url, initial_bidding_url_b); } -// Join interest groups with local (private) update URLs, and run auctions from -// both a a main frame loaded with public address space, and with a private +// Join interest groups with local update URLs, and run auctions from +// both a a main frame loaded with public address space, and with a local // address space. The auctions trigger updates the interest groups, but only the -// frame using a private address space successfully updates the IG, since frames +// frame using a local address space successfully updates the IG, since frames // from public address spaces are blocked from making requests to servers with -// private addresses. +// local addresses. // // Different interest groups (with different origins) are used for the public -// and private auction, to avoid running into update rate limits. -IN_PROC_BROWSER_TEST_F(InterestGroupPrivateNetworkBrowserTest, - PrivateNetProtectionsApplyToPostAuctionUpdates) { +// and local auction, to avoid running into update rate limits. +IN_PROC_BROWSER_TEST_F(InterestGroupLocalNetworkBrowserTest, + LocalNetProtectionsApplyToPostAuctionUpdates) { // Fetches for the interest group-related scripts and updates are always // local, it's where they're updated from that matters. Interest group A will - // be updated from an auction on a public origin, and B from a private one. + // be updated from an auction on a public origin, and B from a local one. // Only the second update will succeed. // // It's important to do the successful update last, so that the first update @@ -10563,7 +10563,7 @@ } kTestCases[] = { {interest_group_a_origin, /*run_auction_from_public_address_space=*/true, - // This header treats a response from a server on a private IP as if the + // This header treats a response from a server on a local IP as if the // server were on public address space. https_server_->GetURL( "c.test",
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc index 50d11b0..891cd62 100644 --- a/content/browser/navigation_browsertest.cc +++ b/content/browser/navigation_browsertest.cc
@@ -6053,7 +6053,7 @@ static constexpr char kPervasivePayload[] = "/cache_transparency/pervasive.js"; static constexpr char kCacheUsedHistogram[] = - "Network.CacheTransparency.SingleKeyedCacheIsUsed"; + "Network.CacheTransparency2.SingleKeyedCacheIsUsed"; base::test::ScopedFeatureList feature_list_; GURL pervasive_payload_url_;
diff --git a/content/browser/portal/portal_browsertest.cc b/content/browser/portal/portal_browsertest.cc index a8fdf42..5882134 100644 --- a/content/browser/portal/portal_browsertest.cc +++ b/content/browser/portal/portal_browsertest.cc
@@ -2141,8 +2141,7 @@ main_frame->browser_accessibility_manager()->SignalEndOfTest(); ASSERT_TRUE(end_of_test_waiter.WaitForNotification()); } - // If DCHECKs are enabled and in sanitizer builds, a failure will cause this - // test to crash rather than complete. + EXPECT_EQ(0, main_frame->accessibility_fatal_error_count_for_testing()); } IN_PROC_BROWSER_TEST_F(PortalBrowserTest, @@ -2195,8 +2194,7 @@ adoption_observer.WaitUntilPortalCreated(); ASSERT_TRUE(waiter.WaitForNotification()); } - // If DCHECKs are enabled and in sanitizer builds, a failure will cause this - // test to crash rather than complete. + EXPECT_EQ(0, main_frame->accessibility_fatal_error_count_for_testing()); } IN_PROC_BROWSER_TEST_F(PortalBrowserTest,
diff --git a/content/browser/preloading/prefetch/prefetch_url_loader_interceptor.cc b/content/browser/preloading/prefetch/prefetch_url_loader_interceptor.cc index eac8d8d..851788d 100644 --- a/content/browser/preloading/prefetch/prefetch_url_loader_interceptor.cc +++ b/content/browser/preloading/prefetch/prefetch_url_loader_interceptor.cc
@@ -93,11 +93,11 @@ loader_callback_ = std::move(callback); url_ = tenative_resource_request.url; GetPrefetch(url_, base::BindOnce( - &PrefetchURLLoaderInterceptor::OnGotPrefetchToServce, + &PrefetchURLLoaderInterceptor::OnGotPrefetchToServe, weak_factory_.GetWeakPtr(), tenative_resource_request)); } -void PrefetchURLLoaderInterceptor::OnGotPrefetchToServce( +void PrefetchURLLoaderInterceptor::OnGotPrefetchToServe( const network::ResourceRequest& tenative_resource_request, base::WeakPtr<PrefetchContainer> prefetch_container) { // The |url_| might be different from |prefetch_container->GetURL()| because
diff --git a/content/browser/preloading/prefetch/prefetch_url_loader_interceptor.h b/content/browser/preloading/prefetch/prefetch_url_loader_interceptor.h index 9afa7be..daf3eba1 100644 --- a/content/browser/preloading/prefetch/prefetch_url_loader_interceptor.h +++ b/content/browser/preloading/prefetch/prefetch_url_loader_interceptor.h
@@ -57,7 +57,7 @@ // Checks the prefetch retrieved via |GetPrefetch| to see if it can be used // for |tenative_resource_request|. - void OnGotPrefetchToServce( + void OnGotPrefetchToServe( const network::ResourceRequest& tenative_resource_request, base::WeakPtr<PrefetchContainer> prefetch_container);
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc index ee847c6..099f4e2 100644 --- a/content/browser/renderer_host/navigation_request.cc +++ b/content/browser/renderer_host/navigation_request.cc
@@ -6826,7 +6826,7 @@ !is_mhtml_subframe_loaded_from_achive; } -void NavigationRequest::UpdatePrivateNetworkRequestPolicy() { +void NavigationRequest::UpdateLocalNetworkRequestPolicy() { // It is useless to update this state for same-document navigations as well // as pages served from the back-forward cache or prerendered pages. DCHECK(!IsSameDocument()); @@ -6842,7 +6842,7 @@ frame_tree_node_->navigator().controller().GetBrowserContext(); url::Origin origin = GetOriginToCommit().value(); - if (client->ShouldAllowInsecurePrivateNetworkRequests(context, origin)) { + if (client->ShouldAllowInsecureLocalNetworkRequests(context, origin)) { // The content browser client decided to make an exception for this URL. local_network_request_policy_ = network::mojom::LocalNetworkRequestPolicy::kAllow; @@ -6932,7 +6932,7 @@ } if (!IsSameDocument() && !IsPageActivation()) - UpdatePrivateNetworkRequestPolicy(); + UpdateLocalNetworkRequestPolicy(); RenderFrameHostImpl* previous_render_frame_host = frame_tree_node_->current_frame_host();
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h index bfebbb29..86bf3cb6 100644 --- a/content/browser/renderer_host/navigation_request.h +++ b/content/browser/renderer_host/navigation_request.h
@@ -1537,7 +1537,7 @@ // // Must not be called for same-document navigation requests nor for requests // served from the back-forward cache or from prerendered pages. - void UpdatePrivateNetworkRequestPolicy(); + void UpdateLocalNetworkRequestPolicy(); // Called when the navigation is ready to be committed. This will update the // |state_| and inform the delegate.
diff --git a/content/browser/renderer_host/private_network_access_browsertest.cc b/content/browser/renderer_host/private_network_access_browsertest.cc index bdf25b7c..2e10ba6 100644 --- a/content/browser/renderer_host/private_network_access_browsertest.cc +++ b/content/browser/renderer_host/private_network_access_browsertest.cc
@@ -128,7 +128,7 @@ } // A |ContentBrowserClient| implementation that allows modifying the return -// value of |ShouldAllowInsecurePrivateNetworkRequests()| at will. +// value of |ShouldAllowInsecureLocalNetworkRequests()| at will. class PolicyTestContentBrowserClient : public ContentBrowserTestContentBrowserClient { public: @@ -142,11 +142,11 @@ ~PolicyTestContentBrowserClient() override = default; // Adds an origin to the allowlist. - void SetAllowInsecurePrivateNetworkRequestsFrom(const url::Origin& origin) { + void SetAllowInsecureLocalNetworkRequestsFrom(const url::Origin& origin) { allowlisted_origins_.insert(origin); } - bool ShouldAllowInsecurePrivateNetworkRequests( + bool ShouldAllowInsecureLocalNetworkRequests( content::BrowserContext* browser_context, const url::Origin& origin) override { return allowlisted_origins_.find(origin) != allowlisted_origins_.end(); @@ -2711,7 +2711,7 @@ GURL url = InsecurePublicURL(kDefaultPath); PolicyTestContentBrowserClient client; - client.SetAllowInsecurePrivateNetworkRequestsFrom(url::Origin::Create(url)); + client.SetAllowInsecureLocalNetworkRequestsFrom(url::Origin::Create(url)); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -2735,7 +2735,7 @@ GURL url = InsecurePublicURL(kDefaultPath); PolicyTestContentBrowserClient client; - client.SetAllowInsecurePrivateNetworkRequestsFrom(url::Origin::Create(url)); + client.SetAllowInsecureLocalNetworkRequestsFrom(url::Origin::Create(url)); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -2758,7 +2758,7 @@ GURL url = InsecurePublicURL(kDefaultPath); PolicyTestContentBrowserClient client; - client.SetAllowInsecurePrivateNetworkRequestsFrom(url::Origin::Create(url)); + client.SetAllowInsecureLocalNetworkRequestsFrom(url::Origin::Create(url)); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -2781,7 +2781,7 @@ GURL url = InsecurePublicURL(kDefaultPath); PolicyTestContentBrowserClient client; - client.SetAllowInsecurePrivateNetworkRequestsFrom(url::Origin::Create(url)); + client.SetAllowInsecureLocalNetworkRequestsFrom(url::Origin::Create(url)); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -2805,7 +2805,7 @@ GURL url = InsecurePublicURL(kDefaultPath); PolicyTestContentBrowserClient client; - client.SetAllowInsecurePrivateNetworkRequestsFrom(url::Origin::Create(url)); + client.SetAllowInsecureLocalNetworkRequestsFrom(url::Origin::Create(url)); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -2831,7 +2831,7 @@ GURL url = InsecurePublicURL(kDefaultPath); PolicyTestContentBrowserClient client; - client.SetAllowInsecurePrivateNetworkRequestsFrom(url::Origin::Create(url)); + client.SetAllowInsecureLocalNetworkRequestsFrom(url::Origin::Create(url)); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -3310,7 +3310,7 @@ GURL url = InsecureLocalURL(kTreatAsPublicAddressPath); PolicyTestContentBrowserClient client; - client.SetAllowInsecurePrivateNetworkRequestsFrom(url::Origin::Create(url)); + client.SetAllowInsecureLocalNetworkRequestsFrom(url::Origin::Create(url)); EXPECT_TRUE(NavigateToURL(shell(), url));
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc index f081634..eaa54bf6 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -2800,11 +2800,22 @@ } void RenderFrameHostImpl::AccessibilityFatalError() { - // In fail fast mode, would have crashed where error occurred. CHECK(!BrowserAccessibilityManager::IsFailFastMode()); - // Try to rebuild the accessibility tree from the root. browser_accessibility_manager_.reset(); - if (!accessibility_reset_token_) { + if (accessibility_reset_token_ || !render_accessibility_) + return; + + accessibility_fatal_error_count_++; + if (accessibility_fatal_error_count_ > max_accessibility_resets_) { + // This will both create an "Aw Snap..." and generate a second crash report + // in addition to the DumpWithoutCrashing() for the first reset. + render_accessibility_->FatalError(); + } else { + // Crash keys set in BrowserAccessibilityManager::Unserialize(). + if (accessibility_fatal_error_count_ == 1) { + // Only send crash report first time -- don't skew crash stats too much. + base::debug::DumpWithoutCrashing(); + } AccessibilityReset(); } } @@ -4016,6 +4027,11 @@ } bool RenderFrameHostImpl::IsMainFrameThirdPartyStoragePartitioningEnabled() { + // If we're in the main frame the state of third-party storage partitioning + // doesn't matter as the StorageKey will be first-party no matter what. + if (is_main_frame()) { + return false; + } RuntimeFeatureStateDocumentData* rfs_document_data_for_storage_key = RuntimeFeatureStateDocumentData::GetForCurrentDocument(GetMainFrame()); @@ -4029,8 +4045,10 @@ } // If the enterprise policy blocks, we have directive to override the // current value of net::features::ThirdPartyStoragePartitioning. + // We can safely read the last comitted-origin (even during navigation) + // as we know we are not in the main-frame since that case is filtered above. if (!GetContentClient()->browser()->IsThirdPartyStoragePartitioningAllowed( - GetBrowserContext())) { + GetBrowserContext(), GetMainFrame()->GetLastCommittedOrigin())) { return false; } return blink::StorageKey::IsThirdPartyStoragePartitioningEnabled(); @@ -4151,7 +4169,7 @@ IsMainFrameThirdPartyStoragePartitioningEnabled())); // Apply private network request policy according to our new origin. - if (GetContentClient()->browser()->ShouldAllowInsecurePrivateNetworkRequests( + if (GetContentClient()->browser()->ShouldAllowInsecureLocalNetworkRequests( GetBrowserContext(), new_frame_origin)) { local_network_request_policy_ = network::mojom::LocalNetworkRequestPolicy::kAllow; @@ -12469,6 +12487,8 @@ // if necessary. media_device_id_salt_base_ = BrowserContext::CreateRandomMediaDeviceIDSalt(); + accessibility_fatal_error_count_ = 0; + UpdateIsolatableSandboxedIframeTracking(navigation_request); // After commit, the browser process's access of the features' state becomes
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h index a665bdd7..0c9248c 100644 --- a/content/browser/renderer_host/render_frame_host_impl.h +++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -2573,6 +2573,10 @@ std::unique_ptr<mojo::MessageFilter> CreateMessageFilterForAssociatedReceiver( const char* interface_name); + int accessibility_fatal_error_count_for_testing() const { + return accessibility_fatal_error_count_; + } + // TODO(https://crbug.com/1179502): FrameTree and FrameTreeNode are not const // as with prerenderer activation the page needs to move between // FrameTreeNodes and FrameTrees. Note that FrameTreeNode can only change for @@ -4214,6 +4218,11 @@ // assigned) and a complete replacement accessibility tree. int accessibility_reset_token_ = 0; + // A count of the number of times we received an unexpected fatal + // accessibility error and needed to reset accessibility, so we don't keep + // trying to reset forever. + int accessibility_fatal_error_count_ = 0; + // The last AXTreeData for this frame received from the RenderFrame. ui::AXTreeData ax_tree_data_;
diff --git a/content/browser/scheduler/responsiveness/calculator.cc b/content/browser/scheduler/responsiveness/calculator.cc index 6fcde97..f7b4a030 100644 --- a/content/browser/scheduler/responsiveness/calculator.cc +++ b/content/browser/scheduler/responsiveness/calculator.cc
@@ -164,21 +164,6 @@ "Browser.MainThreadsCongestion.RunningOnly.Periodic", num_congested_slices); } - // Emit the old name until M107. - UMA_HISTOGRAM_COUNTS_1000( - "Browser.Responsiveness.JankyIntervalsPerThirtySeconds", - num_congested_slices); - // Only kFirstInterval and kPeriodic are reported with a suffix, stages - // in between are only part of the unsuffixed histogram. - if (startup_stage_ == StartupStage::kFirstInterval) { - UMA_HISTOGRAM_COUNTS_1000( - "Browser.Responsiveness.JankyIntervalsPerThirtySeconds.Initial", - num_congested_slices); - } else if (startup_stage_ == StartupStage::kPeriodic) { - UMA_HISTOGRAM_COUNTS_1000( - "Browser.Responsiveness.JankyIntervalsPerThirtySeconds.Periodic", - num_congested_slices); - } break; } case CongestionType::kQueueAndExecution: { @@ -199,19 +184,6 @@ num_congested_slices, 1, kMaxCongestedSlices, 50); } - // Emit the old name until M107. - UMA_HISTOGRAM_CUSTOM_COUNTS( - "Browser.Responsiveness.JankyIntervalsPerThirtySeconds3", - num_congested_slices, 1, kMaxCongestedSlices, 50); - if (startup_stage_ == StartupStage::kFirstIntervalAfterFirstIdle) { - UMA_HISTOGRAM_CUSTOM_COUNTS( - "Browser.Responsiveness.JankyIntervalsPerThirtySeconds3.Initial", - num_congested_slices, 1, kMaxCongestedSlices, 50); - } else if (startup_stage_ == StartupStage::kPeriodic) { - UMA_HISTOGRAM_CUSTOM_COUNTS( - "Browser.Responsiveness.JankyIntervalsPerThirtySeconds3.Periodic", - num_congested_slices, 1, kMaxCongestedSlices, 50); - } break; } }
diff --git a/content/browser/webid/fedcm_metrics.cc b/content/browser/webid/fedcm_metrics.cc index 81b43c6e..62ca71a 100644 --- a/content/browser/webid/fedcm_metrics.cc +++ b/content/browser/webid/fedcm_metrics.cc
@@ -82,9 +82,7 @@ IdentityRequestDialogController::DismissReason dismiss_reason) { if (is_disabled_) return; - base::UmaHistogramEnumeration( - "Blink.FedCm.CancelReason", dismiss_reason, - IdentityRequestDialogController::DismissReason::COUNT); + base::UmaHistogramEnumeration("Blink.FedCm.CancelReason", dismiss_reason); } void FedCmMetrics::RecordTokenResponseAndTurnaroundTime(
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc index ea40da0..7d420a18 100644 --- a/content/browser/webid/federated_auth_request_impl.cc +++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -1305,8 +1305,8 @@ // the virtual keyboard showing on Android. bool should_embargo = false; switch (dismiss_reason) { - case IdentityRequestDialogController::DismissReason::CLOSE_BUTTON: - case IdentityRequestDialogController::DismissReason::SWIPE: + case IdentityRequestDialogController::DismissReason::kCloseButton: + case IdentityRequestDialogController::DismissReason::kSwipe: should_embargo = true; break; default: @@ -1722,7 +1722,7 @@ void FederatedAuthRequestImpl::DismissAccountsDialogForDevtools() { // We pick a reason here does not trigger embargo. - OnDialogDismissed(IdentityRequestDialogController::DismissReason::OTHER); + OnDialogDismissed(IdentityRequestDialogController::DismissReason::kOther); } bool FederatedAuthRequestImpl::GetSingleReturningAccount(
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc index 2a84d3d..ad622fd 100644 --- a/content/browser/webid/federated_auth_request_impl_unittest.cc +++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -535,7 +535,7 @@ case AccountsDialogAction::kClose: base::SequencedTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, base::BindOnce(std::move(dismiss_callback), - DismissReason::CLOSE_BUTTON)); + DismissReason::kCloseButton)); break; case AccountsDialogAction::kNone: break; @@ -556,7 +556,7 @@ case IdpSigninStatusMismatchDialogAction::kClose: base::SequencedTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, base::BindOnce(std::move(dismiss_callback), - DismissReason::CLOSE_BUTTON)); + DismissReason::kCloseButton)); break; case IdpSigninStatusMismatchDialogAction::kNone: break;
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc index 42a10948..e996294b5 100644 --- a/content/public/browser/content_browser_client.cc +++ b/content/public/browser/content_browser_client.cc
@@ -1311,7 +1311,7 @@ return url.SchemeIsLocal(); } -bool ContentBrowserClient::ShouldAllowInsecurePrivateNetworkRequests( +bool ContentBrowserClient::ShouldAllowInsecureLocalNetworkRequests( BrowserContext* browser_context, const url::Origin& origin) { return false; @@ -1442,7 +1442,8 @@ } bool ContentBrowserClient::IsThirdPartyStoragePartitioningAllowed( - content::BrowserContext* browser_context) { + content::BrowserContext*, + const url::Origin&) { return true; }
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h index 986dc23..1acdf47 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h
@@ -2243,7 +2243,7 @@ // // |browser_context| must not be nullptr. Caller retains ownership. // |origin| is the origin of a navigation ready to commit. - virtual bool ShouldAllowInsecurePrivateNetworkRequests( + virtual bool ShouldAllowInsecureLocalNetworkRequests( BrowserContext* browser_context, const url::Origin& origin); @@ -2397,7 +2397,8 @@ // from being enabled if it returns false. If it returns true, then // we fallback on the base feature to determine if partitioning is on. virtual bool IsThirdPartyStoragePartitioningAllowed( - content::BrowserContext* browser_context); + content::BrowserContext* browser_context, + const url::Origin& top_level_origin); }; } // namespace content
diff --git a/content/public/browser/identity_request_dialog_controller.cc b/content/public/browser/identity_request_dialog_controller.cc index cacc996..c27b79ac 100644 --- a/content/public/browser/identity_request_dialog_controller.cc +++ b/content/public/browser/identity_request_dialog_controller.cc
@@ -60,7 +60,7 @@ AccountSelectionCallback on_selected, DismissCallback dismiss_callback) { if (!is_interception_enabled_) { - std::move(dismiss_callback).Run(DismissReason::OTHER); + std::move(dismiss_callback).Run(DismissReason::kOther); } } @@ -70,7 +70,7 @@ const std::string& idp_for_display, DismissCallback dismiss_callback) { if (!is_interception_enabled_) { - std::move(dismiss_callback).Run(DismissReason::OTHER); + std::move(dismiss_callback).Run(DismissReason::kOther); } }
diff --git a/content/public/browser/identity_request_dialog_controller.h b/content/public/browser/identity_request_dialog_controller.h index b356a99e..daf3923 100644 --- a/content/public/browser/identity_request_dialog_controller.h +++ b/content/public/browser/identity_request_dialog_controller.h
@@ -68,12 +68,12 @@ // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.content.webid // GENERATED_JAVA_CLASS_NAME_OVERRIDE: IdentityRequestDialogDismissReason enum class DismissReason { - OTHER = 0, - CLOSE_BUTTON = 1, - SWIPE = 2, - VIRTUAL_KEYBOARD_SHOWN = 3, + kOther = 0, + kCloseButton = 1, + kSwipe = 2, + kVirtualKeyboardShown = 3, - COUNT = 4, + kMaxValue = kVirtualKeyboardShown, }; using AccountSelectionCallback =
diff --git a/content/test/data/accessibility/html/hgroup-expected-blink.txt b/content/test/data/accessibility/html/hgroup-expected-blink.txt new file mode 100644 index 0000000..873a775f --- /dev/null +++ b/content/test/data/accessibility/html/hgroup-expected-blink.txt
@@ -0,0 +1,13 @@ +rootWebArea +++genericContainer ignored +++++genericContainer ignored +++++++group ignored +++++++++heading name='Heading 1' hierarchicalLevel=1 +++++++++++staticText name='Heading 1' +++++++++++++inlineTextBox name='Heading 1' +++++++++heading name='Heading 2' hierarchicalLevel=6 +++++++++++staticText name='Heading 2' +++++++++++++inlineTextBox name='Heading 2' +++++++++paragraph +++++++++++staticText name='My test' +++++++++++++inlineTextBox name='My test'
diff --git a/content/test/data/accessibility/html/hgroup.html b/content/test/data/accessibility/html/hgroup.html new file mode 100644 index 0000000..df1a12f --- /dev/null +++ b/content/test/data/accessibility/html/hgroup.html
@@ -0,0 +1,20 @@ +<!-- +@BLINK-ALLOW:hierarchicalLevel* +@MAC-ALLOW:AXDisclosureLevel +@WIN-ALLOW:level:* +@WIN-ALLOW:xml-roles* +@AURALINUX-ALLOW:level:* +@AURALINUX-ALLOW:xml-roles* +--> +<!DOCTYPE html> +<html> + +<body> + <hgroup> + <h1>Heading 1</h1> + <h6>Heading 2</h6> + <p>My test</p> + </hgroup> +</body> + +</html>
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt index 4c69f01..79bb78f1 100644 --- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt +++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -398,9 +398,13 @@ crbug.com/1418987 [ linux amd-0x7340 angle-vulkan ] Pixel_OffscreenCanvasAccelerated2D [ RetryOnFailure ] crbug.com/1418987 [ linux amd-0x7340 angle-vulkan ] Pixel_OffscreenCanvasWebGLDefaultWorker [ RetryOnFailure ] -# Flakes on Pixel 4, times out on Pixel 6 -crbug.com/1383180 [ android android-pixel-6 android-webview-instrumentation angle-disabled no-passthrough ] Pixel_MediaRecorderFrom2DCanvas [ Failure ] -crbug.com/1383180 [ android android-pixel-6 ] Pixel_Video_MP4_Rounded_Corner [ Failure ] +# Flakes on Pixel 4 and Pixel 6 +# finder:group-start crbug.com/1383180 +crbug.com/1383180 [ android android-pixel-4 android-chromium ] Pixel_MediaRecorderFrom2DCanvas [ RetryOnFailure ] +crbug.com/1383180 [ android android-pixel-6 android-chromium ] Pixel_MediaRecorderFrom2DCanvas [ RetryOnFailure ] +crbug.com/1383180 [ android android-pixel-4 android-chromium ] Pixel_Video_Context_Loss_VP9 [ RetryOnFailure ] +crbug.com/1383180 [ android android-pixel-6 android-chromium ] Pixel_Video_MP4_Rounded_Corner [ RetryOnFailure ] +# finder:group-end ####################################################################### # Automated Entries After This Point - Do Not Manually Add Below Here #
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h index f74be997..ff9cad3 100644 --- a/extensions/browser/extension_function_histogram_value.h +++ b/extensions/browser/extension_function_histogram_value.h
@@ -1831,6 +1831,7 @@ SMARTCARDPROVIDERPRIVATE_REPORTCONNECTRESULT = 1768, SMARTCARDPROVIDERPRIVATE_REPORTDISCONNECTRESULT = 1769, WMDESKSPRIVATE_GETDESKBYID = 1770, + AUTOFILLPRIVATE_ISVALIDIBAN = 1771, // Last entry: Add new entries above, then run: // tools/metrics/histograms/update_extension_histograms.py ENUM_BOUNDARY
diff --git a/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory.cc b/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory.cc index 0ad8a33..4b45505a 100644 --- a/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory.cc +++ b/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory.cc
@@ -7,6 +7,7 @@ #include "build/build_config.h" #include "components/viz/common/gpu/vulkan_context_provider.h" #include "components/viz/common/resources/shared_image_format.h" +#include "gpu/command_buffer/common/shared_image_usage.h" #include "gpu/command_buffer/service/shared_image/external_vk_image_backing.h" #include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h" #include "gpu/command_buffer/service/shared_image/shared_image_representation.h" @@ -201,8 +202,15 @@ } #endif - if (gmb_type != gfx::EMPTY_BUFFER && !CanImportGpuMemoryBuffer(gmb_type)) { - return false; + if (gmb_type == gfx::EMPTY_BUFFER) { + if (usage & SHARED_IMAGE_USAGE_CPU_WRITE) { + // Only CPU writable when the client provides a NativePixmap. + return false; + } + } else { + if (!CanImportGpuMemoryBuffer(gmb_type)) { + return false; + } } if (thread_safe) {
diff --git a/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory.mm b/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory.mm index 685b7f1..cca8075 100644 --- a/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory.mm +++ b/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory.mm
@@ -21,6 +21,7 @@ #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/skia/include/core/SkPromiseImageTexture.h" #include "ui/gfx/buffer_format_util.h" +#include "ui/gfx/gpu_memory_buffer.h" #include "ui/gfx/mac/io_surface.h" #include "ui/gl/buffer_format_utils.h" #include "ui/gl/buildflags.h" @@ -233,8 +234,15 @@ if (thread_safe) { return false; } + // Never used with shared memory GMBs. - if (gmb_type == gfx::SHARED_MEMORY_BUFFER) { + if (gmb_type != gfx::EMPTY_BUFFER && gmb_type != gfx::IO_SURFACE_BUFFER) { + return false; + } + + if (usage & SHARED_IMAGE_USAGE_CPU_WRITE && + gmb_type != gfx::IO_SURFACE_BUFFER) { + // Only CPU writable when the client provides a IOSurface. return false; }
diff --git a/gpu/command_buffer/service/shared_image/ozone_image_backing_factory.cc b/gpu/command_buffer/service/shared_image/ozone_image_backing_factory.cc index 1858a36..873c64b 100644 --- a/gpu/command_buffer/service/shared_image/ozone_image_backing_factory.cc +++ b/gpu/command_buffer/service/shared_image/ozone_image_backing_factory.cc
@@ -237,6 +237,11 @@ return false; } + if (usage & SHARED_IMAGE_USAGE_CPU_WRITE && gmb_type != gfx::NATIVE_PIXMAP) { + // Only CPU writable when the client provides a NativePixmap. + return false; + } + bool used_by_skia = (usage & SHARED_IMAGE_USAGE_RASTER) || (usage & SHARED_IMAGE_USAGE_DISPLAY_READ) || (usage & SHARED_IMAGE_USAGE_DISPLAY_WRITE);
diff --git a/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing.cc b/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing.cc index e7f18e3..640569c 100644 --- a/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing.cc +++ b/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing.cc
@@ -14,9 +14,18 @@ #include "gpu/command_buffer/service/shared_context_state.h" #include "gpu/command_buffer/service/shared_image/shared_image_representation.h" #include "gpu/command_buffer/service/skia_utils.h" +#include "third_party/skia/include/core/SkAlphaType.h" #include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/core/SkColorSpace.h" +#include "third_party/skia/include/core/SkColorType.h" +#include "third_party/skia/include/core/SkImageInfo.h" +#include "third_party/skia/include/core/SkPixmap.h" +#include "third_party/skia/include/core/SkPromiseImageTexture.h" +#include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/core/SkSurfaceProps.h" +#include "third_party/skia/include/core/SkTextureCompressionType.h" namespace gpu { @@ -234,8 +243,9 @@ if (format().IsCompressed()) { textures_[0].backend_texture = context_state_->gr_context()->createCompressedBackendTexture( - size().width(), size().height(), SkImage::kETC1_CompressionType, - pixels.data(), pixels.size(), GrMipMapped::kNo, GrProtected::kNo); + size().width(), size().height(), + SkTextureCompressionType::kETC1_RGB8, pixels.data(), pixels.size(), + GrMipMapped::kNo, GrProtected::kNo); } else { auto info = AsSkImageInfo(); if (!stride) {
diff --git a/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory.cc b/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory.cc index 7c09d58..634db40 100644 --- a/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory.cc +++ b/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory.cc
@@ -16,7 +16,10 @@ #include "gpu/command_buffer/service/shared_image/wrapped_sk_image_backing.h" #include "gpu/config/gpu_finch_features.h" #include "skia/buildflags.h" +#include "third_party/skia/include/core/SkAlphaType.h" +#include "third_party/skia/include/core/SkColorType.h" #include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/core/SkTextureCompressionType.h" #include "third_party/skia/include/gpu/GrDirectContext.h" #include "third_party/skia/include/gpu/GrTypes.h" @@ -168,7 +171,7 @@ return false; } auto backend_format = context_state_->gr_context()->compressedBackendFormat( - SkImage::kETC1_CompressionType); + SkTextureCompressionType::kETC1_RGB8); if (!backend_format.isValid()) { return false; }
diff --git a/gpu/command_buffer/service/skia_utils.cc b/gpu/command_buffer/service/skia_utils.cc index 34a70bc..cc5dea19 100644 --- a/gpu/command_buffer/service/skia_utils.cc +++ b/gpu/command_buffer/service/skia_utils.cc
@@ -14,8 +14,13 @@ #include "gpu/config/gpu_finch_features.h" #include "gpu/config/gpu_switches.h" #include "gpu/config/skia_limits.h" +#include "third_party/skia/include/core/SkImage.h" +#include "third_party/skia/include/core/SkRefCnt.h" +#include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/core/SkTextureCompressionType.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/GrContextThreadSafeProxy.h" +#include "third_party/skia/include/gpu/GrDirectContext.h" #include "third_party/skia/include/gpu/gl/GrGLTypes.h" #include "ui/base/ui_base_features.h" #include "ui/gfx/geometry/size.h" @@ -137,7 +142,7 @@ if (gl_storage_format == GL_ETC1_RGB8_OES) { GrGLFormat gr_gl_format = gr_context_thread_safe - ->compressedBackendFormat(SkImage::kETC1_CompressionType) + ->compressedBackendFormat(SkTextureCompressionType::kETC1_RGB8) .asGLFormat(); if (gr_gl_format == GrGLFormat::kCOMPRESSED_ETC1_RGB8) { internal_format = GL_ETC1_RGB8_OES;
diff --git a/gpu/skia_bindings/grcontext_for_gles2_interface.h b/gpu/skia_bindings/grcontext_for_gles2_interface.h index f923bf2..7adeffde 100644 --- a/gpu/skia_bindings/grcontext_for_gles2_interface.h +++ b/gpu/skia_bindings/grcontext_for_gles2_interface.h
@@ -9,6 +9,8 @@ #include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/include/gpu/GrContextOptions.h" +class GrDirectContext; + namespace gpu { struct Capabilities; class ContextSupport; @@ -46,7 +48,7 @@ void FreeGpuResources(); private: - sk_sp<class GrDirectContext> gr_context_; + sk_sp<GrDirectContext> gr_context_; raw_ptr<gpu::ContextSupport> context_support_; };
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd index 1fbe6e0..47524ba1 100644 --- a/ios/chrome/app/strings/ios_strings.grd +++ b/ios/chrome/app/strings/ios_strings.grd
@@ -372,6 +372,9 @@ <message name="IDS_IOS_CONSISTENCY_PROMO_CHOOSE_ACCOUNT" desc="Presents the list of account on the device and invites the user to choose an account to sign-in."> Choose an Account </message> + <message name="IDS_IOS_CONSISTENCY_PROMO_SIGN_IN" desc="Button to sign in from the consistency menu when there is no Google account on the device."> + Sign In… + </message> <message name="IDS_IOS_CONSISTENCY_PROMO_SKIP" desc="Dismisses the sign-in promo on the web."> Skip </message> @@ -956,7 +959,7 @@ Sign in to manage the content you see </message> <message name="IDS_IOS_FEED_CARD_SIGN_IN_ONLY_PROMO_LABEL" desc="Label of the feed card sign-in only promo UI. [iOS only]"> - You can manage your preferences after you sign in to Chrome with your Google Account. + Sign in to manage your preferences. </message> <message name="IDS_IOS_FEED_CUSTOM_SEARCH_ENGINE_LABEL" desc="Message informing users with custom search engines that the feed is provided by Google."> Provided by Google
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONSISTENCY_PROMO_SIGN_IN.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONSISTENCY_PROMO_SIGN_IN.png.sha1 new file mode 100644 index 0000000..5745a84 --- /dev/null +++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONSISTENCY_PROMO_SIGN_IN.png.sha1
@@ -0,0 +1 @@ +4b011b30d4140b365216f9e00ede311ca3f17749 \ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FEED_CARD_SIGN_IN_ONLY_PROMO_LABEL.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FEED_CARD_SIGN_IN_ONLY_PROMO_LABEL.png.sha1 index b1b4cce..3f20bd9 100644 --- a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FEED_CARD_SIGN_IN_ONLY_PROMO_LABEL.png.sha1 +++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FEED_CARD_SIGN_IN_ONLY_PROMO_LABEL.png.sha1
@@ -1 +1 @@ -a2beb89c2f3c1331d539979b7eaa3cbc3726b211 \ No newline at end of file +a55acf4e63d6a75dc8ed3e9713ca14c7060f4372 \ No newline at end of file
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm index 4935b28..bd419b1 100644 --- a/ios/chrome/browser/flags/about_flags.mm +++ b/ios/chrome/browser/flags/about_flags.mm
@@ -529,6 +529,31 @@ kNewTabPageRetentionTileAblationHideMVTOnly, std::size(kNewTabPageRetentionTileAblationHideMVTOnly), nullptr}}; +const FeatureEntry::FeatureParam kEnableExpKitTextClassifierDate[] = { + {"date", "true"}}; +const FeatureEntry::FeatureParam kEnableExpKitTextClassifierAddress[] = { + {"address", "true"}}; +const FeatureEntry::FeatureParam kEnableExpKitTextClassifierPhoneNumber[] = { + {"phonenumber", "true"}}; +const FeatureEntry::FeatureParam kEnableExpKitTextClassifierEmail[] = { + {"email", "true"}}; +const FeatureEntry::FeatureParam kEnableExpKitTextClassifierAll[] = { + {"date", "true"}, + {"address", "true"}, + {"phonenumber", "true"}, + {"email", "true"}}; +const FeatureEntry::FeatureVariation kEnableExpKitTextClassifierVariations[] = { + {"Enabled for all entities", kEnableExpKitTextClassifierAll, + std::size(kEnableExpKitTextClassifierAll), nullptr}, + {"Enabled for date", kEnableExpKitTextClassifierDate, + std::size(kEnableExpKitTextClassifierDate), nullptr}, + {"Enabled for address", kEnableExpKitTextClassifierAddress, + std::size(kEnableExpKitTextClassifierAddress), nullptr}, + {"Enabled for phonenumber", kEnableExpKitTextClassifierPhoneNumber, + std::size(kEnableExpKitTextClassifierPhoneNumber), nullptr}, + {"Enabled for email", kEnableExpKitTextClassifierEmail, + std::size(kEnableExpKitTextClassifierEmail), nullptr}}; + const FeatureEntry::FeatureParam kEnableExperienceKitMapsWithSrp[] = { {kExperienceKitMapsVariationName, kEnableExperienceKitMapsVariationSrp}}; const FeatureEntry::FeatureVariation kEnableExperienceKitMapsVariations[] = { @@ -563,6 +588,9 @@ const FeatureEntry::FeatureParam kTabInactivityThresholdThreeWeeks[] = { {kTabInactivityThresholdParameterName, kTabInactivityThresholdThreeWeeksParam}}; +const FeatureEntry::FeatureParam kTabInactivityThresholdOneMinuteDemo[] = { + {kTabInactivityThresholdParameterName, + kTabInactivityThresholdOneMinuteDemoParam}}; const FeatureEntry::FeatureVariation kTabInactivityThresholdVariations[] = { {"One week", kTabInactivityThresholdOneWeek, @@ -571,6 +599,8 @@ std::size(kTabInactivityThresholdTwoWeeks), nullptr}, {"Three weeks", kTabInactivityThresholdThreeWeeks, std::size(kTabInactivityThresholdThreeWeeks), nullptr}, + {"One minute [Demo]", kTabInactivityThresholdOneMinuteDemo, + std::size(kTabInactivityThresholdOneMinuteDemo), nullptr}, }; const FeatureEntry::FeatureParam @@ -1018,14 +1048,13 @@ flag_descriptions::kAppleCalendarExperienceKitName, flag_descriptions::kAppleCalendarExperienceKitDescription, flags_ui::kOsIos, FEATURE_VALUE_TYPE(kEnableExpKitAppleCalendar)}, - {"enable-expkit-calendar-text-classifier", - flag_descriptions::kEnableExpKitCalendarTextClassifierName, - flag_descriptions::kEnableExpKitCalendarTextClassifierDescription, - flags_ui::kOsIos, FEATURE_VALUE_TYPE(kEnableExpKitCalendarTextClassifier)}, {"enable-expkit-text-classifier", flag_descriptions::kEnableExpKitTextClassifierName, flag_descriptions::kEnableExpKitTextClassifierDescription, - flags_ui::kOsIos, FEATURE_VALUE_TYPE(kEnableExpKitTextClassifier)}, + flags_ui::kOsIos, + FEATURE_WITH_PARAMS_VALUE_TYPE(kEnableExpKitTextClassifier, + kEnableExpKitTextClassifierVariations, + "ExpKitTextClassifier")}, {"experience-kit-maps", flag_descriptions::kMapsExperienceKitName, flag_descriptions::kMapsExperienceKitDescription, flags_ui::kOsIos, FEATURE_WITH_PARAMS_VALUE_TYPE(kMapsExperienceKit,
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc index e4bb1ce..2f78cf6a 100644 --- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc +++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -187,12 +187,6 @@ "When enabled, one tapping or long pressing on a phone number will trigger " "the phone number experience."; -extern const char kEnableExpKitCalendarTextClassifierName[] = - "Text Classifier in Experience Kit Calendar"; -extern const char kEnableExpKitCalendarTextClassifierDescription[] = - "When enabled, Experience Kit Calendar will use Text Classifier library in " - "entity detection where possible."; - extern const char kEnableExpKitTextClassifierName[] = "Text Classifier in Experience Kit"; extern const char kEnableExpKitTextClassifierDescription[] = @@ -987,7 +981,9 @@ const char kTabInactivityThresholdName[] = "Change Tab inactivity threshold"; const char kTabInactivityThresholdDescription[] = "When enabled, the tabs older than the threshold are considered inactive " - "and set aside in the Inactive Tabs section of the TabGrid."; + "and set aside in the Inactive Tabs section of the TabGrid." + "IMPORTANT: If you ever used the in-app settings for Inactive Tabs, this " + "flag is never read again."; const char kUseLoadSimulatedRequestForOfflinePageName[] = "Use loadSimulatedRequest:responseHTMLString: when displaying offline "
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h index 2163fce..ff6bf3e 100644 --- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h +++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -158,11 +158,6 @@ extern const char kPhoneNumberDescription[]; // Title and description for the flag to enable text classifier entity detection -// in experience kit calendar. -extern const char kEnableExpKitCalendarTextClassifierName[]; -extern const char kEnableExpKitCalendarTextClassifierDescription[]; - -// Title and description for the flag to enable text classifier entity detection // in experience kit for different entity types. extern const char kEnableExpKitTextClassifierName[]; extern const char kEnableExpKitTextClassifierDescription[];
diff --git a/ios/chrome/browser/segmentation_platform/segmentation_platform_config.mm b/ios/chrome/browser/segmentation_platform/segmentation_platform_config.mm index 9eaf8fd..19a310c 100644 --- a/ios/chrome/browser/segmentation_platform/segmentation_platform_config.mm +++ b/ios/chrome/browser/segmentation_platform/segmentation_platform_config.mm
@@ -113,7 +113,9 @@ } configs.emplace_back(GetConfigForCrossDeviceSegments()); - configs.emplace_back(DeviceSwitcherModel::GetConfig()); + if (base::FeatureList::IsEnabled(features::kSegmentationPlatformDeviceSwitcher)) { + configs.emplace_back(DeviceSwitcherModel::GetConfig()); + } // Add new configs here.
diff --git a/ios/chrome/browser/shared/ui/table_view/cells/table_view_multi_line_text_edit_item.mm b/ios/chrome/browser/shared/ui/table_view/cells/table_view_multi_line_text_edit_item.mm index 050f1d9..37a971b 100644 --- a/ios/chrome/browser/shared/ui/table_view/cells/table_view_multi_line_text_edit_item.mm +++ b/ios/chrome/browser/shared/ui/table_view/cells/table_view_multi_line_text_edit_item.mm
@@ -5,6 +5,7 @@ #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_multi_line_text_edit_item.h" #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_multi_line_text_edit_item_delegate.h" +#import "ios/chrome/browser/shared/ui/table_view/chrome_table_view_styler.h" #import "ios/chrome/browser/ui/icons/symbols.h" #import "ios/chrome/common/ui/colors/semantic_color_names.h" #import "ios/chrome/common/ui/table_view/table_view_cells_constants.h" @@ -46,6 +47,9 @@ cell.textView.text = self.text; cell.textView.editable = self.editingEnabled; cell.textView.delegate = self; + cell.textView.backgroundColor = styler.cellBackgroundColor + ? styler.cellBackgroundColor + : styler.tableViewBackgroundColor; if (self.label.length) { cell.textView.accessibilityIdentifier =
diff --git a/ios/chrome/browser/supervised_user/BUILD.gn b/ios/chrome/browser/supervised_user/BUILD.gn index 377bd60..8770a62c 100644 --- a/ios/chrome/browser/supervised_user/BUILD.gn +++ b/ios/chrome/browser/supervised_user/BUILD.gn
@@ -9,7 +9,10 @@ "kids_chrome_management_client_factory.mm", "supervised_user_settings_service_factory.h", "supervised_user_settings_service_factory.mm", + "supervised_user_url_filter_tab_helper.h", + "supervised_user_url_filter_tab_helper.mm", ] + deps = [ "//components/keyed_service/core", "//components/keyed_service/ios", @@ -18,6 +21,8 @@ "//ios/chrome/browser/application_context", "//ios/chrome/browser/browser_state", "//ios/chrome/browser/signin", + "//ios/net", + "//net", "//services/network/public/cpp", ] } @@ -37,3 +42,23 @@ "//ios/web/public/test", ] } + +source_set("eg2_tests") { + configs += [ + "//build/config/compiler:enable_arc", + "//build/config/ios:xctest_config", + ] + testonly = true + sources = [ "supervised_user_url_filter_egtest.mm" ] + deps = [ + "//base", + "//components/supervised_user/core/common", + "//ios/chrome/browser/policy:eg_test_support+eg2", + "//ios/chrome/test/earl_grey:eg_test_support+eg2", + "//ios/testing/earl_grey:eg_test_support+eg2", + "//ios/third_party/earl_grey2:test_lib", + "//ios/web/public/test/http_server", + "//net:test_support", + ] + frameworks = [ "UIKit.framework" ] +}
diff --git a/ios/chrome/browser/supervised_user/supervised_user_url_filter_egtest.mm b/ios/chrome/browser/supervised_user/supervised_user_url_filter_egtest.mm new file mode 100644 index 0000000..8de2cd0 --- /dev/null +++ b/ios/chrome/browser/supervised_user/supervised_user_url_filter_egtest.mm
@@ -0,0 +1,58 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import <string> + +#import "base/feature_list.h" +#import "components/supervised_user/core/common/features.h" +#import "ios/chrome/test/earl_grey/chrome_earl_grey.h" +#import "ios/chrome/test/earl_grey/chrome_matchers.h" +#import "ios/chrome/test/earl_grey/chrome_test_case.h" +#import "ios/testing/earl_grey/app_launch_configuration.h" +#import "ios/testing/earl_grey/earl_grey_test.h" +#import "net/base/net_errors.h" +#import "net/test/embedded_test_server/embedded_test_server.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +// Tests the SupervisedUserURLFilter. +@interface SupervisedUserURLFilterTestCase : ChromeTestCase +@end + +@implementation SupervisedUserURLFilterTestCase + +- (AppLaunchConfiguration)appConfigurationForTestCase { + AppLaunchConfiguration config; + config.features_enabled.push_back( + supervised_user::kFilterWebsitesForSupervisedUsersOnThirdParty); + return config; +} + +- (void)setUp { + [super setUp]; + GREYAssertTrue(self.testServer->Start(), @"Test server failed to start."); +} + +- (void)tearDown { + [super tearDown]; +} + +// Tests that a page load is blocked when the URL is filtered. +- (void)testBlockedSiteDisplaysErrorOnPageLoad { + [ChromeEarlGrey loadURL:self.testServer->GetURL("/filtered")]; + [ChromeEarlGrey + waitForWebStateContainingText:net::ErrorToShortString( + net::ERR_BLOCKED_BY_ADMINISTRATOR)]; +} + +// Tests that unfiltered sites are loaded normally. +- (void)testUnfilteredSiteIsLoaded { + [ChromeEarlGrey loadURL:self.testServer->GetURL("/echo")]; + // Expected text set by embedded_test_server handler. + [ChromeEarlGrey waitForWebStateContainingText:"Echo"]; +} + +@end
diff --git a/ios/chrome/browser/supervised_user/supervised_user_url_filter_tab_helper.h b/ios/chrome/browser/supervised_user/supervised_user_url_filter_tab_helper.h new file mode 100644 index 0000000..fb7499a --- /dev/null +++ b/ios/chrome/browser/supervised_user/supervised_user_url_filter_tab_helper.h
@@ -0,0 +1,36 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_URL_FILTER_TAB_HELPER_H_ +#define IOS_CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_URL_FILTER_TAB_HELPER_H_ + +#import "ios/web/public/navigation/web_state_policy_decider.h" +#import "ios/web/public/web_state_user_data.h" + +// Handles filtered URL requests for supervised users. +class SupervisedUserURLFilterTabHelper + : public web::WebStatePolicyDecider, + public web::WebStateUserData<SupervisedUserURLFilterTabHelper> { + public: + explicit SupervisedUserURLFilterTabHelper(web::WebState* web_state); + ~SupervisedUserURLFilterTabHelper() override; + + // web::WebStatePolicyDecider + void ShouldAllowRequest( + NSURLRequest* request, + web::WebStatePolicyDecider::RequestInfo request_info, + web::WebStatePolicyDecider::PolicyDecisionCallback callback) override; + + private: + friend class web::WebStateUserData<SupervisedUserURLFilterTabHelper>; + + SupervisedUserURLFilterTabHelper(const SupervisedUserURLFilterTabHelper&) = + delete; + SupervisedUserURLFilterTabHelper& operator=( + const SupervisedUserURLFilterTabHelper&) = delete; + + WEB_STATE_USER_DATA_KEY_DECL(); +}; + +#endif // IOS_CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_URL_FILTER_TAB_HELPER_H_
diff --git a/ios/chrome/browser/supervised_user/supervised_user_url_filter_tab_helper.mm b/ios/chrome/browser/supervised_user/supervised_user_url_filter_tab_helper.mm new file mode 100644 index 0000000..ac5d31a --- /dev/null +++ b/ios/chrome/browser/supervised_user/supervised_user_url_filter_tab_helper.mm
@@ -0,0 +1,49 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ios/chrome/browser/supervised_user/supervised_user_url_filter_tab_helper.h" + +#import "ios/chrome/browser/browser_state/chrome_browser_state.h" +#import "ios/net/protocol_handler_util.h" +#import "net/base/mac/url_conversions.h" +#import "net/base/net_errors.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +namespace { + +const char kFilteredURLExample[] = "/filtered"; + +NSError* CreateBlockedUrlError() { + return [NSError errorWithDomain:net::kNSErrorDomain + code:net::ERR_BLOCKED_BY_ADMINISTRATOR + userInfo:nil]; +} + +} // namespace + +SupervisedUserURLFilterTabHelper::~SupervisedUserURLFilterTabHelper() = default; + +SupervisedUserURLFilterTabHelper::SupervisedUserURLFilterTabHelper( + web::WebState* web_state) + : web::WebStatePolicyDecider(web_state) {} + +void SupervisedUserURLFilterTabHelper::ShouldAllowRequest( + NSURLRequest* request, + web::WebStatePolicyDecider::RequestInfo request_info, + web::WebStatePolicyDecider::PolicyDecisionCallback callback) { + // TODO(b/265761985): integrate with SupervisedUserService::GetURLFilter(). + if ([request.URL.absoluteString containsString:@(kFilteredURLExample)]) { + std::move(callback).Run( + web::WebStatePolicyDecider::PolicyDecision::CancelAndDisplayError( + CreateBlockedUrlError())); + return; + } + + std::move(callback).Run(web::WebStatePolicyDecider::PolicyDecision::Allow()); +} + +WEB_STATE_USER_DATA_KEY_IMPL(SupervisedUserURLFilterTabHelper)
diff --git a/ios/chrome/browser/sync/prefs/ios_chrome_syncable_prefs_database.cc b/ios/chrome/browser/sync/prefs/ios_chrome_syncable_prefs_database.cc index 7e5f914..9e48a9e 100644 --- a/ios/chrome/browser/sync/prefs/ios_chrome_syncable_prefs_database.cc +++ b/ios/chrome/browser/sync/prefs/ios_chrome_syncable_prefs_database.cc
@@ -15,6 +15,10 @@ namespace { // Not an enum class to ease cast to int. namespace syncable_prefs_ids { +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. +// Please also add new entries to `SyncablePref` enum in +// tools/metrics/histograms/enums.xml. enum { // Starts with 200000 to avoid clash with prefs listed in // common_syncable_prefs_database.cc and
diff --git a/ios/chrome/browser/tabs/BUILD.gn b/ios/chrome/browser/tabs/BUILD.gn index d27509b..5777a81 100644 --- a/ios/chrome/browser/tabs/BUILD.gn +++ b/ios/chrome/browser/tabs/BUILD.gn
@@ -54,6 +54,7 @@ "//components/safe_browsing/ios/browser:allow_list", "//components/security_state/ios", "//components/strings", + "//components/supervised_user/core/common", "//ios/chrome/browser/app_launcher", "//ios/chrome/browser/application_context", "//ios/chrome/browser/autofill", @@ -100,6 +101,7 @@ "//ios/chrome/browser/snapshots", "//ios/chrome/browser/ssl", "//ios/chrome/browser/store_kit", + "//ios/chrome/browser/supervised_user", "//ios/chrome/browser/sync", "//ios/chrome/browser/translate", "//ios/chrome/browser/ui/download:features",
diff --git a/ios/chrome/browser/tabs/inactive_tabs/features.h b/ios/chrome/browser/tabs/inactive_tabs/features.h index 133e3559..ed40e9f 100644 --- a/ios/chrome/browser/tabs/inactive_tabs/features.h +++ b/ios/chrome/browser/tabs/inactive_tabs/features.h
@@ -22,6 +22,7 @@ extern const char kTabInactivityThresholdOneWeekParam[]; extern const char kTabInactivityThresholdTwoWeeksParam[]; extern const char kTabInactivityThresholdThreeWeeksParam[]; +extern const char kTabInactivityThresholdOneMinuteDemoParam[]; // Convenience method for determining if Inactive Tabs is enabled. bool IsInactiveTabsEnabled();
diff --git a/ios/chrome/browser/tabs/inactive_tabs/features.mm b/ios/chrome/browser/tabs/inactive_tabs/features.mm index ee55274..153566e 100644 --- a/ios/chrome/browser/tabs/inactive_tabs/features.mm +++ b/ios/chrome/browser/tabs/inactive_tabs/features.mm
@@ -20,13 +20,15 @@ "TabInactivityThreshold", base::FEATURE_DISABLED_BY_DEFAULT); -const char kTabInactivityThresholdParameterName[] = "default"; +const char kTabInactivityThresholdParameterName[] = "variant"; const char kTabInactivityThresholdOneWeekParam[] = "tab-inactivity-threshold-one-week"; const char kTabInactivityThresholdTwoWeeksParam[] = "tab-inactivity-threshold-two-weeks"; const char kTabInactivityThresholdThreeWeeksParam[] = "tab-inactivity-threshold-three-weeks"; +const char kTabInactivityThresholdOneMinuteDemoParam[] = + "tab-inactivity-threshold-one-minute-demo"; bool IsInactiveTabsEnabled() { if (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET) { @@ -62,6 +64,8 @@ return base::Days(14); } else if (feature_param == kTabInactivityThresholdThreeWeeksParam) { return base::Days(21); + } else if (feature_param == kTabInactivityThresholdOneMinuteDemoParam) { + return base::Minutes(1); } return base::Days(14); }
diff --git a/ios/chrome/browser/tabs/inactive_tabs/utils.mm b/ios/chrome/browser/tabs/inactive_tabs/utils.mm index 0a658cf2..5fc0813 100644 --- a/ios/chrome/browser/tabs/inactive_tabs/utils.mm +++ b/ios/chrome/browser/tabs/inactive_tabs/utils.mm
@@ -19,19 +19,26 @@ // Returns true if the given web state last is inactive determined by the given // threshold. -bool IsInactive(int threshold, web::WebState* web_state) { - const int time_since_last_activation = - (base::Time::Now() - web_state->GetLastActiveTime()).InDays(); - // Note: Even though a week is 7 days, the threshold value is returned with - // one extra day in all cases (> instead of >= operator) as it matches the - // user expectations in the following case: - // - // The user opens a tab every Monday. Last Monday it was opened at - // 10:05am. The tab should not immediately be considered inactive at - // 10:06am today. - // - // The padding is here to encompass a flexibility of a day. - return time_since_last_activation > threshold; +bool IsInactive(const base::TimeDelta& threshold, web::WebState* web_state) { + const base::TimeDelta time_since_last_activation = + base::Time::Now() - web_state->GetLastActiveTime(); + if (threshold > base::Days(1)) { + // Note: Even though a week is 7 days, the threshold value is returned with + // one extra day in all cases (> instead of >= operator) as it matches the + // user expectations in the following case: + // + // The user opens a tab every Monday. Last Monday it was opened at + // 10:05am. The tab should not immediately be considered inactive at + // 10:06am today. + // + // The padding is here to encompass a flexibility of a day. + return time_since_last_activation.InDays() > threshold.InDays(); + } else { + // This is the demo mode. Compare the times with no one-day padding. + // TODO(crbug.com/1412108): Remove this once the experimental flag is + // removed. + return time_since_last_activation > threshold; + } } } // namespace @@ -52,7 +59,7 @@ DCHECK(IsInactiveTabsEnabled()); WebStateList* active_web_state_list = active_browser->GetWebStateList(); WebStateList* inactive_web_state_list = inactive_browser->GetWebStateList(); - const int inactivity_threshold = InactiveTabsTimeThreshold().InDays(); + const base::TimeDelta inactivity_threshold = InactiveTabsTimeThreshold(); for (int index = active_web_state_list->GetIndexOfFirstNonPinnedWebState(); index < active_web_state_list->count();) { @@ -81,7 +88,7 @@ DCHECK(IsInactiveTabsEnabled()); WebStateList* active_web_state_list = active_browser->GetWebStateList(); WebStateList* inactive_web_state_list = inactive_browser->GetWebStateList(); - const int inactivity_threshold = InactiveTabsTimeThreshold().InDays(); + const base::TimeDelta inactivity_threshold = InactiveTabsTimeThreshold(); int removed_web_state_number = 0; for (int index = 0; index < inactive_web_state_list->count();) {
diff --git a/ios/chrome/browser/tabs/tab_helper_util.mm b/ios/chrome/browser/tabs/tab_helper_util.mm index 0db9a62..591ed86 100644 --- a/ios/chrome/browser/tabs/tab_helper_util.mm +++ b/ios/chrome/browser/tabs/tab_helper_util.mm
@@ -21,6 +21,7 @@ #import "components/omnibox/common/omnibox_features.h" #import "components/safe_browsing/core/common/features.h" #import "components/safe_browsing/ios/browser/safe_browsing_url_allow_list.h" +#import "components/supervised_user/core/common/features.h" #import "components/ukm/ios/ukm_url_recorder.h" #import "ios/chrome/browser/app_launcher/app_launcher_abuse_detector.h" #import "ios/chrome/browser/app_launcher/app_launcher_tab_helper.h" @@ -80,6 +81,7 @@ #import "ios/chrome/browser/shared/public/features/features.h" #import "ios/chrome/browser/snapshots/snapshot_tab_helper.h" #import "ios/chrome/browser/ssl/captive_portal_tab_helper.h" +#import "ios/chrome/browser/supervised_user/supervised_user_url_filter_tab_helper.h" #import "ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.h" #import "ios/chrome/browser/translate/chrome_ios_translate_client.h" #import "ios/chrome/browser/voice/voice_search_navigations_tab_helper.h" @@ -183,6 +185,11 @@ PolicyUrlBlockingTabHelper::CreateForWebState(web_state); + if (base::FeatureList::IsEnabled( + supervised_user::kFilterWebsitesForSupervisedUsersOnThirdParty)) { + SupervisedUserURLFilterTabHelper::CreateForWebState(web_state); + } + ImageFetchTabHelper::CreateForWebState(web_state); NewTabPageTabHelper::CreateForWebState(web_state);
diff --git a/ios/chrome/browser/ui/authentication/cells/signin_promo_view.mm b/ios/chrome/browser/ui/authentication/cells/signin_promo_view.mm index fca5aaa..0d9d4699 100644 --- a/ios/chrome/browser/ui/authentication/cells/signin_promo_view.mm +++ b/ios/chrome/browser/ui/authentication/cells/signin_promo_view.mm
@@ -80,6 +80,21 @@ 5.0, // kButtonStackViewSubViewSpacing }; +const PromoStyleValues kCompactHorizontalStyle = { + 20.0, // kStackViewTopPadding + 14.0, // kStackViewBottomPadding + 42.0, // kStackViewTrailingMargin + 14.0, // kContentStackViewSubViewSpacing + 0.0, // kTextStackViewSubViewSpacing + 0.0, // kButtonTitleHorizontalContentInset + 0.0, // kButtonTitleVerticalContentInset + 0.0, // kButtonCornerRadius + -9.0, // kCloseButtonTrailingMargin + 9.0, // kCloseButtonTopMargin + 6.0, // kMainPromoSubViewSpacing + 0.0, // kButtonStackViewSubViewSpacing +}; + const PromoStyleValues kTitledCompactPromoStyle = { 18.0, // kStackViewTopPadding 18.0, // kStackViewBottomPadding @@ -111,6 +126,8 @@ constexpr CGFloat kNonProfileImageHeightWidth = 56.0; // Size of the font for the headline. constexpr CGFloat kSignInPromoHeadlineFontSize = 17.0; +// Constant for the size of the compact style text. +constexpr CGFloat kCompactStyleTextSize = 15.0; } @interface SigninPromoView () @@ -139,7 +156,9 @@ @property(nonatomic, strong) NSArray<NSLayoutConstraint*>* compactVerticalLayoutConstraints; @property(nonatomic, strong) - NSArray<NSLayoutConstraint*>* titledCompactLayoutConstraints; + NSArray<NSLayoutConstraint*>* compactHorizontalLayoutConstraints; +@property(nonatomic, strong) + NSArray<NSLayoutConstraint*>* compactTitledLayoutConstraints; // Constraints for the image size. @property(nonatomic, strong) NSArray<NSLayoutConstraint*>* imageConstraints; @end @@ -519,7 +538,7 @@ return _standardLayoutConstraints; } -// Constraints specific to compact vertical layout. +// Constraints specific to the compact vertical layout. - (NSArray<NSLayoutConstraint*>*)compactVerticalLayoutConstraints { if (!_compactVerticalLayoutConstraints) { _compactVerticalLayoutConstraints = @[ @@ -547,10 +566,39 @@ return _compactVerticalLayoutConstraints; } +// Constraints specific to compact horitzontal layout. +- (NSArray<NSLayoutConstraint*>*)compactHorizontalLayoutConstraints { + if (!_compactHorizontalLayoutConstraints) { + _compactHorizontalLayoutConstraints = @[ + // Content padding. + [self.contentStackView.topAnchor + constraintEqualToAnchor:self.topAnchor + constant:kCompactHorizontalStyle.kStackViewTopPadding], + [self.contentStackView.bottomAnchor + constraintEqualToAnchor:self.bottomAnchor + constant:-kCompactHorizontalStyle + .kStackViewBottomPadding], + [self.contentStackView.trailingAnchor + constraintEqualToAnchor:self.trailingAnchor + constant:-kCompactHorizontalStyle + .kStackViewTrailingMargin], + [self.closeButton.trailingAnchor + constraintEqualToAnchor:self.trailingAnchor + constant:kCompactHorizontalStyle + .kCloseButtonTrailingMargin], + [self.closeButton.topAnchor + constraintEqualToAnchor:self.topAnchor + constant:kCompactHorizontalStyle + .kCloseButtonTopMargin], + ]; + } + return _compactHorizontalLayoutConstraints; +} + // Constraints specific to titled compact layout. -- (NSArray<NSLayoutConstraint*>*)titledCompactLayoutConstraints { - if (!_titledCompactLayoutConstraints) { - _titledCompactLayoutConstraints = @[ +- (NSArray<NSLayoutConstraint*>*)compactTitledLayoutConstraints { + if (!_compactTitledLayoutConstraints) { + _compactTitledLayoutConstraints = @[ // Content padding. [self.contentStackView.topAnchor constraintEqualToAnchor:self.topAnchor @@ -574,7 +622,7 @@ .kCloseButtonTopMargin], ]; } - return _titledCompactLayoutConstraints; + return _compactTitledLayoutConstraints; } #pragma mark - Private @@ -698,11 +746,67 @@ } #endif // __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_15_0 - constraintsToActivate = self.titledCompactLayoutConstraints; + constraintsToActivate = self.compactTitledLayoutConstraints; break; } - // TODO(crbug.com/1412758): Implement new styles. - case SigninPromoViewStyleCompactHorizontal: + case SigninPromoViewStyleCompactHorizontal: { + // Lays out content for the horizontal compact view. + self.buttonVerticalStackView.alignment = UIStackViewAlignmentLeading; + self.buttonVerticalStackView.spacing = + kCompactHorizontalStyle.kButtonStackViewSubViewSpacing; + self.mainPromoStackView.alignment = UIStackViewAlignmentLeading; + self.mainPromoStackView.spacing = + kCompactHorizontalStyle.kMainPromoSubViewSpacing; + self.contentStackView.alignment = UIStackViewAlignmentTop; + self.contentStackView.axis = UILayoutConstraintAxisHorizontal; + self.contentStackView.spacing = + kCompactHorizontalStyle.kContentStackViewSubViewSpacing; + self.textVerticalStackView.alignment = UIStackViewAlignmentLeading; + self.textVerticalStackView.spacing = + kCompactHorizontalStyle.kTextStackViewSubViewSpacing; + self.textLabel.textAlignment = NSTextAlignmentNatural; + self.secondaryButton.hidden = YES; + + // Configures fonts for the compact horizontal layout. + self.textLabel.font = + [[UIFont preferredFontForTextStyle:UIFontTextStyleBody] + fontWithSize:kCompactStyleTextSize]; + self.textLabel.textColor = [UIColor colorNamed:kGrey800Color]; + + // In the Compact Horizontal style, the primary button is plain. + [self.primaryButton setTitleColor:[UIColor colorNamed:kBlueColor] + forState:UIControlStateNormal]; + self.primaryButton.backgroundColor = nil; + self.primaryButton.layer.cornerRadius = + kCompactHorizontalStyle.kButtonCornerRadius; + self.primaryButton.clipsToBounds = NO; + + // TODO(crbug.com/1418068): Simplify after minimum version required is >= + // iOS 15. + if (base::ios::IsRunningOnIOS15OrLater() && + IsUIButtonConfigurationEnabled()) { + if (@available(iOS 15, *)) { + self.primaryButton.configuration.contentInsets = + NSDirectionalEdgeInsetsMake( + kCompactHorizontalStyle.kButtonTitleVerticalContentInset, + kCompactHorizontalStyle.kButtonTitleHorizontalContentInset, + kCompactHorizontalStyle.kButtonTitleVerticalContentInset, + kCompactHorizontalStyle.kButtonTitleHorizontalContentInset); + } + } +#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_15_0 + else { + self.primaryButton.contentEdgeInsets = UIEdgeInsetsMake( + kCompactHorizontalStyle.kButtonTitleVerticalContentInset, + kCompactHorizontalStyle.kButtonTitleHorizontalContentInset, + kCompactHorizontalStyle.kButtonTitleVerticalContentInset, + kCompactHorizontalStyle.kButtonTitleHorizontalContentInset); + } +#endif // __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_15_0 + + constraintsToActivate = self.compactHorizontalLayoutConstraints; + break; + } case SigninPromoViewStyleCompactVertical: { self.contentStackView.axis = UILayoutConstraintAxisVertical; self.contentStackView.spacing = @@ -718,8 +822,9 @@ self.secondaryButton.hidden = YES; self.imageView.hidden = NO; - self.textLabel.font = [[UIFont - preferredFontForTextStyle:UIFontTextStyleBody] fontWithSize:15]; + self.textLabel.font = + [[UIFont preferredFontForTextStyle:UIFontTextStyleBody] + fontWithSize:kCompactStyleTextSize]; self.textLabel.textColor = [UIColor colorNamed:kGrey800Color]; [self.primaryButton setTitleColor:[UIColor colorNamed:kBlueColor]
diff --git a/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.mm b/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.mm index 0ff5458a..d7b5332 100644 --- a/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.mm +++ b/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.mm
@@ -96,6 +96,7 @@ case signin_metrics::AccessPoint::ACCESS_POINT_NTP_SIGNED_OUT_ICON: case signin_metrics::AccessPoint::ACCESS_POINT_DESKTOP_SIGNIN_MANAGER: case signin_metrics::AccessPoint::ACCESS_POINT_FOR_YOU_FRE: + case signin_metrics::AccessPoint::ACCESS_POINT_CREATOR_FEED_FOLLOW: case signin_metrics::AccessPoint::ACCESS_POINT_MAX: return false; } @@ -165,6 +166,7 @@ case signin_metrics::AccessPoint::ACCESS_POINT_NTP_FEED_BOTTOM_PROMO: case signin_metrics::AccessPoint::ACCESS_POINT_DESKTOP_SIGNIN_MANAGER: case signin_metrics::AccessPoint::ACCESS_POINT_FOR_YOU_FRE: + case signin_metrics::AccessPoint::ACCESS_POINT_CREATOR_FEED_FOLLOW: case signin_metrics::AccessPoint::ACCESS_POINT_MAX: NOTREACHED() << "Unexpected value for access point " << static_cast<int>(access_point); @@ -236,6 +238,7 @@ case signin_metrics::AccessPoint::ACCESS_POINT_NTP_FEED_BOTTOM_PROMO: case signin_metrics::AccessPoint::ACCESS_POINT_DESKTOP_SIGNIN_MANAGER: case signin_metrics::AccessPoint::ACCESS_POINT_FOR_YOU_FRE: + case signin_metrics::AccessPoint::ACCESS_POINT_CREATOR_FEED_FOLLOW: case signin_metrics::AccessPoint::ACCESS_POINT_MAX: NOTREACHED() << "Unexpected value for access point " << static_cast<int>(access_point); @@ -307,6 +310,7 @@ case signin_metrics::AccessPoint::ACCESS_POINT_NTP_FEED_BOTTOM_PROMO: case signin_metrics::AccessPoint::ACCESS_POINT_DESKTOP_SIGNIN_MANAGER: case signin_metrics::AccessPoint::ACCESS_POINT_FOR_YOU_FRE: + case signin_metrics::AccessPoint::ACCESS_POINT_CREATOR_FEED_FOLLOW: case signin_metrics::AccessPoint::ACCESS_POINT_MAX: NOTREACHED() << "Unexpected value for access point " << static_cast<int>(access_point); @@ -367,6 +371,7 @@ case signin_metrics::AccessPoint::ACCESS_POINT_NTP_FEED_BOTTOM_PROMO: case signin_metrics::AccessPoint::ACCESS_POINT_DESKTOP_SIGNIN_MANAGER: case signin_metrics::AccessPoint::ACCESS_POINT_FOR_YOU_FRE: + case signin_metrics::AccessPoint::ACCESS_POINT_CREATOR_FEED_FOLLOW: case signin_metrics::AccessPoint::ACCESS_POINT_MAX: return nullptr; } @@ -425,6 +430,7 @@ case signin_metrics::AccessPoint::ACCESS_POINT_NTP_FEED_BOTTOM_PROMO: case signin_metrics::AccessPoint::ACCESS_POINT_DESKTOP_SIGNIN_MANAGER: case signin_metrics::AccessPoint::ACCESS_POINT_FOR_YOU_FRE: + case signin_metrics::AccessPoint::ACCESS_POINT_CREATOR_FEED_FOLLOW: case signin_metrics::AccessPoint::ACCESS_POINT_MAX: return nullptr; }
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm index d9b0b5f4..091ba2e5 100644 --- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm +++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -1614,10 +1614,13 @@ NSLayoutYAxisAnchor* topAnchor; // On iPad, the toolbar is underneath the tab strip. // On iPhone, it is underneath the top of the screen. - if ([self canShowTabStrip]) { + if (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET) { topAnchor = self.tabStripView.bottomAnchor; } else { topAnchor = [self view].topAnchor; + // TODO(crbug.com/1423799): Dcheck added for investigation purposes, remove + // once crash root cause is found. + DCHECK(![self canShowTabStrip]); } // Only add leading and trailing constraints once as they are never updated.
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm index 817816cb..cd41c71 100644 --- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm +++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
@@ -41,6 +41,7 @@ #endif namespace { +const CGFloat kTopPadding = 8.0; const CGFloat kBottomPadding = 8.0; const CGFloat kFooterHeight = 12.0; /// Percentage of the suggestion height that needs to be visible in order to @@ -254,6 +255,7 @@ UIScrollViewContentInsetAdjustmentAutomatic; [self.tableView setDirectionalLayoutMargins:NSDirectionalEdgeInsetsMake( 0, 0, kBottomPadding, 0)]; + self.tableView.contentInset = UIEdgeInsetsMake(kTopPadding, 0, 0, 0); self.tableView.sectionHeaderHeight = 0.1; self.tableView.estimatedRowHeight = 0; @@ -889,9 +891,10 @@ CGFloat windowHeight = CGRectGetHeight(currentWindow.bounds); CGFloat bottomInset = windowHeight - self.tableView.visibleSize.height - self.keyboardHeight - absoluteRect.origin.y - - kBottomPadding; + kBottomPadding - kTopPadding; bottomInset = MAX(kBottomPadding, -bottomInset); - self.tableView.contentInset = UIEdgeInsetsMake(0, 0, bottomInset, 0); + self.tableView.contentInset = + UIEdgeInsetsMake(kTopPadding, 0, bottomInset, 0); self.tableView.scrollIndicatorInsets = self.tableView.contentInset; }
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm index c54431e..3c72b52 100644 --- a/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm +++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm
@@ -154,6 +154,11 @@ // signed-in. @property(nonatomic, readonly) NSString* userEmail; +// Timer used to keep track of the time that passed after user passed the +// authentication and navigated to the details view. Once it runs out, view +// navigates to the password list view. +@property(nonatomic, strong) NSTimer* authValidityTimer; + @end @implementation PasswordDetailsTableViewController @@ -184,6 +189,7 @@ self.tableView.accessibilityIdentifier = kPasswordDetailsViewControllerId; self.tableView.allowsSelectionDuringEditing = YES; + [self setOrExtendAuthValidityTimer]; } - (void)viewDidDisappear:(BOOL)animated { @@ -194,6 +200,7 @@ #pragma mark - ChromeTableViewController - (void)editButtonPressed { + [self setOrExtendAuthValidityTimer]; // If there are no passwords, proceed with editing without // reauthentication. if (![self hasAtLeastOnePassword]) { @@ -446,6 +453,7 @@ - (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath { + [self setOrExtendAuthValidityTimer]; TableViewModel* model = self.tableViewModel; NSInteger itemType = [model itemTypeForIndexPath:indexPath]; switch (itemType) { @@ -668,6 +676,7 @@ } - (void)tableViewItemDidChange:(TableViewTextEditItem*)tableViewItem { + [self setOrExtendAuthValidityTimer]; BOOL usernameValid = [self checkIfValidUsernames]; BOOL passwordValid = [self checkIfValidPasswords]; BOOL noteValid = [self checkIfValidNotes]; @@ -688,6 +697,7 @@ #pragma mark - TableViewMultiLineTextEditItemDelegate - (void)textViewItemDidChange:(TableViewMultiLineTextEditItem*)tableViewItem { + [self setOrExtendAuthValidityTimer]; // Update save button state based on the note's length and validity of other // input fields. BOOL noteValid = tableViewItem.text.length <= kMaxNoteCharAmount; @@ -762,12 +772,13 @@ // Shows reauthentication dialog if needed. If the reauthentication is // successful reveals the password. -// TODO(crbug.com/1414897): Add 5 min timeout and remove reauth in password -// details page with notes enabled. - (void)attemptToShowPasswordFor:(ReauthenticationReason)reason { // If password was already shown (before editing or copying) or the flag to // override auth is YES, we don't need to request reauth again. - if (self.isPasswordShown || self.showPasswordWithoutAuth) { + // With password notes feature enabled the authentication happens during + // navigation from the password list view to the password details view. + if (self.isPasswordShown || self.showPasswordWithoutAuth || + IsPasswordNotesWithBackupEnabled()) { [self showPasswordFor:reason]; return; } @@ -1150,10 +1161,46 @@ [self.passwordDetailsInfoItems addObject:passwordItem]; } +// Moves password at specified index from profile store to account store. +- (void)moveCredentialToAccountStore:(int)passwordIndex { + DCHECK_GE(passwordIndex, 0); + DCHECK(self.handler); + + [self.handler moveCredentialToAccountStore:self.passwords[passwordIndex]]; + [self showToast:l10n_util::GetNSStringF( + IDS_IOS_PASSWORD_SAVED_TO_ACCOUNT_SNACKBAR_MESSAGE, + base::SysNSStringToUTF16(self.userEmail)) + forSuccess:YES]; +} + +// Navigates to password manager list view when the timeout for a valid +// authentication has passed. +- (void)authValidityTimerFired:(NSTimer*)timer { + [self.navigationController popViewControllerAnimated:YES]; +} + +// Starts the timer after passing an authentication to open password details +// view or extends it on an interaction with the details view. +- (void)setOrExtendAuthValidityTimer { + if (!IsPasswordNotesWithBackupEnabled()) { + return; + } + + [self.authValidityTimer invalidate]; + self.authValidityTimer = [NSTimer + scheduledTimerWithTimeInterval:syncer::kPasswordNotesAuthValidity.Get() + .InSeconds() + target:self + selector:@selector(authValidityTimerFired:) + userInfo:nil + repeats:NO]; +} + #pragma mark - Actions // Called when the user tapped on the show/hide button near password. - (void)didTapShowHideButton:(UIButton*)buttonView { + [self setOrExtendAuthValidityTimer]; [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:NO]; if (IsPasswordGroupingEnabled()) { @@ -1188,6 +1235,7 @@ // Called when the user tap error info icon in the username input. - (void)didTapUsernameErrorInfo:(UIButton*)buttonView { + [self setOrExtendAuthValidityTimer]; NSString* text = l10n_util::GetNSString(IDS_IOS_USERNAME_ALREADY_USED); NSAttributedString* attributedText = [[NSAttributedString alloc] @@ -1226,6 +1274,7 @@ // Copies the password information to system pasteboard and shows a toast of // success/failure. - (void)copyPasswordDetails:(id)sender { + [self setOrExtendAuthValidityTimer]; UIPasteboard* generalPasteboard = [UIPasteboard generalPasteboard]; UIMenuController* menu = base::mac::ObjCCastStrict<UIMenuController>(sender); PasswordDetailsMenuItem* menuItem = @@ -1306,7 +1355,18 @@ } - (void)didTapMoveButton:(UIGestureRecognizer*)gestureRecognizer { + [self setOrExtendAuthValidityTimer]; UIView* view = gestureRecognizer.view; + // Only one password at position 0 shows if no grouping applied. + int passwordIndex = IsPasswordGroupingEnabled() ? view.tag : 0; + + // With password notes feature enabled the authentication happens during + // navigation from the password list view to the password details view. + if (IsPasswordNotesWithBackupEnabled()) { + [self moveCredentialToAccountStore:passwordIndex]; + return; + } + if (![self.reauthModule canAttemptReauth]) { return; } @@ -1320,15 +1380,8 @@ if (result == ReauthenticationResult::kFailure) { return; } - // Only one password at position 0 shows if no grouping applied. - int position = IsPasswordGroupingEnabled() ? view.tag : 0; - DCHECK_GE(position, 0); - DCHECK(self.handler); - [self.handler moveCredentialToAccountStore:self.passwords[position]]; - [self showToast:l10n_util::GetNSStringF( - IDS_IOS_PASSWORD_SAVED_TO_ACCOUNT_SNACKBAR_MESSAGE, - base::SysNSStringToUTF16(self.userEmail)) - forSuccess:YES]; + + [self moveCredentialToAccountStore:passwordIndex]; }; [self.reauthModule attemptReauthWithLocalizedReason:
diff --git a/ios/chrome/browser/ui/settings/password/password_manager_egtest.mm b/ios/chrome/browser/ui/settings/password/password_manager_egtest.mm index e4e1b6d6..c909b86 100644 --- a/ios/chrome/browser/ui/settings/password/password_manager_egtest.mm +++ b/ios/chrome/browser/ui/settings/password/password_manager_egtest.mm
@@ -575,7 +575,8 @@ [self isRunningTest:@selector(testAddPasswordLayoutWithLongNotes)] || [self isRunningTest:@selector (testAddPasswordSaveButtonStateOnFieldChanges)] || - [self isRunningTest:@selector(testLayoutWithLongNotes)]) { + [self isRunningTest:@selector(testLayoutWithLongNotes)] || + [self isRunningTest:@selector(testShowHidePasswordWithNotesEnabled)]) { config.features_enabled.push_back(syncer::kPasswordNotesWithBackup); } @@ -1408,10 +1409,6 @@ username:@"concrete username"] performAction:grey_tap()]; - [PasswordSettingsAppInterface setUpMockReauthenticationModule]; - [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: - ReauthenticationResult::kSuccess]; - TapEdit(); [[EarlGrey selectElementWithMatcher:TooLongNoteFooter()] assertWithMatcher:grey_nil()]; @@ -2829,6 +2826,36 @@ performAction:grey_tap()]; } +// Tests that reauthentication is not required to show password when notes are +// enabled since the reauthentication happens before navigating to the details +// view in this scenario. +- (void)testShowHidePasswordWithNotesEnabled { + SaveExamplePasswordForm(); + + OpenPasswordManager(); + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + + [[self interactionForSinglePasswordEntryWithDomain:@"example.com" + username:@"concrete username"] + performAction:grey_tap()]; + + [GetInteractionForPasswordDetailItem(ShowPasswordButton()) + assertWithMatcher:grey_sufficientlyVisible()]; + [GetInteractionForPasswordDetailItem(ShowPasswordButton()) + performAction:grey_tap()]; + [GetInteractionForPasswordDetailItem(HidePasswordButton()) + assertWithMatcher:grey_sufficientlyVisible()]; + [GetInteractionForPasswordDetailItem(HidePasswordButton()) + performAction:grey_tap()]; + [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()] + performAction:grey_tap()]; + [[EarlGrey selectElementWithMatcher:SettingsDoneButton()] + performAction:grey_tap()]; +} + // Tests that the favicons for the password managers metrics are logged // properly when there are passwords with a favicon. // TODO(crbug.com/1348585): Fix to re-enable.
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_button.swift b/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_button.swift index edfeef0..3178366 100644 --- a/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_button.swift +++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_button.swift
@@ -111,11 +111,13 @@ Image(systemName: "chevron.right") .foregroundColor(.textTertiary) .fontWeight(.semibold) + .flipsForRightToLeftLayoutDirection(true) } else { // fontWeight is not available on Image. Wrap it in a Text. Text(Image(systemName: "chevron.right")) .foregroundColor(.textTertiary) .fontWeight(.semibold) + .flipsForRightToLeftLayoutDirection(true) } }
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_coordinator.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_coordinator.mm index c597f59..33c7ef6c 100644 --- a/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_coordinator.mm +++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_coordinator.mm
@@ -126,7 +126,7 @@ - (void)gridViewController:(GridViewController*)gridViewController didCloseItemWithID:(NSString*)itemID { - // TODO(crbug.com/1408053): Close the tab. + [self.mediator closeItemWithID:itemID]; } - (void)didTapPlusSignInGridViewController:
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_mediator.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_mediator.h index 14968ea..76c81858 100644 --- a/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_mediator.h +++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_mediator.h
@@ -24,6 +24,9 @@ NS_DESIGNATED_INITIALIZER; - (instancetype)init NS_UNAVAILABLE; +// Tells the receiver to close the item with the `itemID` identifier. +- (void)closeItemWithID:(NSString*)itemID; + @end #endif // IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_INACTIVE_TABS_INACTIVE_TABS_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_mediator.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_mediator.mm index 8b4aa1e4..64a6608 100644 --- a/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_mediator.mm +++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_mediator.mm
@@ -124,6 +124,14 @@ [_snapshotCache removeObserver:self]; } +- (void)closeItemWithID:(NSString*)itemID { + // TODO(crbug.com/1418021): Add metrics when the user closes an inactive tab. + int index = GetTabIndex(_webStateList, itemID, /*pinned=*/NO); + if (index != WebStateList::kInvalidIndex) { + _webStateList->CloseWebStateAt(index, WebStateList::CLOSE_USER_ACTION); + } +} + #pragma mark - CRWWebStateObserver - (void)webStateDidStartLoading:(web::WebState*)webState {
diff --git a/ios/chrome/test/earl_grey2/BUILD.gn b/ios/chrome/test/earl_grey2/BUILD.gn index 24216f7e..4000ca7 100644 --- a/ios/chrome/test/earl_grey2/BUILD.gn +++ b/ios/chrome/test/earl_grey2/BUILD.gn
@@ -90,6 +90,7 @@ "//ios/chrome/browser/policy_url_blocking:eg2_tests", "//ios/chrome/browser/prerender:eg2_tests", "//ios/chrome/browser/safe_browsing:eg2_tests", + "//ios/chrome/browser/supervised_user:eg2_tests", "//ios/chrome/browser/ui/autofill:eg2_tests", "//ios/chrome/browser/ui/autofill/form_input_accessory:eg2_tests", "//ios/chrome/browser/ui/autofill/manual_fill:eg2_tests",
diff --git a/media/audio/audio_encoders_unittest.cc b/media/audio/audio_encoders_unittest.cc index 849e7ce7..6b90c480 100644 --- a/media/audio/audio_encoders_unittest.cc +++ b/media/audio/audio_encoders_unittest.cc
@@ -28,9 +28,17 @@ #if BUILDFLAG(IS_WIN) #include "base/win/scoped_com_initializer.h" #include "media/gpu/windows/mf_audio_encoder.h" + +// The AAC tests are failing on Arm64. Disable the AAC part of these tests until +// those failures can be fixed. TOOO(https://crbug.com/1424215): FIx tests, +// and/or investigate if AAC support should be turned off in Chrome for Arm64 +// Windows, or if these are an issue with the tests. +#if !defined(ARCH_CPU_ARM64) #define HAS_AAC_ENCODER 1 #endif +#endif // IS_WIN + #if BUILDFLAG(IS_MAC) && BUILDFLAG(USE_PROPRIETARY_CODECS) #include "media/filters/mac/audio_toolbox_audio_encoder.h" #define HAS_AAC_ENCODER 1
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc index 802bae1e..a54e382 100644 --- a/net/http/http_cache_transaction.cc +++ b/net/http/http_cache_transaction.cc
@@ -111,7 +111,7 @@ void RecordPervasivePayloadIndex(const char* histogram_name, int index) { if (index != -1) { - base::UmaHistogramExactLinear(histogram_name, index, 323); + base::UmaHistogramCustomCounts(histogram_name, index, 1, 323, 323); } } @@ -693,12 +693,13 @@ if (hex_result != request_->checksum) { DVLOG(2) << "Pervasive payload checksum mismatch for \"" << request_->url << "\": got " << hex_result << ", expected " << request_->checksum; - RecordPervasivePayloadIndex("Network.CacheTransparency.MismatchedChecksums", - request_->pervasive_payloads_index_for_logging); + RecordPervasivePayloadIndex( + "Network.CacheTransparency2.MismatchedChecksums", + request_->pervasive_payloads_index_for_logging); return false; } RecordPervasivePayloadIndex( - "Network.CacheTransparency.SingleKeyedCacheIsUsed", + "Network.CacheTransparency2.SingleKeyedCacheIsUsed", request_->pervasive_payloads_index_for_logging); return true; } @@ -1618,7 +1619,7 @@ } if (response_.single_keyed_cache_entry_unusable) { - RecordPervasivePayloadIndex("Network.CacheTransparency.MarkedUnusable", + RecordPervasivePayloadIndex("Network.CacheTransparency2.MarkedUnusable", request_->pervasive_payloads_index_for_logging); // We've read the single keyed entry and it turned out to be unusable. Let's
diff --git a/services/network/public/mojom/restricted_udp_socket.mojom b/services/network/public/mojom/restricted_udp_socket.mojom index 73e7346..be18dcb 100644 --- a/services/network/public/mojom/restricted_udp_socket.mojom +++ b/services/network/public/mojom/restricted_udp_socket.mojom
@@ -8,6 +8,9 @@ import "services/network/public/mojom/network_param.mojom"; import "services/network/public/mojom/udp_socket.mojom"; +[EnableIf=is_chromeos] +import "services/network/public/mojom/socket_connection_tracker.mojom"; + // The mode that RestrictedUDPSocket is operating in. // See Connect() and Bind() methods of network.mojom.UDPSocket for more details. enum RestrictedUDPSocketMode { @@ -18,6 +21,12 @@ struct RestrictedUDPSocketParams { // These options will be passed to the OS to configure the actual UDP Socket. UDPSocketOptions? socket_options; + + [EnableIf=is_chromeos] + // The caller may choose to supply a |connection_tracker| if they lack access + // to the endpoints of the actual connection but would like to get informed + // when it shuts down. + pending_remote<SocketConnectionTracker>? connection_tracker; }; // This wraps network.mojom.UDPSocket, exposing only the functionality needed
diff --git a/services/network/restricted_udp_socket.cc b/services/network/restricted_udp_socket.cc index 3d97e4e8..c7f9172 100644 --- a/services/network/restricted_udp_socket.cc +++ b/services/network/restricted_udp_socket.cc
@@ -53,6 +53,13 @@ /*callback=*/std::move(callback))); } +#if BUILDFLAG(IS_CHROMEOS) +void RestrictedUDPSocket::AttachConnectionTracker( + mojo::PendingRemote<mojom::SocketConnectionTracker> connection_tracker) { + connection_tracker_ = std::move(connection_tracker); +} +#endif // BUILDFLAG(IS_CHROMEOS) + void RestrictedUDPSocket::OnResolveCompleteForSendTo( std::vector<uint8_t> data, SendToCallback callback,
diff --git a/services/network/restricted_udp_socket.h b/services/network/restricted_udp_socket.h index ced83c0..9e0f8af 100644 --- a/services/network/restricted_udp_socket.h +++ b/services/network/restricted_udp_socket.h
@@ -11,6 +11,10 @@ #include "net/dns/public/host_resolver_results.h" #include "services/network/public/mojom/restricted_udp_socket.mojom.h" +#if BUILDFLAG(IS_CHROMEOS) +#include "services/network/public/mojom/socket_connection_tracker.mojom.h" +#endif // BUILDFLAG(IS_CHROMEOS) + namespace network { class UDPSocket; @@ -35,6 +39,11 @@ const net::HostPortPair& dest_addr, SendToCallback callback) override; +#if BUILDFLAG(IS_CHROMEOS) + void AttachConnectionTracker( + mojo::PendingRemote<mojom::SocketConnectionTracker>); +#endif // BUILDFLAG(IS_CHROMEOS) + private: void OnResolveCompleteForSendTo( std::vector<uint8_t> data, @@ -47,6 +56,10 @@ std::unique_ptr<UDPSocket> udp_socket_; net::MutableNetworkTrafficAnnotationTag traffic_annotation_; std::unique_ptr<SimpleHostResolver> resolver_; + +#if BUILDFLAG(IS_CHROMEOS) + mojo::PendingRemote<mojom::SocketConnectionTracker> connection_tracker_; +#endif // BUILDFLAG(IS_CHROMEOS) }; } // namespace network
diff --git a/services/network/socket_factory.cc b/services/network/socket_factory.cc index d975c51..802a9e1 100644 --- a/services/network/socket_factory.cc +++ b/services/network/socket_factory.cc
@@ -75,10 +75,16 @@ std::move(callback)); break; } - restricted_udp_socket_receivers_.Add( - std::make_unique<RestrictedUDPSocket>( - std::move(udp_socket), traffic_annotation, std::move(resolver)), - std::move(receiver)); + auto restricted_udp_socket = std::make_unique<RestrictedUDPSocket>( + std::move(udp_socket), traffic_annotation, std::move(resolver)); +#if BUILDFLAG(IS_CHROMEOS) + if (params && params->connection_tracker) { + restricted_udp_socket->AttachConnectionTracker( + std::move(params->connection_tracker)); + } +#endif // BUILDFLAG(IS_CHROMEOS) + restricted_udp_socket_receivers_.Add(std::move(restricted_udp_socket), + std::move(receiver)); } void SocketFactory::CreateTCPServerSocket(
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc index fbce1848..880f28a 100644 --- a/services/network/url_loader.cc +++ b/services/network/url_loader.cc
@@ -667,8 +667,8 @@ // URLLoaderCompletionStatus with it later. pervasive_payload_requested_ = true; url_request_->set_pervasive_payloads_index_for_logging(index.value()); - base::UmaHistogramExactLinear("Network.CacheTransparency.URLMatched", - index.value(), 323); + base::UmaHistogramCustomCounts("Network.CacheTransparency2.URLMatched", + index.value(), 1, 323, 323); DVLOG(2) << "Found pervasive payload: " << request.url.spec(); } }
diff --git a/services/network/url_loader_unittest.cc b/services/network/url_loader_unittest.cc index b2b5584..1174a240 100644 --- a/services/network/url_loader_unittest.cc +++ b/services/network/url_loader_unittest.cc
@@ -7351,7 +7351,7 @@ // necessary to the test, but helps ensure it is passing for the right reason. // TODO(ricea): Stop checking this histogram if it is removed. static constexpr char kMarkedUnusableHistogram[] = - "Network.CacheTransparency.MarkedUnusable"; + "Network.CacheTransparency2.MarkedUnusable"; set_expect_redirect(true); @@ -7371,7 +7371,7 @@ // TODO(ricea): Stop checking this histogram if it is removed. histogram_tester().ExpectTotalCount( - "Network.CacheTransparency.SingleKeyedCacheIsUsed", 0); + "Network.CacheTransparency2.SingleKeyedCacheIsUsed", 0); // The count includes the target of the redirects. EXPECT_EQ(4, network_request_count());
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json index 60d60aa..7ccde458 100644 --- a/testing/buildbot/chromium.gpu.fyi.json +++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -2135,53 +2135,6 @@ "isolated_scripts": [ { "args": [ - "pixel", - "--show-stdout", - "--browser=android-webview-instrumentation", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc", - "--dont-restore-color-profile-after-test", - "--test-machine-name", - "${buildername}", - "--git-revision=${got_revision}" - ], - "isolate_name": "telemetry_gpu_integration_test_android_webview", - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "android_webview_pixel_skia_gold_test", - "precommit_args": [ - "--gerrit-issue=${patch_issue}", - "--gerrit-patchset=${patch_set}", - "--buildbucket-id=${buildbucket_build_id}" - ], - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "should_retry_with_patch": false, - "swarming": { - "can_use_on_swarming_builders": true, - "containment_type": "AUTO", - "dimension_sets": [ - { - "device_os": "TP1A.220624.021", - "device_os_type": "userdebug", - "device_type": "oriole", - "os": "Android", - "pool": "chromium.tests.gpu" - } - ], - "idempotent": false, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_android_webview/" - }, - { - "args": [ "context_lost", "--show-stdout", "--browser=android-chromium",
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl index fb9e784..cade4f32 100644 --- a/testing/buildbot/waterfalls.pyl +++ b/testing/buildbot/waterfalls.pyl
@@ -4350,7 +4350,6 @@ ], 'test_suites': { 'gpu_telemetry_tests': 'gpu_pixel_4_and_6_telemetry_tests', - 'android_webview_gpu_telemetry_tests': 'android_webview_gpu_telemetry_tests', }, }, 'Android FYI Release (Samsung A13)': {
diff --git a/testing/libfuzzer/fuzzers/BUILD.gn b/testing/libfuzzer/fuzzers/BUILD.gn index ffff32bf..a607910 100644 --- a/testing/libfuzzer/fuzzers/BUILD.gn +++ b/testing/libfuzzer/fuzzers/BUILD.gn
@@ -472,9 +472,6 @@ } fuzzer_test("command_buffer_fuzzer") { - # Tell ClusterFuzz to ignore this fuzzer for now. - additional_configs = [ "//testing/libfuzzer:no_clusterfuzz" ] - sources = [ "command_buffer_fuzzer/cmd_buf_fuzz.cc", "command_buffer_fuzzer/cmd_buf_fuzz.h", @@ -511,6 +508,7 @@ "//components/viz/common:resource_format", "//components/viz/test:test_support", # TODO: huge; is there something more # targeted? + "//content/public/common:static_switches", "//gpu:gles2", "//gpu:gpu", "//gpu:test_support",
diff --git a/testing/libfuzzer/fuzzers/command_buffer_fuzzer/cmd_buf_fuzz.cc b/testing/libfuzzer/fuzzers/command_buffer_fuzzer/cmd_buf_fuzz.cc index 45504c5..030d98e 100644 --- a/testing/libfuzzer/fuzzers/command_buffer_fuzzer/cmd_buf_fuzz.cc +++ b/testing/libfuzzer/fuzzers/command_buffer_fuzzer/cmd_buf_fuzz.cc
@@ -22,6 +22,7 @@ #include "build/build_config.h" #include "command_buffer/common/constants.h" #include "command_buffer/common/sync_token.h" +#include "content/public/common/content_switches.h" #include "gpu/command_buffer/client/shared_memory_limits.h" #include "gpu/command_buffer/client/webgpu_cmd_helper.h" #include "gpu/command_buffer/client/webgpu_implementation.h" @@ -45,36 +46,46 @@ namespace gpu::cmdbuf::fuzzing { -CmdBufFuzz::CmdBufFuzz() : base::TestSuite(0, nullptr) { +/* One-time fuzzer initialization. Called only once during fuzzer startup. */ +CmdBufFuzz::CmdBufFuzz() : base::TestSuite(0, (char**)nullptr) { + CommandLineInit(); GfxInit(); } -void CmdBufFuzz::GfxInit() { - VLOG(3) << "Detecting platform specific features and setting preferences " - "accordingly"; - gpu::GpuPreferences preferences; - preferences.enable_webgpu = true; - preferences.disable_software_rasterizer = false; -#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && BUILDFLAG(USE_DAWN) +/* Enable WebGPU features and disable GPU hardware acceleration. */ +void CmdBufFuzz::CommandLineInit() { [[maybe_unused]] auto* command_line = base::CommandLine::ForCurrentProcess(); - /* --disable-gpu: force software rendering + //--disable-gpu to force software rendering command_line->AppendSwitchASCII(switches::kDisableGpu, "1"); - */ + // --use-webgpu-adapter=swiftshader + command_line->AppendSwitchASCII( + switches::kUseWebGPUAdapter, + switches::kVulkanImplementationNameSwiftshader); +#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && BUILDFLAG(USE_DAWN) //--enable-features=Vulkan command_line->AppendSwitchASCII(switches::kEnableFeatures, gl::kANGLEImplementationVulkanName); //--use-vulkan=swiftshader command_line->AppendSwitchASCII( switches::kUseVulkan, switches::kVulkanImplementationNameSwiftshader); - // --use-webgpu-adapter=swiftshader - command_line->AppendSwitchASCII( - switches::kUseWebGPUAdapter, - switches::kVulkanImplementationNameSwiftshader); - preferences.gr_context_type = gpu::GrContextType::kVulkan; - // Use SwiftShader so fuzzer can work without a physical GPU - preferences.use_vulkan = gpu::VulkanImplementationName::kSwiftshader; - preferences.use_webgpu_adapter = WebGPUAdapterName::kSwiftShader; #endif +} + +/* Initialize the graphics stack in single-process mode with WebGPU. */ +void CmdBufFuzz::GfxInit() { + VLOG(3) << "GfxInit() started"; + + VLOG(3) << "Detecting platform specific features and setting preferences " + "accordingly"; + gpu::GpuPreferences preferences; + preferences.enable_webgpu = true; + preferences.disable_software_rasterizer = false; +#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && BUILDFLAG(USE_DAWN) + preferences.gr_context_type = gpu::GrContextType::kVulkan; + // Use SwiftShader so fuzzing can work without a physical GPU. + preferences.use_vulkan = gpu::VulkanImplementationName::kSwiftshader; +#endif + preferences.use_webgpu_adapter = WebGPUAdapterName::kSwiftShader; preferences.enable_unsafe_webgpu = true; preferences.enable_gpu_service_logging_gpu = true; @@ -82,7 +93,7 @@ // required to use gpu::CreateBufferUsageAndFormatExceptionList(). // TODO(bookholt): It's not obvious whether having legit values from - // gpu::CreateBufferUsageAndFormatExceptionList() is really required for + // gpu::CreateBufferUsageAndFormatExceptionList() is really desired for // fuzzing, but it's a starting point. // TODO(bookholt): OS specific windowing init. @@ -106,10 +117,12 @@ SharedMemoryLimits::ForWebGPUContext()); /* WebGPUInProcessContext does a lot of handy setup and uses threads to - simulate cross-process communication. It's not a 100% match to a real - browser instance (e.g. it uses a char* as backing memory for the - CommandBuffer's RingBuffer instead of a platform specific shared memory - implementation), but it's a good starting point. + simulate cross-process communication in a single process binary, but it's + not a 100% match to a real browser instance. E.g. it uses a char* as + backing memory for the CommandBuffer's RingBuffer instead of a + platform-specific shared memory implementation, and it bypasses Mojo + entirely, but it's compatible with our single-process fuzze_test build + target, so it's a decent starting point. The thinking is to let WebGPUInProcessContext create a working WebGPU Context with associated data structures and then we'll tune or replace @@ -161,16 +174,14 @@ // command_buffer_->SetGetBuffer(command_buffer_id_); webgpu_impl_ = webgpu_context_->GetImplementation(); - VLOG(3) << "Startup init complete"; + VLOG(3) << "GfxInit complete"; } CmdBufFuzz::~CmdBufFuzz() = default; +/* Per-test case setup. */ void CmdBufFuzz::RuntimeInit() { - // TODO: WebGPU decoder init - // command_buffer_->set_handler(decoder_.get()); - - // initialize command buffer + // Verify command buffer is ready for the new test case. if (!cmd_helper_->HaveRingBuffer()) { VLOG(3) << "CommandBuffer (re-)init"; cmd_helper_->Initialize(shared_memory_limits_->command_buffer_size); @@ -182,6 +193,7 @@ } } +/* Deserialize a proto message from a test case into a gpu::SyncToken. */ gpu::SyncToken CmdBufFuzz::SyncTokenFromProto(fuzzing::SyncToken token_proto) { // TODO(bookholt): Pick buffer_id from a narrower range of sensible values. CommandBufferId buffer_id = @@ -198,15 +210,16 @@ return sync_token; } +/* Dummy SignalSyncToken callback function. */ void CmdBufFuzz::SignalSyncTokenCallback() {} +/* Fuzzing happens here. */ void CmdBufFuzz::RunCommandBuffer(fuzzing::CmdBufSession session) { if (!session.actions_size()) { VLOG(3) << "Empty test case :("; return; } - // Do per-testcase setup. RuntimeInit(); while (action_index_ < session.actions_size()) { @@ -326,8 +339,8 @@ case fuzzing::InProcessCommandBufferOp::kSetLock: { VLOG(3) << "kSetLock: unsupported op"; // Interesting feature to fuzz, but unsupported by - // InProcessCommandBuffer :( - // TODO(bookholt): support for SetLock would require either + // InProcessCommandBuffer :( TODO(bookholt): support for SetLock + // would require either // - adding libfuzzer support to BrowserTests or // - snapshot fuzzing support break; @@ -341,9 +354,9 @@ case fuzzing::InProcessCommandBufferOp::kGetNamespaceID: { VLOG(3) << "kGetNamespaceID: unsupported op"; - // Valid values in gpu/command_buffer/common/constants.h - // Calling this function is a NOOP from a fuzzer perspective, but - // other CommandBufferOps will want to implement it. + // Valid values in gpu/command_buffer/common/constants.h Calling + // this function is a NOOP from a fuzzer perspective, but other + // CommandBufferOps will want to implement it. break; } @@ -487,8 +500,8 @@ case fuzzing::InProcessCommandBufferOp::kGetTransferCacheForTest: { VLOG(3) << "kGetTransferCacheForTest"; - // TODO(bookholt): Worth a think about refactoring to make use - // of the TransferCache. + // TODO(bookholt): Worth a think about refactoring to make use of + // the TransferCache. command_buffer_->GetTransferCacheForTest(); break; } @@ -513,8 +526,8 @@ case fuzzing::InProcessCommandBufferOp::kGetSharedImageInterface: { VLOG(3) << "kGetSharedImageInterface: unsupported op"; - // TODO(bookholt): Worth a think about refactoring to make use - // of the gpu::SharedImageInterface. + // TODO(bookholt): Worth a think about refactoring to make use of + // the gpu::SharedImageInterface. command_buffer_->GetSharedImageInterface(); break; } @@ -534,26 +547,32 @@ // does. Investigate how/why LPM generates this value and consider tuning // to avoid it because it seems like a waste of performance. case fuzzing::Action::ACTION_NOT_SET: { - DLOG(ERROR) << "No Action set"; + DLOG(WARNING) << "No Action set"; } break; } action_index_++; } // Prepare for next testcase. - Reset(); + if (!Reset()) { + // See crbug.com/1424591 + VLOG(1) << "Test case reset failed. Re-initializing graphics."; + // Re-initialize the graphics stack to (hopefully!) avoid sync problems. + GfxInit(); + return; + } } -void CmdBufFuzz::CmdBufReset() { - // webgpu_cmd_helper_->FreeRingBuffer(); - // memset(buffer_->memory(), 0, kCommandBufferSize); - cmd_helper_->Finish(); +/* Make CommandBuffer ready for the next test case. */ +bool CmdBufFuzz::CmdBufReset() { + // Wait for handling of commands from the previous test case to complete. + return cmd_helper_->Finish(); } -void CmdBufFuzz::Reset() { +/* Clean up from the last test case and get ready for the next one. */ +bool CmdBufFuzz::Reset() { action_index_ = 0; - // Wait for in-flight commands to finish - CmdBufReset(); + return CmdBufReset(); } // Keeper of global fuzzing state @@ -561,16 +580,18 @@ } // namespace gpu::cmdbuf::fuzzing +/* One-time early initialization at process startup. */ extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { - logging::SetMinLogLevel(logging::LOGGING_VERBOSE); - CHECK(base::i18n::InitializeICU()); - base::CommandLine::Init(*argc, *argv); - mojo::core::Init(); + logging::SetMinLogLevel(logging::LOGGING_VERBOSE); // Enable logging. + CHECK(base::i18n::InitializeICU()); // Unicode support for cmd line parsing. + base::CommandLine::Init(*argc, *argv); // Parse cmd line args. + mojo::core::Init(); // Initialize Mojo; probably unnecessary. gpu::cmdbuf::fuzzing::wgpuf_setup = new gpu::cmdbuf::fuzzing::CmdBufFuzz(); return 0; } +/* Fuzzer entry point. Called on every iteration with a fresh test case. */ DEFINE_PROTO_FUZZER(gpu::cmdbuf::fuzzing::CmdBufSession& session) { gpu::cmdbuf::fuzzing::wgpuf_setup->RunCommandBuffer(session); return;
diff --git a/testing/libfuzzer/fuzzers/command_buffer_fuzzer/cmd_buf_fuzz.h b/testing/libfuzzer/fuzzers/command_buffer_fuzzer/cmd_buf_fuzz.h index d64cfe3..c1f8fdec 100644 --- a/testing/libfuzzer/fuzzers/command_buffer_fuzzer/cmd_buf_fuzz.h +++ b/testing/libfuzzer/fuzzers/command_buffer_fuzzer/cmd_buf_fuzz.h
@@ -43,14 +43,15 @@ public: CmdBufFuzz(); ~CmdBufFuzz() override; + void CommandLineInit(); void GfxInit(); void RuntimeInit(); - void Reset(); + bool Reset(); // CommandBuffer ops void RunCommandBuffer(cmdbuf::fuzzing::CmdBufSession); - void CmdBufReset(); + bool CmdBufReset(); gpu::SyncToken SyncTokenFromProto(cmdbuf::fuzzing::SyncToken token_proto); void SignalSyncTokenCallback();
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index b89e6d57..d6f24b0 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -5574,6 +5574,21 @@ ] } ], + "GpuCleanupInBackground": [ + { + "platforms": [ + "android" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "GpuCleanupInBackground" + ] + } + ] + } + ], "GridAndGroupAndroidM5": [ { "platforms": [ @@ -6268,16 +6283,27 @@ ] } ], - "IOSFeedSyncPromo": [ + "IOSFeedSyncPromoFastFollows": [ { "platforms": [ "ios" ], "experiments": [ { - "name": "Enabled", + "name": "Enabled_Compact_Horizontal_20230316", "params": { - "compact": "true" + "DiscoverFeedTopSyncPromoStyle": "2", + "autodismissImpressions": "3" + }, + "enable_features": [ + "EnableDiscoverFeedTopSyncPromo" + ] + }, + { + "name": "Enabled_Compact_Vertical_20230316", + "params": { + "DiscoverFeedTopSyncPromoStyle": "3", + "autodismissImpressions": "3" }, "enable_features": [ "EnableDiscoverFeedTopSyncPromo"
diff --git a/third_party/android_deps/BUILD.gn b/third_party/android_deps/BUILD.gn index 37b4ee4e..4d5d7a3 100644 --- a/third_party/android_deps/BUILD.gn +++ b/third_party/android_deps/BUILD.gn
@@ -7,6 +7,7 @@ # Library groups if (!limit_android_deps) { java_group("dagger_java") { + preferred_dep = true if (defined(dagger_java_target)) { deps = [ dagger_java_target ] } else { @@ -26,6 +27,7 @@ } java_group("espresso_java") { + preferred_dep = true testonly = true # TODO(crbug.com/1005891): Update to use espresso targets below once androidx @@ -34,6 +36,7 @@ } java_group("guava_android_java") { + preferred_dep = true if (defined(guava_android_target)) { deps = [ guava_android_target ] } else { @@ -43,6 +46,7 @@ } java_group("robolectric_all_java") { + preferred_dep = true testonly = true deps = [ @@ -60,6 +64,7 @@ } java_group("material_design_java") { + preferred_dep = true if (defined(material_design_target)) { deps = [ material_design_target ] } else { @@ -70,6 +75,7 @@ } java_group("protobuf_lite_runtime_java") { + preferred_dep = true deps = [ android_proto_runtime ] } @@ -84,6 +90,7 @@ } java_group("playcore_java") { + preferred_dep = true if (defined(playcore_target)) { deps = [ playcore_target ] } else { @@ -1531,6 +1538,15 @@ } # This is generated, do not edit. Update BuildConfigGenerator.groovy instead. + java_prebuilt("org_mockito_mockito_subclass_java") { + jar_path = "libs/org_mockito_mockito_subclass/mockito-subclass-5.1.1.jar" + output_name = "org_mockito_mockito_subclass" + supports_android = true + testonly = true + deps = [ ":org_mockito_mockito_core_java" ] + } + + # This is generated, do not edit. Update BuildConfigGenerator.groovy instead. java_prebuilt("org_robolectric_robolectric_java") { jar_path = "libs/org_robolectric_robolectric/robolectric-4.8.1.jar" output_name = "org_robolectric_robolectric"
diff --git a/third_party/android_deps/additional_readme_paths.json b/third_party/android_deps/additional_readme_paths.json index 062ea29..bdf60591 100644 --- a/third_party/android_deps/additional_readme_paths.json +++ b/third_party/android_deps/additional_readme_paths.json
@@ -144,6 +144,7 @@ "libs/org_jsoup_jsoup", "libs/org_mockito_mockito_android", "libs/org_mockito_mockito_core", + "libs/org_mockito_mockito_subclass", "libs/org_objenesis_objenesis", "libs/org_ow2_asm_asm", "libs/org_ow2_asm_asm_analysis",
diff --git a/third_party/android_deps/build.gradle b/third_party/android_deps/build.gradle index 599d1633..b92e3eeb 100644 --- a/third_party/android_deps/build.gradle +++ b/third_party/android_deps/build.gradle
@@ -185,8 +185,10 @@ // Version 1.2 is needed by espresso-web, but we'll newer 1.2.1. androidTestCompile 'org.ccil.cowan.tagsoup:tagsoup:1.2.1' - androidTestCompile "org.mockito:mockito-android:5.1.1" - androidTestCompile "org.mockito:mockito-core:5.1.1" + String mockitoVersion = '5.1.1' + androidTestCompile "org.mockito:mockito-android:${mockitoVersion}" + androidTestCompile "org.mockito:mockito-core:${mockitoVersion}" + androidTestCompile "org.mockito:mockito-subclass:${mockitoVersion}" // Depended on by downstream guava_java rewrite. androidTestCompile "org.checkerframework:checker-qual:3.25.0"
diff --git a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy index d21ea2b..a0e27c5 100644 --- a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy +++ b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
@@ -200,6 +200,9 @@ org_mockito_mockito_core: new PropertyOverride( licenseUrl: 'https://raw.githubusercontent.com/mockito/mockito/main/LICENSE', licenseName: 'The MIT License'), + org_mockito_mockito_subclass: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/mockito/mockito/main/LICENSE', + licenseName: 'The MIT License'), org_objenesis_objenesis: new PropertyOverride( url: 'http://objenesis.org/index.html', licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
diff --git a/third_party/android_deps/libs/org_mockito_mockito_subclass/3pp/3pp.pb b/third_party/android_deps/libs/org_mockito_mockito_subclass/3pp/3pp.pb new file mode 100644 index 0000000..d743b51b --- /dev/null +++ b/third_party/android_deps/libs/org_mockito_mockito_subclass/3pp/3pp.pb
@@ -0,0 +1,16 @@ +# Copyright 2021 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This is generated, do not edit. Update BuildConfigGenerator.groovy instead. + +create { + source { + script { name: "fetch.py" } + } +} + +upload { + pkg_prefix: "chromium/third_party/android_deps/libs" + universal: true +}
diff --git a/third_party/android_deps/libs/org_mockito_mockito_subclass/3pp/fetch.py b/third_party/android_deps/libs/org_mockito_mockito_subclass/3pp/fetch.py new file mode 100755 index 0000000..9ee1942 --- /dev/null +++ b/third_party/android_deps/libs/org_mockito_mockito_subclass/3pp/fetch.py
@@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +# Copyright 2021 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This is generated, do not edit. Update BuildConfigGenerator.groovy and +# 3ppFetch.template instead. + +import argparse +import json +import os +import re +import urllib.request + +_REPO_URL = 'https://repo.maven.apache.org/maven2' +_GROUP_NAME = 'org/mockito' +_MODULE_NAME = 'mockito-subclass' +_FILE_EXT = 'jar' +_OVERRIDE_LATEST = None +_PATCH_VERSION = 'cr1' + + +def do_latest(): + if _OVERRIDE_LATEST is not None: + print(_OVERRIDE_LATEST + f'.{_PATCH_VERSION}') + return + maven_metadata_url = '{}/{}/{}/maven-metadata.xml'.format( + _REPO_URL, _GROUP_NAME, _MODULE_NAME) + metadata = urllib.request.urlopen(maven_metadata_url).read().decode( + 'utf-8') + # Do not parse xml with the python included parser since it is susceptible + # to maliciously crafted xmls. Only use regular expression parsing to be + # safe. RE should be enough to handle what we need to extract. + match = re.search('<latest>([^<]+)</latest>', metadata) + if match: + latest = match.group(1) + else: + # if no latest info was found just hope the versions are sorted and the + # last one is the latest (as is commonly the case). + latest = re.findall('<version>([^<]+)</version>', metadata)[-1] + print(latest + f'.{_PATCH_VERSION}') + + +def get_download_url(version): + # Remove the patch version when getting the download url + version_no_patch, patch = version.rsplit('.', 1) + if patch.startswith('cr'): + version = version_no_patch + file_url = '{0}/{1}/{2}/{3}/{2}-{3}.{4}'.format(_REPO_URL, _GROUP_NAME, + _MODULE_NAME, version, + _FILE_EXT) + file_name = file_url.rsplit('/', 1)[-1] + + partial_manifest = { + 'url': [file_url], + 'name': [file_name], + 'ext': '.' + _FILE_EXT, + } + print(json.dumps(partial_manifest)) + + +def main(): + ap = argparse.ArgumentParser() + sub = ap.add_subparsers() + + latest = sub.add_parser('latest') + latest.set_defaults(func=lambda _opts: do_latest()) + + download = sub.add_parser('get_url') + download.set_defaults( + func=lambda _opts: get_download_url(os.environ['_3PP_VERSION'])) + + opts = ap.parse_args() + opts.func(opts) + + +if __name__ == '__main__': + main()
diff --git a/third_party/android_deps/libs/org_mockito_mockito_subclass/LICENSE b/third_party/android_deps/libs/org_mockito_mockito_subclass/LICENSE new file mode 100644 index 0000000..e0840a4 --- /dev/null +++ b/third_party/android_deps/libs/org_mockito_mockito_subclass/LICENSE
@@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2007 Mockito contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file
diff --git a/third_party/android_deps/libs/org_mockito_mockito_subclass/OWNERS b/third_party/android_deps/libs/org_mockito_mockito_subclass/OWNERS new file mode 100644 index 0000000..aea47a05 --- /dev/null +++ b/third_party/android_deps/libs/org_mockito_mockito_subclass/OWNERS
@@ -0,0 +1 @@ +file://third_party/android_deps/OWNERS
diff --git a/third_party/android_deps/libs/org_mockito_mockito_subclass/README.chromium b/third_party/android_deps/libs/org_mockito_mockito_subclass/README.chromium new file mode 100644 index 0000000..8b59f40 --- /dev/null +++ b/third_party/android_deps/libs/org_mockito_mockito_subclass/README.chromium
@@ -0,0 +1,14 @@ +Name: mockito-subclass +Short Name: mockito-subclass +URL: https://github.com/mockito/mockito +Version: 5.1.1 +License: The MIT License +License File: NOT_SHIPPED +CPEPrefix: unknown +Security Critical: no + +Description: +Mockito preconfigured subclass mock maker + +Local Modifications: +No modifications.
diff --git a/third_party/android_deps/libs/org_mockito_mockito_subclass/cipd.yaml b/third_party/android_deps/libs/org_mockito_mockito_subclass/cipd.yaml new file mode 100644 index 0000000..fe6a9fb --- /dev/null +++ b/third_party/android_deps/libs/org_mockito_mockito_subclass/cipd.yaml
@@ -0,0 +1,10 @@ +# Copyright 2018 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# To create CIPD package run the following command. +# cipd create --pkg-def cipd.yaml -tag version:2@5.1.1.cr1 +package: chromium/third_party/android_deps/libs/org_mockito_mockito_subclass +description: "mockito-subclass" +data: +- file: mockito-subclass-5.1.1.jar
diff --git a/third_party/blink/common/input/web_coalesced_input_event_mojom_traits.cc b/third_party/blink/common/input/web_coalesced_input_event_mojom_traits.cc index 053d68b..cae8aad 100644 --- a/third_party/blink/common/input/web_coalesced_input_event_mojom_traits.cc +++ b/third_party/blink/common/input/web_coalesced_input_event_mojom_traits.cc
@@ -36,7 +36,7 @@ pointer.tangential_pressure, pointer.twist, pointer.button, pointer.pointer_type, pointer.movement_x, pointer.movement_y, pointer.is_raw_movement_event, pointer.PositionInWidget(), - pointer.PositionInScreen(), std::move(mouse_data)); + pointer.PositionInScreen(), std::move(mouse_data), pointer.device_id); } void PointerPropertiesFromPointerData( @@ -54,6 +54,7 @@ pointer_properties->movement_y = pointer_data->movement_y; pointer_properties->is_raw_movement_event = pointer_data->is_raw_movement_event; + pointer_properties->device_id = pointer_data->device_id; } void TouchPointPropertiesFromPointerData(
diff --git a/third_party/blink/public/common/input/web_pointer_properties.h b/third_party/blink/public/common/input/web_pointer_properties.h index 661c05e..33940cc6 100644 --- a/third_party/blink/public/common/input/web_pointer_properties.h +++ b/third_party/blink/public/common/input/web_pointer_properties.h
@@ -109,6 +109,9 @@ // TODO(crbug.com/982379): Figure out how to avoid using this boolean. bool is_raw_movement_event = false; + // Contains unique device id for pen on supported devices. + int32_t device_id = -1; + protected: // Widget coordinate, which is relative to the bound of current RenderWidget // (e.g. a plugin or OOPIF inside a RenderView). Similar to viewport
diff --git a/third_party/blink/public/mojom/input/input_handler.mojom b/third_party/blink/public/mojom/input/input_handler.mojom index 9b41db62..020807b 100644 --- a/third_party/blink/public/mojom/input/input_handler.mojom +++ b/third_party/blink/public/mojom/input/input_handler.mojom
@@ -63,6 +63,7 @@ gfx.mojom.PointF widget_position; gfx.mojom.PointF screen_position; MouseData? mouse_data; + int32 device_id; }; struct WheelData {
diff --git a/third_party/blink/renderer/bindings/generated_in_core.gni b/third_party/blink/renderer/bindings/generated_in_core.gni index 5b7b14b..05eee338 100644 --- a/third_party/blink/renderer/bindings/generated_in_core.gni +++ b/third_party/blink/renderer/bindings/generated_in_core.gni
@@ -1733,8 +1733,12 @@ "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_dommatrix_float32array_float64array.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_dompointinit_unrestricteddouble.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_dompointinit_unrestricteddouble.h", - "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_double_doubleornullsequence_null.cc", - "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_double_doubleornullsequence_null.h", + "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_double_string_timelinerangeoffset.cc", + "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_double_string_timelinerangeoffset.h", + + "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_double_doubleorstringortimelinerangeoffsetornullsequence_string_timelinerangeoffset_null.cc", + "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_double_doubleorstringortimelinerangeoffsetornullsequence_string_timelinerangeoffset_null.h", + "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_double_doublesequence.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_double_doublesequence.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_double_string.cc",
diff --git a/third_party/blink/renderer/core/animation/animation.cc b/third_party/blink/renderer/core/animation/animation.cc index 7f1164ae..31893f7 100644 --- a/third_party/blink/renderer/core/animation/animation.cc +++ b/third_party/blink/renderer/core/animation/animation.cc
@@ -944,6 +944,12 @@ reset_current_time_on_resume_ = false; + // Set the timeline if needed for resolving timeline offsets in kefyrames. + if (auto* keyframe_effect = DynamicTo<KeyframeEffect>(effect())) { + ViewTimeline* view_timeline = DynamicTo<ViewTimeline>(timeline); + keyframe_effect->Model()->SetViewTimelineIfRequired(view_timeline); + } + if (timeline) { if (!timeline->IsMonotonicallyIncreasing()) { ApplyPendingPlaybackRate(); @@ -1165,8 +1171,13 @@ // The effect is no longer associated with CSS properties. if (new_effect) { new_effect->SetIgnoreCssTimingProperties(); - if (KeyframeEffect* keyframe_effect = DynamicTo<KeyframeEffect>(new_effect)) + if (KeyframeEffect* keyframe_effect = + DynamicTo<KeyframeEffect>(new_effect)) { keyframe_effect->SetIgnoreCSSKeyframes(); + // Set the timeline if needed for resolving timeline offsets in kefyrames. + ViewTimeline* view_timeline = DynamicTo<ViewTimeline>(timeline()); + keyframe_effect->Model()->SetViewTimelineIfRequired(view_timeline); + } } // The remaining steps are for handling CSS animation and transition events.
diff --git a/third_party/blink/renderer/core/animation/animation_timeline.h b/third_party/blink/renderer/core/animation/animation_timeline.h index 3c44e29..eb6fbb9d 100644 --- a/third_party/blink/renderer/core/animation/animation_timeline.h +++ b/third_party/blink/renderer/core/animation/animation_timeline.h
@@ -90,7 +90,8 @@ // Schedules animation timing update on next frame. virtual void ScheduleServiceOnNextFrame(); - Animation* Play(AnimationEffect*, ExceptionState& = ASSERT_NO_EXCEPTION); + virtual Animation* Play(AnimationEffect*, + ExceptionState& = ASSERT_NO_EXCEPTION); virtual bool NeedsAnimationTimingUpdate(); virtual bool HasAnimations() const { return !animations_.empty(); }
diff --git a/third_party/blink/renderer/core/animation/base_keyframe.idl b/third_party/blink/renderer/core/animation/base_keyframe.idl index 8d7e30b..e8d6c2b 100644 --- a/third_party/blink/renderer/core/animation/base_keyframe.idl +++ b/third_party/blink/renderer/core/animation/base_keyframe.idl
@@ -6,8 +6,9 @@ enum CompositeOperationOrAuto {"replace", "add", "accumulate", "auto"}; +typedef (double or TimelineRangeOffset or DOMString) KeyframeOffset; dictionary BaseKeyframe { - double? offset = null; + KeyframeOffset? offset = null; DOMString easing = "linear"; CompositeOperationOrAuto composite = "auto"; };
diff --git a/third_party/blink/renderer/core/animation/base_property_indexed_keyframe.idl b/third_party/blink/renderer/core/animation/base_property_indexed_keyframe.idl index bb50867..dd0ff76 100644 --- a/third_party/blink/renderer/core/animation/base_property_indexed_keyframe.idl +++ b/third_party/blink/renderer/core/animation/base_property_indexed_keyframe.idl
@@ -5,7 +5,7 @@ // https://w3.org/TR/web-animations-1/#dictdef-basepropertyindexedkeyframe dictionary BasePropertyIndexedKeyframe { - (double? or sequence<double?>) offset = []; + (KeyframeOffset? or sequence<KeyframeOffset?>) offset = []; (DOMString or sequence<DOMString>) easing = []; (CompositeOperationOrAuto or sequence<CompositeOperationOrAuto>) composite = []; };
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.cc b/third_party/blink/renderer/core/animation/css/css_animations.cc index 6183864..7a0908a 100644 --- a/third_party/blink/renderer/core/animation/css/css_animations.cc +++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -424,7 +424,7 @@ WebFeature::kCSSAnimationsStackedNeutralKeyframe); } if (has_named_range_keyframes) { - model->SetViewTimeline(DynamicTo<ViewTimeline>(timeline)); + model->SetViewTimelineIfRequired(DynamicTo<ViewTimeline>(timeline)); } return model;
diff --git a/third_party/blink/renderer/core/animation/effect_input.cc b/third_party/blink/renderer/core/animation/effect_input.cc index ac4e5678..d99d2e2 100644 --- a/third_party/blink/renderer/core/animation/effect_input.cc +++ b/third_party/blink/renderer/core/animation/effect_input.cc
@@ -37,15 +37,26 @@ #include "third_party/blink/renderer/bindings/core/v8/script_iterator.h" #include "third_party/blink/renderer/bindings/core/v8/v8_base_keyframe.h" #include "third_party/blink/renderer/bindings/core/v8/v8_base_property_indexed_keyframe.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_timeline_range.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_timeline_range_offset.h" #include "third_party/blink/renderer/bindings/core/v8/v8_union_compositeoperationorauto_compositeoperationorautosequence.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_union_double_doubleornullsequence_null.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_union_double_doubleorstringortimelinerangeoffsetornullsequence_string_timelinerangeoffset_null.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_union_double_string_timelinerangeoffset.h" #include "third_party/blink/renderer/bindings/core/v8/v8_union_string_stringsequence.h" #include "third_party/blink/renderer/core/animation/animation_input_helpers.h" #include "third_party/blink/renderer/core/animation/compositor_animations.h" #include "third_party/blink/renderer/core/animation/css/css_animations.h" #include "third_party/blink/renderer/core/animation/keyframe_effect_model.h" #include "third_party/blink/renderer/core/animation/string_keyframe.h" +#include "third_party/blink/renderer/core/css/css_identifier_value.h" +#include "third_party/blink/renderer/core/css/css_primitive_value_mappings.h" #include "third_party/blink/renderer/core/css/css_style_sheet.h" +#include "third_party/blink/renderer/core/css/css_value_id_mappings_generated.h" +#include "third_party/blink/renderer/core/css/parser/css_parser.h" +#include "third_party/blink/renderer/core/css/properties/computed_style_utils.h" +#include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h" +#include "third_party/blink/renderer/core/css/resolver/element_resolve_context.h" +#include "third_party/blink/renderer/core/css/style_sheet_contents.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/dom/node_computed_style.h" @@ -65,6 +76,12 @@ namespace { +// Names of classes autogenerated from IDL get very long. +// Create shorter aliases. +using BaseKeyframeOffset = V8UnionDoubleOrStringOrTimelineRangeOffset; +using BasePropertyIndexedKeyframeOffset = + V8UnionDoubleOrDoubleOrStringOrTimelineRangeOffsetOrNullSequenceOrStringOrTimelineRangeOffsetOrNull; + // Converts the composite property of a BasePropertyIndexedKeyframe into a // vector of absl::optional<EffectModel::CompositeOperation> enums. Vector<absl::optional<EffectModel::CompositeOperation>> ParseCompositeProperty( @@ -90,6 +107,164 @@ return {}; } +struct ParsedOffset { + TimelineOffset::NamedRange range; + double relative_offset; +}; + +absl::optional<ParsedOffset> ParseOffsetFromTimelineRangeOffset( + TimelineRangeOffset* timeline_range_offset, + ExceptionState& exception_state) { + ParsedOffset result; + result.range = timeline_range_offset->hasRangeName() + ? timeline_range_offset->rangeName().AsEnum() + : TimelineOffset::NamedRange::kNone; + if (timeline_range_offset->hasOffset()) { + CSSNumericValue* numeric_value = timeline_range_offset->offset(); + const CSSPrimitiveValue* css_value = + DynamicTo<CSSPrimitiveValue>(numeric_value->ToCSSValue()); + + if (!css_value || !css_value->IsPercentage()) { + exception_state.ThrowTypeError( + "CSSNumericValue must be a percentage for a keyframe offset"); + return absl::nullopt; + } + result.relative_offset = css_value->GetDoubleValue() / 100; + } else { + exception_state.ThrowTypeError( + "timeline offset must be a range offset pair. Missing the offset."); + return absl::nullopt; + } + return result; +} + +absl::optional<ParsedOffset> ParseOffsetFromCssText( + Document& document, + String css_text, + ExceptionState& exception_state) { + const CSSParserContext* context = + document.ElementSheet().Contents()->ParserContext(); + CSSTokenizer tokenizer(css_text); + const auto tokens = tokenizer.TokenizeToEOF(); + CSSParserTokenRange token_range(tokens); + token_range.ConsumeWhitespace(); + + // <number> + { + CSSParserTokenRange range_copy = token_range; + const CSSPrimitiveValue* primitive = css_parsing_utils::ConsumeNumber( + range_copy, *context, CSSPrimitiveValue::ValueRange::kAll); + if (primitive && range_copy.AtEnd()) { + return ParsedOffset( + {TimelineOffset::NamedRange::kNone, primitive->GetValue<double>()}); + } + } + + // <percent> + { + CSSParserTokenRange range_copy = token_range; + const CSSPrimitiveValue* primitive = css_parsing_utils::ConsumePercent( + range_copy, *context, CSSPrimitiveValue::ValueRange::kAll); + if (primitive && range_copy.AtEnd()) { + return ParsedOffset({TimelineOffset::NamedRange::kNone, + primitive->GetValue<double>() / 100}); + } + } + + // <range-name> <percent> + auto* range_name_percent = + To<CSSValueList>(css_parsing_utils::ConsumeTimelineRangeNameAndPercent( + token_range, *context)); + if (!range_name_percent || !token_range.AtEnd()) { + exception_state.ThrowTypeError( + "timeline offset must be of the form [timeline-range-name] " + "<percentage>"); + return absl::nullopt; + } + TimelineOffset::NamedRange range = + To<CSSIdentifierValue>(range_name_percent->Item(0)) + .ConvertTo<TimelineOffset::NamedRange>(); + double relative_offset = + To<CSSPrimitiveValue>(range_name_percent->Item(1)).GetFloatValue() / 100; + + return ParsedOffset({range, relative_offset}); +} + +template <typename T> +absl::optional<ParsedOffset> ParseOffset(Document& document, + T* keyframe_offset, + ExceptionState& exception_state) { + if (!keyframe_offset) { + return absl::nullopt; + } + + if (keyframe_offset->IsDouble()) { + return ParsedOffset( + {TimelineOffset::NamedRange::kNone, keyframe_offset->GetAsDouble()}); + } + + if (keyframe_offset->IsTimelineRangeOffset()) { + return ParseOffsetFromTimelineRangeOffset( + keyframe_offset->GetAsTimelineRangeOffset(), exception_state); + } + + if (keyframe_offset->IsString()) { + return ParseOffsetFromCssText(document, keyframe_offset->GetAsString(), + exception_state); + } + + // If calling using a PropertyIndexKeyframe, we must already have handled + // sequences. + NOTREACHED(); + return absl::nullopt; +} + +void SetKeyframeOffset(Keyframe& keyframe, ParsedOffset& offset) { + if (offset.range == V8TimelineRange::Enum::kNone) { + keyframe.SetOffset(offset.relative_offset); + } else { + TimelineOffset timeline_offset( + offset.range, Length::Percent(100 * offset.relative_offset)); + keyframe.SetTimelineOffset(timeline_offset); + } +} + +Vector<absl::optional<ParsedOffset>> ExtractPropertyIndexedKeyframeOffsets( + Document& document, + BasePropertyIndexedKeyframe& base_property_indexed_keyframe, + ExceptionState& exception_state) { + Vector<absl::optional<ParsedOffset>> offsets; + + if (!base_property_indexed_keyframe.hasOffset()) { + return offsets; + } + + BasePropertyIndexedKeyframeOffset* keyframe_offset = + base_property_indexed_keyframe.offset(); + + if (keyframe_offset->IsNull()) { + return offsets; + } + + if (keyframe_offset->IsDoubleOrStringOrTimelineRangeOffsetOrNullSequence()) { + // iterate through all offsets in the list. + const HeapVector<Member<BaseKeyframeOffset>>& list = + keyframe_offset + ->GetAsDoubleOrStringOrTimelineRangeOffsetOrNullSequence(); + for (BaseKeyframeOffset* base_keyframe_offset : list) { + absl::optional<ParsedOffset> parsed_offset = + ParseOffset(document, base_keyframe_offset, exception_state); + offsets.push_back(parsed_offset); + } + return offsets; + } + + absl::optional<ParsedOffset> parsed_offset = + ParseOffset(document, keyframe_offset, exception_state); + offsets.push_back(parsed_offset); + return offsets; +} + void SetKeyframeValue(Element* element, Document& document, StringKeyframe& keyframe, @@ -283,6 +458,11 @@ ExceptionState& exception_state) { v8::Isolate* isolate = script_state->GetIsolate(); + // https://www.w3.org/TR/web-animations-1/#processing-a-keyframes-argument + // This implementation relaxes steps 6 and 7, which require the keyframes to + // be loosely sorted and bounded between 0 and 1. The sorting and bounds + // only apply to keyframes without timeline offsets. + // This loop captures step 5 of the procedure to process a keyframes argument, // in the case where the argument is iterable. HeapVector<Member<const BaseKeyframe>> processed_base_keyframes; @@ -325,31 +505,43 @@ return {}; // 6. If processed keyframes is not loosely sorted by offset, throw a - // TypeError and abort these steps. + // TypeError and abort these steps. double previous_offset = -std::numeric_limits<double>::infinity(); + Vector<absl::optional<ParsedOffset>> offsets; const wtf_size_t num_processed_keyframes = processed_base_keyframes.size(); for (wtf_size_t i = 0; i < num_processed_keyframes; ++i) { - if (!processed_base_keyframes[i]->hasOffsetNonNull()) - continue; + const BaseKeyframe* base_keyframe = processed_base_keyframes[i]; + absl::optional<ParsedOffset> offset = + ParseOffset(document, base_keyframe->offset(), exception_state); + if (exception_state.HadException()) { + return {}; + } + offsets.push_back(offset); - double offset = processed_base_keyframes[i]->offsetNonNull(); - if (offset < previous_offset) { + if (!offset || offset->range != TimelineOffset::NamedRange::kNone) { + continue; + } + + double numeric_offset = offset->relative_offset; + if (numeric_offset < previous_offset) { exception_state.ThrowTypeError( "Offsets must be montonically non-decreasing."); return {}; } - previous_offset = offset; + previous_offset = numeric_offset; } // 7. If there exist any keyframe in processed keyframes whose keyframe - // offset is non-null and less than zero or greater than one, throw a - // TypeError and abort these steps. + // offset is non-null and less than zero or greater than one, throw a + // TypeError and abort these steps. for (wtf_size_t i = 0; i < num_processed_keyframes; ++i) { - if (!processed_base_keyframes[i]->hasOffsetNonNull()) + absl::optional<ParsedOffset> offset = offsets[i]; + if (!offset || offset->range != TimelineOffset::NamedRange::kNone) { continue; + } - double offset = processed_base_keyframes[i]->offsetNonNull(); - if (offset < 0 || offset > 1) { + double numeric_offset = offset->relative_offset; + if (numeric_offset < 0 || numeric_offset > 1) { exception_state.ThrowTypeError( "Offsets must be null or in the range [0,1]."); return {}; @@ -361,14 +553,14 @@ // Now we create the actual Keyframe object. We start by assigning the // offset and composite values; conceptually these were actually added in // step 5 above but we didn't have a keyframe object then. - const BaseKeyframe* base_keyframe = processed_base_keyframes[i]; auto* keyframe = MakeGarbageCollected<StringKeyframe>(); - if (base_keyframe->hasOffset()) { - keyframe->SetOffset(base_keyframe->offset()); + if (offsets[i]) { + SetKeyframeOffset(*keyframe, offsets[i].value()); } // 8.1. For each property-value pair in frame, parse the property value // using the syntax specified for that property. + const BaseKeyframe* base_keyframe = processed_base_keyframes[i]; for (const auto& pair : processed_properties[i]) { // TODO(crbug.com/777971): Make parsing of property values spec-compliant. SetKeyframeValue(element, document, *keyframe, pair.first, pair.second, @@ -461,13 +653,12 @@ if (exception_state.HadException()) return {}; - Vector<absl::optional<double>> offsets; - if (property_indexed_keyframe->offset()->IsNull()) - offsets.push_back(absl::nullopt); - else if (property_indexed_keyframe->offset()->IsDouble()) - offsets.push_back(property_indexed_keyframe->offset()->GetAsDouble()); - else - offsets = property_indexed_keyframe->offset()->GetAsDoubleOrNullSequence(); + Vector<absl::optional<ParsedOffset>> offsets = + ExtractPropertyIndexedKeyframeOffsets( + document, *property_indexed_keyframe, exception_state); + if (exception_state.HadException()) { + return {}; + } // The web-animations spec explicitly states that easings should be kept as // DOMStrings here and not parsed into timing functions until later. @@ -567,28 +758,36 @@ auto* keyframe = keyframes.at(keys[i]); if (i < offsets.size()) { - absl::optional<double> offset = offsets[i]; + absl::optional<ParsedOffset> parsed_offset = offsets[i]; + absl::optional<double> numeric_offset; + if (parsed_offset.has_value() && + parsed_offset.value().range == TimelineOffset::NamedRange::kNone) { + numeric_offset = parsed_offset.value().relative_offset; + } + // 6. If processed keyframes is not loosely sorted by offset, throw a // TypeError and abort these steps. - if (offset.has_value()) { - if (offset.value() < previous_offset) { + if (numeric_offset.has_value()) { + if (numeric_offset.value() < previous_offset) { exception_state.ThrowTypeError( - "Offsets must be montonically non-decreasing."); + "Offsets must be monotonically non-decreasing."); return {}; } - previous_offset = offset.value(); + previous_offset = numeric_offset.value(); } // 7. If there exist any keyframe in processed keyframes whose keyframe // offset is non-null and less than zero or greater than one, throw a // TypeError and abort these steps. - if (offset.has_value() && (offset.value() < 0 || offset.value() > 1)) { + if (numeric_offset.has_value() && + (numeric_offset.value() < 0 || numeric_offset.value() > 1)) { exception_state.ThrowTypeError( "Offsets must be null or in the range [0,1]."); return {}; } - - keyframe->SetOffset(offset); + if (parsed_offset) { + SetKeyframeOffset(*keyframe, parsed_offset.value()); + } } // At this point in the code we have read all the properties we will read
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect.cc b/third_party/blink/renderer/core/animation/keyframe_effect.cc index 24e23519..bf952267 100644 --- a/third_party/blink/renderer/core/animation/keyframe_effect.cc +++ b/third_party/blink/renderer/core/animation/keyframe_effect.cc
@@ -41,6 +41,7 @@ #include "third_party/blink/renderer/core/animation/element_animations.h" #include "third_party/blink/renderer/core/animation/sampled_effect.h" #include "third_party/blink/renderer/core/animation/timing_input.h" +#include "third_party/blink/renderer/core/animation/view_timeline.h" #include "third_party/blink/renderer/core/css/parser/css_selector_parser.h" #include "third_party/blink/renderer/core/css/properties/css_property_ref.h" #include "third_party/blink/renderer/core/css/properties/longhands.h" @@ -257,8 +258,13 @@ // https://w3.org/TR/web-animations-1/#dom-keyframeeffect-getkeyframes HeapVector<ScriptValue> KeyframeEffect::getKeyframes( ScriptState* script_state) { - if (Animation* animation = GetAnimation()) + if (Animation* animation = GetAnimation()) { animation->FlushPendingUpdates(); + if (ViewTimeline* view_timeline = + DynamicTo<ViewTimeline>(animation->timeline())) { + view_timeline->ResolveTimelineOffsets(/* invalidate_effect */ false); + } + } HeapVector<ScriptValue> computed_keyframes; if (!model_->HasFrames() || !script_state->ContextIsValid())
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect_model.cc b/third_party/blink/renderer/core/animation/keyframe_effect_model.cc index 7c3b0a7..b2fa00b 100644 --- a/third_party/blink/renderer/core/animation/keyframe_effect_model.cc +++ b/third_party/blink/renderer/core/animation/keyframe_effect_model.cc
@@ -304,11 +304,30 @@ return changed; } -void KeyframeEffectModelBase::SetViewTimeline(const ViewTimeline* timeline) { +void KeyframeEffectModelBase::SetViewTimelineIfRequired( + const ViewTimeline* timeline) { if (view_timeline_ == timeline) { return; } + bool has_timeline_offset_in_keyframe = false; + for (const auto& keyframe : keyframes_) { + if (keyframe->GetTimelineOffset()) { + has_timeline_offset_in_keyframe = true; + break; + } + } + + if (!has_timeline_offset_in_keyframe) { + // Keyframes are essentially immutable once the keyframe model is + // constructed. Thus, we should never be in a position where + // has_timeline_offset_in_keyframe changes from true to false between + // checks, and we should never have a set view timeline that needs to be + // cleared. + DCHECK(!view_timeline_); + return; + } + if (view_timeline_ && !timeline) { // Clear offsets that are resolved from timeline offsets. bool needs_update = false;
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect_model.h b/third_party/blink/renderer/core/animation/keyframe_effect_model.h index 6e5cf05..fc7ceef 100644 --- a/third_party/blink/renderer/core/animation/keyframe_effect_model.h +++ b/third_party/blink/renderer/core/animation/keyframe_effect_model.h
@@ -170,7 +170,7 @@ virtual KeyframeEffectModelBase* Clone() = 0; - void SetViewTimeline(const ViewTimeline* timeline); + void SetViewTimelineIfRequired(const ViewTimeline* timeline); // Ensure timeline offsets are properly resolved. If any of the offsets // changed, the keyframes are resorted and cached data is cleared. Returns
diff --git a/third_party/blink/renderer/core/animation/view_timeline.cc b/third_party/blink/renderer/core/animation/view_timeline.cc index 3f83b1df..f2614ba 100644 --- a/third_party/blink/renderer/core/animation/view_timeline.cc +++ b/third_party/blink/renderer/core/animation/view_timeline.cc
@@ -551,6 +551,14 @@ return has_keyframe_update; } +Animation* ViewTimeline::Play(AnimationEffect* effect, + ExceptionState& exception_state) { + if (auto* keyframe_effect = DynamicTo<KeyframeEffect>(effect)) { + keyframe_effect->Model()->SetViewTimelineIfRequired(this); + } + return AnimationTimeline::Play(effect, exception_state); +} + void ViewTimeline::FlushStyleUpdate() { ScrollTimeline::FlushStyleUpdate(); ResolveTimelineOffsets(/* invalidate_effect */ false);
diff --git a/third_party/blink/renderer/core/animation/view_timeline.h b/third_party/blink/renderer/core/animation/view_timeline.h index c350c32..c180696 100644 --- a/third_party/blink/renderer/core/animation/view_timeline.h +++ b/third_party/blink/renderer/core/animation/view_timeline.h
@@ -68,6 +68,9 @@ bool ResolveTimelineOffsets(bool invalidate_effect) const; + Animation* Play(AnimationEffect*, + ExceptionState& = ASSERT_NO_EXCEPTION) override; + void Trace(Visitor*) const override; protected:
diff --git a/third_party/blink/renderer/core/css/css_value.cc b/third_party/blink/renderer/core/css/css_value.cc index 2b5c3a5..9cc8e98 100644 --- a/third_party/blink/renderer/core/css/css_value.cc +++ b/third_party/blink/renderer/core/css/css_value.cc
@@ -712,4 +712,150 @@ NOTREACHED(); } +#if DCHECK_IS_ON() +String CSSValue::ClassTypeToString() const { + switch (GetClassType()) { + case kNumericLiteralClass: + return "NumericLiteralClass"; + case kMathFunctionClass: + return "MathFunctionClass"; + case kIdentifierClass: + return "IdentifierClass"; + case kColorClass: + return "ColorClass"; + case kColorMixClass: + return "ColorMixClass"; + case kCounterClass: + return "CounterClass"; + case kQuadClass: + return "QuadClass"; + case kCustomIdentClass: + return "CustomIdentClass"; + case kStringClass: + return "StringClass"; + case kURIClass: + return "URIClass"; + case kValuePairClass: + return "ValuePairClass"; + case kLightDarkValuePairClass: + return "LightDarkValuePairClass"; + case kScrollClass: + return "ScrollClass"; + case kViewClass: + return "ViewClass"; + case kRatioClass: + return "RatioClass"; + case kBasicShapeCircleClass: + return "BasicShapeCircleClass"; + case kBasicShapeEllipseClass: + return "BasicShapeEllipseClass"; + case kBasicShapePolygonClass: + return "BasicShapePolygonClass"; + case kBasicShapeInsetClass: + return "BasicShapeInsetClass"; + case kBasicShapeRectClass: + return "BasicShapeRectClass"; + case kBasicShapeXYWHClass: + return "BasicShapeXYWHClass"; + case kImageClass: + return "ImageClass"; + case kCursorImageClass: + return "CursorImageClass"; + case kCrossfadeClass: + return "CrossfadeClass"; + case kPaintClass: + return "PaintClass"; + case kLinearGradientClass: + return "LinearGradientClass"; + case kRadialGradientClass: + return "RadialGradientClass"; + case kConicGradientClass: + return "ConicGradientClass"; + case kLinearTimingFunctionClass: + return "LinearTimingFunctionClass"; + case kCubicBezierTimingFunctionClass: + return "CubicBezierTimingFunctionClass"; + case kStepsTimingFunctionClass: + return "StepsTimingFunctionClass"; + case kBorderImageSliceClass: + return "BorderImageSliceClass"; + case kFontFeatureClass: + return "FontFeatureClass"; + case kFontFaceSrcClass: + return "FontFaceSrcClass"; + case kFontFamilyClass: + return "FontFamilyClass"; + case kFontStyleRangeClass: + return "FontStyleRangeClass"; + case kFontVariationClass: + return "FontVariationClass"; + case kAlternateClass: + return "AlternateClass"; + case kInheritedClass: + return "InheritedClass"; + case kInitialClass: + return "InitialClass"; + case kUnsetClass: + return "UnsetClass"; + case kRevertClass: + return "RevertClass"; + case kRevertLayerClass: + return "RevertLayerClass"; + case kReflectClass: + return "ReflectClass"; + case kShadowClass: + return "ShadowClass"; + case kUnicodeRangeClass: + return "UnicodeRangeClass"; + case kGridTemplateAreasClass: + return "GridTemplateAreasClass"; + case kPathClass: + return "PathClass"; + case kRayClass: + return "RayClass"; + case kVariableReferenceClass: + return "VariableReferenceClass"; + case kCustomPropertyDeclarationClass: + return "CustomPropertyDeclarationClass"; + case kPendingSubstitutionValueClass: + return "PendingSubstitutionValueClass"; + case kPendingSystemFontValueClass: + return "PendingSystemFontValueClass"; + case kInvalidVariableValueClass: + return "InvalidVariableValueClass"; + case kCyclicVariableValueClass: + return "CyclicVariableValueClass"; + case kLayoutFunctionClass: + return "LayoutFunctionClass"; + case kCSSContentDistributionClass: + return "CSSContentDistributionClass"; + case kKeyframeShorthandClass: + return "KeyframeShorthandClass"; + case kInitialColorValueClass: + return "InitialColorValueClass"; + case kImageSetOptionClass: + return "ImageSetOptionClass"; + case kImageSetTypeClass: + return "ImageSetTypeClass"; + case kValueListClass: + return "ValueListClass"; + case kFunctionClass: + return "FunctionClass"; + case kImageSetClass: + return "ImageSetClass"; + case kGridLineNamesClass: + return "GridLineNamesClass"; + case kGridAutoRepeatClass: + return "GridAutoRepeatClass"; + case kGridIntegerRepeatClass: + return "GridIntegerRepeatClass"; + case kAxisClass: + return "AxisClass"; + default: + NOTREACHED(); + return "Unknown ClassType"; + } +} +#endif + } // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_value.h b/third_party/blink/renderer/core/css/css_value.h index c542c849..274fde5 100644 --- a/third_party/blink/renderer/core/css/css_value.h +++ b/third_party/blink/renderer/core/css/css_value.h
@@ -213,6 +213,10 @@ } bool IsScopedValue() const { return !needs_tree_scope_population_; } +#if DCHECK_IS_ON() + String ClassTypeToString() const; +#endif + void TraceAfterDispatch(blink::Visitor* visitor) const {} void Trace(Visitor*) const;
diff --git a/third_party/blink/renderer/core/events/pointer_event.cc b/third_party/blink/renderer/core/events/pointer_event.cc index faab97b..66d194da 100644 --- a/third_party/blink/renderer/core/events/pointer_event.cc +++ b/third_party/blink/renderer/core/events/pointer_event.cc
@@ -89,6 +89,9 @@ PointerEventUtil::TransformToAzimuthInValidRange(azimuth_angle_), PointerEventUtil::TransformToAltitudeInValidRange(altitude_angle_)); } + if (initializer->hasDeviceId()) { + device_id_ = initializer->deviceId(); + } } bool PointerEvent::IsMouseEvent() const {
diff --git a/third_party/blink/renderer/core/events/pointer_event.h b/third_party/blink/renderer/core/events/pointer_event.h index fa8c8dc2..2c5a82cd 100644 --- a/third_party/blink/renderer/core/events/pointer_event.h +++ b/third_party/blink/renderer/core/events/pointer_event.h
@@ -103,6 +103,8 @@ Document* GetDocument() const; + int32_t deviceId() const { return device_id_; } + void Trace(Visitor*) const override; private: @@ -127,6 +129,8 @@ HeapVector<Member<PointerEvent>> coalesced_events_; HeapVector<Member<PointerEvent>> predicted_events_; + + int32_t device_id_; }; template <>
diff --git a/third_party/blink/renderer/core/events/pointer_event.idl b/third_party/blink/renderer/core/events/pointer_event.idl index 640a313..4b1a432 100644 --- a/third_party/blink/renderer/core/events/pointer_event.idl +++ b/third_party/blink/renderer/core/events/pointer_event.idl
@@ -20,6 +20,7 @@ [MeasureAs=PointerEventAttributeCount] readonly attribute long twist; [MeasureAs=PointerEventAttributeCount] readonly attribute DOMString pointerType; [MeasureAs=PointerEventAttributeCount] readonly attribute boolean isPrimary; + [MeasureAs=PointerEventAttributeCount, RuntimeEnabled=PointerEventDeviceId] readonly attribute long deviceId; // https://w3c.github.io/pointerevents/extension.html#extensions-to-the-pointerevent-interface sequence<PointerEvent> getCoalescedEvents();
diff --git a/third_party/blink/renderer/core/events/pointer_event_factory.cc b/third_party/blink/renderer/core/events/pointer_event_factory.cc index 6177db77..3d0315a9 100644 --- a/third_party/blink/renderer/core/events/pointer_event_factory.cc +++ b/third_party/blink/renderer/core/events/pointer_event_factory.cc
@@ -197,6 +197,10 @@ last_global_position = event.PositionInScreen(); + if (pointer_event_init->hasDeviceId()) { + new_event_init->setDeviceId(pointer_event_init->deviceId()); + } + PointerEvent* pointer_event = PointerEvent::Create(type, new_event_init, event.TimeStamp()); // Set the trusted flag for these events at the creation time as oppose to @@ -352,6 +356,7 @@ SetLastPosition(pointer_event_init->pointerId(), web_pointer_event.PositionInScreen(), event_type); + return PointerEvent::Create(type, pointer_event_init, web_pointer_event.TimeStamp()); } @@ -388,7 +393,8 @@ PointerEvent* PointerEventFactory::CreatePointerCancelEvent( const int pointer_id, - base::TimeTicks platfrom_time_stamp) { + base::TimeTicks platfrom_time_stamp, + const int32_t device_id) { DCHECK(pointer_id_mapping_.Contains(pointer_id)); pointer_id_mapping_.Set( pointer_id, @@ -404,6 +410,8 @@ SetEventSpecificFields(pointer_event_init, event_type_names::kPointercancel); + pointer_event_init->setDeviceId(device_id); + return PointerEvent::Create(event_type_names::kPointercancel, pointer_event_init, platfrom_time_stamp); } @@ -432,6 +440,7 @@ pointer_event->tangentialPressure()); pointer_event_init->setTwist(pointer_event->twist()); pointer_event_init->setView(pointer_event->view()); + pointer_event_init->setDeviceId(pointer_event->deviceId()); SetEventSpecificFields(pointer_event_init, type);
diff --git a/third_party/blink/renderer/core/events/pointer_event_factory.h b/third_party/blink/renderer/core/events/pointer_event_factory.h index 361eb71e..323fe7d 100644 --- a/third_party/blink/renderer/core/events/pointer_event_factory.h +++ b/third_party/blink/renderer/core/events/pointer_event_factory.h
@@ -5,6 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EVENTS_POINTER_EVENT_FACTORY_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_EVENTS_POINTER_EVENT_FACTORY_H_ +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/blink/public/common/input/web_pointer_event.h" #include "third_party/blink/public/common/input/web_pointer_properties.h" #include "third_party/blink/renderer/core/core_export.h" @@ -42,7 +43,8 @@ LocalDOMWindow* view); PointerEvent* CreatePointerCancelEvent(const PointerId pointer_id, - base::TimeTicks platfrom_time_stamp); + base::TimeTicks platfrom_time_stamp, + const int32_t device_id); // For creating raw update events in chorded button case. PointerEvent* CreatePointerRawUpdateEvent(PointerEvent*);
diff --git a/third_party/blink/renderer/core/events/pointer_event_factory_test.cc b/third_party/blink/renderer/core/events/pointer_event_factory_test.cc index 2e99a62..9634da8 100644 --- a/third_party/blink/renderer/core/events/pointer_event_factory_test.cc +++ b/third_party/blink/renderer/core/events/pointer_event_factory_test.cc
@@ -129,7 +129,8 @@ int unique_id, bool is_primary) { PointerEvent* pointer_event = pointer_event_factory_.CreatePointerCancelEvent( - unique_id, WebInputEvent::GetStaticTimeStampForTests()); + unique_id, WebInputEvent::GetStaticTimeStampForTests(), + /* deviceId */ -1); EXPECT_EQ("pointercancel", pointer_event->type()); EXPECT_EQ(unique_id, pointer_event->pointerId()); EXPECT_EQ(is_primary, pointer_event->isPrimary());
diff --git a/third_party/blink/renderer/core/events/pointer_event_init.idl b/third_party/blink/renderer/core/events/pointer_event_init.idl index 2a76a4b..9e0d4a9 100644 --- a/third_party/blink/renderer/core/events/pointer_event_init.idl +++ b/third_party/blink/renderer/core/events/pointer_event_init.idl
@@ -17,6 +17,7 @@ long twist = 0; DOMString pointerType = ""; boolean isPrimary = false; + long deviceId = -1; // https://w3c.github.io/pointerevents/extension.html#extensions-to-the-pointerevent-interface sequence<PointerEvent> coalescedEvents = [];
diff --git a/third_party/blink/renderer/core/input/pointer_event_manager.cc b/third_party/blink/renderer/core/input/pointer_event_manager.cc index 51c6e73..f05d797 100644 --- a/third_party/blink/renderer/core/input/pointer_event_manager.cc +++ b/third_party/blink/renderer/core/input/pointer_event_manager.cc
@@ -318,7 +318,8 @@ WebPointerProperties::PointerType::kMouse) { canceled_pointer_events.push_back( pointer_event_factory_.CreatePointerCancelEvent( - PointerEventFactory::kMouseId, web_pointer_event.TimeStamp())); + PointerEventFactory::kMouseId, web_pointer_event.TimeStamp(), + web_pointer_event.device_id)); } else { // TODO(nzolghadr): Maybe canceling all the non-hovering pointers is not // the best strategy here. See the github issue for more details: @@ -332,7 +333,8 @@ for (PointerId pointer_id : non_hovering_pointer_ids) { canceled_pointer_events.push_back( pointer_event_factory_.CreatePointerCancelEvent( - pointer_id, web_pointer_event.TimeStamp())); + pointer_id, web_pointer_event.TimeStamp(), + web_pointer_event.device_id)); } non_hovering_pointers_canceled_ = true; @@ -691,7 +693,8 @@ SendTouchPointerEvent( pointer_event_target.target_element, pointer_event_factory_.CreatePointerCancelEvent( - core_pointer_event->pointerId(), event.TimeStamp()), + core_pointer_event->pointerId(), event.TimeStamp(), + core_pointer_event->deviceId()), event.hovering); }
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h index 67bbe388..407a6aba 100644 --- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h +++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h
@@ -11,18 +11,32 @@ namespace blink { struct NGFlexBreakTokenData final : NGBlockBreakTokenData { + // NGFlexBreakBeforeRow is used to maintain the state of break before rows + // during flex fragmentation. kNotBreakBeforeRow implies that we are either + // fragmenting a column-based flex container, or the current break token does + // not represent a break before a row. If kAtStartOfBreakBeforeRow is set, + // then the current break token represents a break before a row, and it is the + // first time we broke before the given row. If kPastStartOfBreakBeforeRow is + // set, then the current break token similarly represents a break before a + // row, but it is not the first time we've broken before the given row. + enum NGFlexBreakBeforeRow { + kNotBreakBeforeRow, + kAtStartOfBreakBeforeRow, + kPastStartOfBreakBeforeRow + }; + NGFlexBreakTokenData(const NGBlockBreakTokenData* break_token_data, const HeapVector<NGFlexLine>& flex_lines, const Vector<EBreakBetween>& row_break_between, const HeapVector<Member<LayoutBox>>& oof_children, LayoutUnit intrinsic_block_size, - bool broke_before_row) + NGFlexBreakBeforeRow break_before_row) : NGBlockBreakTokenData(kFlexBreakTokenData, break_token_data), flex_lines(flex_lines), row_break_between(row_break_between), oof_children(oof_children), intrinsic_block_size(intrinsic_block_size), - broke_before_row(broke_before_row) {} + break_before_row(break_before_row) {} void Trace(Visitor* visitor) const override { visitor->Trace(flex_lines); @@ -34,10 +48,17 @@ Vector<EBreakBetween> row_break_between; HeapVector<Member<LayoutBox>> oof_children; LayoutUnit intrinsic_block_size; - // |broke_before_row| is only used in the case of row flex containers. If this - // is true, that means that the next row to be processed had broken before, - // as represented by a break before its first child. - bool broke_before_row = false; + // `break_before_row` is only used in the case of row flex containers. If this + // is set to anything other than kNotBreakBeforeRow, that means that the next + // row to be processed has broken before, as represented by a break before its + // first child. + // + // We do not clamp row gaps, so we can have more than one break before a row. + // There are certain adjustments we only want to make the first time a row + // breaks before. Thus, we will also track if the current break before is the + // first, or if we are past the first break before row (as distinguished by + // the kAtStartOfBreakBeforeRow and kPastStartOfBreakBeforeRow values). + NGFlexBreakBeforeRow break_before_row = kNotBreakBeforeRow; }; template <>
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc index 25c1b80..98a13300 100644 --- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
@@ -13,7 +13,6 @@ #include "third_party/blink/renderer/core/layout/layout_button.h" #include "third_party/blink/renderer/core/layout/layout_flexible_box.h" #include "third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.h" -#include "third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h" #include "third_party/blink/renderer/core/layout/ng/flex/ng_flex_child_iterator.h" #include "third_party/blink/renderer/core/layout/ng/flex/ng_flex_data.h" #include "third_party/blink/renderer/core/layout/ng/flex/ng_flex_item_iterator.h" @@ -1097,7 +1096,8 @@ Vector<EBreakBetween> row_break_between_outputs; HeapVector<NGFlexLine> flex_line_outputs; HeapVector<Member<LayoutBox>> oof_children; - bool broke_before_row = false; + NGFlexBreakTokenData::NGFlexBreakBeforeRow break_before_row = + NGFlexBreakTokenData::kNotBreakBeforeRow; ClearCollectionScope<HeapVector<NGFlexLine>> scope(&flex_line_outputs); bool use_empty_line_block_size; @@ -1107,7 +1107,7 @@ total_intrinsic_block_size_ = flex_data->intrinsic_block_size; flex_line_outputs = flex_data->flex_lines; row_break_between_outputs = flex_data->row_break_between; - broke_before_row = flex_data->broke_before_row; + break_before_row = flex_data->break_before_row; oof_children = flex_data->oof_children; use_empty_line_block_size = @@ -1148,7 +1148,7 @@ NGLayoutResult::EStatus status = GiveItemsFinalPositionAndSizeForFragmentation( - &flex_line_outputs, &row_break_between_outputs, &broke_before_row); + &flex_line_outputs, &row_break_between_outputs, &break_before_row); if (status != NGLayoutResult::kSuccess) return container_builder_.Abort(status); @@ -1211,7 +1211,7 @@ MakeGarbageCollected<NGFlexBreakTokenData>( container_builder_.GetBreakTokenData(), flex_line_outputs, row_break_between_outputs, oof_children, - total_intrinsic_block_size_, broke_before_row)); + total_intrinsic_block_size_, break_before_row)); } #if DCHECK_IS_ON() @@ -1546,11 +1546,11 @@ NGFlexLayoutAlgorithm::GiveItemsFinalPositionAndSizeForFragmentation( HeapVector<NGFlexLine>* flex_line_outputs, Vector<EBreakBetween>* row_break_between_outputs, - bool* broke_before_row) { + NGFlexBreakTokenData::NGFlexBreakBeforeRow* break_before_row) { DCHECK(InvolvedInBlockFragmentation(container_builder_)); DCHECK(flex_line_outputs); DCHECK(row_break_between_outputs); - DCHECK(broke_before_row); + DCHECK(break_before_row); NGFlexItemIterator item_iterator(*flex_line_outputs, BreakToken(), is_column_); @@ -1572,9 +1572,11 @@ previously_consumed_block_size = BreakToken()->ConsumedBlockSize(); BaselineAccumulator baseline_accumulator(Style()); - for (auto entry = item_iterator.NextItem(*broke_before_row); + bool broke_before_row = + *break_before_row != NGFlexBreakTokenData::kNotBreakBeforeRow; + for (auto entry = item_iterator.NextItem(broke_before_row); NGFlexItem* flex_item = entry.flex_item; - entry = item_iterator.NextItem(*broke_before_row)) { + entry = item_iterator.NextItem(broke_before_row)) { wtf_size_t flex_item_idx = entry.flex_item_idx; wtf_size_t flex_line_idx = entry.flex_line_idx; NGFlexLine& line_output = (*flex_line_outputs)[flex_line_idx]; @@ -1626,11 +1628,56 @@ // the item or row expanded by. This allows for things like margins // and alignment offsets to not get sliced by a forced break. line_output.item_offset_adjustment += previously_consumed_block_size; - } else if (!is_column_ && flex_item_idx == 0 && *broke_before_row) { - LayoutUnit total_row_block_offset = - row_block_offset + line_output.item_offset_adjustment; - line_output.item_offset_adjustment += - previously_consumed_block_size - total_row_block_offset; + } else if (!is_column_ && flex_item_idx == 0 && broke_before_row) { + // If this is the first time we are handling a break before a row, + // adjust the offset of items in the row to accommodate the break. The + // following cases need to be considered: + // + // 1. If we are not the first line in the container, and the previous + // sibling row overflowed the fragmentainer in the block axis, flex + // items in the current row should be adjusted upward in the block + // direction to account for the overflowed content. + // + // 2. Otherwise, the current row gap should be decreased by the amount + // of extra space in the previous fragmentainer remaining after the + // block-end of the previous row. The reason being that we should not + // clamp row gaps between breaks, similarly to how flex item margins are + // handled during fragmentation. + // + // 3. If the entire row gap was accounted for in the previous + // fragmentainer, the block-offsets of the flex items in the current row + // will need to be adjusted downward in the block direction to + // accommodate the extra space consumed by the container. + if (*break_before_row == + NGFlexBreakTokenData::kAtStartOfBreakBeforeRow) { + // Calculate the amount of space remaining in the previous + // fragmentainer after the block-end of the previous flex row, if any. + LayoutUnit previous_row_end = + is_first_line + ? LayoutUnit() + : (*flex_line_outputs)[flex_line_idx - 1].LineCrossEnd(); + LayoutUnit fragmentainer_space_remaining = + (previously_consumed_block_size - previous_row_end) + .ClampNegativeToZero(); + + // If there was any remaining space after the previous flex line, + // determine how much of the row gap was consumed in the previous + // fragmentainer, if any. + LayoutUnit consumed_row_gap; + if (fragmentainer_space_remaining) { + LayoutUnit total_row_block_offset = + row_block_offset + line_output.item_offset_adjustment; + LayoutUnit row_gap = total_row_block_offset - previous_row_end; + DCHECK_GE(row_gap, LayoutUnit()); + consumed_row_gap = std::min(row_gap, fragmentainer_space_remaining); + } + + // Adjust the item offsets to account for any overflow or consumed row + // gap in the previous fragmentainer. + LayoutUnit row_adjustment = previously_consumed_block_size - + previous_row_end - consumed_row_gap; + line_output.item_offset_adjustment += row_adjustment; + } } else { LayoutUnit total_item_block_offset = offset.block_offset + line_output.item_offset_adjustment; @@ -1666,8 +1713,9 @@ container_builder_.AddBreakBeforeChild(flex_item->ng_input_node, kBreakAppealPerfect, /* is_forced_break */ false); - if (early_break_->Type() == NGEarlyBreak::kLine) - *broke_before_row = true; + if (early_break_->Type() == NGEarlyBreak::kLine) { + *break_before_row = NGFlexBreakTokenData::kAtStartOfBreakBeforeRow; + } ConsumeRemainingFragmentainerSpace(previously_consumed_block_size, &line_output); // For column flex containers, continue to the next column. For rows, @@ -1743,29 +1791,36 @@ if (!is_column_) { has_container_separation = offset.block_offset > row_block_offset && - (!item_break_token || (*broke_before_row && flex_item_idx == 0 && + (!item_break_token || (broke_before_row && flex_item_idx == 0 && item_break_token->IsBreakBefore())); // Don't attempt to break before a row if the fist item is resuming // layout. In which case, the row should be resuming layout, as well. if (flex_item_idx == 0 && (!item_break_token || - (item_break_token->IsBreakBefore() && *broke_before_row))) { + (item_break_token->IsBreakBefore() && broke_before_row))) { // Rows have no layout result, so if the row breaks before, we // will break before the first item in the row instead. bool row_container_separation = has_processed_first_line_; - bool is_first_for_row = !item_break_token || *broke_before_row; + bool is_first_for_row = !item_break_token || broke_before_row; NGBreakStatus row_break_status = BreakBeforeRowIfNeeded( - line_output, (*row_break_between_outputs)[flex_line_idx], - flex_line_idx, flex_item->ng_input_node, row_container_separation, + line_output, row_block_offset, + (*row_break_between_outputs)[flex_line_idx], flex_line_idx, + flex_item->ng_input_node, row_container_separation, is_first_for_row); if (row_break_status == NGBreakStatus::kBrokeBefore) { ConsumeRemainingFragmentainerSpace(previously_consumed_block_size, &line_output); - *broke_before_row = true; + if (broke_before_row) { + *break_before_row = + NGFlexBreakTokenData::kPastStartOfBreakBeforeRow; + } else { + *break_before_row = + NGFlexBreakTokenData::kAtStartOfBreakBeforeRow; + } DCHECK_EQ(status, NGLayoutResult::kSuccess); break; } - *broke_before_row = false; + *break_before_row = NGFlexBreakTokenData::kNotBreakBeforeRow; if (row_break_status == NGBreakStatus::kNeedsEarlierBreak) { status = NGLayoutResult::kNeedsEarlierBreak; break; @@ -1955,7 +2010,7 @@ } if (!container_builder_.HasInflowChildBreakInside() && - !item_iterator.NextItem(*broke_before_row).flex_item) { + !item_iterator.NextItem(broke_before_row).flex_item) { container_builder_.SetHasSeenAllChildren(); } @@ -2538,6 +2593,7 @@ NGBreakStatus NGFlexLayoutAlgorithm::BreakBeforeRowIfNeeded( const NGFlexLine& row, + LayoutUnit row_block_offset, EBreakBetween row_break_between, wtf_size_t row_index, NGLayoutInputNode child, @@ -2547,9 +2603,7 @@ DCHECK(InvolvedInBlockFragmentation(container_builder_)); LayoutUnit fragmentainer_block_offset = - ConstraintSpace().FragmentainerOffset() + row.cross_axis_offset; - if (BreakToken()) - fragmentainer_block_offset -= BreakToken()->ConsumedBlockSize(); + ConstraintSpace().FragmentainerOffset() + row_block_offset; if (has_container_separation) { if (IsForcedBreakValue(ConstraintSpace(), row_break_between)) {
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h index aa940085..8d321f3 100644 --- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h +++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
@@ -9,6 +9,7 @@ #include "third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h" #include "third_party/blink/renderer/core/layout/flexible_box_algorithm.h" +#include "third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h" #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h" namespace blink { @@ -96,7 +97,7 @@ NGLayoutResult::EStatus GiveItemsFinalPositionAndSizeForFragmentation( HeapVector<NGFlexLine>* flex_line_outputs, Vector<EBreakBetween>* row_break_between_outputs, - bool* broke_before_row); + NGFlexBreakTokenData::NGFlexBreakBeforeRow* break_before_row); NGLayoutResult::EStatus PropagateFlexItemInfo(FlexItem* flex_item, wtf_size_t flex_line_idx, LogicalOffset offset, @@ -137,10 +138,12 @@ // a layout result, so when breaking before a row, we will insert a // fragmentainer break before the first child in a row. |child| should be // those associated with the first child in the row. |row|, - // |row_break_between|, |row_index|, |has_container_separation| and - // |is_first_for_row| are specific to the row itself. See + // |row_block_offset|, |row_break_between|, |row_index|, + // |has_container_separation| and |is_first_for_row| are specific to the row + // itself. See // |::blink::BreakBeforeChildIfNeeded()| for more documentation. NGBreakStatus BreakBeforeRowIfNeeded(const NGFlexLine& row, + LayoutUnit row_block_offset, EBreakBetween row_break_between, wtf_size_t row_index, NGLayoutInputNode child,
diff --git a/third_party/blink/renderer/core/svg/properties/svg_animated_property.h b/third_party/blink/renderer/core/svg/properties/svg_animated_property.h index a293ac7..80850db 100644 --- a/third_party/blink/renderer/core/svg/properties/svg_animated_property.h +++ b/third_party/blink/renderer/core/svg/properties/svg_animated_property.h
@@ -56,7 +56,7 @@ virtual void SetAnimatedValue(SVGPropertyBase*) = 0; - virtual SVGParsingError AttributeChanged(const String&) = 0; + virtual SVGParsingError AttributeChanged(const AtomicString&) = 0; virtual bool NeedsSynchronizeAttribute() const; virtual void SynchronizeAttribute(); @@ -124,7 +124,7 @@ bool IsAnimating() const override { return current_value_ != base_value_; } - SVGParsingError AttributeChanged(const String& value) override { + SVGParsingError AttributeChanged(const AtomicString& value) override { static_assert(Property::kInitialValueBits <= kInitialValueStorageBits, "enough bits for the initial value");
diff --git a/third_party/blink/renderer/core/svg/svg_animated_length.cc b/third_party/blink/renderer/core/svg/svg_animated_length.cc index 5796343..a7c708ee 100644 --- a/third_party/blink/renderer/core/svg/svg_animated_length.cc +++ b/third_party/blink/renderer/core/svg/svg_animated_length.cc
@@ -34,7 +34,7 @@ namespace blink { -SVGParsingError SVGAnimatedLength::AttributeChanged(const String& value) { +SVGParsingError SVGAnimatedLength::AttributeChanged(const AtomicString& value) { SVGParsingError parse_status = SVGAnimatedProperty<SVGLength>::AttributeChanged(value);
diff --git a/third_party/blink/renderer/core/svg/svg_animated_length.h b/third_party/blink/renderer/core/svg/svg_animated_length.h index 254be2e..ad2b93d 100644 --- a/third_party/blink/renderer/core/svg/svg_animated_length.h +++ b/third_party/blink/renderer/core/svg/svg_animated_length.h
@@ -55,7 +55,7 @@ css_property_id, static_cast<unsigned>(initial_value)) {} - SVGParsingError AttributeChanged(const String&) override; + SVGParsingError AttributeChanged(const AtomicString&) override; // TODO(fs): This doesn't handle calc expressions. For that, we'd probably // need to rewrap the CSSMathExpressionNode with a kValueRangeNonNegative
diff --git a/third_party/blink/renderer/core/svg/svg_fe_convolve_matrix_element.cc b/third_party/blink/renderer/core/svg/svg_fe_convolve_matrix_element.cc index b87d920..cf23f08 100644 --- a/third_party/blink/renderer/core/svg/svg_fe_convolve_matrix_element.cc +++ b/third_party/blink/renderer/core/svg/svg_fe_convolve_matrix_element.cc
@@ -55,7 +55,7 @@ svg_names::kOrderAttr, 3) {} - SVGParsingError AttributeChanged(const String&) override; + SVGParsingError AttributeChanged(const AtomicString&) override; protected: static SVGParsingError CheckValue(SVGParsingError parse_status, int value) { @@ -69,7 +69,7 @@ } }; -SVGParsingError SVGAnimatedOrder::AttributeChanged(const String& value) { +SVGParsingError SVGAnimatedOrder::AttributeChanged(const AtomicString& value) { SVGParsingError parse_status = SVGAnimatedIntegerOptionalInteger::AttributeChanged(value); // Check for semantic errors.
diff --git a/third_party/blink/renderer/core/svg/svg_fit_to_view_box.cc b/third_party/blink/renderer/core/svg/svg_fit_to_view_box.cc index a032a2e..31a65f2 100644 --- a/third_party/blink/renderer/core/svg/svg_fit_to_view_box.cc +++ b/third_party/blink/renderer/core/svg/svg_fit_to_view_box.cc
@@ -39,10 +39,11 @@ SVGAnimatedViewBoxRect(SVGElement* context_element) : SVGAnimatedRect(context_element, svg_names::kViewBoxAttr) {} - SVGParsingError AttributeChanged(const String&) override; + SVGParsingError AttributeChanged(const AtomicString&) override; }; -SVGParsingError SVGAnimatedViewBoxRect::AttributeChanged(const String& value) { +SVGParsingError SVGAnimatedViewBoxRect::AttributeChanged( + const AtomicString& value) { SVGParsingError parse_status = SVGAnimatedRect::AttributeChanged(value); if (parse_status == SVGParseStatus::kNoError &&
diff --git a/third_party/blink/renderer/core/svg/svg_geometry_element.cc b/third_party/blink/renderer/core/svg/svg_geometry_element.cc index efad06b..2bf026b1 100644 --- a/third_party/blink/renderer/core/svg/svg_geometry_element.cc +++ b/third_party/blink/renderer/core/svg/svg_geometry_element.cc
@@ -49,7 +49,7 @@ svg_names::kPathLengthAttr, MakeGarbageCollected<SVGNumber>()) {} - SVGParsingError AttributeChanged(const String& value) override { + SVGParsingError AttributeChanged(const AtomicString& value) override { SVGParsingError parse_status = SVGAnimatedNumber::AttributeChanged(value); if (parse_status == SVGParseStatus::kNoError && BaseValue()->Value() < 0) parse_status = SVGParseStatus::kNegativeValue;
diff --git a/third_party/blink/renderer/core/svg/svg_path.cc b/third_party/blink/renderer/core/svg/svg_path.cc index ef6f4518..8f4d99fc 100644 --- a/third_party/blink/renderer/core/svg/svg_path.cc +++ b/third_party/blink/renderer/core/svg/svg_path.cc
@@ -33,7 +33,10 @@ #include "third_party/blink/renderer/core/svg/svg_path_byte_stream_source.h" #include "third_party/blink/renderer/core/svg/svg_path_utilities.h" #include "third_party/blink/renderer/platform/graphics/path.h" +#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" +#include "third_party/blink/renderer/platform/heap/persistent.h" +#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h" namespace blink { @@ -78,6 +81,19 @@ return AddPathByteStreams(*from_stream, by_stream, repeat_count); } +// Entries in this cache are kept with a WeakMember. They'll be removed from +// the map if nothing is referencing them - as such this doesn't grow +// unbounded in size. +// NOTE: This cache exists for a relatively minor gain in MotionMark. If at any +// point it becomes burdensome please re-evaluate its existence. +using CSSPathValueCache = + HeapHashMap<AtomicString, WeakMember<const cssvalue::CSSPathValue>>; +static CSSPathValueCache& GetCSSPathValueCache() { + DEFINE_STATIC_LOCAL(Persistent<CSSPathValueCache>, cache, + (MakeGarbageCollected<CSSPathValueCache>())); + return *cache; +} + } // namespace SVGPath::SVGPath() : path_value_(CSSPathValue::EmptyPathValue()) {} @@ -94,12 +110,23 @@ return MakeGarbageCollected<SVGPath>(*path_value_); } -SVGParsingError SVGPath::SetValueAsString(const String& string) { +SVGParsingError SVGPath::SetValueAsString(const AtomicString& string) { + CSSPathValueCache& cache = GetCSSPathValueCache(); + auto it = cache.find(string); + if (it != cache.end()) { + path_value_ = it->value; + return SVGParseStatus::kNoError; + } + std::unique_ptr<SVGPathByteStream> byte_stream = std::make_unique<SVGPathByteStream>(); SVGParsingError parse_status = BuildByteStreamFromString(string, *byte_stream); path_value_ = MakeGarbageCollected<CSSPathValue>(std::move(byte_stream)); + + if (parse_status == SVGParseStatus::kNoError) { + cache.insert(string, path_value_); + } return parse_status; }
diff --git a/third_party/blink/renderer/core/svg/svg_path.h b/third_party/blink/renderer/core/svg/svg_path.h index 6279e75bd..86712f4d 100644 --- a/third_party/blink/renderer/core/svg/svg_path.h +++ b/third_party/blink/renderer/core/svg/svg_path.h
@@ -57,7 +57,7 @@ SVGPath* Clone() const; SVGPropertyBase* CloneForAnimation(const String&) const override; String ValueAsString() const override; - SVGParsingError SetValueAsString(const String&); + SVGParsingError SetValueAsString(const AtomicString&); void Add(const SVGPropertyBase*, const SVGElement*) override; void CalculateAnimatedValue(
diff --git a/third_party/blink/renderer/core/svg/svg_static_string_list.cc b/third_party/blink/renderer/core/svg/svg_static_string_list.cc index 8d9cf14..efa8657c 100644 --- a/third_party/blink/renderer/core/svg/svg_static_string_list.cc +++ b/third_party/blink/renderer/core/svg/svg_static_string_list.cc
@@ -71,7 +71,8 @@ return tear_off_.Get(); } -SVGParsingError SVGStaticStringList::AttributeChanged(const String& value) { +SVGParsingError SVGStaticStringList::AttributeChanged( + const AtomicString& value) { ClearBaseValueNeedsSynchronization(); return value_->SetValueAsString(value); }
diff --git a/third_party/blink/renderer/core/svg/svg_static_string_list.h b/third_party/blink/renderer/core/svg/svg_static_string_list.h index 97233b0..05f8013 100644 --- a/third_party/blink/renderer/core/svg/svg_static_string_list.h +++ b/third_party/blink/renderer/core/svg/svg_static_string_list.h
@@ -63,7 +63,7 @@ bool IsAnimating() const override; void SetAnimatedValue(SVGPropertyBase*) override; - SVGParsingError AttributeChanged(const String&) override; + SVGParsingError AttributeChanged(const AtomicString&) override; SVGStringListBase* Value() { return value_.Get(); } SVGStringListTearOff* TearOff();
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc index 09d5e2d..cf47728 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -1303,6 +1303,10 @@ if (GetNode()->HasTagName(html_names::kAddressTag)) return ax::mojom::blink::Role::kGroup; + if (GetNode()->HasTagName(html_names::kHgroupTag)) { + return ax::mojom::blink::Role::kGroup; + } + if (IsA<HTMLDialogElement>(*GetNode())) return ax::mojom::blink::Role::kDialog;
diff --git a/third_party/blink/renderer/modules/imagecapture/image_capture.cc b/third_party/blink/renderer/modules/imagecapture/image_capture.cc index 7cee8c4..abf7061 100644 --- a/third_party/blink/renderer/modules/imagecapture/image_capture.cc +++ b/third_party/blink/renderer/modules/imagecapture/image_capture.cc
@@ -235,15 +235,34 @@ } void CopyConstraintSet(const MediaTrackConstraintSet* source, - MediaTrackConstraintSet* destination, - CopyPanTiltZoom copy_pan_tilt_zoom) { + MediaTrackConstraintSet* destination) { // Merge any present |source| members into |destination|. - CopyCommonMembers(source, destination, copy_pan_tilt_zoom); + // Constraints come always from JavaScript (unlike capabilities and settings) + // so pan, tilt and zoom constraints are never privileged information and can + // always be copied. + CopyCommonMembers(source, destination, CopyPanTiltZoom(true)); if (source->hasPointsOfInterest()) { destination->setPointsOfInterest(source->pointsOfInterest()); } } +void CopyConstraints(const MediaTrackConstraints* source, + MediaTrackConstraints* destination) { + HeapVector<Member<MediaTrackConstraintSet>> destination_constraint_sets; + for (const auto* source_constraint_set : AllSupportedConstraintSets(source)) { + if (source_constraint_set == source) { + CopyConstraintSet(source_constraint_set, destination); + } else { + auto* destination_constraint_set = MediaTrackConstraintSet::Create(); + CopyConstraintSet(source_constraint_set, destination_constraint_set); + destination_constraint_sets.push_back(destination_constraint_set); + } + } + if (!destination_constraint_sets.empty()) { + destination->setAdvanced(std::move(destination_constraint_sets)); + } +} + void CopySettings(const MediaTrackSettings* source, MediaTrackSettings* destination, CopyPanTiltZoom copy_pan_tilt_zoom) { @@ -502,6 +521,10 @@ return track.readyState() != "live" || !track.enabled() || track.muted(); } +BackgroundBlurMode ParseBackgroundBlur(bool blink_mode) { + return blink_mode ? BackgroundBlurMode::BLUR : BackgroundBlurMode::OFF; +} + MeteringMode ParseMeteringMode(const String& blink_mode) { if (blink_mode == "manual") return MeteringMode::MANUAL; @@ -605,12 +628,18 @@ return true; } // There is a previous exact constraint represented by |effective_setting|. - // |exact_constraint| must be equal to it. + // |exact_constraint| must be effectively equal to it (coordinates clamped to + // [0, 1] must be equal). return effective_setting->size() == exact_constraint.size() && std::equal(effective_setting->begin(), effective_setting->end(), exact_constraint.begin(), [](const Point2D* a, const Point2D* b) { - return a->x() == b->x() && a->y() == b->y(); + return (a->x() <= 0.0 ? b->x() <= 0.0 + : a->x() >= 1.0 ? b->x() >= 1.0 + : b->x() == a->x()) && + (a->y() <= 0.0 ? b->y() <= 0.0 + : a->y() >= 1.0 ? b->y() >= 1.0 + : b->y() == a->y()); }); } @@ -844,7 +873,7 @@ constraint_set_type); } -// For `ConstrainBoolean` constraints and `MediaSettingsRange` effective +// For `ConstrainBoolean` constraints and `sequence<boolean>` effective // capabilities such as torch and backgroundBlur. bool CheckValueConstraint( const Vector<bool>& effective_capability, @@ -923,6 +952,332 @@ } } +// Apply exact value constraints to photo settings and return new effective +// capabilities. +// +// Roughly the SelectSettings algorithm steps 3 and 5. +// https://www.w3.org/TR/mediacapture-streams/#dfn-selectsettings +// +// TODO(crbug.com/708723): Integrate image capture constraints processing with +// the main implementation and remove these support functions. + +// For exact `boolean` constraints and `sequence<boolean>` effective +// capabilities such as torch and backgroundBlur. +Vector<bool> ApplyExactValueConstraint(bool* has_setting_ptr, + bool* setting_ptr, + const Vector<bool>& effective_capability, + bool exact_constraint) { + // Update the setting. + *has_setting_ptr = true; + *setting_ptr = exact_constraint; + // Update the effective capability. + return {exact_constraint}; +} + +// For exact `double` constraints and `MediaSettingsRange` effective +// capabilities such as exposureCompensation, ..., zoom. +MediaSettingsRange* ApplyExactValueConstraint( + bool* has_setting_ptr, + double* setting_ptr, + const MediaSettingsRange* effective_capability, + double exact_constraint) { + // Update the setting. + *has_setting_ptr = true; + *setting_ptr = exact_constraint; + // Update the effective capability. + auto* new_effective_capability = MediaSettingsRange::Create(); + new_effective_capability->setMax(exact_constraint); + new_effective_capability->setMin(exact_constraint); + return new_effective_capability; +} + +// For exact `DOMString` constraints and `sequence<DOMString>` effective +// capabilities such as whiteBalanceMode, exposureMode and focusMode. +Vector<String> ApplyExactValueConstraint( + bool* has_setting_ptr, + MeteringMode* setting_ptr, + const Vector<String>& effective_capability, + const String& exact_constraint) { + // Update the setting. + *has_setting_ptr = true; + *setting_ptr = ParseMeteringMode(exact_constraint); + // Update the effective capability. + return {exact_constraint}; +} + +// Apply ideal value constraints to photo settings and return effective +// capabilities intact (ideal constraints have no effect on effective +// capabilities). +// +// Roughly the SelectSettings algorithm step 3. +// https://www.w3.org/TR/mediacapture-streams/#dfn-selectsettings +// +// TODO(crbug.com/708723): Integrate image capture constraints processing with +// the main implementation and remove these support functions. + +// For ideal `boolean` constraints and `sequence<boolean>` effective +// capabilities such as torch and backgroundBlur. +Vector<bool> ApplyIdealValueConstraint(bool* has_setting_ptr, + bool* setting_ptr, + const Vector<bool>& effective_capability, + bool ideal_constraint) { + // Clamp and update the setting. + *has_setting_ptr = true; + *setting_ptr = base::Contains(effective_capability, ideal_constraint) + ? ideal_constraint + : effective_capability[0]; + // Keep the effective capability intact. + return effective_capability; +} + +// For ideal `double` constraints and `MediaSettingsRange` effective +// capabilities such as exposureCompensation, ..., zoom. +MediaSettingsRange* ApplyIdealValueConstraint( + bool* has_setting_ptr, + double* setting_ptr, + MediaSettingsRange* effective_capability, + absl::optional<double> ideal_constraint, + double current_setting) { + // Clamp and update the setting. + *has_setting_ptr = true; + *setting_ptr = + std::clamp(ideal_constraint ? *ideal_constraint : current_setting, + effective_capability->min(), effective_capability->max()); + // Keep the effective capability intact. + return effective_capability; +} + +// For ideal `DOMString` constraints and `sequence<DOMString>` effective +// capabilities such as whiteBalanceMode, exposureMode and focusMode. +Vector<String> ApplyIdealValueConstraint( + bool* has_setting_ptr, + MeteringMode* setting_ptr, + const Vector<String>& effective_capability, + const String& ideal_constraint, + const String& current_setting) { + // Validate and update the setting. + *has_setting_ptr = true; + *setting_ptr = ParseMeteringMode( + base::Contains(effective_capability, ideal_constraint) ? ideal_constraint + : current_setting); + // Keep the effective capability intact. + return effective_capability; +} + +// Apply value constraints to photo settings and return new effective +// capabilities. +// +// Roughly the SelectSettings algorithm steps 3 and 5. +// https://www.w3.org/TR/mediacapture-streams/#dfn-selectsettings +// +// TODO(crbug.com/708723): Integrate image capture constraints processing with +// the main implementation and remove these support functions. + +// For `ConstrainBoolean` constraints and `sequence<boolean>` effective +// capabilities such as torch and backgroundBlur. +Vector<bool> ApplyValueConstraint( + bool* has_setting_ptr, + bool* setting_ptr, + const Vector<bool>& effective_capability, + const V8UnionBooleanOrConstrainBooleanParameters* constraint, + MediaTrackConstraintSetType constraint_set_type) { + DCHECK(CheckValueConstraint(effective_capability, constraint, + constraint_set_type)); + if (!IsValueConstraint(constraint, constraint_set_type)) { + // Keep the effective capability intact. + return effective_capability; + } + using ContentType = V8UnionBooleanOrConstrainBooleanParameters::ContentType; + switch (constraint->GetContentType()) { + case ContentType::kBoolean: + if (IsBareValueToBeTreatedAsExact(constraint_set_type)) { + return ApplyExactValueConstraint(has_setting_ptr, setting_ptr, + effective_capability, + constraint->GetAsBoolean()); + } + // We classify ideal bare value constraints as value constraints only in + // the basic constraint set in which they have an effect on + // the SelectSettings algorithm. + DCHECK_EQ(constraint_set_type, MediaTrackConstraintSetType::kBasic); + return ApplyIdealValueConstraint(has_setting_ptr, setting_ptr, + effective_capability, + constraint->GetAsBoolean()); + case ContentType::kConstrainBooleanParameters: { + DCHECK_NE(constraint_set_type, + MediaTrackConstraintSetType::kFirstAdvanced); + // TODO(crbug.com/1408091): Add support for ConstrainBooleanParameters. + return effective_capability; + } + } +} + +// For `ConstrainDouble` constraints and `MediaSettingsRange` effective +// capabilities such as exposureCompensation, ..., focusDistance. +MediaSettingsRange* ApplyValueConstraint( + bool* has_setting_ptr, + double* setting_ptr, + const MediaSettingsRange* effective_capability, + const V8UnionConstrainDoubleRangeOrDouble* constraint, + MediaTrackConstraintSetType constraint_set_type, + double current_setting) { + DCHECK(CheckValueConstraint(effective_capability, constraint, + constraint_set_type)); + if (!IsValueConstraint(constraint, constraint_set_type)) { + // Keep the effective capability intact. + return const_cast<MediaSettingsRange*>(effective_capability); + } + using ContentType = V8UnionConstrainDoubleRangeOrDouble::ContentType; + switch (constraint->GetContentType()) { + case ContentType::kDouble: + if (IsBareValueToBeTreatedAsExact(constraint_set_type)) { + return ApplyExactValueConstraint(has_setting_ptr, setting_ptr, + effective_capability, + constraint->GetAsDouble()); + } + // We classify ideal bare value constraints as value constraints only in + // the basic constraint set in which they have an effect on + // the SelectSettings algorithm. + DCHECK_EQ(constraint_set_type, MediaTrackConstraintSetType::kBasic); + return ApplyIdealValueConstraint( + has_setting_ptr, setting_ptr, + const_cast<MediaSettingsRange*>(effective_capability), + constraint->GetAsDouble(), current_setting); + case ContentType::kConstrainDoubleRange: { + DCHECK_NE(constraint_set_type, + MediaTrackConstraintSetType::kFirstAdvanced); + // TODO(crbug.com/1408091): Add support for ConstrainDoubleRange. + return const_cast<MediaSettingsRange*>(effective_capability); + } + } +} + +// For `(boolean or ConstrainDouble)` constraints and `MediaSettingsRange` +// effective capabilities such as pan, tilt and zoom. +MediaSettingsRange* ApplyValueConstraint( + bool* has_setting_ptr, + double* setting_ptr, + const MediaSettingsRange* effective_capability, + const V8UnionBooleanOrConstrainDoubleRangeOrDouble* constraint, + MediaTrackConstraintSetType constraint_set_type, + double current_setting) { + if (!IsValueConstraint(constraint, constraint_set_type)) { + // Keep the effective capability intact. + return const_cast<MediaSettingsRange*>(effective_capability); + } + // We classify boolean constraints for double constrainable properties as + // existence constraints instead of as value constraints. + DCHECK(!constraint->IsBoolean()); + return ApplyValueConstraint( + has_setting_ptr, setting_ptr, effective_capability, + constraint->GetAsV8UnionConstrainDoubleRangeOrDouble(), + constraint_set_type, current_setting); +} + +// For `ConstrainDOMString` constraints and `sequence<DOMString>` effective +// capabilities such as whiteBalanceMode, exposureMode and focusMode. +Vector<String> ApplyValueConstraint( + bool* has_setting_ptr, + MeteringMode* setting_ptr, + const Vector<String>& effective_capability, + const V8UnionConstrainDOMStringParametersOrStringOrStringSequence* + constraint, + MediaTrackConstraintSetType constraint_set_type, + const String& current_setting) { + DCHECK(CheckValueConstraint(effective_capability, constraint, + constraint_set_type)); + if (!IsValueConstraint(constraint, constraint_set_type)) { + // Keep the effective capability intact. + return effective_capability; + } + using ContentType = + V8UnionConstrainDOMStringParametersOrStringOrStringSequence::ContentType; + switch (constraint->GetContentType()) { + case ContentType::kString: + if (IsBareValueToBeTreatedAsExact(constraint_set_type)) { + return ApplyExactValueConstraint(has_setting_ptr, setting_ptr, + effective_capability, + constraint->GetAsString()); + } + // We classify ideal bare value constraints as value constraints only in + // the basic constraint set in which they have an effect on + // the SelectSettings algorithm. + DCHECK_EQ(constraint_set_type, MediaTrackConstraintSetType::kBasic); + return ApplyIdealValueConstraint( + has_setting_ptr, setting_ptr, effective_capability, + constraint->GetAsString(), current_setting); + default: + DCHECK_NE(constraint_set_type, + MediaTrackConstraintSetType::kFirstAdvanced); + // TODO(crbug.com/1408091): Add support for StringSequence and for + // ConstrainDOMStringParameters. + return effective_capability; + } +} + +// For `ConstrainPoint2D` constraints such as `pointsOfInterest`. +// There is no capability for `pointsOfInterest` in `MediaTrackCapabilities` +// to be used as a storage for an effective capability. +// As a substitute, we use `MediaTrackSettings` and its `pointsOfInterest` +// field to convey restrictions placed by previous exact `pointsOfInterest` +// constraints. +void ApplyValueConstraint(bool* has_setting_ptr, + Vector<media::mojom::blink::Point2DPtr>* setting_ptr, + const HeapVector<Member<Point2D>>* effective_setting, + const HeapVector<Member<Point2D>>& constraint) { + // Update the setting. + *has_setting_ptr = true; + setting_ptr->clear(); + for (const auto& point : constraint) { + auto mojo_point = media::mojom::blink::Point2D::New(); + mojo_point->x = std::clamp(point->x(), 0.0, 1.0); + mojo_point->y = std::clamp(point->y(), 0.0, 1.0); + setting_ptr->push_back(std::move(mojo_point)); + } +} + +// For `ConstrainPoint2D` constraints such as `pointsOfInterest`. +// There is no capability for `pointsOfInterest` in `MediaTrackCapabilities` +// to be used as a storage for an effective capability. +// As a substitute, we use `MediaTrackSettings` and its `pointsOfInterest` +// field to convey restrictions placed by previous exact `pointsOfInterest` +// constraints. +absl::optional<HeapVector<Member<Point2D>>> ApplyValueConstraint( + bool* has_setting_ptr, + Vector<media::mojom::blink::Point2DPtr>* setting_ptr, + const HeapVector<Member<Point2D>>* effective_setting, + const V8UnionConstrainPoint2DParametersOrPoint2DSequence* constraint, + MediaTrackConstraintSetType constraint_set_type) { + DCHECK( + CheckValueConstraint(effective_setting, constraint, constraint_set_type)); + if (!IsValueConstraint(constraint, constraint_set_type)) { + // Keep the effective capability intact. + return absl::nullopt; + } + using ContentType = + V8UnionConstrainPoint2DParametersOrPoint2DSequence::ContentType; + switch (constraint->GetContentType()) { + case ContentType::kPoint2DSequence: + if (IsBareValueToBeTreatedAsExact(constraint_set_type)) { + ApplyValueConstraint(has_setting_ptr, setting_ptr, effective_setting, + constraint->GetAsPoint2DSequence()); + return constraint->GetAsPoint2DSequence(); + } + // We classify ideal bare value constraints as value constraints only in + // the basic constraint set in which they have an effect on + // the SelectSettings algorithm. + DCHECK_EQ(constraint_set_type, MediaTrackConstraintSetType::kBasic); + ApplyValueConstraint(has_setting_ptr, setting_ptr, effective_setting, + constraint->GetAsPoint2DSequence()); + return absl::nullopt; + case ContentType::kConstrainPoint2DParameters: { + DCHECK_NE(constraint_set_type, + MediaTrackConstraintSetType::kFirstAdvanced); + // TODO(crbug.com/1408091): Add support for ConstrainPoint2DParameters. + return absl::nullopt; + } + } +} + } // anonymous namespace ImageCapture* ImageCapture::Create(ExecutionContext* context, @@ -1121,7 +1476,7 @@ bool ImageCapture::CheckAndApplyMediaTrackConstraintsToSettings( media::mojom::blink::PhotoSettings* settings, const MediaTrackConstraints* constraints, - ScriptPromiseResolver* resolver) { + ScriptPromiseResolver* resolver) const { if (!IsPageVisible()) { for (const MediaTrackConstraintSet* constraint_set : AllSupportedConstraintSets(constraints)) { @@ -1138,10 +1493,6 @@ } } - MediaTrackConstraintSet* temp_constraint_set = - current_constraint_set_ ? current_constraint_set_.Get() - : MediaTrackConstraintSet::Create(); - // The "effective capability" C of an object O as the possibly proper subset // of the possible values of C (as returned by getCapabilities) taking into // consideration environmental limitations and/or restrictions placed by @@ -1168,183 +1519,17 @@ GetMediaTrackConstraintSetType(constraint_set, constraints); const bool may_reject = MayRejectWithOverconstrainedError(constraint_set_type); - if (!CheckMediaTrackConstraintSet( - effective_capabilities, effective_settings, constraint_set, - constraint_set_type, may_reject ? resolver : nullptr)) { - if (!may_reject) { - continue; - } + if (CheckMediaTrackConstraintSet(effective_capabilities, effective_settings, + constraint_set, constraint_set_type, + may_reject ? resolver : nullptr)) { + ApplyMediaTrackConstraintSetToSettings(&*settings, effective_capabilities, + effective_settings, constraint_set, + constraint_set_type); + } else if (may_reject) { return false; } - - // TODO(crbug.com/1408091): Add support for the basic constraint set and for - // advanced constraint sets beyond the first one and remove check. - DCHECK_EQ(constraint_set, constraints->advanced()[0]); - - // TODO(mcasas): support other Mode types beyond simple string i.e. the - // equivalents of "sequence<DOMString>"" or "ConstrainDOMStringParameters". - settings->has_white_balance_mode = - constraint_set->hasWhiteBalanceMode() && - constraint_set->whiteBalanceMode()->IsString(); - if (settings->has_white_balance_mode) { - const auto white_balance_mode = - constraint_set->whiteBalanceMode()->GetAsString(); - temp_constraint_set->setWhiteBalanceMode( - constraint_set->whiteBalanceMode()); - settings->white_balance_mode = ParseMeteringMode(white_balance_mode); - } - settings->has_exposure_mode = constraint_set->hasExposureMode() && - constraint_set->exposureMode()->IsString(); - if (settings->has_exposure_mode) { - const auto exposure_mode = constraint_set->exposureMode()->GetAsString(); - temp_constraint_set->setExposureMode(constraint_set->exposureMode()); - settings->exposure_mode = ParseMeteringMode(exposure_mode); - } - - settings->has_focus_mode = constraint_set->hasFocusMode() && - constraint_set->focusMode()->IsString(); - if (settings->has_focus_mode) { - const auto focus_mode = constraint_set->focusMode()->GetAsString(); - temp_constraint_set->setFocusMode(constraint_set->focusMode()); - settings->focus_mode = ParseMeteringMode(focus_mode); - } - - // TODO(mcasas): support ConstrainPoint2DParameters. - if (constraint_set->hasPointsOfInterest() && - constraint_set->pointsOfInterest()->IsPoint2DSequence()) { - for (const auto& point : - constraint_set->pointsOfInterest()->GetAsPoint2DSequence()) { - auto mojo_point = media::mojom::blink::Point2D::New(); - mojo_point->x = point->x(); - mojo_point->y = point->y(); - settings->points_of_interest.push_back(std::move(mojo_point)); - } - temp_constraint_set->setPointsOfInterest( - constraint_set->pointsOfInterest()); - } - - // TODO(mcasas): support ConstrainDoubleRange where applicable. - settings->has_exposure_compensation = - constraint_set->hasExposureCompensation() && - constraint_set->exposureCompensation()->IsDouble(); - if (settings->has_exposure_compensation) { - const auto exposure_compensation = - constraint_set->exposureCompensation()->GetAsDouble(); - temp_constraint_set->setExposureCompensation( - constraint_set->exposureCompensation()); - settings->exposure_compensation = exposure_compensation; - } - - settings->has_exposure_time = constraint_set->hasExposureTime() && - constraint_set->exposureTime()->IsDouble(); - if (settings->has_exposure_time) { - const auto exposure_time = constraint_set->exposureTime()->GetAsDouble(); - temp_constraint_set->setExposureTime(constraint_set->exposureTime()); - settings->exposure_time = exposure_time; - } - settings->has_color_temperature = - constraint_set->hasColorTemperature() && - constraint_set->colorTemperature()->IsDouble(); - if (settings->has_color_temperature) { - const auto color_temperature = - constraint_set->colorTemperature()->GetAsDouble(); - temp_constraint_set->setColorTemperature( - constraint_set->colorTemperature()); - settings->color_temperature = color_temperature; - } - settings->has_iso = - constraint_set->hasIso() && constraint_set->iso()->IsDouble(); - if (settings->has_iso) { - const auto iso = constraint_set->iso()->GetAsDouble(); - temp_constraint_set->setIso(constraint_set->iso()); - settings->iso = iso; - } - - settings->has_brightness = constraint_set->hasBrightness() && - constraint_set->brightness()->IsDouble(); - if (settings->has_brightness) { - const auto brightness = constraint_set->brightness()->GetAsDouble(); - temp_constraint_set->setBrightness(constraint_set->brightness()); - settings->brightness = brightness; - } - settings->has_contrast = - constraint_set->hasContrast() && constraint_set->contrast()->IsDouble(); - if (settings->has_contrast) { - const auto contrast = constraint_set->contrast()->GetAsDouble(); - temp_constraint_set->setContrast(constraint_set->contrast()); - settings->contrast = contrast; - } - settings->has_saturation = constraint_set->hasSaturation() && - constraint_set->saturation()->IsDouble(); - if (settings->has_saturation) { - const auto saturation = constraint_set->saturation()->GetAsDouble(); - temp_constraint_set->setSaturation(constraint_set->saturation()); - settings->saturation = saturation; - } - settings->has_sharpness = constraint_set->hasSharpness() && - constraint_set->sharpness()->IsDouble(); - if (settings->has_sharpness) { - const auto sharpness = constraint_set->sharpness()->GetAsDouble(); - temp_constraint_set->setSharpness(constraint_set->sharpness()); - settings->sharpness = sharpness; - } - - settings->has_focus_distance = constraint_set->hasFocusDistance() && - constraint_set->focusDistance()->IsDouble(); - if (settings->has_focus_distance) { - const auto focus_distance = - constraint_set->focusDistance()->GetAsDouble(); - temp_constraint_set->setFocusDistance(constraint_set->focusDistance()); - settings->focus_distance = focus_distance; - } - - settings->has_pan = - constraint_set->hasPan() && constraint_set->pan()->IsDouble(); - if (settings->has_pan) { - const auto pan = constraint_set->pan()->GetAsDouble(); - temp_constraint_set->setPan(constraint_set->pan()); - settings->pan = pan; - } - - settings->has_tilt = - constraint_set->hasTilt() && constraint_set->tilt()->IsDouble(); - if (settings->has_tilt) { - const auto tilt = constraint_set->tilt()->GetAsDouble(); - temp_constraint_set->setTilt(constraint_set->tilt()); - settings->tilt = tilt; - } - - settings->has_zoom = - constraint_set->hasZoom() && constraint_set->zoom()->IsDouble(); - if (settings->has_zoom) { - const auto zoom = constraint_set->zoom()->GetAsDouble(); - temp_constraint_set->setZoom(constraint_set->zoom()); - settings->zoom = zoom; - } - - // TODO(mcasas): support ConstrainBooleanParameters where applicable. - settings->has_torch = - constraint_set->hasTorch() && constraint_set->torch()->IsBoolean(); - if (settings->has_torch) { - const auto torch = constraint_set->torch()->GetAsBoolean(); - temp_constraint_set->setTorch(constraint_set->torch()); - settings->torch = torch; - } - - settings->has_background_blur_mode = - constraint_set->hasBackgroundBlur() && - constraint_set->backgroundBlur()->IsBoolean(); - if (settings->has_background_blur_mode) { - const auto background_blur = - constraint_set->backgroundBlur()->GetAsBoolean(); - temp_constraint_set->setBackgroundBlur(constraint_set->backgroundBlur()); - settings->background_blur_mode = - background_blur ? BackgroundBlurMode::BLUR : BackgroundBlurMode::OFF; - } } - current_constraint_set_ = temp_constraint_set; - return true; } @@ -1441,6 +1626,18 @@ return; } + // TODO(crbug.com/1423282): This is not spec compliant. The current + // constraints are used by `GetMediaTrackConstraints()` which is used by + // `MediaStreamTrackImpl::getConstraints()` which should return + // the constraints that were the argument to the most recent successful + // invocation of the ApplyConstraints algorithm. + // https://w3c.github.io/mediacapture-main/#dom-constrainablepattern-getconstraints + // + // At this point the ApplyConstraints algorithm is still ongoing and not + // succeeded yet. Move this to `OnMojoSetPhotoOptions()` or such. + current_constraints_ = MediaTrackConstraints::Create(); + CopyConstraints(constraints, current_constraints_); + service_requests_.insert(resolver); service_->SetPhotoOptions( @@ -1518,18 +1715,11 @@ } MediaTrackConstraints* ImageCapture::GetMediaTrackConstraints() const { - if (!current_constraint_set_) { - return nullptr; - } - MediaTrackConstraints* constraints = MediaTrackConstraints::Create(); - HeapVector<Member<MediaTrackConstraintSet>> advanced_constraints; - advanced_constraints.push_back(current_constraint_set_); - constraints->setAdvanced(advanced_constraints); - return constraints; + return current_constraints_; } void ImageCapture::ClearMediaTrackConstraints() { - current_constraint_set_ = nullptr; + current_constraints_ = nullptr; // TODO(mcasas): Clear also any PhotoSettings that the device might have got // configured, for that we need to know a "default" state of the device; take @@ -1595,6 +1785,158 @@ // TODO(crbug.com/708723): Integrate image capture constraints processing with // the main implementation and remove this support function. +void ImageCapture::ApplyMediaTrackConstraintSetToSettings( + media::mojom::blink::PhotoSettings* settings, + MediaTrackCapabilities* effective_capabilities, + MediaTrackSettings* effective_settings, + const MediaTrackConstraintSet* constraint_set, + MediaTrackConstraintSetType constraint_set_type) const { + // Apply value constraints to photo settings and update effective + // capabilities. + // + // Roughly the SelectSettings algorithm steps 3 and 5. + // https://www.w3.org/TR/mediacapture-streams/#dfn-selectsettings + if (constraint_set->hasWhiteBalanceMode() && + effective_capabilities->hasWhiteBalanceMode()) { + effective_capabilities->setWhiteBalanceMode(ApplyValueConstraint( + &settings->has_white_balance_mode, &settings->white_balance_mode, + effective_capabilities->whiteBalanceMode(), + constraint_set->whiteBalanceMode(), constraint_set_type, + settings_->whiteBalanceMode())); + } + if (constraint_set->hasExposureMode() && + effective_capabilities->hasExposureMode()) { + effective_capabilities->setExposureMode(ApplyValueConstraint( + &settings->has_exposure_mode, &settings->exposure_mode, + effective_capabilities->exposureMode(), constraint_set->exposureMode(), + constraint_set_type, settings_->exposureMode())); + } + if (constraint_set->hasFocusMode() && + effective_capabilities->hasFocusMode()) { + effective_capabilities->setFocusMode(ApplyValueConstraint( + &settings->has_focus_mode, &settings->focus_mode, + effective_capabilities->focusMode(), constraint_set->focusMode(), + constraint_set_type, settings_->focusMode())); + } + if (constraint_set->hasPointsOfInterest()) { + // There is no |settings->has_points_of_interest|. + bool has_points_of_interest = !settings->points_of_interest.empty(); + absl::optional new_effective_setting = ApplyValueConstraint( + &has_points_of_interest, &settings->points_of_interest, + effective_settings->hasPointsOfInterest() + ? &effective_settings->pointsOfInterest() + : nullptr, + constraint_set->pointsOfInterest(), constraint_set_type); + if (new_effective_setting) { + effective_settings->setPointsOfInterest(*new_effective_setting); + } + } + if (constraint_set->hasExposureCompensation() && + effective_capabilities->hasExposureCompensation()) { + effective_capabilities->setExposureCompensation(ApplyValueConstraint( + &settings->has_exposure_compensation, &settings->exposure_compensation, + effective_capabilities->exposureCompensation(), + constraint_set->exposureCompensation(), constraint_set_type, + settings_->exposureCompensation())); + } + if (constraint_set->hasExposureTime() && + effective_capabilities->hasExposureTime()) { + effective_capabilities->setExposureTime(ApplyValueConstraint( + &settings->has_exposure_time, &settings->exposure_time, + effective_capabilities->exposureTime(), constraint_set->exposureTime(), + constraint_set_type, settings_->exposureTime())); + } + if (constraint_set->hasColorTemperature() && + effective_capabilities->hasColorTemperature()) { + effective_capabilities->setColorTemperature(ApplyValueConstraint( + &settings->has_color_temperature, &settings->color_temperature, + effective_capabilities->colorTemperature(), + constraint_set->colorTemperature(), constraint_set_type, + settings_->colorTemperature())); + } + if (constraint_set->hasIso() && effective_capabilities->hasIso()) { + effective_capabilities->setIso(ApplyValueConstraint( + &settings->has_iso, &settings->iso, effective_capabilities->iso(), + constraint_set->iso(), constraint_set_type, settings_->iso())); + } + if (constraint_set->hasBrightness() && + effective_capabilities->hasBrightness()) { + effective_capabilities->setBrightness(ApplyValueConstraint( + &settings->has_brightness, &settings->brightness, + effective_capabilities->brightness(), constraint_set->brightness(), + constraint_set_type, settings_->brightness())); + } + if (constraint_set->hasContrast() && effective_capabilities->hasContrast()) { + effective_capabilities->setContrast(ApplyValueConstraint( + &settings->has_contrast, &settings->contrast, + effective_capabilities->contrast(), constraint_set->contrast(), + constraint_set_type, settings_->contrast())); + } + if (constraint_set->hasSaturation() && + effective_capabilities->hasSaturation()) { + effective_capabilities->setSaturation(ApplyValueConstraint( + &settings->has_saturation, &settings->saturation, + effective_capabilities->saturation(), constraint_set->saturation(), + constraint_set_type, settings_->saturation())); + } + if (constraint_set->hasSharpness() && + effective_capabilities->hasSharpness()) { + effective_capabilities->setSharpness(ApplyValueConstraint( + &settings->has_sharpness, &settings->sharpness, + effective_capabilities->sharpness(), constraint_set->sharpness(), + constraint_set_type, settings_->sharpness())); + } + if (constraint_set->hasFocusDistance() && + effective_capabilities->hasFocusDistance()) { + effective_capabilities->setFocusDistance(ApplyValueConstraint( + &settings->has_focus_distance, &settings->focus_distance, + effective_capabilities->focusDistance(), + constraint_set->focusDistance(), constraint_set_type, + settings_->focusDistance())); + } + if (constraint_set->hasPan() && effective_capabilities->hasPan()) { + effective_capabilities->setPan(ApplyValueConstraint( + &settings->has_pan, &settings->pan, effective_capabilities->pan(), + constraint_set->pan(), constraint_set_type, settings_->pan())); + } + if (constraint_set->hasTilt() && effective_capabilities->hasTilt()) { + effective_capabilities->setTilt(ApplyValueConstraint( + &settings->has_tilt, &settings->tilt, effective_capabilities->tilt(), + constraint_set->tilt(), constraint_set_type, settings_->tilt())); + } + if (constraint_set->hasZoom() && effective_capabilities->hasZoom()) { + effective_capabilities->setZoom(ApplyValueConstraint( + &settings->has_zoom, &settings->zoom, effective_capabilities->zoom(), + constraint_set->zoom(), constraint_set_type, settings_->zoom())); + } + if (constraint_set->hasTorch() && effective_capabilities->hasTorch() && + effective_capabilities->torch()) { + const auto& new_effective_capability = + ApplyValueConstraint(&settings->has_torch, &settings->torch, + effective_settings->hasTorch() + ? Vector<bool>({effective_settings->torch()}) + : Vector<bool>({false, true}), + constraint_set->torch(), constraint_set_type); + if (new_effective_capability.size() == 1u) { + effective_settings->setTorch(new_effective_capability[0]); + } + } + if (constraint_set->hasBackgroundBlur() && + effective_capabilities->hasBackgroundBlur()) { + bool has_setting = false; + bool setting; + effective_capabilities->setBackgroundBlur(ApplyValueConstraint( + &has_setting, &setting, effective_capabilities->backgroundBlur(), + constraint_set->backgroundBlur(), constraint_set_type)); + if (has_setting) { + settings->has_background_blur_mode = true; + settings->background_blur_mode = ParseBackgroundBlur(setting); + } + } +} + +// TODO(crbug.com/708723): Integrate image capture constraints processing with +// the main implementation and remove this support function. bool ImageCapture::CheckMediaTrackConstraintSet( const MediaTrackCapabilities* effective_capabilities, const MediaTrackSettings* effective_settings, @@ -2078,8 +2420,8 @@ resolver->Resolve(photo_capabilities_); } -bool ImageCapture::IsPageVisible() { - return DomWindow() ? DomWindow()->document()->IsPageVisible() : false; +bool ImageCapture::IsPageVisible() const { + return DomWindow() && DomWindow()->document()->IsPageVisible(); } const String& ImageCapture::SourceId() const { @@ -2224,11 +2566,10 @@ // Copy settings. CopySettings(settings_, clone->settings_, CopyPanTiltZoom(true)); - // Copy current constraint set. - if (current_constraint_set_) { - clone->current_constraint_set_ = MediaTrackConstraintSet::Create(); - CopyConstraintSet(current_constraint_set_, clone->current_constraint_set_, - CopyPanTiltZoom(true)); + // Copy current constraints. + if (current_constraints_) { + clone->current_constraints_ = MediaTrackConstraints::Create(); + CopyConstraints(current_constraints_, clone->current_constraints_); } return clone; @@ -2242,7 +2583,7 @@ visitor->Trace(capabilities_); visitor->Trace(settings_); visitor->Trace(photo_settings_); - visitor->Trace(current_constraint_set_); + visitor->Trace(current_constraints_); visitor->Trace(photo_capabilities_); visitor->Trace(service_requests_); ScriptWrappable::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/imagecapture/image_capture.h b/third_party/blink/renderer/modules/imagecapture/image_capture.h index 7b9aada..1dcd4d5f 100644 --- a/third_party/blink/renderer/modules/imagecapture/image_capture.h +++ b/third_party/blink/renderer/modules/imagecapture/image_capture.h
@@ -65,7 +65,7 @@ bool CheckAndApplyMediaTrackConstraintsToSettings( media::mojom::blink::PhotoSettings*, const MediaTrackConstraints*, - ScriptPromiseResolver*); + ScriptPromiseResolver*) const; void GetMediaTrackCapabilities(MediaTrackCapabilities*) const; void SetMediaTrackConstraints(ScriptPromiseResolver*, const MediaTrackConstraints* constraints); @@ -97,6 +97,16 @@ using PromiseResolverFunction = base::OnceCallback<void(ScriptPromiseResolver*)>; + // Called by `CheckAndApplyMediaTrackConstraintsToSettings()` to apply + // a single constraint set to photo settings and to effective capabilities. + void ApplyMediaTrackConstraintSetToSettings( + media::mojom::blink::PhotoSettings*, + MediaTrackCapabilities* effective_capabilities, + MediaTrackSettings* effective_settings, + const MediaTrackConstraintSet*, + MediaTrackConstraintSetType) const; + // Called by `CheckAndApplyMediaTrackConstraintsToSettings()` check if + // effective capabilities satisfy a single constraint set. bool CheckMediaTrackConstraintSet( const MediaTrackCapabilities* effective_capabilities, const MediaTrackSettings* effective_settings, @@ -145,7 +155,7 @@ void ResolveWithPhotoCapabilities(ScriptPromiseResolver*); // Returns true if page is visible. Otherwise returns false. - bool IsPageVisible(); + bool IsPageVisible() const; // Call UpdateMediaTrackSettingsAndCapabilities with |photo_state| and call // |callback| with whether local changes to background blur settings and @@ -175,7 +185,7 @@ Member<MediaTrackCapabilities> capabilities_; Member<MediaTrackSettings> settings_; - Member<MediaTrackConstraintSet> current_constraint_set_; + Member<MediaTrackConstraints> current_constraints_; Member<PhotoSettings> photo_settings_; Member<PhotoCapabilities> photo_capabilities_;
diff --git a/third_party/blink/renderer/modules/imagecapture/image_capture_test.cc b/third_party/blink/renderer/modules/imagecapture/image_capture_test.cc index 2886fd1..46215c00 100644 --- a/third_party/blink/renderer/modules/imagecapture/image_capture_test.cc +++ b/third_party/blink/renderer/modules/imagecapture/image_capture_test.cc
@@ -227,12 +227,12 @@ EXPECT_FALSE(settings->has_focus_mode); } ASSERT_EQ(settings->points_of_interest.size(), 3u); - EXPECT_EQ(settings->points_of_interest[0]->x, -0.75); - EXPECT_EQ(settings->points_of_interest[0]->y, -0.25); + EXPECT_EQ(settings->points_of_interest[0]->x, 0.0); + EXPECT_EQ(settings->points_of_interest[0]->y, 0.0); EXPECT_EQ(settings->points_of_interest[1]->x, 0.25); EXPECT_EQ(settings->points_of_interest[1]->y, 0.75); - EXPECT_EQ(settings->points_of_interest[2]->x, 1.25); - EXPECT_EQ(settings->points_of_interest[2]->y, 1.75); + EXPECT_EQ(settings->points_of_interest[2]->x, 1.0); + EXPECT_EQ(settings->points_of_interest[2]->y, 1.0); EXPECT_TRUE(settings->has_exposure_compensation); EXPECT_EQ(settings->exposure_compensation, all_capabilities->exposureCompensation()->min() +
diff --git a/third_party/blink/renderer/modules/mediastream/apply_constraints_processor.cc b/third_party/blink/renderer/modules/mediastream/apply_constraints_processor.cc index 143a33b5..141de21 100644 --- a/third_party/blink/renderer/modules/mediastream/apply_constraints_processor.cc +++ b/third_party/blink/renderer/modules/mediastream/apply_constraints_processor.cc
@@ -16,6 +16,7 @@ #include "third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio.h" #include "third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_content.h" #include "third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device.h" +#include "third_party/blink/renderer/modules/mediastream/media_stream_utils.h" #include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h" #include "third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h" #include "third_party/blink/renderer/platform/mediastream/media_stream_source.h" @@ -39,11 +40,19 @@ } // namespace +BASE_FEATURE(kApplyConstraintsRestartsVideoContentSources, + "ApplyConstraintsRestartsVideoContentSources", + base::FEATURE_ENABLED_BY_DEFAULT); + ApplyConstraintsProcessor::ApplyConstraintsProcessor( + LocalFrame* frame, MediaDevicesDispatcherCallback media_devices_dispatcher_cb, scoped_refptr<base::SingleThreadTaskRunner> task_runner) - : media_devices_dispatcher_cb_(std::move(media_devices_dispatcher_cb)), - task_runner_(std::move(task_runner)) {} + : frame_(frame), + media_devices_dispatcher_cb_(std::move(media_devices_dispatcher_cb)), + task_runner_(std::move(task_runner)) { + DCHECK(frame_); +} ApplyConstraintsProcessor::~ApplyConstraintsProcessor() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); @@ -106,9 +115,21 @@ return; } + // The crop version is lost if the capture is restarted, because of this we + // don't try to restart the source if cropTo() has ever been called. const blink::MediaStreamDevice& device_info = video_source_->device(); if (device_info.type == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) { ProcessVideoDeviceRequest(); + } else if (base::FeatureList::IsEnabled( + kApplyConstraintsRestartsVideoContentSources) && + video_source_->GetCropVersion() == 0 && + (device_info.type == + mojom::blink::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE || + device_info.type == + mojom::blink::MediaStreamType::DISPLAY_VIDEO_CAPTURE || + device_info.type == mojom::blink::MediaStreamType:: + DISPLAY_VIDEO_CAPTURE_THIS_TAB)) { + ProcessVideoContentRequest(); } else { FinalizeVideoRequest(); } @@ -122,8 +143,8 @@ if (AbortIfVideoRequestStateInvalid()) return; - // TODO(guidou): Support restarting the source even if there is more than - // one track in the source. https://crbug.com/768205 + // TODO(crbug.com/768205): Support restarting the source even if there is more + // than one track in the source. if (video_source_->NumTracks() > 1U) { FinalizeVideoRequest(); return; @@ -139,17 +160,35 @@ // to know all the formats potentially supported by the source. GetMediaDevicesDispatcher()->GetAllVideoInputDeviceFormats( String(video_source_->device().id.data()), - WTF::BindOnce(&ApplyConstraintsProcessor::MaybeStopSourceForRestart, - WrapWeakPersistent(this))); + WTF::BindOnce( + &ApplyConstraintsProcessor::MaybeStopVideoDeviceSourceForRestart, + WrapWeakPersistent(this))); } -void ApplyConstraintsProcessor::MaybeStopSourceForRestart( +void ApplyConstraintsProcessor::ProcessVideoContentRequest() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + if (AbortIfVideoRequestStateInvalid()) { + return; + } + + // TODO(crbug.com/768205): Support restarting the source even if there is more + // than one track in the source. + if (video_source_->NumTracks() > 1U) { + FinalizeVideoRequest(); + return; + } + + MaybeStopVideoContentSourceForRestart(); +} + +void ApplyConstraintsProcessor::MaybeStopVideoDeviceSourceForRestart( const Vector<media::VideoCaptureFormat>& formats) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (AbortIfVideoRequestStateInvalid()) return; - blink::VideoCaptureSettings settings = SelectVideoSettings(formats); + blink::VideoCaptureSettings settings = SelectVideoDeviceSettings(formats); if (!settings.HasValue()) { ApplyConstraintsFailed(settings.failed_constraint_name()); return; @@ -164,13 +203,38 @@ if (video_device_request_trace_) video_device_request_trace_->AddStep("StopForRestart"); - video_source_->StopForRestart( - WTF::BindOnce(&ApplyConstraintsProcessor::MaybeSourceStoppedForRestart, - WrapWeakPersistent(this))); + video_source_->StopForRestart(WTF::BindOnce( + &ApplyConstraintsProcessor::MaybeDeviceSourceStoppedForRestart, + WrapWeakPersistent(this))); } } -void ApplyConstraintsProcessor::MaybeSourceStoppedForRestart( +void ApplyConstraintsProcessor::MaybeStopVideoContentSourceForRestart() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + if (AbortIfVideoRequestStateInvalid()) { + return; + } + + blink::VideoCaptureSettings settings = SelectVideoContentSettings(); + + if (!settings.HasValue()) { + ApplyConstraintsFailed(settings.failed_constraint_name()); + return; + } + + if (video_source_->GetCurrentFormat() == settings.Format()) { + video_source_->ReconfigureTrack(GetCurrentVideoTrack(), + settings.track_adapter_settings()); + ApplyConstraintsSucceeded(); + GetCurrentVideoTrack()->NotifyConstraintsConfigurationComplete(); + } else { + video_source_->StopForRestart(WTF::BindOnce( + &ApplyConstraintsProcessor::MaybeRestartStoppedVideoContentSource, + WrapWeakPersistent(this))); + } +} + +void ApplyConstraintsProcessor::MaybeDeviceSourceStoppedForRestart( blink::MediaStreamVideoSource::RestartResult result) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); @@ -188,11 +252,39 @@ DCHECK_EQ(result, blink::MediaStreamVideoSource::RestartResult::IS_STOPPED); GetMediaDevicesDispatcher()->GetAvailableVideoInputDeviceFormats( String(video_source_->device().id.data()), - WTF::BindOnce(&ApplyConstraintsProcessor::FindNewFormatAndRestart, + WTF::BindOnce( + &ApplyConstraintsProcessor::FindNewFormatAndRestartDeviceSource, + WrapWeakPersistent(this))); +} + +void ApplyConstraintsProcessor::MaybeRestartStoppedVideoContentSource( + blink::MediaStreamVideoSource::RestartResult result) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + if (AbortIfVideoRequestStateInvalid()) { + return; + } + + if (result == blink::MediaStreamVideoSource::RestartResult::IS_RUNNING) { + FinalizeVideoRequest(); + return; + } + + DCHECK_EQ(result, blink::MediaStreamVideoSource::RestartResult::IS_STOPPED); + + blink::VideoCaptureSettings settings = SelectVideoContentSettings(); + // |settings| should have a value. If it does not due to some unexpected + // reason (perhaps a race with another renderer process), restart the source + // with the old format. + DCHECK(video_source_->GetCurrentFormat()); + video_source_->Restart( + settings.HasValue() ? settings.Format() + : *video_source_->GetCurrentFormat(), + WTF::BindOnce(&ApplyConstraintsProcessor::MaybeSourceRestarted, WrapWeakPersistent(this))); } -void ApplyConstraintsProcessor::FindNewFormatAndRestart( +void ApplyConstraintsProcessor::FindNewFormatAndRestartDeviceSource( const Vector<media::VideoCaptureFormat>& formats) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); @@ -202,7 +294,7 @@ if (video_device_request_trace_) video_device_request_trace_->AddStep("Restart"); - blink::VideoCaptureSettings settings = SelectVideoSettings(formats); + blink::VideoCaptureSettings settings = SelectVideoDeviceSettings(formats); DCHECK(video_source_->GetCurrentFormat()); // |settings| should have a value. If it does not due to some unexpected // reason (perhaps a race with another renderer process), restart the source @@ -246,7 +338,7 @@ } else { format = GetCurrentVideoTrack()->GetComputedSourceFormat(); } - blink::VideoCaptureSettings settings = SelectVideoSettings({format}); + blink::VideoCaptureSettings settings = SelectVideoDeviceSettings({format}); if (settings.HasValue()) { if (settings.min_frame_rate().has_value()) { @@ -262,7 +354,8 @@ } } -blink::VideoCaptureSettings ApplyConstraintsProcessor::SelectVideoSettings( +blink::VideoCaptureSettings +ApplyConstraintsProcessor::SelectVideoDeviceSettings( Vector<media::VideoCaptureFormat> formats) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(current_request_); @@ -302,6 +395,15 @@ settings.height, settings.frame_rate); } +blink::VideoCaptureSettings +ApplyConstraintsProcessor::SelectVideoContentSettings() { + DCHECK(video_source_); + gfx::Size screen_size = MediaStreamUtils::GetScreenSize(frame_); + return blink::SelectSettingsVideoContentCapture( + current_request_->Constraints(), video_source_->device().type, + screen_size.width(), screen_size.height()); +} + blink::MediaStreamAudioSource* ApplyConstraintsProcessor::GetCurrentAudioSource() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
diff --git a/third_party/blink/renderer/modules/mediastream/apply_constraints_processor.h b/third_party/blink/renderer/modules/mediastream/apply_constraints_processor.h index 504ad45..1f2fc2d 100644 --- a/third_party/blink/renderer/modules/mediastream/apply_constraints_processor.h +++ b/third_party/blink/renderer/modules/mediastream/apply_constraints_processor.h
@@ -5,6 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_APPLY_CONSTRAINTS_PROCESSOR_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_APPLY_CONSTRAINTS_PROCESSOR_H_ +#include "base/feature_list.h" #include "base/functional/callback.h" #include "base/task/single_thread_task_runner.h" #include "base/threading/thread_checker.h" @@ -12,6 +13,7 @@ #include "media/capture/video_capture_types.h" #include "third_party/blink/public/mojom/mediastream/media_devices.mojom-blink-forward.h" #include "third_party/blink/public/web/modules/mediastream/media_stream_video_source.h" +#include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/modules/mediastream/apply_constraints_request.h" #include "third_party/blink/renderer/modules/mediastream/media_stream_constraints_util.h" #include "third_party/blink/renderer/modules/modules_export.h" @@ -22,6 +24,11 @@ class MediaStreamAudioSource; class MediaStreamVideoTrack; +// If this feature is enabled, a call to applyConstraints() on a video content +// source will make the source restart with the new format. +MODULES_EXPORT BASE_DECLARE_FEATURE( + kApplyConstraintsRestartsVideoContentSources); + // ApplyConstraintsProcessor is responsible for processing applyConstraints() // requests. Only one applyConstraints() request can be processed at a time. // ApplyConstraintsProcessor must be created, called and destroyed on the main @@ -32,6 +39,7 @@ using MediaDevicesDispatcherCallback = base::RepeatingCallback< blink::mojom::blink::MediaDevicesDispatcherHost*()>; ApplyConstraintsProcessor( + LocalFrame* frame, MediaDevicesDispatcherCallback media_devices_dispatcher_cb, scoped_refptr<base::SingleThreadTaskRunner> task_runner); @@ -48,28 +56,38 @@ void ProcessRequest(blink::ApplyConstraintsRequest* request, base::OnceClosure callback); - void Trace(Visitor* visitor) const { visitor->Trace(current_request_); } + void Trace(Visitor* visitor) const { + visitor->Trace(current_request_); + visitor->Trace(frame_); + } private: // Helpers for video device-capture requests. void ProcessVideoDeviceRequest(); - void MaybeStopSourceForRestart( + void MaybeStopVideoDeviceSourceForRestart( const Vector<media::VideoCaptureFormat>& formats); - void MaybeSourceStoppedForRestart( + void MaybeDeviceSourceStoppedForRestart( blink::MediaStreamVideoSource::RestartResult result); - void FindNewFormatAndRestart( + void FindNewFormatAndRestartDeviceSource( const Vector<media::VideoCaptureFormat>& formats); - void MaybeSourceRestarted( + blink::VideoCaptureSettings SelectVideoDeviceSettings( + Vector<media::VideoCaptureFormat> formats); + + // Helpers for video content-capture requests. + void ProcessVideoContentRequest(); + void MaybeStopVideoContentSourceForRestart(); + void MaybeRestartStoppedVideoContentSource( blink::MediaStreamVideoSource::RestartResult result); + blink::VideoCaptureSettings SelectVideoContentSettings(); // Helpers for all video requests. void ProcessVideoRequest(); blink::MediaStreamVideoTrack* GetCurrentVideoTrack(); blink::MediaStreamVideoSource* GetCurrentVideoSource(); bool AbortIfVideoRequestStateInvalid(); // Returns true if aborted. - blink::VideoCaptureSettings SelectVideoSettings( - Vector<media::VideoCaptureFormat> formats); void FinalizeVideoRequest(); + void MaybeSourceRestarted( + blink::MediaStreamVideoSource::RestartResult result); // Helpers for audio requests. void ProcessAudioRequest(); @@ -96,6 +114,7 @@ blink::MediaStreamVideoSource* video_source_ = nullptr; base::OnceClosure request_completed_cb_; + const Member<LocalFrame> frame_; MediaDevicesDispatcherCallback media_devices_dispatcher_cb_; THREAD_CHECKER(thread_checker_);
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track_impl_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_track_impl_test.cc index 3d50c846..9c38da5 100644 --- a/third_party/blink/renderer/modules/mediastream/media_stream_track_impl_test.cc +++ b/third_party/blink/renderer/modules/mediastream/media_stream_track_impl_test.cc
@@ -4,14 +4,18 @@ #include "third_party/blink/renderer/modules/mediastream/media_stream_track_impl.h" -#include <iostream> +#include <tuple> + #include "base/run_loop.h" #include "base/test/gmock_callback_support.h" +#include "base/test/scoped_feature_list.h" #include "media/base/video_frame.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/mojom/mediastream/media_devices.mojom-blink.h" #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h" #include "third_party/blink/public/web/modules/mediastream/media_stream_video_source.h" #include "third_party/blink/public/web/web_heap.h" +#include "third_party/blink/renderer/bindings/core/v8/script_promise.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise_tester.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_constrain_long_range.h" @@ -19,7 +23,11 @@ #include "third_party/blink/renderer/bindings/modules/v8/v8_union_constrainlongrange_long.h" #include "third_party/blink/renderer/core/streams/readable_stream.h" #include "third_party/blink/renderer/core/streams/readable_stream_default_reader.h" +#include "third_party/blink/renderer/modules/mediastream/apply_constraints_processor.h" #include "third_party/blink/renderer/modules/mediastream/local_media_stream_audio_source.h" +#include "third_party/blink/renderer/modules/mediastream/media_constraints.h" +#include "third_party/blink/renderer/modules/mediastream/media_constraints_impl.h" +#include "third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_content.h" #include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h" #include "third_party/blink/renderer/modules/mediastream/mock_media_stream_video_sink.h" #include "third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.h" @@ -35,6 +43,13 @@ namespace blink { namespace { +const gfx::Size kTestScreenSize{kDefaultScreenCastWidth, + kDefaultScreenCastHeight}; +constexpr int kReducedWidth = 640; +constexpr int kReducedHeight = 320; +constexpr float kAspectRatio = kReducedWidth / kReducedHeight; +constexpr float kMaxFrameRate = 11.0f; +constexpr float kMinFrameRate = 0.0f; class TestObserver : public GarbageCollected<TestObserver>, public MediaStreamTrack::Observer { @@ -89,6 +104,64 @@ source, std::move(platform_track)); } +media::VideoCaptureFormat GetDefaultVideoContentCaptureFormat() { + MediaConstraints constraints; + constraints.Initialize(); + return blink::SelectSettingsVideoContentCapture( + constraints, mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE, + kTestScreenSize.width(), kTestScreenSize.height()) + .Format(); +} + +std::tuple<MediaStreamComponent*, MockMediaStreamVideoSource*> +MakeMockDisplayVideoCaptureComponent() { + auto platform_source = std::make_unique<MockMediaStreamVideoSource>( + GetDefaultVideoContentCaptureFormat(), false); + platform_source->SetDevice( + MediaStreamDevice(mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE, + "fakeSourceId", "fakeWindowCapturer")); + MockMediaStreamVideoSource* platform_source_ptr = platform_source.get(); + MediaStreamSource* source = MakeGarbageCollected<MediaStreamSource>( + "id", MediaStreamSource::StreamType::kTypeVideo, "name", + false /* remote */, std::move(platform_source)); + auto platform_track = std::make_unique<MediaStreamVideoTrack>( + platform_source_ptr, + WebPlatformMediaStreamSource::ConstraintsOnceCallback(), + true /* enabled */); + MediaStreamComponent* component = + MakeGarbageCollected<MediaStreamComponentImpl>(source, + std::move(platform_track)); + return std::make_tuple(component, platform_source_ptr); +} + +MediaTrackConstraints* MakeMediaTrackConstraints( + absl::optional<int> exact_width, + absl::optional<int> exact_height, + absl::optional<float> min_frame_rate, + absl::optional<float> max_frame_rate, + absl::optional<float> aspect_ratio = absl::nullopt) { + MediaConstraints constraints; + MediaTrackConstraintSetPlatform basic; + if (exact_width) { + basic.width.SetExact(*exact_width); + } + if (exact_height) { + basic.height.SetExact(*exact_height); + } + if (min_frame_rate) { + basic.frame_rate.SetMin(*min_frame_rate); + } + if (max_frame_rate) { + basic.frame_rate.SetMax(*max_frame_rate); + } + if (aspect_ratio) { + basic.aspect_ratio.SetExact(*aspect_ratio); + } + + constraints.Initialize(basic, Vector<MediaTrackConstraintSetPlatform>()); + return media_constraints_impl::ConvertConstraints(constraints); +} + } // namespace class MediaStreamTrackImplTest : public testing::Test { @@ -267,4 +340,346 @@ EXPECT_EQ(clone_constraints->width()->GetAsConstrainLongRange()->max(), 240); } +TEST_F(MediaStreamTrackImplTest, ApplyConstraintsUpdatesSourceFormat) { + V8TestingScope v8_scope; + MediaStreamComponent* component; + MockMediaStreamVideoSource* platform_source_ptr; + std::tie(component, platform_source_ptr) = + MakeMockDisplayVideoCaptureComponent(); + MediaStreamTrack* track = MakeGarbageCollected<MediaStreamTrackImpl>( + v8_scope.GetExecutionContext(), component); + + // Start the source. + platform_source_ptr->StartMockedSource(); + // Verify that initial settings are not the same as the constraints. + EXPECT_NE(platform_source_ptr->max_requested_width(), kReducedWidth); + EXPECT_NE(platform_source_ptr->max_requested_height(), kReducedHeight); + EXPECT_NE(platform_source_ptr->max_requested_frame_rate(), kMaxFrameRate); + // Apply new frame rate constraints. + MediaTrackConstraints* track_constraints = MakeMediaTrackConstraints( + kReducedWidth, kReducedHeight, kMinFrameRate, kMaxFrameRate); + ScriptPromise apply_constraints_promise = + track->applyConstraints(v8_scope.GetScriptState(), track_constraints); + + ScriptPromiseTester tester(v8_scope.GetScriptState(), + apply_constraints_promise); + tester.WaitUntilSettled(); + EXPECT_TRUE(tester.IsFulfilled()); + // Verify updated settings and that the source was restarted. + EXPECT_EQ(platform_source_ptr->restart_count(), 1); + EXPECT_EQ(platform_source_ptr->max_requested_width(), kReducedWidth); + EXPECT_EQ(platform_source_ptr->max_requested_height(), kReducedHeight); + EXPECT_EQ(platform_source_ptr->max_requested_frame_rate(), kMaxFrameRate); +} + +TEST_F(MediaStreamTrackImplTest, + ApplyConstraintsFramerateDoesNotAffectResolution) { + V8TestingScope v8_scope; + MediaStreamComponent* component; + MockMediaStreamVideoSource* platform_source_ptr; + std::tie(component, platform_source_ptr) = + MakeMockDisplayVideoCaptureComponent(); + MediaStreamTrack* track = MakeGarbageCollected<MediaStreamTrackImpl>( + v8_scope.GetExecutionContext(), component); + + // Start the source. + platform_source_ptr->StartMockedSource(); + // Get initial settings and verify that initial frame rate is not same as the + // new constraint. + int initialWidth = platform_source_ptr->max_requested_width(); + int initialHeight = platform_source_ptr->max_requested_height(); + float initialFrameRate = platform_source_ptr->max_requested_frame_rate(); + EXPECT_NE(initialFrameRate, kMaxFrameRate); + // Apply new frame rate constraints. + MediaTrackConstraints* track_constraints = MakeMediaTrackConstraints( + absl::nullopt, absl::nullopt, kMinFrameRate, kMaxFrameRate); + ScriptPromise apply_constraints_promise = + track->applyConstraints(v8_scope.GetScriptState(), track_constraints); + + ScriptPromiseTester tester(v8_scope.GetScriptState(), + apply_constraints_promise); + tester.WaitUntilSettled(); + EXPECT_TRUE(tester.IsFulfilled()); + // Verify updated settings and that the source was restarted. + EXPECT_EQ(platform_source_ptr->restart_count(), 1); + EXPECT_EQ(platform_source_ptr->max_requested_width(), initialWidth); + EXPECT_EQ(platform_source_ptr->max_requested_height(), initialHeight); + EXPECT_EQ(platform_source_ptr->max_requested_frame_rate(), kMaxFrameRate); +} + +TEST_F(MediaStreamTrackImplTest, + ApplyConstraintsResolutionDoesNotAffectFramerate) { + V8TestingScope v8_scope; + MediaStreamComponent* component; + MockMediaStreamVideoSource* platform_source_ptr; + std::tie(component, platform_source_ptr) = + MakeMockDisplayVideoCaptureComponent(); + MediaStreamTrack* track = MakeGarbageCollected<MediaStreamTrackImpl>( + v8_scope.GetExecutionContext(), component); + + // Start the source. + platform_source_ptr->StartMockedSource(); + // Get initial settings and verify that the initial resolution is not the same + // as the new constraint. + int initialWidth = platform_source_ptr->max_requested_width(); + int initialHeight = platform_source_ptr->max_requested_height(); + float initialFrameRate = platform_source_ptr->max_requested_frame_rate(); + EXPECT_NE(initialWidth, kReducedWidth); + EXPECT_NE(initialHeight, kReducedHeight); + // Apply new frame rate constraints. + MediaTrackConstraints* track_constraints = MakeMediaTrackConstraints( + kReducedWidth, kReducedHeight, absl::nullopt, absl::nullopt); + ScriptPromise apply_constraints_promise = + track->applyConstraints(v8_scope.GetScriptState(), track_constraints); + + ScriptPromiseTester tester(v8_scope.GetScriptState(), + apply_constraints_promise); + tester.WaitUntilSettled(); + EXPECT_TRUE(tester.IsFulfilled()); + // Verify updated settings and that the source was restarted. + EXPECT_EQ(platform_source_ptr->restart_count(), 1); + EXPECT_EQ(platform_source_ptr->max_requested_width(), kReducedWidth); + EXPECT_EQ(platform_source_ptr->max_requested_height(), kReducedHeight); + EXPECT_EQ(platform_source_ptr->max_requested_frame_rate(), initialFrameRate); +} + +TEST_F(MediaStreamTrackImplTest, + ApplyConstraintsWidthDoesNotAffectAspectRatio) { + V8TestingScope v8_scope; + MediaStreamComponent* component; + MockMediaStreamVideoSource* platform_source_ptr; + std::tie(component, platform_source_ptr) = + MakeMockDisplayVideoCaptureComponent(); + MediaStreamTrack* track = MakeGarbageCollected<MediaStreamTrackImpl>( + v8_scope.GetExecutionContext(), component); + + // Start the source. + platform_source_ptr->StartMockedSource(); + // Get initial settings and verify that the initial resolution is not the same + // as the new constraint. + int initialWidth = platform_source_ptr->max_requested_width(); + int initialHeight = platform_source_ptr->max_requested_height(); + float initialFrameRate = platform_source_ptr->max_requested_frame_rate(); + EXPECT_NE(initialWidth, kReducedWidth); + EXPECT_NE(initialHeight, kReducedHeight); + // Apply new frame rate constraints. + MediaTrackConstraints* track_constraints = MakeMediaTrackConstraints( + kReducedWidth, absl::nullopt, absl::nullopt, absl::nullopt); + ScriptPromise apply_constraints_promise = + track->applyConstraints(v8_scope.GetScriptState(), track_constraints); + + ScriptPromiseTester tester(v8_scope.GetScriptState(), + apply_constraints_promise); + tester.WaitUntilSettled(); + EXPECT_TRUE(tester.IsFulfilled()); + // Verify updated settings and that the source was restarted. + EXPECT_EQ(platform_source_ptr->restart_count(), 1); + float aspect_ratio = + static_cast<float>(initialWidth) / static_cast<float>(initialHeight); + EXPECT_EQ(platform_source_ptr->max_requested_width(), kReducedWidth); + EXPECT_EQ(platform_source_ptr->max_requested_height(), + kReducedWidth / aspect_ratio); + EXPECT_EQ(platform_source_ptr->max_requested_frame_rate(), initialFrameRate); +} + +TEST_F(MediaStreamTrackImplTest, ApplyConstraintsWidthAndAspectRatio) { + V8TestingScope v8_scope; + MediaStreamComponent* component; + MockMediaStreamVideoSource* platform_source_ptr; + std::tie(component, platform_source_ptr) = + MakeMockDisplayVideoCaptureComponent(); + MediaStreamTrack* track = MakeGarbageCollected<MediaStreamTrackImpl>( + v8_scope.GetExecutionContext(), component); + + // Start the source. + platform_source_ptr->StartMockedSource(); + // Get initial settings and verify that the initial resolution is not the same + // as the new constraint. + int initialWidth = platform_source_ptr->max_requested_width(); + int initialHeight = platform_source_ptr->max_requested_height(); + float initialFrameRate = platform_source_ptr->max_requested_frame_rate(); + EXPECT_NE(initialWidth, kReducedWidth); + EXPECT_NE(initialHeight, kReducedHeight); + // Apply new frame rate constraints. + MediaTrackConstraints* track_constraints = MakeMediaTrackConstraints( + kReducedWidth, absl::nullopt, absl::nullopt, absl::nullopt, kAspectRatio); + ScriptPromise apply_constraints_promise = + track->applyConstraints(v8_scope.GetScriptState(), track_constraints); + + ScriptPromiseTester tester(v8_scope.GetScriptState(), + apply_constraints_promise); + tester.WaitUntilSettled(); + EXPECT_TRUE(tester.IsFulfilled()); + // Verify updated settings and that the source was restarted. + EXPECT_EQ(platform_source_ptr->restart_count(), 1); + EXPECT_EQ(platform_source_ptr->max_requested_width(), kReducedWidth); + EXPECT_EQ(platform_source_ptr->max_requested_height(), + kReducedWidth / kAspectRatio); + EXPECT_EQ(platform_source_ptr->max_requested_frame_rate(), initialFrameRate); +} + +// cropTo() is not supported on Android. +#if BUILDFLAG(IS_ANDROID) +#define MAYBE_ApplyConstraintsDoesNotUpdateFormatForCroppedSources \ + DISABLED_ApplyConstraintsDoesNotUpdateFormatForCroppedSources +#else +#define MAYBE_ApplyConstraintsDoesNotUpdateFormatForCroppedSources \ + ApplyConstraintsDoesNotUpdateFormatForCroppedSources +#endif + +TEST_F(MediaStreamTrackImplTest, + MAYBE_ApplyConstraintsDoesNotUpdateFormatForCroppedSources) { + V8TestingScope v8_scope; + MediaStreamComponent* component; + MockMediaStreamVideoSource* platform_source_ptr; + std::tie(component, platform_source_ptr) = + MakeMockDisplayVideoCaptureComponent(); + MediaStreamTrack* track = MakeGarbageCollected<MediaStreamTrackImpl>( + v8_scope.GetExecutionContext(), component); + + // Start the source. + platform_source_ptr->StartMockedSource(); + // Get initial settings and verify that resolution and frame rate are + // different than the new constraints. + int initialWidth = platform_source_ptr->max_requested_width(); + int initialHeight = platform_source_ptr->max_requested_height(); + float initialFrameRate = platform_source_ptr->max_requested_frame_rate(); + EXPECT_NE(initialWidth, kReducedWidth); + EXPECT_NE(initialHeight, kReducedHeight); + EXPECT_NE(initialFrameRate, kMaxFrameRate); + // Apply new constraints. + MediaTrackConstraints* track_constraints = MakeMediaTrackConstraints( + kReducedWidth, kReducedHeight, kMinFrameRate, kMaxFrameRate); + EXPECT_CALL(*platform_source_ptr, GetCropVersion) + .WillRepeatedly(testing::Return(1)); + ScriptPromise apply_constraints_promise = + track->applyConstraints(v8_scope.GetScriptState(), track_constraints); + + ScriptPromiseTester tester(v8_scope.GetScriptState(), + apply_constraints_promise); + tester.WaitUntilSettled(); + EXPECT_TRUE(tester.IsFulfilled()); + // Verify that the settings are not updated and that the source was not + // restarted. + EXPECT_EQ(platform_source_ptr->restart_count(), 0); + EXPECT_EQ(platform_source_ptr->max_requested_width(), initialWidth); + EXPECT_EQ(platform_source_ptr->max_requested_height(), initialHeight); + EXPECT_EQ(platform_source_ptr->max_requested_frame_rate(), initialFrameRate); +} + +TEST_F(MediaStreamTrackImplTest, + ApplyConstraintsDoesNotUpdateSourceFormatIfDisabled) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatures( + // Enabled features. + {}, + // Disabled features. + {kApplyConstraintsRestartsVideoContentSources}); + + V8TestingScope v8_scope; + MediaStreamComponent* component; + MockMediaStreamVideoSource* platform_source_ptr; + std::tie(component, platform_source_ptr) = + MakeMockDisplayVideoCaptureComponent(); + MediaStreamTrack* track = MakeGarbageCollected<MediaStreamTrackImpl>( + v8_scope.GetExecutionContext(), component); + + // Start the source. + platform_source_ptr->StartMockedSource(); + // Get initial settings and verify that resolution and frame rate are + // different than the new constraints. + int initialWidth = platform_source_ptr->max_requested_width(); + int initialHeight = platform_source_ptr->max_requested_height(); + float initialFrameRate = platform_source_ptr->max_requested_frame_rate(); + EXPECT_NE(initialWidth, kReducedWidth); + EXPECT_NE(initialHeight, kReducedHeight); + EXPECT_NE(initialFrameRate, kMaxFrameRate); + // Apply new constraints. + MediaTrackConstraints* track_constraints = MakeMediaTrackConstraints( + kReducedWidth, kReducedHeight, kMinFrameRate, kMaxFrameRate); + ScriptPromise apply_constraints_promise = + track->applyConstraints(v8_scope.GetScriptState(), track_constraints); + + ScriptPromiseTester tester(v8_scope.GetScriptState(), + apply_constraints_promise); + tester.WaitUntilSettled(); + EXPECT_TRUE(tester.IsFulfilled()); + // Verify that the settings are not updated and that the source was not + // restarted. + EXPECT_EQ(platform_source_ptr->restart_count(), 0); + EXPECT_EQ(platform_source_ptr->max_requested_width(), initialWidth); + EXPECT_EQ(platform_source_ptr->max_requested_height(), initialHeight); + EXPECT_EQ(platform_source_ptr->max_requested_frame_rate(), initialFrameRate); +} + +TEST_F(MediaStreamTrackImplTest, ApplyConstraintsWithUnchangedConstraints) { + V8TestingScope v8_scope; + MediaStreamComponent* component; + MockMediaStreamVideoSource* platform_source_ptr; + std::tie(component, platform_source_ptr) = + MakeMockDisplayVideoCaptureComponent(); + MediaStreamTrack* track = MakeGarbageCollected<MediaStreamTrackImpl>( + v8_scope.GetExecutionContext(), component); + + // Start the source. + platform_source_ptr->StartMockedSource(); + // Get initial settings + int initialWidth = platform_source_ptr->max_requested_width(); + int initialHeight = platform_source_ptr->max_requested_height(); + float initialFrameRate = platform_source_ptr->max_requested_frame_rate(); + // Apply new constraints that are fulfilled by the current settings. + MediaTrackConstraints* track_constraints = MakeMediaTrackConstraints( + initialWidth, initialHeight, initialFrameRate, initialFrameRate); + ScriptPromise apply_constraints_promise = + track->applyConstraints(v8_scope.GetScriptState(), track_constraints); + + ScriptPromiseTester tester(v8_scope.GetScriptState(), + apply_constraints_promise); + tester.WaitUntilSettled(); + EXPECT_TRUE(tester.IsFulfilled()); + // Verify that the settings are the same and that the source did not restart. + EXPECT_EQ(platform_source_ptr->restart_count(), 0); + EXPECT_EQ(platform_source_ptr->max_requested_width(), initialWidth); + EXPECT_EQ(platform_source_ptr->max_requested_height(), initialHeight); + EXPECT_EQ(platform_source_ptr->max_requested_frame_rate(), initialFrameRate); +} + +TEST_F(MediaStreamTrackImplTest, ApplyConstraintsCannotRestartSource) { + V8TestingScope v8_scope; + MediaStreamComponent* component; + MockMediaStreamVideoSource* platform_source_ptr; + std::tie(component, platform_source_ptr) = + MakeMockDisplayVideoCaptureComponent(); + MediaStreamTrack* track = MakeGarbageCollected<MediaStreamTrackImpl>( + v8_scope.GetExecutionContext(), component); + + // Start the source. + platform_source_ptr->DisableStopForRestart(); + platform_source_ptr->StartMockedSource(); + // Get initial settings and verify that resolution and frame rate are + // different than the new constraints. + int initialWidth = platform_source_ptr->max_requested_width(); + int initialHeight = platform_source_ptr->max_requested_height(); + float initialFrameRate = platform_source_ptr->max_requested_frame_rate(); + EXPECT_NE(initialWidth, kReducedWidth); + EXPECT_NE(initialHeight, kReducedHeight); + EXPECT_NE(initialFrameRate, kMaxFrameRate); + // Apply new constraints. + MediaTrackConstraints* track_constraints = MakeMediaTrackConstraints( + kReducedWidth, kReducedHeight, kMinFrameRate, kMaxFrameRate); + ScriptPromise apply_constraints_promise = + track->applyConstraints(v8_scope.GetScriptState(), track_constraints); + + ScriptPromiseTester tester(v8_scope.GetScriptState(), + apply_constraints_promise); + tester.WaitUntilSettled(); + EXPECT_TRUE(tester.IsFulfilled()); + // Verify that the settings are not updated and that the source was not + // restarted. + EXPECT_EQ(platform_source_ptr->restart_count(), 0); + EXPECT_EQ(platform_source_ptr->max_requested_width(), initialWidth); + EXPECT_EQ(platform_source_ptr->max_requested_height(), initialHeight); + EXPECT_EQ(platform_source_ptr->max_requested_frame_rate(), initialFrameRate); +} + } // namespace blink
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_utils.cc b/third_party/blink/renderer/modules/mediastream/media_stream_utils.cc index ac6af0bfa..d02f94c 100644 --- a/third_party/blink/renderer/modules/mediastream/media_stream_utils.cc +++ b/third_party/blink/renderer/modules/mediastream/media_stream_utils.cc
@@ -7,7 +7,10 @@ #include "base/memory/ptr_util.h" #include "base/task/single_thread_task_runner.h" #include "third_party/blink/public/web/modules/mediastream/media_stream_video_source.h" +#include "third_party/blink/renderer/core/frame/local_frame.h" +#include "third_party/blink/renderer/core/page/chrome_client.h" #include "third_party/blink/renderer/modules/mediastream/media_stream.h" +#include "third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_content.h" #include "third_party/blink/renderer/modules/mediastream/media_stream_track_impl.h" #include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h" #include "third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h" @@ -18,6 +21,7 @@ #include "third_party/blink/renderer/platform/mediastream/media_stream_source.h" #include "third_party/blink/renderer/platform/mediastream/webaudio_media_stream_source.h" #include "third_party/blink/renderer/platform/scheduler/public/thread.h" +#include "ui/display/screen_info.h" namespace blink { @@ -34,4 +38,20 @@ component); } +gfx::Size MediaStreamUtils::GetScreenSize(LocalFrame* frame) { + const gfx::Size kDefaultScreenSize(kDefaultScreenCastWidth, + kDefaultScreenCastHeight); + // Can be null in tests. + if (!frame) { + return kDefaultScreenSize; + } + const display::ScreenInfo& info = + frame->GetChromeClient().GetScreenInfo(*frame); + // If no screen size info, use the default. + if (info.rect.size().IsEmpty()) { + return kDefaultScreenSize; + } + return info.rect.size(); +} + } // namespace blink
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_utils.h b/third_party/blink/renderer/modules/mediastream/media_stream_utils.h index f30aa28b..6b994888 100644 --- a/third_party/blink/renderer/modules/mediastream/media_stream_utils.h +++ b/third_party/blink/renderer/modules/mediastream/media_stream_utils.h
@@ -8,10 +8,12 @@ #include "base/memory/scoped_refptr.h" #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-blink.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" +#include "ui/gfx/geometry/size.h" namespace blink { class ExecutionContext; +class LocalFrame; class MediaStreamComponent; class MediaStreamSource; class MediaStreamTrack; @@ -24,6 +26,8 @@ static MediaStreamTrack* CreateLocalAudioTrack(ExecutionContext*, MediaStreamSource*); + + static gfx::Size GetScreenSize(LocalFrame* frame); }; } // namespace blink
diff --git a/third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.cc b/third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.cc index 99b580b..fa2a931 100644 --- a/third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.cc +++ b/third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.cc
@@ -30,9 +30,6 @@ : MediaStreamVideoSource(scheduler::GetSingleThreadTaskRunnerForTesting()), format_(format), respond_to_request_refresh_frame_(respond_to_request_refresh_frame), - max_requested_height_(format.frame_size.height()), - max_requested_width_(format.frame_size.width()), - max_requested_frame_rate_(format.frame_rate), attempted_to_start_(false) {} MockMediaStreamVideoSource::~MockMediaStreamVideoSource() {} @@ -139,6 +136,7 @@ OnRestartDone(false); return; } + ++restart_count_; is_stopped_for_restart_ = false; format_ = new_format; OnRestartDone(true);
diff --git a/third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.h b/third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.h index 76abdf5e..77abecc0 100644 --- a/third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.h +++ b/third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.h
@@ -66,11 +66,9 @@ void DeliverNewCropVersion(uint32_t crop_version); const media::VideoCaptureFormat& start_format() const { return format_; } - int max_requested_height() const { return max_requested_height_; } - int max_requested_width() const { return max_requested_width_; } - absl::optional<double> max_requested_frame_rate() const { - return max_requested_frame_rate_; - } + int max_requested_height() const { return format_.frame_size.height(); } + int max_requested_width() const { return format_.frame_size.width(); } + float max_requested_frame_rate() const { return format_.frame_rate; } void SetMutedState(bool muted_state) override { blink::MediaStreamVideoSource::SetMutedState(muted_state); @@ -82,6 +80,7 @@ void EnableRestart() { can_restart_ = true; } void DisableRestart() { can_restart_ = false; } + int restart_count() const { return restart_count_; } bool is_suspended() { return is_suspended_; } @@ -107,13 +106,11 @@ private: media::VideoCaptureFormat format_; bool respond_to_request_refresh_frame_; - int max_requested_height_ = 0; - int max_requested_width_ = 0; - absl::optional<double> max_requested_frame_rate_; bool attempted_to_start_; bool is_stopped_for_restart_ = false; bool can_stop_for_restart_ = true; bool can_restart_ = true; + int restart_count_ = 0; bool is_suspended_ = false; blink::VideoCaptureDeliverFrameCB frame_callback_; EncodedVideoFrameCB encoded_frame_callback_;
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_client.cc b/third_party/blink/renderer/modules/mediastream/user_media_client.cc index 6c91019..81ee1367 100644 --- a/third_party/blink/renderer/modules/mediastream/user_media_client.cc +++ b/third_party/blink/renderer/modules/mediastream/user_media_client.cc
@@ -108,6 +108,7 @@ user_media_processor_(user_media_processor), apply_constraints_processor_( MakeGarbageCollected<ApplyConstraintsProcessor>( + frame, WTF::BindRepeating( [](UserMediaClient* client) -> mojom::blink::MediaDevicesDispatcherHost* {
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 3149953..096ccfd 100644 --- a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc +++ b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
@@ -36,7 +36,6 @@ #include "third_party/blink/public/web/web_local_frame_client.h" #include "third_party/blink/renderer/core/frame/local_dom_window.h" #include "third_party/blink/renderer/core/frame/local_frame.h" -#include "third_party/blink/renderer/core/page/chrome_client.h" #include "third_party/blink/renderer/modules/mediastream/local_media_stream_audio_source.h" #include "third_party/blink/renderer/modules/mediastream/local_video_capturer_source.h" #include "third_party/blink/renderer/modules/mediastream/media_constraints.h" @@ -45,6 +44,7 @@ #include "third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio.h" #include "third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_content.h" #include "third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device.h" +#include "third_party/blink/renderer/modules/mediastream/media_stream_utils.h" #include "third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.h" #include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h" #include "third_party/blink/renderer/modules/mediastream/processed_local_audio_source.h" @@ -60,7 +60,6 @@ #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" #include "third_party/blink/renderer/platform/wtf/wtf_size_t.h" -#include "ui/display/screen_info.h" #include "ui/gfx/geometry/size.h" namespace blink { @@ -911,7 +910,7 @@ SendLogMessage( base::StringPrintf("SelectVideoContentSettings. request_id=%d.", current_request_info_->request_id())); - gfx::Size screen_size = GetScreenSize(); + gfx::Size screen_size = MediaStreamUtils::GetScreenSize(frame_); blink::VideoCaptureSettings settings = blink::SelectSettingsVideoContentCapture( current_request_info_->request()->VideoConstraints(), @@ -1084,9 +1083,9 @@ if (stream_devices->video_device.has_value()) { String video_device_id(stream_devices->video_device.value().id.data()); current_request_info_->AddNativeVideoFormats( - video_device_id, - {media::VideoCaptureFormat(GetScreenSize(), format.frame_rate, - format.pixel_format)}); + video_device_id, {media::VideoCaptureFormat( + MediaStreamUtils::GetScreenSize(frame_), + format.frame_rate, format.pixel_format)}); } } StartTracks(label); @@ -1152,17 +1151,6 @@ } } -gfx::Size UserMediaProcessor::GetScreenSize() { - gfx::Size screen_size(blink::kDefaultScreenCastWidth, - blink::kDefaultScreenCastHeight); - if (frame_) { // Can be null in tests. - const display::ScreenInfo& info = - frame_->GetChromeClient().GetScreenInfo(*frame_); - screen_size = info.rect.size(); - } - return screen_size; -} - void UserMediaProcessor::OnStreamGeneratedForCancelledRequest( const mojom::blink::StreamDevices& stream_devices) { SendLogMessage("OnStreamGeneratedForCancelledRequest()");
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_processor.h b/third_party/blink/renderer/modules/mediastream/user_media_processor.h index b8c979d0..16fb636e 100644 --- a/third_party/blink/renderer/modules/mediastream/user_media_processor.h +++ b/third_party/blink/renderer/modules/mediastream/user_media_processor.h
@@ -28,10 +28,6 @@ #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "third_party/blink/renderer/platform/wtf/vector.h" -namespace gfx { -class Size; -} - namespace blink { class AudioCaptureSettings; class LocalFrame; @@ -177,8 +173,6 @@ const Vector<String>& device_ids, const Vector<media::VideoCaptureFormat>& formats); - gfx::Size GetScreenSize(); - void OnStreamGenerationFailed( int32_t request_id, blink::mojom::blink::MediaStreamRequestResult result);
diff --git a/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_handler.cc b/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_handler.cc index c588864e..57eaade 100644 --- a/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_handler.cc +++ b/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_handler.cc
@@ -328,9 +328,9 @@ "RealtimeAudioDestinationHandler::StartPlatformDestination", "sink information (when starting a new destination)", audio_utilities::GetSinkInfoForTracing( - sink_descriptor_, latency_hint_, + sink_descriptor_, latency_hint_, MaxChannelCount(), sample_rate_.has_value() ? sample_rate_.value() : -1, - MaxChannelCount(), GetCallbackBufferSize())); + GetCallbackBufferSize())); DCHECK(IsMainThread()); if (platform_destination_->IsPlaying()) { @@ -384,9 +384,9 @@ TRACE_EVENT1("webaudio", "RealtimeAudioDestinationHandler::SetSinkDescriptor", "sink information (when descriptor change requested)", audio_utilities::GetSinkInfoForTracing( - sink_descriptor, latency_hint_, + sink_descriptor, latency_hint_, MaxChannelCount(), sample_rate_.has_value() ? sample_rate_.value() : -1, - MaxChannelCount(), GetCallbackBufferSize())); + GetCallbackBufferSize())); DCHECK(IsMainThread()); // Create a pending AudioDestination to replace the current one.
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc b/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc index d7a0e04..07fc6f26 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc +++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
@@ -92,6 +92,7 @@ WGPUAdapterProperties properties = {}; GetProcs().adapterGetProperties(handle_, &properties); is_fallback_adapter_ = properties.adapterType == WGPUAdapterType_CPU; + backend_type_ = properties.backendType; vendor_ = properties.vendorName; architecture_ = properties.architecture; @@ -139,6 +140,10 @@ return is_fallback_adapter_; } +WGPUBackendType GPUAdapter::backendType() const { + return backend_type_; +} + bool GPUAdapter::SupportsMultiPlanarFormats() const { return GetProcs().adapterHasFeature(handle_, WGPUFeatureName_DawnMultiPlanarFormats);
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter.h b/third_party/blink/renderer/modules/webgpu/gpu_adapter.h index 27742ace..4fe63df 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_adapter.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter.h
@@ -38,6 +38,7 @@ GPUSupportedFeatures* features() const; GPUSupportedLimits* limits() const { return limits_; } bool isFallbackAdapter() const; + WGPUBackendType backendType() const; void invalidate() { is_invalid_ = true; } bool SupportsMultiPlanarFormats() const; @@ -65,6 +66,7 @@ WGPUAdapter handle_; Member<GPU> gpu_; bool is_fallback_adapter_; + WGPUBackendType backend_type_; bool is_invalid_ = false; Member<GPUSupportedLimits> limits_; Member<GPUSupportedFeatures> features_;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_queue.cc b/third_party/blink/renderer/modules/webgpu/gpu_queue.cc index b7c7f90f..b4507d1 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_queue.cc +++ b/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
@@ -24,6 +24,7 @@ #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h" #include "third_party/blink/renderer/modules/webgpu/dawn_conversions.h" #include "third_party/blink/renderer/modules/webgpu/external_texture_helper.h" +#include "third_party/blink/renderer/modules/webgpu/gpu_adapter.h" #include "third_party/blink/renderer/modules/webgpu/gpu_buffer.h" #include "third_party/blink/renderer/modules/webgpu/gpu_command_buffer.h" #include "third_party/blink/renderer/modules/webgpu/gpu_device.h" @@ -678,14 +679,17 @@ image = unaccelerated_image.get(); #endif // BUILDFLAG(IS_LINUX) -#if BUILDFLAG(IS_MAC) -// TODO(crbug.com/1418291): -// Using webgpu mailbox texture to upload cpu-backed resource on mac uploads all 0. -// Disable that upload path if the image is not texture-backed. - if (!image->IsTextureBacked()) { - use_webgpu_mailbox_texture = false; + // TODO(crbug.com/1418291, crbug.com/1424119): + // Using a webgpu mailbox texture to upload a cpu-backed resource on Metal and + // OpenGLES uploads all zeros. Disable that upload path if the image is not + // texture-backed. + auto backendType = device()->adapter()->backendType(); + if (backendType == WGPUBackendType_Metal || + backendType == WGPUBackendType_OpenGLES) { + if (!image->IsTextureBacked()) { + use_webgpu_mailbox_texture = false; + } } -#endif // BUILDFLAG(IS_MAC) bool noop = copy_size.width == 0 || copy_size.height == 0 || copy_size.depthOrArrayLayers == 0;
diff --git a/third_party/blink/renderer/platform/audio/audio_destination.cc b/third_party/blink/renderer/platform/audio/audio_destination.cc index 4ac4ba6..2907770 100644 --- a/third_party/blink/renderer/platform/audio/audio_destination.cc +++ b/third_party/blink/renderer/platform/audio/audio_destination.cc
@@ -330,7 +330,7 @@ "sink information", audio_utilities::GetSinkInfoForTracing( sink_descriptor, latency_hint, - web_audio_device_->SampleRate(), number_of_output_channels, + number_of_output_channels, web_audio_device_->SampleRate(), callback_buffer_size_)); metric_reporter_.Initialize(
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index cde3e90..4f9a550 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -2564,6 +2564,10 @@ origin_trial_feature_name: "PNaCl", }, { + name: "PointerEventDeviceId", + status: "test", + }, + { name: "PointerLockOptions", origin_trial_feature_name: "PointerLockOptions", public: true,
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 31d0e988..60d48d3 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -1481,7 +1481,6 @@ ### Tests failing with LayoutNGFlexFragmentation enabled: crbug.com/1367912 external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-058.html [ Failure ] -crbug.com/1413089 external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-063-print.html [ Failure ] crbug.com/1367912 external/wpt/css/css-break/flexbox/single-line-column-flex-fragmentation-043.html [ Failure ] crbug.com/1225630 fast/multicol/flexbox/doubly-nested-with-zero-width-flexbox-and-forced-break-crash.html [ Skip Timeout ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites index 24edb12..bb510c1 100644 --- a/third_party/blink/web_tests/VirtualTestSuites +++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -1863,6 +1863,15 @@ "expires": "Jul 1, 2023" }, { + "prefix": "disable-device-id-pointer-event", + "platforms": ["Linux", "Mac", "Win"], + "bases": ["fast/events/pointerevents/device-id"], + "args": [ + "--disable-blink-features=PointerEventDeviceId" + ], + "expires": "Jul 1, 2023" + }, + { "prefix": "webrtc-legacy-stats-trial-disabled", "platforms": ["Linux", "Mac", "Win"], "bases": [
diff --git a/third_party/blink/web_tests/accessibility/element-role-mapping-normal-expected.txt b/third_party/blink/web_tests/accessibility/element-role-mapping-normal-expected.txt index 0eb8e4b3..1e18086 100644 --- a/third_party/blink/web_tests/accessibility/element-role-mapping-normal-expected.txt +++ b/third_party/blink/web_tests/accessibility/element-role-mapping-normal-expected.txt
@@ -43,6 +43,10 @@ Written by Julie Visit us at:www.chromium.org +Main Heading +Subheading + +Smaller Subheading January @@ -200,6 +204,16 @@ " AXRole: AXInlineTextBox " " + AXRole: AXGroup + AXRole: AXHeading "Main Heading" + AXRole: AXStaticText "Main Heading" + AXRole: AXInlineTextBox "Main Heading" + AXRole: AXHeading "Subheading" + AXRole: AXStaticText "Subheading" + AXRole: AXInlineTextBox "Subheading" + AXRole: AXParagraph + AXRole: AXStaticText "Smaller Subheading" + AXRole: AXInlineTextBox "Smaller Subheading" AXRole: AXParagraph AXRole: AXStaticText "January" AXRole: AXInlineTextBox "January"
diff --git a/third_party/blink/web_tests/accessibility/element-role-mapping-normal.html b/third_party/blink/web_tests/accessibility/element-role-mapping-normal.html index 33c0714..8c5ba67 100644 --- a/third_party/blink/web_tests/accessibility/element-role-mapping-normal.html +++ b/third_party/blink/web_tests/accessibility/element-role-mapping-normal.html
@@ -77,6 +77,11 @@ Written by Julie<br> Visit us at:www.chromium.org<br> </address> +<hgroup> + <h1>Main Heading</h1> + <h5>Subheading</h5> + <p>Smaller Subheading</p> +</hgroup> <p>January <dialog open>This is an open dialog window</dialog></p> <table> <caption>Caption</caption>
diff --git a/third_party/blink/web_tests/animations/web-animations/keyframe-exceptions.html b/third_party/blink/web_tests/animations/web-animations/keyframe-exceptions.html deleted file mode 100644 index c561d02..0000000 --- a/third_party/blink/web_tests/animations/web-animations/keyframe-exceptions.html +++ /dev/null
@@ -1,44 +0,0 @@ -<!DOCTYPE html> -<script src="../../resources/testharness.js"></script> -<script src="../../resources/testharnessreport.js"></script> - -<script> -function assert_throws_with_message(e, f, m) { - try { - f(); - } catch(exception) { - assert_equals(exception.name, e); - assert_equals(exception.message, m); - } -} - -test(function() { - assert_throws_with_message('TypeError', - function() { - document.documentElement.animate([ - {offset: 0.6}, - {offset: 0.4} - ]); - }, - "Failed to execute 'animate' on 'Element': Offsets must be montonically non-decreasing." - ); - - assert_throws_with_message('TypeError', - function() { - document.documentElement.animate([ - {offset: 'a donkey'} - ]); - }, - "Failed to execute 'animate' on 'Element': Failed to read the 'offset' property from 'BaseKeyframe': The provided double value is non-finite." - ); - - assert_throws_with_message('TypeError', - function() { - document.documentElement.animate([ - {offset: -1} - ]); - }, - "Failed to execute 'animate' on 'Element': Offsets must be null or in the range [0,1]." - ); -}); -</script>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json index 676b054..7d2bf89 100644 --- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json +++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -242266,7 +242266,7 @@ ] ], "nth-child-in-shadow-root.html": [ - "443a12d229f793c201248583934fa3c05d7f81af", + "9e6a5b129a37576f310266525c35c2cf7af0bcea", [ null, [ @@ -242343,6 +242343,19 @@ {} ] ], + "nth-child-of-in-shadow-root.html": [ + "1e949a3bd0e991d97d3ae87fc7420c81047477e6", + [ + null, + [ + [ + "/css/selectors/invalidation/nth-child-of-in-shadow-root-ref.html", + "==" + ] + ], + {} + ] + ], "nth-child-of-sibling.html": [ "5beaad6a58108dde8607e9940c4fb017e4056ab2", [ @@ -242396,7 +242409,7 @@ ] ], "nth-last-child-in-shadow-root.html": [ - "890abfe99720b056f068a52e76e7b1580189ffb2", + "a14f4765674cb45cc3b93f48125606a278612d24", [ null, [ @@ -242460,6 +242473,19 @@ {} ] ], + "nth-last-child-of-in-shadow-root.html": [ + "d403111785723ce4e96d4273c224a8d9d5e7b108", + [ + null, + [ + [ + "/css/selectors/invalidation/nth-last-child-of-in-shadow-root-ref.html", + "==" + ] + ], + {} + ] + ], "nth-last-child-of-sibling.html": [ "b7b9bd70ab8e3f3a2c95613709d26e501e5671aa", [ @@ -265759,16 +265785,6 @@ } }, "support": { - ".cache": { - "gitignore2.json": [ - "6b410e2a8e898bf82c7e7451b85d7df3a3ec217c", - [] - ], - "mtime.json": [ - "a979d0f5b0bf777a3d9e3f6695bca6f95b5ff78c", - [] - ] - }, ".gitignore": [ "d93e645d547894b50149d3726de2654957b6e06f", [] @@ -330573,7 +330589,7 @@ [] ], "nth-child-in-shadow-root-ref.html": [ - "f28d358cdc53d71501fd35c0c6dc2db41e042b2b", + "5e9600ad0e1c751ff37a9fe8d74ffe04b03b264b", [] ], "nth-child-of-attr-largedom-ref.html": [ @@ -330596,6 +330612,10 @@ "a4e4b7fd9fc44bd189aba0423d536dcb82201a9f", [] ], + "nth-child-of-in-shadow-root-ref.html": [ + "f28d358cdc53d71501fd35c0c6dc2db41e042b2b", + [] + ], "nth-child-of-sibling-ref.html": [ "0388fc7c5ab112271e3b2d54293765c8ad87eff0", [] @@ -330609,7 +330629,7 @@ [] ], "nth-last-child-in-shadow-root-ref.html": [ - "3eb330f58c50ed9c3cc5aefd67477e05dd1a9c33", + "c7adfe35579b1308658045dc8571aee8cceefe57", [] ], "nth-last-child-of-attr-ref.html": [ @@ -330628,6 +330648,10 @@ "21e6ae0f6f8c50a6d10f91b755cb35cf91713672", [] ], + "nth-last-child-of-in-shadow-root-ref.html": [ + "3eb330f58c50ed9c3cc5aefd67477e05dd1a9c33", + [] + ], "nth-last-child-of-sibling-ref.html": [ "349a7b66fb5f4480e5f994dc635b27623dcc3672", [] @@ -357486,7 +357510,7 @@ [] ], "popover-utils.js": [ - "7eb9e6327a285bbb880ae53c5e92c60b746cc7c0", + "b8b5817851167e9e985ca1a3f54d03a517ed9ef1", [] ] } @@ -543097,7 +543121,7 @@ ] ], "popover-attribute-all-elements.html": [ - "5fceee88c09400b8f9bad131db4ef77ce65ffafc", + "9ee659962b8997cca1813a26a068736c7d94699a", [ null, { @@ -543107,7 +543131,7 @@ ] ], "popover-attribute-basic.html": [ - "c5937cd3237e55b04d93798ad4271a2cca0a22d7", + "56d746e9dbe93bb663b44d7cf08ae679e1cacf57", [ null, { @@ -620844,171 +620868,6 @@ } ] ], - "per-frame-qp-encoding.https.any.js": [ - "3207fa8356c83ef9566348f9246d0f6570068615", - [ - "webcodecs/per-frame-qp-encoding.https.any.html?av1", - { - "script_metadata": [ - [ - "global", - "window,dedicatedworker" - ], - [ - "script", - "/webcodecs/video-encoder-utils.js" - ], - [ - "variant", - "?av1" - ], - [ - "variant", - "?vp9_p0" - ], - [ - "variant", - "?vp9_p2" - ] - ] - } - ], - [ - "webcodecs/per-frame-qp-encoding.https.any.html?vp9_p0", - { - "script_metadata": [ - [ - "global", - "window,dedicatedworker" - ], - [ - "script", - "/webcodecs/video-encoder-utils.js" - ], - [ - "variant", - "?av1" - ], - [ - "variant", - "?vp9_p0" - ], - [ - "variant", - "?vp9_p2" - ] - ] - } - ], - [ - "webcodecs/per-frame-qp-encoding.https.any.html?vp9_p2", - { - "script_metadata": [ - [ - "global", - "window,dedicatedworker" - ], - [ - "script", - "/webcodecs/video-encoder-utils.js" - ], - [ - "variant", - "?av1" - ], - [ - "variant", - "?vp9_p0" - ], - [ - "variant", - "?vp9_p2" - ] - ] - } - ], - [ - "webcodecs/per-frame-qp-encoding.https.any.worker.html?av1", - { - "script_metadata": [ - [ - "global", - "window,dedicatedworker" - ], - [ - "script", - "/webcodecs/video-encoder-utils.js" - ], - [ - "variant", - "?av1" - ], - [ - "variant", - "?vp9_p0" - ], - [ - "variant", - "?vp9_p2" - ] - ] - } - ], - [ - "webcodecs/per-frame-qp-encoding.https.any.worker.html?vp9_p0", - { - "script_metadata": [ - [ - "global", - "window,dedicatedworker" - ], - [ - "script", - "/webcodecs/video-encoder-utils.js" - ], - [ - "variant", - "?av1" - ], - [ - "variant", - "?vp9_p0" - ], - [ - "variant", - "?vp9_p2" - ] - ] - } - ], - [ - "webcodecs/per-frame-qp-encoding.https.any.worker.html?vp9_p2", - { - "script_metadata": [ - [ - "global", - "window,dedicatedworker" - ], - [ - "script", - "/webcodecs/video-encoder-utils.js" - ], - [ - "variant", - "?av1" - ], - [ - "variant", - "?vp9_p0" - ], - [ - "variant", - "?vp9_p2" - ] - ] - } - ] - ], "reconfiguring-encoder.https.any.js": [ "bc7e9b74fb917639b581b72096741395df1b9198", [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-065-ref.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-065-ref.html new file mode 100644 index 0000000..50cf3c3 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-065-ref.html
@@ -0,0 +1,35 @@ +<!DOCTYPE html> +<title> + Multi-line row flex fragmentation: row gaps should not be truncated by + fragmentainer breaks (similar to flex-item margins). +</title> +<style> + .multicol { + columns: 2; + column-fill: auto; + width: 300px; + height: 100px; + margin: 20px; + background: yellow; + } + .flex { + display: flex; + flex-wrap: wrap; + background: gray; + } + .flex > div { + contain: size; + width: 100%; + height: 50px; + background: cyan; + } +</style> +<p>Flex row gaps should <strong>not</strong> be truncated when a row breaks.</p> +<div class="multicol"> + <div class="flex"> + <div></div> + <div style="margin-top:100px;"></div> + <div style="margin-top:100px;"></div> + <div style="margin-top:100px;"></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-065.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-065.html new file mode 100644 index 0000000..10ae8ca --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-065.html
@@ -0,0 +1,37 @@ +<!DOCTYPE html> +<title> + Multi-line row flex fragmentation: row gaps should not be truncated by + fragmentainer breaks (similar to flex-item margins). +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="multi-line-row-flex-fragmentation-065-ref.html"> +<style> + .multicol { + columns: 2; + column-fill: auto; + width: 300px; + height: 100px; + margin: 20px; + background: yellow; + } + .flex { + display: flex; + flex-wrap: wrap; + background: gray; + } + .flex > div { + contain: size; + width: 100%; + height: 50px; + background: cyan; + } +</style> +<p>Flex row gaps should <strong>not</strong> be truncated when a row breaks.</p> +<div class="multicol"> + <div class="flex" style="row-gap:100px;"> + <div></div> + <div></div> + <div></div> + <div></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-066.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-066.html new file mode 100644 index 0000000..b4c7fcd --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-066.html
@@ -0,0 +1,32 @@ +<!DOCTYPE html> +<title> + Multi-line row flex fragmentation: tall content inside constrained block. +</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/6056#issuecomment-951767882"> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<style> + #flex { + display: flex; + flex-wrap: wrap; + height: 25px; + background: red; + } + #flex > div { + width: 100%; + background: green; + contain: size; + } +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div style="width:100px; height:100px; background:red;"> + <div style="columns:2; column-fill:auto; column-gap:0; height:100px;"> + <div style="height:50px; background:green;"></div> + <div id="flex"> + <div style="height:150px; position:relative;"> + <div style="position:absolute; top:50px; height:100px; width:100%; background:white;"></div> + </div> + <div style="height:100px;"></div> + </div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-067-ref.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-067-ref.html new file mode 100644 index 0000000..4d230d1 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-067-ref.html
@@ -0,0 +1,36 @@ +<!DOCTYPE html> +<title> + Multi-line row flex fragmentation: row gaps should not be truncated by + fragmentainer breaks (similar to flex-item margins). Test with forced breaks. +</title> +<style> + .multicol { + columns: 2; + column-fill: auto; + width: 300px; + height: 100px; + margin: 20px; + background: yellow; + } + .flex { + display: flex; + flex-wrap: wrap; + background: gray; + } + .flex > div { + contain: size; + width: 100%; + height: 50px; + background: cyan; + break-before: always; + } +</style> +<p>Flex row gaps should <strong>not</strong> be truncated when a row breaks.</p> +<div class="multicol"> + <div class="flex"> + <div></div> + <div style="margin-top:100px; break-after:avoid;"></div> + <div style="margin-top:100px;"></div> + <div style="margin-top:100px;"></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-067.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-067.html new file mode 100644 index 0000000..797089ca --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-067.html
@@ -0,0 +1,38 @@ +<!DOCTYPE html> +<title> + Multi-line row flex fragmentation: row gaps should not be truncated by + fragmentainer breaks (similar to flex-item margins). Test with forced breaks. +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="multi-line-row-flex-fragmentation-067-ref.html"> +<style> + .multicol { + columns: 2; + column-fill: auto; + width: 300px; + height: 100px; + margin: 20px; + background: yellow; + } + .flex { + display: flex; + flex-wrap: wrap; + background: gray; + } + .flex > div { + contain: size; + width: 100%; + height: 50px; + background: cyan; + break-before: always; + } +</style> +<p>Flex row gaps should <strong>not</strong> be truncated when a row breaks.</p> +<div class="multicol"> + <div class="flex" style="row-gap:100px;"> + <div></div> + <div style="break-after:avoid;"></div> + <div></div> + <div></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-068-ref.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-068-ref.html new file mode 100644 index 0000000..04263fb7 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-068-ref.html
@@ -0,0 +1,34 @@ +<!DOCTYPE html> +<title> + Multi-line row flex fragmentation: row gaps should not be truncated by + fragmentainer breaks (similar to flex-item margins). Test with content overflow. +</title> +<style> + .multicol { + columns: 2; + column-fill: auto; + width: 300px; + height: 100px; + margin: 20px; + background: yellow; + } + .flex { + display: flex; + flex-wrap: wrap; + background: gray; + } + .flex > div { + contain: size; + width: 100%; + height: 50px; + background: cyan; + } +</style> +<p>Flex row gaps should <strong>not</strong> be truncated when a row breaks.</p> +<div class="multicol"> + <div style="height:25px;"></div> + <div class="flex" style="height:25px;"> + <div style="height:150px;"></div> + <div style="margin-top:100px;"></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-068.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-068.html new file mode 100644 index 0000000..809f0c9 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-068.html
@@ -0,0 +1,36 @@ +<!DOCTYPE html> +<title> + Multi-line row flex fragmentation: row gaps should not be truncated by + fragmentainer breaks (similar to flex-item margins). Test with content overflow. +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="multi-line-row-flex-fragmentation-068-ref.html"> +<style> + .multicol { + columns: 2; + column-fill: auto; + width: 300px; + height: 100px; + margin: 20px; + background: yellow; + } + .flex { + display: flex; + flex-wrap: wrap; + background: gray; + } + .flex > div { + contain: size; + width: 100%; + height: 50px; + background: cyan; + } +</style> +<p>Flex row gaps should <strong>not</strong> be truncated when a row breaks.</p> +<div class="multicol"> + <div style="height:25px;"></div> + <div class="flex" style="row-gap:100px; height:25px;"> + <div style="height:150px;"></div> + <div></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-069-ref.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-069-ref.html new file mode 100644 index 0000000..723b3dbc --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-069-ref.html
@@ -0,0 +1,34 @@ +<!DOCTYPE html> +<title> + Multi-line row flex fragmentation: row gaps should not be truncated by + fragmentainer breaks (similar to flex-item margins). +</title> +<style> + .multicol { + columns: 2; + column-fill: auto; + width: 300px; + height: 100px; + margin: 20px; + background: yellow; + } + .flex { + display: flex; + flex-wrap: wrap; + background: gray; + } + .flex > div { + contain: size; + width: 100%; + height: 50px; + background: cyan; + } +</style> +<p>Flex row gaps should <strong>not</strong> be truncated when a row breaks.</p> +<div class="multicol"> + <div style="height:25px;"></div> + <div class="flex"> + <div></div> + <div style="margin-top:100px;"></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-069.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-069.html new file mode 100644 index 0000000..3c3ee17 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-069.html
@@ -0,0 +1,36 @@ +<!DOCTYPE html> +<title> + Multi-line row flex fragmentation: row gaps should not be truncated by + fragmentainer breaks (similar to flex-item margins). +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="multi-line-row-flex-fragmentation-069-ref.html"> +<style> + .multicol { + columns: 2; + column-fill: auto; + width: 300px; + height: 100px; + margin: 20px; + background: yellow; + } + .flex { + display: flex; + flex-wrap: wrap; + background: gray; + } + .flex > div { + contain: size; + width: 100%; + height: 50px; + background: cyan; + } +</style> +<p>Flex row gaps should <strong>not</strong> be truncated when a row breaks.</p> +<div class="multicol"> + <div style="height:25px;"></div> + <div class="flex" style="row-gap:100px;"> + <div></div> + <div></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-070-ref.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-070-ref.html new file mode 100644 index 0000000..ee511d2 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-070-ref.html
@@ -0,0 +1,34 @@ +<!DOCTYPE html> +<title> + Multi-line row flex fragmentation: row gaps should not be truncated by + fragmentainer breaks (similar to flex-item margins). Tests row gap that + is larger than the fragmentainer block-size. +</title> +<style> + .multicol { + columns: 2; + column-fill: auto; + width: 300px; + height: 100px; + margin: 20px; + background: yellow; + } + .flex { + display: flex; + flex-wrap: wrap; + background: gray; + } + .flex > div { + contain: size; + width: 100%; + height: 50px; + background: cyan; + } +</style> +<p>Flex row gaps should <strong>not</strong> be truncated when a row breaks.</p> +<div class="multicol"> + <div class="flex"> + <div></div> + <div style="margin-top:150px;"></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-070.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-070.html new file mode 100644 index 0000000..ea301c3 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-070.html
@@ -0,0 +1,36 @@ +<!DOCTYPE html> +<title> + Multi-line row flex fragmentation: row gaps should not be truncated by + fragmentainer breaks (similar to flex-item margins). Tests row gap that + is larger than the fragmentainer block-size. +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="multi-line-row-flex-fragmentation-070-ref.html"> +<style> + .multicol { + columns: 2; + column-fill: auto; + width: 300px; + height: 100px; + margin: 20px; + background: yellow; + } + .flex { + display: flex; + flex-wrap: wrap; + background: gray; + } + .flex > div { + contain: size; + width: 100%; + height: 50px; + background: cyan; + } +</style> +<p>Flex row gaps should <strong>not</strong> be truncated when a row breaks.</p> +<div class="multicol"> + <div class="flex" style="row-gap:150px;"> + <div></div> + <div></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-071-ref.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-071-ref.html new file mode 100644 index 0000000..cd7ca53 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-071-ref.html
@@ -0,0 +1,34 @@ +<!DOCTYPE html> +<title> + Multi-line row flex fragmentation: row gaps should not be truncated by + fragmentainer breaks (similar to flex-item margins). Tests row gap that + is larger than the fragmentainer block-size. +</title> +<style> + .multicol { + columns: 2; + column-fill: auto; + width: 300px; + height: 100px; + margin: 20px; + background: yellow; + } + .flex { + display: flex; + flex-wrap: wrap; + background: gray; + } + .flex > div { + contain: size; + width: 100%; + height: 50px; + background: cyan; + } +</style> +<p>Flex row gaps should <strong>not</strong> be truncated when a row breaks.</p> +<div class="multicol"> + <div class="flex"> + <div></div> + <div style="margin-top:400px;"></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-071.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-071.html new file mode 100644 index 0000000..4a855f7 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-071.html
@@ -0,0 +1,36 @@ +<!DOCTYPE html> +<title> + Multi-line row flex fragmentation: row gaps should not be truncated by + fragmentainer breaks (similar to flex-item margins). Tests row gap that + is larger than the fragmentainer block-size. +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="multi-line-row-flex-fragmentation-071-ref.html"> +<style> + .multicol { + columns: 2; + column-fill: auto; + width: 300px; + height: 100px; + margin: 20px; + background: yellow; + } + .flex { + display: flex; + flex-wrap: wrap; + background: gray; + } + .flex > div { + contain: size; + width: 100%; + height: 50px; + background: cyan; + } +</style> +<p>Flex row gaps should <strong>not</strong> be truncated when a row breaks.</p> +<div class="multicol"> + <div class="flex" style="row-gap:400px;"> + <div></div> + <div></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-child-in-shadow-root-ref.html b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-child-in-shadow-root-ref.html index f28d358..5e9600a 100644 --- a/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-child-in-shadow-root-ref.html +++ b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-child-in-shadow-root-ref.html
@@ -1,9 +1,7 @@ -<!DOCTYPE html> -<title>CSS Selectors Invalidation: :nth-child(... of class) within shadow root</title> -<link rel="author" title="Steinar H. Gunderson" href="sesse@chromium.org"> -<link rel="help" href="https://w3c.github.io/csswg-drafts/selectors-4/#child-index"> +<!doctype html> +<meta charset=utf-8> +<title>CSS Test Reference</title> <div> - <div>No green</div> - <div>No green</div> <div style="color: green">Should be green</div> + <div style="color: red">Should be red</div> </div>
diff --git a/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-child-in-shadow-root.html b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-child-in-shadow-root.html index 443a12d..9e6a5b1 100644 --- a/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-child-in-shadow-root.html +++ b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-child-in-shadow-root.html
@@ -1,23 +1,31 @@ -<!DOCTYPE html> -<title>CSS Selectors Invalidation: :nth-child(... of class) within shadow root</title> -<link rel="author" title="Steinar H. Gunderson" href="sesse@chromium.org"> +<!doctype html> +<title>CSS Selectors Invalidation: :nth-child(An+B) within shadow root</title> +<link rel="help" href="https://drafts.csswg.org/selectors-3/#nth-child-pseudo"> +<link rel=help href="https://bugzilla.mozilla.org/show_bug.cgi?id=1480746"> +<link rel="author" title="Zach Hoffman" href="mailto:zach@zrhoffman.net"> <link rel="match" href="nth-child-in-shadow-root-ref.html"> -<link rel="help" href="https://w3c.github.io/csswg-drafts/selectors-4/#child-index"> <script src="/resources/declarative-shadow-dom-polyfill.js"></script> <div id="host"> - <template shadowrootmode="open"> - <style> - div:nth-child(even of .foo) { - color: green; - } - </style> - <div class="foo" id="toggler">No green</div> - <div class="foo">No green</div> - <div class="foo">Should be green</div> - </template> + <template shadowrootmode="open"> + <div id="firstChild">Should be red</div> + <style> + :nth-child(odd) { + color: green; + } + :nth-child(even) { + color: red; + } + </style> + </template> </div> <script> - polyfill_declarative_shadow_dom(host); - host.offsetTop; - host.shadowRoot.getElementById('toggler').classList.toggle('foo'); +polyfill_declarative_shadow_dom(host); +host.offsetTop; +let div = document.createElement("div"); +div.appendChild(document.createTextNode("Should be green")); +requestAnimationFrame(() => + requestAnimationFrame(() => { + host.shadowRoot.insertBefore(div, host.shadowRoot.firstElementChild); + takeScreenshot(); + })); </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-child-of-in-shadow-root-ref.html b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-child-of-in-shadow-root-ref.html new file mode 100644 index 0000000..f28d358 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-child-of-in-shadow-root-ref.html
@@ -0,0 +1,9 @@ +<!DOCTYPE html> +<title>CSS Selectors Invalidation: :nth-child(... of class) within shadow root</title> +<link rel="author" title="Steinar H. Gunderson" href="sesse@chromium.org"> +<link rel="help" href="https://w3c.github.io/csswg-drafts/selectors-4/#child-index"> +<div> + <div>No green</div> + <div>No green</div> + <div style="color: green">Should be green</div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-child-of-in-shadow-root.html b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-child-of-in-shadow-root.html new file mode 100644 index 0000000..1e949a3 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-child-of-in-shadow-root.html
@@ -0,0 +1,23 @@ +<!DOCTYPE html> +<title>CSS Selectors Invalidation: :nth-child(... of class) within shadow root</title> +<link rel="author" title="Steinar H. Gunderson" href="sesse@chromium.org"> +<link rel="match" href="nth-child-of-in-shadow-root-ref.html"> +<link rel="help" href="https://w3c.github.io/csswg-drafts/selectors-4/#child-index"> +<script src="/resources/declarative-shadow-dom-polyfill.js"></script> +<div id="host"> + <template shadowrootmode="open"> + <style> + div:nth-child(even of .foo) { + color: green; + } + </style> + <div class="foo" id="toggler">No green</div> + <div class="foo">No green</div> + <div class="foo">Should be green</div> + </template> +</div> +<script> + polyfill_declarative_shadow_dom(host); + host.offsetTop; + host.shadowRoot.getElementById('toggler').classList.toggle('foo'); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-last-child-in-shadow-root-ref.html b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-last-child-in-shadow-root-ref.html index 3eb330f..c7adfe3 100644 --- a/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-last-child-in-shadow-root-ref.html +++ b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-last-child-in-shadow-root-ref.html
@@ -2,7 +2,6 @@ <meta charset=utf-8> <title>CSS Test Reference</title> <div> + <div style="color: red">Should be red</div> <div style="color: green">Should be green</div> - <div>No green</div> - <div>No green</div> </div>
diff --git a/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-last-child-in-shadow-root.html b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-last-child-in-shadow-root.html index 890abfe..a14f4765 100644 --- a/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-last-child-in-shadow-root.html +++ b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-last-child-in-shadow-root.html
@@ -1,25 +1,31 @@ <!doctype html> -<meta charset="utf-8"> -<title>CSS Selectors Invalidation: :nth-last-child(... of class) within shadow root</title> +<title>CSS Selectors Invalidation: :nth-last-child(An+B) within shadow root</title> +<link rel="help" href="https://drafts.csswg.org/selectors-3/#nth-last-child-pseudo"> +<link rel=help href="https://bugzilla.mozilla.org/show_bug.cgi?id=1480746"> <link rel="author" title="Zach Hoffman" href="mailto:zach@zrhoffman.net"> -<link rel="author" title="Steinar H. Gunderson" href="mailto:sesse@chromium.org"> <link rel="match" href="nth-last-child-in-shadow-root-ref.html"> -<link rel="help" href="https://drafts.csswg.org/selectors-4/#child-index"> <script src="/resources/declarative-shadow-dom-polyfill.js"></script> <div id="host"> - <template shadowrootmode="open"> - <style> - div:nth-last-child(even of .foo) { - color: green; - } - </style> - <div class="foo">Should be green</div> - <div class="foo">No green</div> - <div class="foo" id="toggler">No green</div> - </template> + <template shadowrootmode="open"> + <div id="firstChild">Should be red</div> + <style> + :nth-last-child(odd) { + color: red; + } + :nth-last-child(even) { + color: green; + } + </style> + </template> </div> <script> - polyfill_declarative_shadow_dom(host); - host.offsetTop; - host.shadowRoot.getElementById("toggler").classList.toggle("foo"); +polyfill_declarative_shadow_dom(host); +host.offsetTop; +let div = document.createElement("div"); +div.appendChild(document.createTextNode("Should be green")); +requestAnimationFrame(() => + requestAnimationFrame(() => { + host.shadowRoot.insertBefore(div, host.shadowRoot.firstElementChild.nextSibling); + takeScreenshot(); + })); </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-last-child-of-in-shadow-root-ref.html b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-last-child-of-in-shadow-root-ref.html new file mode 100644 index 0000000..3eb330f --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-last-child-of-in-shadow-root-ref.html
@@ -0,0 +1,8 @@ +<!doctype html> +<meta charset=utf-8> +<title>CSS Test Reference</title> +<div> + <div style="color: green">Should be green</div> + <div>No green</div> + <div>No green</div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-last-child-of-in-shadow-root.html b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-last-child-of-in-shadow-root.html new file mode 100644 index 0000000..d403111 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/nth-last-child-of-in-shadow-root.html
@@ -0,0 +1,25 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Selectors Invalidation: :nth-last-child(... of class) within shadow root</title> +<link rel="author" title="Zach Hoffman" href="mailto:zach@zrhoffman.net"> +<link rel="author" title="Steinar H. Gunderson" href="mailto:sesse@chromium.org"> +<link rel="match" href="nth-last-child-of-in-shadow-root-ref.html"> +<link rel="help" href="https://drafts.csswg.org/selectors-4/#child-index"> +<script src="/resources/declarative-shadow-dom-polyfill.js"></script> +<div id="host"> + <template shadowrootmode="open"> + <style> + div:nth-last-child(even of .foo) { + color: green; + } + </style> + <div class="foo">Should be green</div> + <div class="foo">No green</div> + <div class="foo" id="toggler">No green</div> + </template> +</div> +<script> + polyfill_declarative_shadow_dom(host); + host.offsetTop; + host.shadowRoot.getElementById("toggler").classList.toggle("foo"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-attribute-all-elements.html b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-attribute-all-elements.html index 5fceee88..9ee6599 100644 --- a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-attribute-all-elements.html +++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-attribute-all-elements.html
@@ -24,7 +24,7 @@ element.setAttribute('popover','auto'); document.body.appendChild(element); t.add_cleanup(() => element.remove()); - assertIsFunctionalPopover(element); + assertIsFunctionalPopover(element, true); }, `A <${tag} popover> element should behave as a popover.`); test((t) => { const element = document.createElement(tag); @@ -39,7 +39,7 @@ element.setAttribute('popover','auto'); document.body.appendChild(element); t.add_cleanup(() => element.remove()); - assert_equals(window.getComputedStyle(element).display, "none", "Element isn't rendered because of the [popover]:closed UA sheet rule"); + assertIsFunctionalPopover(element, false); }, `A <${tag} popover> element should not be rendered.`); }); done();
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-attribute-basic.html b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-attribute-basic.html index c5937cd3..56d746e 100644 --- a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-attribute-basic.html +++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-attribute-basic.html
@@ -56,7 +56,7 @@ // Start with the provided examples: Array.from(document.getElementById('popovers').children).forEach(popover => { test((t) => { - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); }, `The element ${popover.outerHTML} should behave as a popover.`); }); Array.from(document.getElementById('nonpopovers').children).forEach(nonPopover => { @@ -121,34 +121,34 @@ test((t) => { const popover = createPopover(t); - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); popover.removeAttribute('popover'); assertNotAPopover(popover); popover.setAttribute('popover','AuTo'); - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); popover.removeAttribute('popover'); popover.setAttribute('PoPoVeR','AuTo'); - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); // Via IDL also popover.popover = 'auto'; - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); popover.popover = 'aUtO'; - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); popover.popover = 'invalid'; // treated as "manual" - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); },'Popover attribute value should be case insensitive'); test((t) => { const popover = createPopover(t); - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); popover.setAttribute('popover','manual'); // Change popover type - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); popover.setAttribute('popover','invalid'); // Change popover type to something invalid - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); popover.popover = 'manual'; // Change popover type via IDL - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); popover.popover = 'invalid'; // Make invalid via IDL (treated as "manual") - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); },'Changing attribute values for popover should work'); test((t) => {
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/resources/popover-utils.js b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/resources/popover-utils.js index 7eb9e63..b8b5817 100644 --- a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/resources/popover-utils.js +++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/resources/popover-utils.js
@@ -130,26 +130,26 @@ } } -function assertIsFunctionalPopover(popover) { +function assertIsFunctionalPopover(popover, checkVisibility) { assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'A popover should start out hidden'); popover.showPopover(); - assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After showPopover(), a popover should be visible'); + if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After showPopover(), a popover should be visible'); assert_throws_dom("InvalidStateError",() => popover.showPopover(),'Calling showPopover on a showing popover should throw InvalidStateError'); popover.hidePopover(); - assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After hidePopover(), a popover should be hidden'); + if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After hidePopover(), a popover should be hidden'); assert_throws_dom("InvalidStateError",() => popover.hidePopover(),'Calling hidePopover on a hidden popover should throw InvalidStateError'); popover.togglePopover(); - assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After togglePopover() on hidden popover, it should be visible'); + if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After togglePopover() on hidden popover, it should be visible'); popover.togglePopover(); - assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After togglePopover() on visible popover, it should be hidden'); + if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After togglePopover() on visible popover, it should be hidden'); popover.togglePopover(/*force=*/true); - assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After togglePopover(true) on hidden popover, it should be visible'); + if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After togglePopover(true) on hidden popover, it should be visible'); popover.togglePopover(/*force=*/true); - assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After togglePopover(true) on visible popover, it should be visible'); + if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After togglePopover(true) on visible popover, it should be visible'); popover.togglePopover(/*force=*/false); - assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After togglePopover(false) on visible popover, it should be hidden'); + if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After togglePopover(false) on visible popover, it should be hidden'); popover.togglePopover(/*force=*/false); - assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After togglePopover(false) on hidden popover, it should be hidden'); + if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After togglePopover(false) on hidden popover, it should be hidden'); const parent = popover.parentElement; popover.remove(); assert_throws_dom("InvalidStateError",() => popover.showPopover(),'Calling showPopover on a disconnected popover should throw InvalidStateError');
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/timeline-offset-in-keyframe.html b/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/timeline-offset-in-keyframe.html new file mode 100644 index 0000000..62a8d13 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/timeline-offset-in-keyframe.html
@@ -0,0 +1,263 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#named-timeline-range"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/web-animations/testcommon.js"></script> +<script src="support/testcommon.js"></script> +<title>Animation range and delay</title> +</head> +<style type="text/css"> + #scroller { + border: 10px solid lightgray; + overflow-y: scroll; + overflow-x: hidden; + width: 300px; + height: 200px; + } + #target { + margin: 800px 10px; + width: 100px; + height: 100px; + z-index: -1; + background-color: green; + } +</style> +<body> + <div id=scroller> + <div id=target></div> + </div> +</body> +<script type="text/javascript"> + async function runTest() { + function assert_progress_equals(anim, expected, errorMessage) { + assert_approx_equals( + anim.effect.getComputedTiming().progress, + expected, 1e-6, errorMessage); + } + + function assert_opacity_equals(expected, errorMessage) { + assert_approx_equals( + parseFloat(getComputedStyle(target).opacity), expected, 1e-6, + errorMessage); + } + + async function runTimelineOffsetsInKeyframesTest(keyframes) { + const testcase = JSON.stringify(keyframes); + const anim = target.animate(keyframes, { + timeline: new ViewTimeline( { subject: target }), + rangeStart: { rangeName: 'contain', offset: CSS.percent(0) }, + rangeEnd: { rangeName: 'contain', offset: CSS.percent(100) }, + duration: 'auto', fill: 'both' + }); + await anim.ready; + await waitForNextFrame(); + + // @ contain 0% + scroller.scrollTop = 700; + await waitForNextFrame(); + + assert_progress_equals( + anim, 0, `Testcase '${testcase}': progress at contain 0%`); + assert_opacity_equals( + 1/3, `Testcase '${testcase}': opacity at contain 0%`); + + // @ contain 50% + scroller.scrollTop = 750; + await waitForNextFrame(); + assert_progress_equals( + anim, 0.5, `Testcase '${testcase}': progress at contain 50%`); + assert_opacity_equals( + 0.5, `Testcase '${testcase}': opacity at contain 50%`); + + // @ contain 100% + scroller.scrollTop = 800; + await waitForNextFrame(); + assert_progress_equals( + anim, 1, `Testcase '${testcase}': progress at contain 100%`); + assert_opacity_equals( + 2/3, `Testcase '${testcase}': opacity at contain 100%`); + anim.cancel(); + } + + async function runParseNumberOrPercentInKeyframesTest(keyframes) { + const anim = target.animate(keyframes, { + timeline: new ViewTimeline( { subject: target }), + rangeStart: { rangeName: 'contain', offset: CSS.percent(0) }, + rangeEnd: { rangeName: 'contain', offset: CSS.percent(100) }, + duration: 'auto', fill: 'both' + }); + await anim.ready; + await waitForNextFrame(); + + const maxScroll = scroller.scrollHeight - scroller.clientHeight; + scroller.scrollTop = maxScroll / 2; + await waitForNextFrame(); + + const testcase = JSON.stringify(keyframes); + assert_progress_equals(anim, 0.5, testcase); + assert_opacity_equals(0.5, testcase); + anim.cancel(); + } + + async function runInvalidKeyframesTest(keyframes) { + assert_throws_js(TypeError, () => { + target.animate(keyframes, { + timeline: new ViewTimeline( { subject: target }), + }); + }, `Invalid keyframes test case "${JSON.stringify(keyframes)}"`); + } + + promise_test(async t => { + // Test equivalent typed-OM and CSS representations of timeline offsets. + // Test array and object form for keyframes. + const keyframeTests = [ + // BaseKeyframe form with offsets expressed as typed-OM. + [ + { + offset: { rangeName: 'cover', offset: CSS.percent(0) }, + opacity: 0 + }, + { + offset: { rangeName: 'cover', offset: CSS.percent(100) }, + opacity: 1 + } + ], + // BaseKeyframe form with offsets expressed as CSS text. + [ + { offset: "cover 0%", opacity: 0 }, + { offset: "cover 100%", opacity: 1 } + ], + // BasePropertyIndexedKeyframe form with offsets expressed as typed-OM. + { + opacity: [0, 1], + offset: [ + { rangeName: 'cover', offset: CSS.percent(0) }, + { rangeName: 'cover', offset: CSS.percent(100) } + ] + }, + // BasePropertyIndexedKeyframe form with offsets expressed as CSS text. + { opacity: [0, 1], offset: [ "cover 0%", "cover 100%" ]} + ]; + + for (let i = 0; i < keyframeTests.length; i++) { + await runTimelineOffsetsInKeyframesTest(keyframeTests[i]); + } + + }, 'Timeline offsets in programmatic keyframes'); + + promise_test(async t => { + const keyframeTests = [ + [{offset: "0.5", opacity: 0.5 }], + [{offset: "50%", opacity: 0.5 }], + [{offset: "calc(20% + 30%)", opacity: 0.5 }] + ]; + + for (let i = 0; i < keyframeTests.length; i++) { + await runParseNumberOrPercentInKeyframesTest(keyframeTests[i]); + } + + }, 'String offsets in programmatic keyframes'); + + promise_test(async t => { + const invalidKeyframeTests = [ + // BasePropertyKefyrame: + [{ offset: { rangeName: 'somewhere', offset: CSS.percent(0) }}], + [{ offset: { rangeName: 'entry', offset: CSS.px(0) }}], + [{ offset: "here 0%" }], + [{ offset: "entry 3px" }], + // BasePropertyIndexedKeyframe with sequence: + { offset: [{ rangeName: 'somewhere', offset: CSS.percent(0) }]}, + { offset: [{ rangeName: 'entry', offset: CSS.px(0) }]}, + { offset: ["here 0%"] }, + { offset: ["entry 3px" ]}, + // BasePropertyIndexedKeyframe without sequence: + { offset: { rangeName: 'somewhere', offset: CSS.percent(0) }}, + { offset: { rangeName: 'entry', offset: CSS.px(0) }}, + { offset: "here 0%" }, + { offset: "entry 3px" }, + // <number> or <percent> as string: + [{ offset: "-1" }], + [{ offset: "2" }], + [{ offset: "-10%" }], + [{ offset: "110%" }], + { offset: ["-1"], opacity: [0.5] }, + { offset: ["2"], opacity: [0.5] }, + { offset: "-1", opacity: 0.5 }, + { offset: "2", opacity: 0.5 }, + // Extra stuff at the end. + [{ offset: "0.5 trailing nonsense" }], + [{ offset: "cover 50% eureka" }] + ]; + for( let i = 0; i < invalidKeyframeTests.length; i++) { + await runInvalidKeyframesTest(invalidKeyframeTests[i]); + } + }, 'Invalid timeline offset in programmatic keyframe throws'); + + + promise_test(async t => { + const anim = target.animate([ + { offset: "cover 0%", opacity: 0 }, + { offset: "cover 100%", opacity: 1 } + ], { + rangeStart: { rangeName: 'contain', offset: CSS.percent(0) }, + rangeEnd: { rangeName: 'contain', offset: CSS.percent(100) }, + duration: 10000, fill: 'both' + }); + + scroller.scrollTop = 750; + + await anim.ready; + assert_opacity_equals(1, `Opacity with document timeline`); + + anim.timeline = new ViewTimeline( { subject: target }); + await waitForNextFrame(); + + assert_progress_equals(anim, 0.5, `Progress at contain 50%`); + assert_opacity_equals(0.5, `Opacity at contain 50%`); + + anim.timeline = document.timeline; + await waitForNextFrame(); + assert_opacity_equals(1, `Opacity after resetting timeline`); + + anim.cancel(); + }, 'Timeline offsets in programmatic keyframes adjust for change in ' + + 'timeline'); + + promise_test(async t => { + const anim = target.animate([], { + timeline: new ViewTimeline( { subject: target }), + rangeStart: { rangeName: 'contain', offset: CSS.percent(0) }, + rangeEnd: { rangeName: 'contain', offset: CSS.percent(100) }, + duration: 'auto', fill: 'both' + }); + + await anim.ready; + await waitForNextFrame(); + + scroller.scrollTop = 750; + await waitForNextFrame(); + assert_progress_equals( + anim, 0.5, `Progress at contain 50% before effect change`); + assert_opacity_equals(1, `Opacity at contain 50% before effect change`); + + anim.effect = new KeyframeEffect(target, [ + { offset: "cover 0%", opacity: 0 }, + { offset: "cover 100%", opacity: 1 } + ], { duration: 'auto', fill: 'both' }); + await waitForNextFrame(); + assert_progress_equals( + anim, 0.5, `Progress at contain 50% after effect change`); + assert_opacity_equals(0.5, `Opacity at contain 50% after effect change`); + }, 'Timeline offsets in programmetic keyframes resolved when updating ' + + 'the animation effect'); + } + + // TODO(kevers): Add tests for getKeyframes once + // https://github.com/w3c/csswg-drafts/issues/8507 is resolved. + + window.onload = runTest; +</script> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/keyframe-effects/keyframe-exceptions.html b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/keyframe-effects/keyframe-exceptions.html new file mode 100644 index 0000000..4cb4be7 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/keyframe-effects/keyframe-exceptions.html
@@ -0,0 +1,39 @@ +<!DOCTYPE html> +<head> +<meta charset="utf-8"> +<link rel="help" src="https://www.w3.org/TR/web-animations-1/#processing-a-keyframes-argument"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/web-animations/testcommon.js"></script> +<script src="support/testcommon.js"></script> +<title>Keyframes with invalid offsets</title> +</head> +<script> + test(() => { + assert_throws_js(TypeError, + function() { + document.documentElement.animate([ + {offset: 0.6}, + {offset: 0.4} + ]); + }); + }, 'Offsets must be loosely sorted'); + + test(() => { + assert_throws_js(TypeError, + function() { + document.documentElement.animate([ + {offset: 'whatever'} + ]); + }); + }, 'Invalid offset'); + + test(() => { + assert_throws_js(TypeError, + function() { + document.documentElement.animate([ + {offset: -1} + ]); + }); + }, 'Offsets must be null or in the range [0,1]'); +</script>
diff --git a/third_party/blink/web_tests/fast/events/pointerevents/device-id/get-device-id-from-pointer-event.html b/third_party/blink/web_tests/fast/events/pointerevents/device-id/get-device-id-from-pointer-event.html new file mode 100644 index 0000000..0976983 --- /dev/null +++ b/third_party/blink/web_tests/fast/events/pointerevents/device-id/get-device-id-from-pointer-event.html
@@ -0,0 +1,50 @@ +<!DOCTYPE html> +<script src="../../../../resources/testharness.js"></script> +<script src="../../../../resources/testharnessreport.js"></script> +<style> + div { + user-select: none; // Prevents text selection on drag. + } +</style> +<div id="logger" draggable="false"></div> +<div id="console"></div> +<!-- This test verifies that if the PointerEventDeviceId flag is enabled, + pointerEvent.deviceId is null. If not, it is undefined. --> +<script> + function sendPenDragAt(x, y) { + return new Promise(function(resolve, reject) { + if (window.chrome && chrome.gpuBenchmarking) { + var pointerActions = + [{source: "pen", + actions: [ + { name: "pointerDown", x: x, y: y }, + { name: "pointerMove", x: x + 3, y: y + 3}, + { name: "pointerUp" }, + ]}]; + chrome.gpuBenchmarking.pointerActionSequence(pointerActions, resolve); + } + else { + reject(); + } + }); + } + + function CheckDeviceId(event) { + eventFired++; + if (self.internals.runtimeFlags.pointerEventDeviceIdEnabled) + assert_equals(event.deviceId, -1, "deviceId is -1"); + else + assert_equals(event.deviceId, undefined, "deviceId is undefined") + } + + window.addEventListener("pointerdown", CheckDeviceId, false); + window.addEventListener("pointermove", CheckDeviceId, false); + + promise_test(async () => { + if (!window.internals) + return; + eventFired = 0; + await sendPenDragAt(100, 100); + assert_true(eventFired == 2); + }, 'PointerEvent.deviceId'); +</script> \ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/events/pointerevents/device-id/pointer-event-has-device-id-from-pointer-event-init.html b/third_party/blink/web_tests/fast/events/pointerevents/device-id/pointer-event-has-device-id-from-pointer-event-init.html new file mode 100644 index 0000000..97a1e7b --- /dev/null +++ b/third_party/blink/web_tests/fast/events/pointerevents/device-id/pointer-event-has-device-id-from-pointer-event-init.html
@@ -0,0 +1,54 @@ +<!DOCTYPE html> +<script src="../../../../resources/testharness.js"></script> +<script src="../../../../resources/testharnessreport.js"></script> +<div id="console"></div> + +<!-- This test verifies that if the kPointerEventDeviceId flag is enabled, + pointerEvent.deviceId can be set via PointerEventInit. If not, it is + undefined. --> +<script> + function CheckDeviceId(event) { + if (internals.runtimeFlags.pointerEventDeviceIdEnabled) + assert_equals(event.deviceId, 1001, "deviceId is populated"); + else + assert_equals(event.deviceId, undefined, "deviceId is undefined"); + } + + promise_test(async () => { + if (!window.internals) + return; + var downEvent = new PointerEvent("pointerdown", + {pointerId: 1, + bubbles: true, + cancelable: true, + pointerType: "pen", + width: 100, + height: 100, + isPrimary: true, + deviceId: 1001 + }); + CheckDeviceId(downEvent); + var moveEvent = new PointerEvent("pointermove", + {pointerId: 1, + bubbles: true, + cancelable: true, + pointerType: "pen", + width: 100, + height: 100, + isPrimary: true, + deviceId: 1001 + }); + CheckDeviceId(moveEvent); + var upEvent = new PointerEvent("pointeup", + {pointerId: 1, + bubbles: true, + cancelable: true, + pointerType: "pen", + width: 100, + height: 100, + isPrimary: true, + deviceId: 1001 + }); + CheckDeviceId(upEvent); + }, 'PointerEvent.deviceId via PointerEventInit'); +</script> \ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/disable-device-id-pointer-event/fast/events/pointerevents/device-id/README.txt b/third_party/blink/web_tests/virtual/disable-device-id-pointer-event/fast/events/pointerevents/device-id/README.txt new file mode 100644 index 0000000..7399b37 --- /dev/null +++ b/third_party/blink/web_tests/virtual/disable-device-id-pointer-event/fast/events/pointerevents/device-id/README.txt
@@ -0,0 +1,2 @@ +This test suite runs the tests in fast/events/pointerevents/device-id +with the flag PointerEventDeviceId disabled. \ No newline at end of file
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt index df3feb6..a586cca 100644 --- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt +++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -7046,6 +7046,7 @@ attribute @@toStringTag getter altitudeAngle getter azimuthAngle + getter deviceId getter height getter isPrimary getter pointerId
diff --git a/third_party/espresso/3pp/3pp.pb b/third_party/espresso/3pp/3pp.pb new file mode 100644 index 0000000..d53352a --- /dev/null +++ b/third_party/espresso/3pp/3pp.pb
@@ -0,0 +1,21 @@ +create { + source { + cipd { + pkg: 'chromium/third_party/espresso' + default_version: '2@2.2.1' + # Doesnt really exist anymore but thats where the Readme says it was from. + original_download_url: 'https://google.github.io/android-testing-support-library/docs/espresso/' + } + patch_version: 'cr1' + } + + build { + dep: 'chromium/third_party/android_build_tools/jetifier' + dep: 'chromium/third_party/jdk' + } +} + +upload { + universal: true + pkg_prefix: 'chromium/third_party' +}
diff --git a/third_party/espresso/3pp/install.sh b/third_party/espresso/3pp/install.sh new file mode 100755 index 0000000..687f5b1 --- /dev/null +++ b/third_party/espresso/3pp/install.sh
@@ -0,0 +1,23 @@ +#!/bin/bash +# Copyright 2023 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e +set -x +set -o pipefail + +PREFIX="$1" +DEPS_PREFIX="$2" + +mkdir "$PREFIX/lib/" + +export JAVA_HOME=$DEPS_PREFIX/current + +JETIFIER_CONFIG="./.3pp/chromium/third_party/espresso/jetifier.config" + +$DEPS_PREFIX/bin/jetifier-standalone -c $JETIFIER_CONFIG -i ./lib/espresso-intents-release-no-dep.jar -o $PREFIX/lib/espresso-intents-release-no-dep.jar +$DEPS_PREFIX/bin/jetifier-standalone -c $JETIFIER_CONFIG -i ./lib/espresso-web-release-no-dep.jar -o $PREFIX/lib/espresso-web-release-no-dep.jar +$DEPS_PREFIX/bin/jetifier-standalone -c $JETIFIER_CONFIG -i ./lib/espresso-core-release-no-dep.jar -o $PREFIX/lib/espresso-core-release-no-dep.jar +$DEPS_PREFIX/bin/jetifier-standalone -c $JETIFIER_CONFIG -i ./lib/espresso-idling-resource-release-no-dep.jar -o $PREFIX/lib/espresso-idling-resource-release-no-dep.jar +$DEPS_PREFIX/bin/jetifier-standalone -c $JETIFIER_CONFIG -i ./lib/espresso-contrib-release-no-dep.jar -o $PREFIX/lib/espresso-contrib-release-no-dep.jar
diff --git a/third_party/espresso/3pp/jetifier.config b/third_party/espresso/3pp/jetifier.config new file mode 100644 index 0000000..dadf164 --- /dev/null +++ b/third_party/espresso/3pp/jetifier.config
@@ -0,0 +1,4798 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# This file has manual edits but was originally from jetifer 1.0.0-beta10 + +{ + "restrictToPackagePrefixes": [ + "android/support/", + "android/arch/", + "android/databinding/", + "com/android/databinding/library/baseAdapters/" + ], + "reversedRestrictToPackagePrefixes": [ + "androidx/", + "com/google/android/material/" + ], + "rules": [ + { + "from": "(.*)BuildConfig", + "to": "ignoreInPreprocessorOnly" + }, + { + "from": "(.*)/package-info", + "to": "ignoreInPreprocessorOnly" + }, + { + "from": "android/support/exifinterface/test/R(.*)", + "to": "ignore" + }, + { + "from": "android/support/v4/os/ResultReceiver(.*)", + "to": "ignore" + }, + { + "from": "(.*)Parcelizer", + "to": "{0}Parcelizer" + }, + { + "from": "android/support/wearable/(.*)", + "to": "ignore" + }, + { + "from": "android/support/compat/R(.*)", + "to": "androidx/core/R{0}" + }, + { + "from": "android/support/mediacompat/R(.*)", + "to": "androidx/media/R{0}" + }, + { + "from": "android/support/v7/cardview/R(.*)", + "to": "androidx/cardview/R{0}" + }, + { + "from": "android/support/percent/R(.*)", + "to": "androidx/percentlayout/R{0}" + }, + { + "from": "android/support/coordinatorlayout/R(.*)", + "to": "androidx/coordinatorlayout/R{0}" + }, + { + "from": "android/support/text/emoji/R(.*)", + "to": "androidx/emoji/R{0}" + }, + { + "from": "android/support/v7/recyclerview/R(.*)", + "to": "androidx/recyclerview/R{0}" + }, + { + "from": "android/support/v7/gridlayout/R(.*)", + "to": "androidx/gridlayout/R{0}" + }, + { + "from": "android/support/v7/appcompat/R(.*)", + "to": "androidx/appcompat/R{0}" + }, + { + "from": "android/support/v7/mediarouter/R(.*)", + "to": "androidx/mediarouter/R{0}" + }, + { + "from": "android/support/v4/app/ActionBarDrawerToggle(.*)", + "to": "androidx/legacy/app/ActionBarDrawerToggle{0}" + }, + { + "from": "android/support/v4/widget/Space(.*)", + "to": "androidx/legacy/widget/Space{0}" + }, + { + "from": "android/support/v4/content/WakefulBroadcastReceiver(.*)", + "to": "androidx/legacy/content/WakefulBroadcastReceiver{0}" + }, + { + "from": "android/support/v13/view/ViewCompat(.*)", + "to": "androidx/legacy/view/ViewCompat{0}" + }, + { + "from": "android/support/v13/app/ActivityCompat(.*)", + "to": "androidx/legacy/app/ActivityCompat{0}" + }, + { + "from": "android/support/v13/app/FragmentCompat(.*)", + "to": "androidx/legacy/app/FragmentCompat{0}" + }, + { + "from": "android/support/v13/app/FragmentPagerAdapter(.*)", + "to": "androidx/legacy/app/FragmentPagerAdapter{0}" + }, + { + "from": "android/support/v13/app/FragmentStatePagerAdapter(.*)", + "to": "androidx/legacy/app/FragmentStatePagerAdapter{0}" + }, + { + "from": "android/support/v13/app/FragmentTabHost(.*)", + "to": "androidx/legacy/app/FragmentTabHost{0}" + }, + { + "from": "android/support/v7/widget/AdapterHelper(.*)", + "to": "androidx/recyclerview/widget/AdapterHelper{0}" + }, + { + "from": "android/support/v7/widget/ChildHelper(.*)", + "to": "androidx/recyclerview/widget/ChildHelper{0}" + }, + { + "from": "android/support/v7/widget/DefaultItemAnimator(.*)", + "to": "androidx/recyclerview/widget/DefaultItemAnimator{0}" + }, + { + "from": "android/support/v7/widget/DividerItemDecoration(.*)", + "to": "androidx/recyclerview/widget/DividerItemDecoration{0}" + }, + { + "from": "android/support/v7/widget/FastScroller(.*)", + "to": "androidx/recyclerview/widget/FastScroller{0}" + }, + { + "from": "android/support/v7/widget/GapWorker(.*)", + "to": "androidx/recyclerview/widget/GapWorker{0}" + }, + { + "from": "android/support/v7/widget/GridLayoutManager(.*)", + "to": "androidx/recyclerview/widget/GridLayoutManager{0}" + }, + { + "from": "android/support/v7/widget/LayoutState(.*)", + "to": "androidx/recyclerview/widget/LayoutState{0}" + }, + { + "from": "android/support/v7/widget/LinearLayoutManager(.*)", + "to": "androidx/recyclerview/widget/LinearLayoutManager{0}" + }, + { + "from": "android/support/v7/widget/LinearSmoothScroller(.*)", + "to": "androidx/recyclerview/widget/LinearSmoothScroller{0}" + }, + { + "from": "android/support/v7/widget/LinearSnapHelper(.*)", + "to": "androidx/recyclerview/widget/LinearSnapHelper{0}" + }, + { + "from": "android/support/v7/widget/OpReorderer(.*)", + "to": "androidx/recyclerview/widget/OpReorderer{0}" + }, + { + "from": "android/support/v7/widget/OrientationHelper(.*)", + "to": "androidx/recyclerview/widget/OrientationHelper{0}" + }, + { + "from": "android/support/v7/widget/PagerSnapHelper(.*)", + "to": "androidx/recyclerview/widget/PagerSnapHelper{0}" + }, + { + "from": "android/support/v7/widget/PositionMap(.*)", + "to": "androidx/recyclerview/widget/PositionMap{0}" + }, + { + "from": "android/support/v7/widget/RecyclerViewAccessibilityDelegate(.*)", + "to": "androidx/recyclerview/widget/RecyclerViewAccessibilityDelegate{0}" + }, + { + "from": "android/support/v7/widget/RecyclerView(.*)", + "to": "androidx/recyclerview/widget/RecyclerView{0}" + }, + { + "from": "android/support/v7/widget/ScrollbarHelper(.*)", + "to": "androidx/recyclerview/widget/ScrollbarHelper{0}" + }, + { + "from": "android/support/v7/widget/SimpleItemAnimator(.*)", + "to": "androidx/recyclerview/widget/SimpleItemAnimator{0}" + }, + { + "from": "android/support/v7/widget/SnapHelper(.*)", + "to": "androidx/recyclerview/widget/SnapHelper{0}" + }, + { + "from": "android/support/v7/widget/StaggeredGridLayoutManager(.*)", + "to": "androidx/recyclerview/widget/StaggeredGridLayoutManager{0}" + }, + { + "from": "android/support/v7/widget/ViewBoundsCheck(.*)", + "to": "androidx/recyclerview/widget/ViewBoundsCheck{0}" + }, + { + "from": "android/support/v7/widget/ViewInfoStore(.*)", + "to": "androidx/recyclerview/widget/ViewInfoStore{0}" + }, + { + "from": "android/support/v7/recyclerview/extensions/(.*)", + "to": "androidx/recyclerview/widget/{0}" + }, + { + "from": "android/support/v7/util/(.*)", + "to": "androidx/recyclerview/widget/{0}" + }, + { + "from": "android/support/v7/widget/helper/(.*)", + "to": "androidx/recyclerview/widget/{0}" + }, + { + "from": "android/support/v7/widget/util/(.*)", + "to": "androidx/recyclerview/widget/{0}" + }, + { + "from": "android/support/wear/(.*)", + "to": "androidx/wear/{0}" + }, + { + "from": "android/support/v7/preference/(.*)", + "to": "androidx/preference/{0}" + }, + { + "from": "android/support/v7/internal/widget/PreferenceImageView(.*)", + "to": "androidx/preference/internal/PreferenceImageView{0}" + }, + { + "from": "android/support/v14/preference/(.*)", + "to": "androidx/preference/{0}" + }, + { + "from": "android/support/v7/graphics/ColorCutQuantizer(.*)", + "to": "androidx/palette/graphics/ColorCutQuantizer{0}" + }, + { + "from": "android/support/v7/graphics/Palette(.*)", + "to": "androidx/palette/graphics/Palette{0}" + }, + { + "from": "android/support/v7/graphics/Target(.*)", + "to": "androidx/palette/graphics/Target{0}" + }, + { + "from": "android/support/v7/app/MediaRoute(.*)", + "to": "androidx/mediarouter/app/MediaRoute{0}" + }, + { + "from": "android/support/v7/app/OverlayListView(.*)", + "to": "androidx/mediarouter/app/OverlayListView{0}" + }, + { + "from": "android/support/v7/media/(.*)", + "to": "androidx/mediarouter/media/{0}" + }, + { + "from": "android/support/v7/widget/CardView(.*)", + "to": "androidx/cardview/widget/CardView{0}" + }, + { + "from": "android/support/v7/widget/RoundRectDrawable(.*)", + "to": "androidx/cardview/widget/RoundRectDrawable{0}" + }, + { + "from": "android/support/v7/widget/RoundRectDrawableWithShadow(.*)", + "to": "androidx/cardview/widget/RoundRectDrawableWithShadow{0}" + }, + { + "from": "android/support/v7/widget/GridLayout(.*)", + "to": "androidx/gridlayout/widget/GridLayout{0}" + }, + { + "from": "android/support/v4/content/res/GrowingArrayUtils(.*)", + "to": "androidx/core/content/res/GrowingArrayUtils{0}" + }, + { + "from": "android/support/v7/content/res/GrowingArrayUtils(.*)", + "to": "androidx/core/content/res/GrowingArrayUtils{0}" + }, + { + "from": "android/support/v4/content/res/ColorStateListInflaterCompat(.*)", + "to": "androidx/core/content/res/ColorStateListInflaterCompat{0}" + }, + { + "from": "android/support/v7/content/res/AppCompatColorStateListInflater(.*)", + "to": "androidx/core/content/res/ColorStateListInflaterCompat{0}" + }, + { + "from": "android/support/v7/(.*)", + "to": "androidx/appcompat/{0}" + }, + { + "from": "android/support/graphics/drawable/(.*)", + "to": "androidx/vectordrawable/graphics/drawable/{0}" + }, + { + "from": "android/support/customtabs/CustomTabs(.*)", + "to": "androidx/browser/customtabs/CustomTabs{0}" + }, + { + "from": "android/support/customtabs/PostMessageService(.*)", + "to": "androidx/browser/customtabs/PostMessageService{0}" + }, + { + "from": "android/support/customtabs/TrustedWebUtils(.*)", + "to": "androidx/browser/customtabs/TrustedWebUtils{0}" + }, + { + "from": "android/support/customtabs/R(.*)", + "to": "androidx/browser/R{0}" + }, + { + "from": "android/support/customtabs/(.*)", + "to": "android/support/customtabs/{0}" + }, + { + "from": "android/support/media/tv/(.*)", + "to": "androidx/tvprovider/media/tv/{0}" + }, + { + "from": "android/support/media/ExifInterface(.*)", + "to": "androidx/exifinterface/media/ExifInterface{0}" + }, + { + "from": "android/support/v17/internal/widget/OutlineOnlyWithChildrenFrameLayout(.*)", + "to": "androidx/leanback/preference/internal/OutlineOnlyWithChildrenFrameLayout{0}" + }, + { + "from": "android/support/v17/preference/(.*)", + "to": "androidx/leanback/preference/{0}" + }, + { + "from": "android/support/v17/leanback/(.*)", + "to": "androidx/leanback/{0}" + }, + { + "from": "android/support/percent/(.*)", + "to": "androidx/percentlayout/widget/{0}" + }, + { + "from": "android/support/app/recommendation/(.*)", + "to": "androidx/recommendation/app/{0}" + }, + { + "from": "android/support/annotation/(.*)", + "to": "androidx/annotation/{0}" + }, + { + "from": "android/support/transition/(.*)", + "to": "androidx/transition/{0}" + }, + { + "from": "android/support/text/emoji/bundled/BundledEmojiCompatConfig(.*)", + "to": "androidx/emoji/bundled/BundledEmojiCompatConfig{0}" + }, + { + "from": "android/support/text/emoji/widget/(.*)", + "to": "androidx/emoji/widget/{0}" + }, + { + "from": "android/support/text/emoji/(.*)", + "to": "androidx/emoji/text/{0}" + }, + { + "from": "android/support/content/(.*)", + "to": "androidx/contentpager/content/{0}" + }, + { + "from": "android/support/v4/view/animation/PathInterpolatorApi14(.*)", + "to": "androidx/core/view/animation/PathInterpolatorApi14{0}" + }, + { + "from": "android/support/v4/view/animation/PathInterpolatorCompat(.*)", + "to": "androidx/core/view/animation/PathInterpolatorCompat{0}" + }, + { + "from": "android/support/animation/(.*)", + "to": "androidx/dynamicanimation/animation/{0}" + }, + { + "from": "android/support/v4/app/SupportActivity(.*)", + "to": "androidx/core/app/ComponentActivity{0}" + }, + { + "from": "android/support/v4/app/Fragment(.*)", + "to": "androidx/fragment/app/Fragment{0}" + }, + { + "from": "android/support/v4/app/BaseFragment(.*)", + "to": "androidx/fragment/app/BaseFragment{0}" + }, + { + "from": "android/support/v4/app/BackStack(.*)", + "to": "androidx/fragment/app/BackStack{0}" + }, + { + "from": "android/support/v4/app/DialogFragment(.*)", + "to": "androidx/fragment/app/DialogFragment{0}" + }, + { + "from": "android/support/v4/app/ListFragment(.*)", + "to": "androidx/fragment/app/ListFragment{0}" + }, + { + "from": "android/support/v4/app/OneShotPreDrawListener(.*)", + "to": "androidx/fragment/app/OneShotPreDrawListener{0}" + }, + { + "from": "android/support/v4/app/SuperNotCalledException(.*)", + "to": "androidx/fragment/app/SuperNotCalledException{0}" + }, + { + "from": "android/support/textclassifier/(.*)", + "to": "androidx/textclassifier/{0}" + }, + { + "from": "android/support/v4/media/app/NotificationCompat(.*)", + "to": "androidx/media/app/NotificationCompat{0}" + }, + { + "from": "android/support/v4/media/AudioAttributesCompat(.*)", + "to": "androidx/media/AudioAttributesCompat{0}" + }, + { + "from": "android/support/v4/media/MediaBrowserCompatUtils(.*)", + "to": "androidx/media/MediaBrowserCompatUtils{0}" + }, + { + "from": "android/support/v4/media/MediaBrowserProtocol(.*)", + "to": "androidx/media/MediaBrowserProtocol{0}" + }, + { + "from": "android/support/v4/media/MediaBrowserServiceCompat(.*)", + "to": "androidx/media/MediaBrowserServiceCompat{0}" + }, + { + "from": "android/support/v4/media/MediaSessionManager(.*)", + "to": "androidx/media/MediaSessionManager{0}" + }, + { + "from": "android/support/v4/media/VolumeProviderCompat(.*)", + "to": "androidx/media/VolumeProviderCompat{0}" + }, + { + "from": "android/support/v4/media/session/MediaButtonReceiver(.*)", + "to": "androidx/media/session/MediaButtonReceiver{0}" + }, + { + "from": "android/support/v4/media/(.*)", + "to": "android/support/v4/media/{0}" + }, + { + "from": "android/support/v4/widget/CursorAdapter(.*)", + "to": "androidx/cursoradapter/widget/CursorAdapter{0}" + }, + { + "from": "android/support/v4/widget/CursorFilter(.*)", + "to": "androidx/cursoradapter/widget/CursorFilter{0}" + }, + { + "from": "android/support/v4/widget/ResourceCursorAdapter(.*)", + "to": "androidx/cursoradapter/widget/ResourceCursorAdapter{0}" + }, + { + "from": "android/support/v4/widget/SimpleCursorAdapter(.*)", + "to": "androidx/cursoradapter/widget/SimpleCursorAdapter{0}" + }, + { + "from": "android/support/v4/app/LoaderManager(.*)", + "to": "androidx/loader/app/LoaderManager{0}" + }, + { + "from": "android/support/v4/content/Loader(.*)", + "to": "androidx/loader/content/Loader{0}" + }, + { + "from": "android/support/v4/content/CursorLoader(.*)", + "to": "androidx/loader/content/CursorLoader{0}" + }, + { + "from": "android/support/v4/content/AsyncTaskLoader(.*)", + "to": "androidx/loader/content/AsyncTaskLoader{0}" + }, + { + "from": "android/support/v4/content/ModernAsyncTask(.*)", + "to": "androidx/loader/content/ModernAsyncTask{0}" + }, + { + "from": "android/support/design/widget/CoordinatorLayout(.*)", + "to": "androidx/coordinatorlayout/widget/CoordinatorLayout{0}" + }, + { + "from": "android/support/v4/widget/DirectedAcyclicGraph(.*)", + "to": "androidx/coordinatorlayout/widget/DirectedAcyclicGraph{0}" + }, + { + "from": "android/support/v4/widget/ViewGroupUtils(.*)", + "to": "androidx/coordinatorlayout/widget/ViewGroupUtils{0}" + }, + { + "from": "android/support/v4/view/ViewPager(.*)", + "to": "androidx/viewpager/widget/ViewPager{0}" + }, + { + "from": "android/support/v4/view/PagerAdapter(.*)", + "to": "androidx/viewpager/widget/PagerAdapter{0}" + }, + { + "from": "android/support/v4/view/PagerTabStrip(.*)", + "to": "androidx/viewpager/widget/PagerTabStrip{0}" + }, + { + "from": "android/support/v4/view/PagerTitleStrip(.*)", + "to": "androidx/viewpager/widget/PagerTitleStrip{0}" + }, + { + "from": "android/support/v4/view/AbsSavedState(.*)", + "to": "androidx/customview/view/AbsSavedState{0}" + }, + { + "from": "android/support/v4/widget/ExploreByTouchHelper(.*)", + "to": "androidx/customview/widget/ExploreByTouchHelper{0}" + }, + { + "from": "android/support/v4/widget/FocusStrategy(.*)", + "to": "androidx/customview/widget/FocusStrategy{0}" + }, + { + "from": "android/support/v4/widget/ViewDragHelper(.*)", + "to": "androidx/customview/widget/ViewDragHelper{0}" + }, + { + "from": "android/support/v4/view/animation/(.*)", + "to": "androidx/interpolator/view/animation/{0}" + }, + { + "from": "android/support/v4/widget/DrawerLayout(.*)", + "to": "androidx/drawerlayout/widget/DrawerLayout{0}" + }, + { + "from": "android/support/v4/widget/SlidingPaneLayout(.*)", + "to": "androidx/slidingpanelayout/widget/SlidingPaneLayout{0}" + }, + { + "from": "android/support/v4/view/AsyncLayoutInflater(.*)", + "to": "androidx/asynclayoutinflater/view/AsyncLayoutInflater{0}" + }, + { + "from": "android/support/v4/widget/SwipeRefreshLayout(.*)", + "to": "androidx/swiperefreshlayout/widget/SwipeRefreshLayout{0}" + }, + { + "from": "android/support/v4/widget/CircularProgressDrawable(.*)", + "to": "androidx/swiperefreshlayout/widget/CircularProgressDrawable{0}" + }, + { + "from": "android/support/v4/widget/CircleImageView(.*)", + "to": "androidx/swiperefreshlayout/widget/CircleImageView{0}" + }, + { + "from": "android/support/v4/util/ArrayMap(.*)", + "to": "androidx/collection/ArrayMap{0}" + }, + { + "from": "android/support/v4/util/ArraySet(.*)", + "to": "androidx/collection/ArraySet{0}" + }, + { + "from": "android/support/v4/util/CircularArray(.*)", + "to": "androidx/collection/CircularArray{0}" + }, + { + "from": "android/support/v4/util/CircularIntArray(.*)", + "to": "androidx/collection/CircularIntArray{0}" + }, + { + "from": "android/support/v4/util/ContainerHelpers(.*)", + "to": "androidx/collection/ContainerHelpers{0}" + }, + { + "from": "android/support/v4/util/LongSparseArray(.*)", + "to": "androidx/collection/LongSparseArray{0}" + }, + { + "from": "android/support/v4/util/LruCache(.*)", + "to": "androidx/collection/LruCache{0}" + }, + { + "from": "android/support/v4/util/MapCollections(.*)", + "to": "androidx/collection/MapCollections{0}" + }, + { + "from": "android/support/v4/util/SimpleArrayMap(.*)", + "to": "androidx/collection/SimpleArrayMap{0}" + }, + { + "from": "android/support/v4/util/SparseArray(.*)", + "to": "androidx/collection/SparseArray{0}" + }, + { + "from": "android/support/v4/provider/DocumentFile(.*)", + "to": "androidx/documentfile/provider/DocumentFile{0}" + }, + { + "from": "android/support/v4/provider/DocumentsContractApi19(.*)", + "to": "androidx/documentfile/provider/DocumentsContractApi19{0}" + }, + { + "from": "android/support/v4/provider/RawDocumentFile(.*)", + "to": "androidx/documentfile/provider/RawDocumentFile{0}" + }, + { + "from": "android/support/v4/provider/SingleDocumentFile(.*)", + "to": "androidx/documentfile/provider/SingleDocumentFile{0}" + }, + { + "from": "android/support/v4/provider/TreeDocumentFile(.*)", + "to": "androidx/documentfile/provider/TreeDocumentFile{0}" + }, + { + "from": "android/support/v4/content/LocalBroadcastManager(.*)", + "to": "androidx/localbroadcastmanager/content/LocalBroadcastManager{0}" + }, + { + "from": "android/support/v4/print/(.*)", + "to": "androidx/print/{0}" + }, + { + "from": "android/support/v4/(.*)", + "to": "androidx/core/{0}" + }, + { + "from": "android/support/v13/(.*)", + "to": "androidx/core/{0}" + }, + { + "from": "android/arch/core/(.*)", + "to": "androidx/arch/core/{0}" + }, + { + "from": "android/arch/lifecycle/(.*)", + "to": "androidx/lifecycle/{0}" + }, + { + "from": "android/arch/paging/(.*)", + "to": "androidx/paging/{0}" + }, + { + "from": "android/arch/persistence/room/(.*)", + "to": "androidx/room/{0}" + }, + { + "from": "android/arch/persistence/(.*)", + "to": "androidx/sqlite/{0}" + }, + { + "from": "android/arch/core/executor/testing/(.*)", + "to": "androidx/arch/core/executor/testing/{0}" + }, + { + "from": "android/support/constraint/solver/(.*)", + "to": "androidx/constraintlayout/solver/{0}" + }, + { + "from": "android/support/constraint/(.*)", + "to": "androidx/constraintlayout/widget/{0}" + }, + { + "from": "com/android/support/v8/renderscript/EnableAsyncTeardown", + "to": "androidx/renderscript/EnableAsyncTeardown" + }, + { + "from": "com/android/support/v8/renderscript/EnableBlurWorkaround", + "to": "androidx/renderscript/EnableBlurWorkaround" + }, + { + "from": "android/support/v8/renderscript/(.*)", + "to": "androidx/renderscript/{0}" + }, + { + "from": "android/databinding/(.*)", + "to": "androidx/databinding/{0}" + }, + { + "from": "android/support/multidex/(.*)", + "to": "androidx/multidex/{0}" + }, + { + "from": "androidx/versionedparcelable/(.*)", + "to": "androidx/versionedparcelable/{0}" + }, + { + "from": "android/support/design/widget/AppBarLayout(.*)", + "to": "com/google/android/material/appbar/AppBarLayout{0}" + }, + { + "from": "android/support/design/widget/BaseTransientBottomBar(.*)", + "to": "com/google/android/material/snackbar/BaseTransientBottomBar{0}" + }, + { + "from": "android/support/design/widget/BottomNavigationView(.*)", + "to": "com/google/android/material/bottomnavigation/BottomNavigationView{0}" + }, + { + "from": "android/support/design/widget/BottomSheet(.*)", + "to": "com/google/android/material/bottomsheet/BottomSheet{0}" + }, + { + "from": "android/support/design/widget/CheckableImageButton(.*)", + "to": "com/google/android/material/internal/CheckableImageButton{0}" + }, + { + "from": "android/support/design/widget/CircularBorderDrawable(.*)", + "to": "com/google/android/material/internal/CircularBorderDrawable{0}" + }, + { + "from": "android/support/design/widget/CollapsingTextHelper(.*)", + "to": "com/google/android/material/internal/CollapsingTextHelper{0}" + }, + { + "from": "android/support/design/widget/CollapsingToolbarLayout(.*)", + "to": "com/google/android/material/appbar/CollapsingToolbarLayout{0}" + }, + { + "from": "android/support/design/widget/CutoutDrawable(.*)", + "to": "com/google/android/material/textfield/CutoutDrawable{0}" + }, + { + "from": "android/support/design/widget/DescendantOffsetUtils(.*)", + "to": "com/google/android/material/internal/DescendantOffsetUtils{0}" + }, + { + "from": "android/support/design/widget/DrawableUtils(.*)", + "to": "com/google/android/material/internal/DrawableUtils{0}" + }, + { + "from": "android/support/design/widget/FloatingActionButton(.*)", + "to": "com/google/android/material/floatingactionbutton/FloatingActionButton{0}" + }, + { + "from": "android/support/design/widget/HeaderBehavior(.*)", + "to": "com/google/android/material/appbar/HeaderBehavior{0}" + }, + { + "from": "android/support/design/widget/HeaderScrollingViewBehavior(.*)", + "to": "com/google/android/material/appbar/HeaderScrollingViewBehavior{0}" + }, + { + "from": "android/support/design/widget/HideBottomViewOnScrollBehavior(.*)", + "to": "com/google/android/material/behavior/HeaderScrollingViewBehavior{0}" + }, + { + "from": "android/support/design/widget/IndicatorViewController(.*)", + "to": "com/google/android/material/textfield/IndicatorViewController{0}" + }, + { + "from": "android/support/design/widget/MathUtils(.*)", + "to": "com/google/android/material/math/MathUtils{0}" + }, + { + "from": "android/support/design/widget/NavigationView(.*)", + "to": "com/google/android/material/navigation/NavigationView{0}" + }, + { + "from": "android/support/design/widget/Shadow(.*)", + "to": "com/google/android/material/shadow/Shadow{0}" + }, + { + "from": "android/support/design/widget/Snackbar(.*)", + "to": "com/google/android/material/snackbar/Snackbar{0}" + }, + { + "from": "android/support/design/widget/SnackbarManager(.*)", + "to": "com/google/android/material/snackbar/SnackbarManager{0}" + }, + { + "from": "android/support/design/widget/StateListAnimator(.*)", + "to": "com/google/android/material/internal/StateListAnimator{0}" + }, + { + "from": "android/support/design/widget/SwipeDismissBehavior(.*)", + "to": "com/google/android/material/behavior/SwipeDismissBehavior{0}" + }, + { + "from": "android/support/design/widget/Tab(.*)", + "to": "com/google/android/material/tabs/Tab{0}" + }, + { + "from": "android/support/design/widget/TextInput(.*)", + "to": "com/google/android/material/textfield/TextInput{0}" + }, + { + "from": "android/support/design/widget/ViewOffsetBehavior(.*)", + "to": "com/google/android/material/appbar/ViewOffsetBehavior{0}" + }, + { + "from": "android/support/design/widget/ViewOffsetHelper(.*)", + "to": "com/google/android/material/appbar/ViewOffsetHelper{0}" + }, + { + "from": "android/support/design/widget/ViewUtilsLollipop(.*)", + "to": "com/google/android/material/appbar/ViewUtilsLollipop{0}" + }, + { + "from": "android/support/design/widget/VisibilityAwareImageButton(.*)", + "to": "com/google/android/material/internal/VisibilityAwareImageButton{0}" + }, + { + "from": "android/support/design/internal/BottomNavigation(.*)", + "to": "com/google/android/material/bottomnavigation/BottomNavigation{0}" + }, + { + "from": "android/support/design/internal/SnackbarContentLayout(.*)", + "to": "com/google/android/material/snackbar/SnackbarContentLayout{0}" + }, + { + "from": "android/support/design/R(.*)", + "to": "com/google/android/material/R{0}" + }, + { + "from": "android/support/design/(.*)", + "to": "com/google/android/material/{0}" + }, + { + "from": "android/support/test/(.*)", + "to": "androidx/test/{0}" + }, + { + "from": "android/support/biometric/(.*)", + "to": "androidx/biometric/{0}" + }, + { + "from": "com/android/databinding/library/baseAdapters/(.*)", + "to": "androidx/databinding/library/baseAdapters/{0}" + } + ], + "slRules": [ + { + "from": "(.*)/package-info", + "to": "ignore" + }, + { + "from": "androidx/navigation/(.*)", + "to": "ignore" + }, + { + "from": "androidx/text/emoji/flatbuffer/(.*)", + "to": "ignore" + }, + { + "from": "androidx/browser/browseractions/(.*)", + "to": "ignore" + }, + { + "from": "androidx/heifwriter/(.*)", + "to": "ignore" + }, + { + "from": "androidx/webkit/(.*)", + "to": "ignore" + }, + { + "from": "androidx/slice/(.*)", + "to": "ignore" + }, + { + "from": "androidx/recyclerview/selection/(.*)", + "to": "ignore" + }, + { + "from": "androidx/car/(.*)", + "to": "ignore" + }, + { + "from": "androidx/media/widget/(.*)", + "to": "ignore" + }, + { + "from": "androidx/media2/(.*)", + "to": "ignore" + }, + { + "from": "androidx/concurrent/futures/(.*)", + "to": "ignore" + }, + { + "from": "androidx/(.*)/ktx/(.*)", + "to": "ignore" + }, + { + "from": "androidx/(.*)/(.*)-ktx/(.*)", + "to": "ignore" + }, + { + "from": "androidx/(.*)/lint/(.*)", + "to": "ignore" + }, + { + "from": "androidx/(.*)/(.*)-lint/(.*)", + "to": "ignore" + }, + { + "from": "androidx/media/(.*)", + "to": "android/support/v4/media/{0}" + }, + { + "from": "androidx/coordinatorlayout/widget/annotations", + "to": "android/support/design/widget/annotations" + }, + { + "from": "androidx/drawerlayout/widget/annotations", + "to": "android/support/v4/widget/annotations" + }, + { + "from": "androidx/exifinterface/media/annotations", + "to": "android/support/media/annotations" + }, + { + "from": "androidx/fragment/app/annotations", + "to": "android/support/v4/app/annotations" + }, + { + "from": "androidx/mediarouter/app/annotations", + "to": "android/support/v7/app/annotations" + }, + { + "from": "androidx/swiperefreshlayout/widget/annotations", + "to": "android/support/v4/widget/annotations" + }, + { + "from": "androidx/browser/customtabs/annotations", + "to": "android/support/customtabs/annotations" + }, + { + "from": "androidx/browser/browseractions/annotations", + "to": "ignore" + } + ], + "packageMap": [ + { + "from": "android/support/exifinterface", + "to": "androidx/exifinterface" + }, + { + "from": "android/support/heifwriter", + "to": "androidx/heifwriter" + }, + { + "from": "android/support/graphics/drawable", + "to": "androidx/vectordrawable" + }, + { + "from": "android/support/graphics/drawable/animated", + "to": "androidx/vectordrawable" + }, + { + "from": "android/support/media/tv", + "to": "androidx/tvprovider" + }, + { + "from": "android/support/textclassifier", + "to": "androidx/textclassifier" + }, + { + "from": "androidx/recyclerview/selection", + "to": "androidx/recyclerview/selection" + }, + { + "from": "android/support/v4", + "to": "androidx/legacy/v4" + }, + { + "from": "android/support/print", + "to": "androidx/print" + }, + { + "from": "android/support/documentfile", + "to": "androidx/documentfile" + }, + { + "from": "android/support/coordinatorlayout", + "to": "androidx/coordinatorlayout" + }, + { + "from": "android/support/swiperefreshlayout", + "to": "androidx/swiperefreshlayout" + }, + { + "from": "android/support/slidingpanelayout", + "to": "androidx/slidingpanelayout" + }, + { + "from": "android/support/asynclayoutinflater", + "to": "androidx/asynclayoutinflater" + }, + { + "from": "android/support/interpolator", + "to": "androidx/interpolator" + }, + { + "from": "android/support/v7/palette", + "to": "androidx/palette" + }, + { + "from": "android/support/v7/cardview", + "to": "androidx/cardview" + }, + { + "from": "android/support/customview", + "to": "androidx/customview" + }, + { + "from": "android/support/loader", + "to": "androidx/loader" + }, + { + "from": "android/support/cursoradapter", + "to": "androidx/cursoradapter" + }, + { + "from": "android/support/v7/mediarouter", + "to": "androidx/mediarouter" + }, + { + "from": "android/support/v7/appcompat", + "to": "androidx/appcompat" + }, + { + "from": "android/support/v7/recyclerview", + "to": "androidx/recyclerview" + }, + { + "from": "android/support/v7/viewpager", + "to": "androidx/viewpager" + }, + { + "from": "android/support/percent", + "to": "androidx/percentlayout" + }, + { + "from": "android/support/v7/gridlayout", + "to": "androidx/gridlayout" + }, + { + "from": "android/support/v13", + "to": "androidx/legacy/v13" + }, + { + "from": "android/support/v7/preference", + "to": "androidx/preference" + }, + { + "from": "android/support/v14/preference", + "to": "androidx/legacy/preference" + }, + { + "from": "android/support/v17/leanback", + "to": "androidx/leanback" + }, + { + "from": "android/support/v17/preference", + "to": "androidx/leanback/preference" + }, + { + "from": "android/support/compat", + "to": "androidx/core" + }, + { + "from": "android/support/mediacompat", + "to": "androidx/media" + }, + { + "from": "android/support/media2", + "to": "androidx/media2" + }, + { + "from": "androidx/media2/exoplayer/external", + "to": "androidx/media2/exoplayer/external" + }, + { + "from": "android/support/fragment", + "to": "androidx/fragment" + }, + { + "from": "android/support/coreutils", + "to": "androidx/legacy/coreutils" + }, + { + "from": "android/support/dynamicanimation", + "to": "androidx/dynamicanimation" + }, + { + "from": "android/support/customtabs", + "to": "androidx/browser" + }, + { + "from": "android/support/coreui", + "to": "androidx/legacy/coreui" + }, + { + "from": "android/support/content", + "to": "androidx/contentpager" + }, + { + "from": "android/support/transition", + "to": "androidx/transition" + }, + { + "from": "android/support/recommendation", + "to": "androidx/recommendation" + }, + { + "from": "android/support/drawerlayout", + "to": "androidx/drawerlayout" + }, + { + "from": "android/support/wear", + "to": "androidx/wear" + }, + { + "from": "android/support/design", + "to": "com/google/android/material" + }, + { + "from": "android/support/text/emoji/appcompat", + "to": "androidx/emoji/appcompat" + }, + { + "from": "android/support/text/emoji/bundled", + "to": "androidx/emoji/bundled" + }, + { + "from": "android/support/text/emoji", + "to": "androidx/emoji" + }, + { + "from": "androidx/text/emoji/bundled", + "to": "androidx/text/emoji/bundled" + }, + { + "from": "android/support/localbroadcastmanager", + "to": "androidx/localbroadcastmanager" + }, + { + "from": "androidx/text/emoji/bundled", + "to": "androidx/text/emoji/bundled" + }, + { + "from": "androidx/webkit", + "to": "androidx/webkit" + }, + { + "from": "androidx/versionedparcelable", + "to": "androidx/versionedparcelable" + }, + { + "from": "androidx/slice/view", + "to": "androidx/slice/view" + }, + { + "from": "androidx/slice/core", + "to": "androidx/slice/core" + }, + { + "from": "androidx/slice/builders", + "to": "androidx/slice/builders" + }, + { + "from": "android/arch/paging/runtime", + "to": "androidx/paging/runtime" + }, + { + "from": "android/arch/core/testing", + "to": "androidx/arch/core/testing" + }, + { + "from": "android/arch/core", + "to": "androidx/arch/core" + }, + { + "from": "android/arch/persistence/db/framework", + "to": "androidx/sqlite/db/framework" + }, + { + "from": "android/arch/persistence/db", + "to": "androidx/sqlite/db" + }, + { + "from": "android/arch/persistence/room/rxjava2", + "to": "androidx/room/rxjava2" + }, + { + "from": "android/arch/persistence/room/rxjava3", + "to": "androidx/room/rxjava3" + }, + { + "from": "android/arch/persistence/room/guava", + "to": "androidx/room/guava" + }, + { + "from": "android/arch/persistence/room/testing", + "to": "androidx/room/testing" + }, + { + "from": "android/arch/persistence/room", + "to": "androidx/room" + }, + { + "from": "android/arch/lifecycle/extensions", + "to": "androidx/lifecycle/extensions" + }, + { + "from": "android/arch/lifecycle/testing", + "to": "androidx/lifecycle/testing" + }, + { + "from": "android/arch/lifecycle/livedata/core", + "to": "androidx/lifecycle/livedata/core" + }, + { + "from": "android/arch/lifecycle", + "to": "androidx/lifecycle" + }, + { + "from": "android/arch/lifecycle/viewmodel", + "to": "androidx/lifecycle/viewmodel" + }, + { + "from": "android/arch/lifecycle/livedata", + "to": "androidx/lifecycle/livedata" + }, + { + "from": "android/arch/lifecycle/reactivestreams", + "to": "androidx/lifecycle/reactivestreams" + }, + { + "from": "android/support/multidex/instrumentation", + "to": "androidx/multidex/instrumentation" + }, + { + "from": "android/support/multidex", + "to": "androidx/multidex" + }, + { + "from": "android/support/biometric", + "to": "androidx/biometric" + } + ], + "pomRules": [ + { + "from": { + "groupId": "com.android.support", + "artifactId": "animated-vector-drawable", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.vectordrawable", + "artifactId": "vectordrawable-animated", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "appcompat-v7", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.appcompat", + "artifactId": "appcompat", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "cardview-v7", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.cardview", + "artifactId": "cardview", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "customtabs", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.browser", + "artifactId": "browser", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "design", + "version": "{oldMaterialVersion}" + }, + "to": { + "groupId": "com.google.android.material", + "artifactId": "material", + "version": "{newMaterialVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "exifinterface", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.exifinterface", + "artifactId": "exifinterface", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "gridlayout-v7", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.gridlayout", + "artifactId": "gridlayout", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "leanback-v17", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.leanback", + "artifactId": "leanback", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "mediarouter-v7", + "version": "{oldMediarouterVersion}" + }, + "to": { + "groupId": "androidx.mediarouter", + "artifactId": "mediarouter", + "version": "{newMediarouterVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "multidex", + "version": "1.0.3" + }, + "to": { + "groupId": "androidx.multidex", + "artifactId": "multidex", + "version": "2.0.0" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "multidex-instrumentation", + "version": "1.0.3" + }, + "to": { + "groupId": "androidx.multidex", + "artifactId": "multidex-instrumentation", + "version": "2.0.0" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "palette-v7", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.palette", + "artifactId": "palette", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "percent", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.percentlayout", + "artifactId": "percentlayout", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "preference-leanback-v17", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.leanback", + "artifactId": "leanback-preference", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "preference-v14", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.legacy", + "artifactId": "legacy-preference-v14", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "preference-v7", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.preference", + "artifactId": "preference", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "recommendation", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.recommendation", + "artifactId": "recommendation", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "recyclerview-v7", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.recyclerview", + "artifactId": "recyclerview", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "support-annotations", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.annotation", + "artifactId": "annotation", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "support-compat", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.core", + "artifactId": "core", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "support-content", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.contentpager", + "artifactId": "contentpager", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "support-core-ui", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.legacy", + "artifactId": "legacy-support-core-ui", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "support-core-utils", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.legacy", + "artifactId": "legacy-support-core-utils", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "support-dynamic-animation", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.dynamicanimation", + "artifactId": "dynamicanimation", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "support-emoji", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.emoji", + "artifactId": "emoji", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "support-emoji-appcompat", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.emoji", + "artifactId": "emoji-appcompat", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "support-emoji-bundled", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.emoji", + "artifactId": "emoji-bundled", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "support-fragment", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.fragment", + "artifactId": "fragment", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "support-media-compat", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.media", + "artifactId": "media", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "support-tv-provider", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.tvprovider", + "artifactId": "tvprovider", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "support-v13", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.legacy", + "artifactId": "legacy-support-v13", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "support-v4", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.legacy", + "artifactId": "legacy-support-v4", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "support-vector-drawable", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.vectordrawable", + "artifactId": "vectordrawable", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "textclassifier", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.textclassifier", + "artifactId": "textclassifier", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "transition", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.transition", + "artifactId": "transition", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "wear", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.wear", + "artifactId": "wear", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "asynclayoutinflater", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.asynclayoutinflater", + "artifactId": "asynclayoutinflater", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "collections", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.collection", + "artifactId": "collection", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "coordinatorlayout", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.coordinatorlayout", + "artifactId": "coordinatorlayout", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "cursoradapter", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.cursoradapter", + "artifactId": "cursoradapter", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "customview", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.customview", + "artifactId": "customview", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "documentfile", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.documentfile", + "artifactId": "documentfile", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "drawerlayout", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.drawerlayout", + "artifactId": "drawerlayout", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "interpolator", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.interpolator", + "artifactId": "interpolator", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "loader", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.loader", + "artifactId": "loader", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "localbroadcastmanager", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.localbroadcastmanager", + "artifactId": "localbroadcastmanager", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "print", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.print", + "artifactId": "print", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "slidingpanelayout", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.slidingpanelayout", + "artifactId": "slidingpanelayout", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "swiperefreshlayout", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.swiperefreshlayout", + "artifactId": "swiperefreshlayout", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "viewpager", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.viewpager", + "artifactId": "viewpager", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.databinding", + "artifactId": "adapters", + "version": "undefined" + }, + "to": { + "groupId": "androidx.databinding", + "artifactId": "databinding-adapters", + "version": "{newDataBindingVersion}" + } + }, + { + "from": { + "groupId": "com.android.databinding", + "artifactId": "baseLibrary", + "version": "undefined" + }, + "to": { + "groupId": "androidx.databinding", + "artifactId": "databinding-common", + "version": "{newDataBindingVersion}" + } + }, + { + "from": { + "groupId": "com.android.databinding", + "artifactId": "compiler", + "version": "undefined" + }, + "to": { + "groupId": "androidx.databinding", + "artifactId": "databinding-compiler", + "version": "{newDataBindingVersion}" + } + }, + { + "from": { + "groupId": "com.android.databinding", + "artifactId": "compilerCommon", + "version": "undefined" + }, + "to": { + "groupId": "androidx.databinding", + "artifactId": "databinding-compiler-common", + "version": "{newDataBindingVersion}" + } + }, + { + "from": { + "groupId": "com.android.databinding", + "artifactId": "library", + "version": "undefined" + }, + "to": { + "groupId": "androidx.databinding", + "artifactId": "databinding-runtime", + "version": "{newDataBindingVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "versionedparcelable", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.versionedparcelable", + "artifactId": "versionedparcelable", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "android.arch.work", + "artifactId": "work-runtime", + "version": "{oldWorkManagerVersion}" + }, + "to": { + "groupId": "androidx.work", + "artifactId": "work-runtime", + "version": "{newWorkManagerVersion}" + } + }, + { + "from": { + "groupId": "android.arch.work", + "artifactId": "work-runtime-ktx", + "version": "{oldWorkManagerVersion}" + }, + "to": { + "groupId": "androidx.work", + "artifactId": "work-runtime-ktx", + "version": "{newWorkManagerVersion}" + } + }, + { + "from": { + "groupId": "android.arch.work", + "artifactId": "work-rxjava2", + "version": "{oldWorkManagerVersion}" + }, + "to": { + "groupId": "androidx.work", + "artifactId": "work-rxjava2", + "version": "{newWorkManagerVersion}" + } + }, + { + "from": { + "groupId": "android.arch.work", + "artifactId": "work-testing", + "version": "{oldWorkManagerVersion}" + }, + "to": { + "groupId": "androidx.work", + "artifactId": "work-testing", + "version": "{newWorkManagerVersion}" + } + }, + { + "from": { + "groupId": "android.arch.navigation", + "artifactId": "navigation-common", + "version": "{oldNavigationVersion}" + }, + "to": { + "groupId": "androidx.navigation", + "artifactId": "navigation-common", + "version": "{newNavigationVersion}" + } + }, + { + "from": { + "groupId": "android.arch.navigation", + "artifactId": "navigation-common-ktx", + "version": "{oldNavigationVersion}" + }, + "to": { + "groupId": "androidx.navigation", + "artifactId": "navigation-common-ktx", + "version": "{newNavigationVersion}" + } + }, + { + "from": { + "groupId": "android.arch.navigation", + "artifactId": "navigation-fragment", + "version": "{oldNavigationVersion}" + }, + "to": { + "groupId": "androidx.navigation", + "artifactId": "navigation-fragment", + "version": "{newNavigationVersion}" + } + }, + { + "from": { + "groupId": "android.arch.navigation", + "artifactId": "navigation-fragment-ktx", + "version": "{oldNavigationVersion}" + }, + "to": { + "groupId": "androidx.navigation", + "artifactId": "navigation-fragment-ktx", + "version": "{newNavigationVersion}" + } + }, + { + "from": { + "groupId": "android.arch.navigation", + "artifactId": "navigation-runtime", + "version": "{oldNavigationVersion}" + }, + "to": { + "groupId": "androidx.navigation", + "artifactId": "navigation-runtime", + "version": "{newNavigationVersion}" + } + }, + { + "from": { + "groupId": "android.arch.navigation", + "artifactId": "navigation-runtime-ktx", + "version": "{oldNavigationVersion}" + }, + "to": { + "groupId": "androidx.navigation", + "artifactId": "navigation-runtime-ktx", + "version": "{newNavigationVersion}" + } + }, + { + "from": { + "groupId": "android.arch.navigation", + "artifactId": "navigation-ui", + "version": "{oldNavigationVersion}" + }, + "to": { + "groupId": "androidx.navigation", + "artifactId": "navigation-ui", + "version": "{newNavigationVersion}" + } + }, + { + "from": { + "groupId": "android.arch.navigation", + "artifactId": "navigation-ui-ktx", + "version": "{oldNavigationVersion}" + }, + "to": { + "groupId": "androidx.navigation", + "artifactId": "navigation-ui-ktx", + "version": "{newNavigationVersion}" + } + }, + { + "from": { + "groupId": "android.arch.core", + "artifactId": "common", + "version": "1.1.1" + }, + "to": { + "groupId": "androidx.arch.core", + "artifactId": "core-common", + "version": "{newArchCoreVersion}" + } + }, + { + "from": { + "groupId": "android.arch.core", + "artifactId": "core", + "version": "1.0.0-alpha3" + }, + "to": { + "groupId": "androidx.arch.core", + "artifactId": "core", + "version": "{newArchCoreVersion}" + } + }, + { + "from": { + "groupId": "android.arch.core", + "artifactId": "core-testing", + "version": "1.1.1" + }, + "to": { + "groupId": "androidx.arch.core", + "artifactId": "core-testing", + "version": "{newArchCoreVersion}" + } + }, + { + "from": { + "groupId": "android.arch.core", + "artifactId": "runtime", + "version": "1.1.1" + }, + "to": { + "groupId": "androidx.arch.core", + "artifactId": "core-runtime", + "version": "{newArchCoreVersion}" + } + }, + { + "from": { + "groupId": "android.arch.lifecycle", + "artifactId": "common", + "version": "1.1.1" + }, + "to": { + "groupId": "androidx.lifecycle", + "artifactId": "lifecycle-common", + "version": "{newLifecycleVersion}" + } + }, + { + "from": { + "groupId": "android.arch.lifecycle", + "artifactId": "common-java8", + "version": "1.1.1" + }, + "to": { + "groupId": "androidx.lifecycle", + "artifactId": "lifecycle-common-java8", + "version": "{newLifecycleVersion}" + } + }, + { + "from": { + "groupId": "android.arch.lifecycle", + "artifactId": "compiler", + "version": "1.1.1" + }, + "to": { + "groupId": "androidx.lifecycle", + "artifactId": "lifecycle-compiler", + "version": "{newLifecycleVersion}" + } + }, + { + "from": { + "groupId": "android.arch.lifecycle", + "artifactId": "extensions", + "version": "1.1.1" + }, + "to": { + "groupId": "androidx.lifecycle", + "artifactId": "lifecycle-extensions", + "version": "{newLifecycleVersion}" + } + }, + { + "from": { + "groupId": "android.arch.lifecycle", + "artifactId": "reactivestreams", + "version": "1.1.1" + }, + "to": { + "groupId": "androidx.lifecycle", + "artifactId": "lifecycle-reactivestreams", + "version": "{newLifecycleVersion}" + } + }, + { + "from": { + "groupId": "android.arch.lifecycle", + "artifactId": "runtime", + "version": "1.1.1" + }, + "to": { + "groupId": "androidx.lifecycle", + "artifactId": "lifecycle-runtime", + "version": "{newLifecycleVersion}" + } + }, + { + "from": { + "groupId": "android.arch.lifecycle", + "artifactId": "viewmodel", + "version": "1.1.1" + }, + "to": { + "groupId": "androidx.lifecycle", + "artifactId": "lifecycle-viewmodel", + "version": "{newLifecycleVersion}" + } + }, + { + "from": { + "groupId": "android.arch.lifecycle", + "artifactId": "livedata", + "version": "1.1.1" + }, + "to": { + "groupId": "androidx.lifecycle", + "artifactId": "lifecycle-livedata", + "version": "{newLifecycleVersion}" + } + }, + { + "from": { + "groupId": "android.arch.lifecycle", + "artifactId": "livedata-core", + "version": "1.1.1" + }, + "to": { + "groupId": "androidx.lifecycle", + "artifactId": "lifecycle-livedata-core", + "version": "{newLifecycleVersion}" + } + }, + { + "from": { + "groupId": "android.arch.paging", + "artifactId": "common", + "version": "1.0.0" + }, + "to": { + "groupId": "androidx.paging", + "artifactId": "paging-common", + "version": "{newPagingVersion}" + } + }, + { + "from": { + "groupId": "android.arch.paging", + "artifactId": "runtime", + "version": "1.0.0" + }, + "to": { + "groupId": "androidx.paging", + "artifactId": "paging-runtime", + "version": "{newPagingVersion}" + } + }, + { + "from": { + "groupId": "android.arch.paging", + "artifactId": "rxjava2", + "version": "1.0.0-alpha1" + }, + "to": { + "groupId": "androidx.paging", + "artifactId": "paging-rxjava2", + "version": "{newPagingVersion}" + } + }, + { + "from": { + "groupId": "android.arch.persistence", + "artifactId": "db", + "version": "{oldRoomVersion}" + }, + "to": { + "groupId": "androidx.sqlite", + "artifactId": "sqlite", + "version": "{newRoomVersion}" + } + }, + { + "from": { + "groupId": "android.arch.persistence", + "artifactId": "db-framework", + "version": "{oldRoomVersion}" + }, + "to": { + "groupId": "androidx.sqlite", + "artifactId": "sqlite-framework", + "version": "{newRoomVersion}" + } + }, + { + "from": { + "groupId": "android.arch.persistence.room", + "artifactId": "common", + "version": "{oldRoomVersion}" + }, + "to": { + "groupId": "androidx.room", + "artifactId": "room-common", + "version": "{newRoomVersion}" + } + }, + { + "from": { + "groupId": "android.arch.persistence.room", + "artifactId": "compiler", + "version": "{oldRoomVersion}" + }, + "to": { + "groupId": "androidx.room", + "artifactId": "room-compiler", + "version": "{newRoomVersion}" + } + }, + { + "from": { + "groupId": "android.arch.persistence.room", + "artifactId": "migration", + "version": "{oldRoomVersion}" + }, + "to": { + "groupId": "androidx.room", + "artifactId": "room-migration", + "version": "{newRoomVersion}" + } + }, + { + "from": { + "groupId": "android.arch.persistence.room", + "artifactId": "runtime", + "version": "{oldRoomVersion}" + }, + "to": { + "groupId": "androidx.room", + "artifactId": "room-runtime", + "version": "{newRoomVersion}" + } + }, + { + "from": { + "groupId": "android.arch.persistence.room", + "artifactId": "rxjava2", + "version": "{oldRoomVersion}" + }, + "to": { + "groupId": "androidx.room", + "artifactId": "room-rxjava2", + "version": "{newRoomVersion}" + } + }, + { + "from": { + "groupId": "android.arch.persistence.room", + "artifactId": "testing", + "version": "{oldRoomVersion}" + }, + "to": { + "groupId": "androidx.room", + "artifactId": "room-testing", + "version": "{newRoomVersion}" + } + }, + { + "from": { + "groupId": "android.arch.persistence.room", + "artifactId": "guava", + "version": "{oldRoomVersion}" + }, + "to": { + "groupId": "androidx.room", + "artifactId": "room-guava", + "version": "{newRoomVersion}" + } + }, + { + "from": { + "groupId": "com.android.support.constraint", + "artifactId": "constraint-layout", + "version": "1.1.0" + }, + "to": { + "groupId": "androidx.constraintlayout", + "artifactId": "constraintlayout", + "version": "1.1.3" + } + }, + { + "from": { + "groupId": "com.android.support.constraint", + "artifactId": "constraint-layout-solver", + "version": "1.1.0" + }, + "to": { + "groupId": "androidx.constraintlayout", + "artifactId": "constraintlayout-solver", + "version": "1.1.3" + } + }, + { + "from": { + "groupId": "com.android.support.test", + "artifactId": "orchestrator", + "version": "1.0.2" + }, + "to": { + "groupId": "androidx.test", + "artifactId": "orchestrator", + "version": "{newTestsVersion}" + } + }, + { + "from": { + "groupId": "com.android.support.test", + "artifactId": "rules", + "version": "1.0.2" + }, + "to": { + "groupId": "androidx.test", + "artifactId": "rules", + "version": "{newTestsVersion}" + } + }, + { + "from": { + "groupId": "com.android.support.test", + "artifactId": "runner", + "version": "1.0.2" + }, + "to": { + "groupId": "androidx.test", + "artifactId": "runner", + "version": "{newTestsVersion}" + } + }, + { + "from": { + "groupId": "com.android.support.test", + "artifactId": "monitor", + "version": "1.0.2" + }, + "to": { + "groupId": "androidx.test", + "artifactId": "monitor", + "version": "{newTestsVersion}" + } + }, + { + "from": { + "groupId": "com.android.support.test.espresso", + "artifactId": "espresso-accessibility", + "version": "3.0.2" + }, + "to": { + "groupId": "androidx.test.espresso", + "artifactId": "espresso-accessibility", + "version": "{newEspressoVersion}" + } + }, + { + "from": { + "groupId": "com.android.support.test.espresso", + "artifactId": "espresso-contrib", + "version": "3.0.2" + }, + "to": { + "groupId": "androidx.test.espresso", + "artifactId": "espresso-contrib", + "version": "{newEspressoVersion}" + } + }, + { + "from": { + "groupId": "com.android.support.test.espresso", + "artifactId": "espresso-core", + "version": "3.0.2" + }, + "to": { + "groupId": "androidx.test.espresso", + "artifactId": "espresso-core", + "version": "{newEspressoVersion}" + } + }, + { + "from": { + "groupId": "com.android.support.test.espresso", + "artifactId": "espresso-idling-resource", + "version": "3.0.2" + }, + "to": { + "groupId": "androidx.test.espresso", + "artifactId": "espresso-idling-resource", + "version": "{newEspressoVersion}" + } + }, + { + "from": { + "groupId": "com.android.support.test.espresso", + "artifactId": "espresso-intents", + "version": "3.0.2" + }, + "to": { + "groupId": "androidx.test.espresso", + "artifactId": "espresso-intents", + "version": "{newEspressoVersion}" + } + }, + { + "from": { + "groupId": "com.android.support.test.espresso", + "artifactId": "espresso-remote", + "version": "3.0.2" + }, + "to": { + "groupId": "androidx.test.espresso", + "artifactId": "espresso-remote", + "version": "{newEspressoVersion}" + } + }, + { + "from": { + "groupId": "com.android.support.test.espresso", + "artifactId": "espresso-web", + "version": "3.0.2" + }, + "to": { + "groupId": "androidx.test.espresso", + "artifactId": "espresso-web", + "version": "{newEspressoVersion}" + } + }, + { + "from": { + "groupId": "com.android.support.test.espresso.idling", + "artifactId": "idling-concurrent", + "version": "3.0.2" + }, + "to": { + "groupId": "androidx.test.espresso.idling", + "artifactId": "idling-concurrent", + "version": "{newEspressoVersion}" + } + }, + { + "from": { + "groupId": "com.android.support.test.espresso.idling", + "artifactId": "idling-net", + "version": "3.0.2" + }, + "to": { + "groupId": "androidx.test.espresso.idling", + "artifactId": "idling-net", + "version": "{newEspressoVersion}" + } + }, + { + "from": { + "groupId": "com.android.support.test.janktesthelper", + "artifactId": "janktesthelper", + "version": "1.0.1" + }, + "to": { + "groupId": "androidx.test.jank", + "artifactId": "janktesthelper", + "version": "{newJankTestHelperVersion}" + } + }, + { + "from": { + "groupId": "com.android.support.test.services", + "artifactId": "test-services", + "version": "1.0.2" + }, + "to": { + "groupId": "androidx.test", + "artifactId": "test-services", + "version": "{newTestsVersion}" + } + }, + { + "from": { + "groupId": "com.android.support.test.uiautomator", + "artifactId": "uiautomator", + "version": "2.1.3" + }, + "to": { + "groupId": "androidx.test.uiautomator", + "artifactId": "uiautomator", + "version": "{newUiAutomatorVersion}" + } + }, + { + "from": { + "groupId": "com.android.support.test.uiautomator", + "artifactId": "uiautomator-v18", + "version": "2.1.3" + }, + "to": { + "groupId": "androidx.test.uiautomator", + "artifactId": "uiautomator", + "version": "{newUiAutomatorVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "car", + "version": "{oldCarVersion}" + }, + "to": { + "groupId": "androidx.car", + "artifactId": "car", + "version": "{newCarVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "slices-core", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.slice", + "artifactId": "slice-core", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "slices-builders", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.slice", + "artifactId": "slice-builders", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "slices-view", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.slice", + "artifactId": "slice-view", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "heifwriter", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.heifwriter", + "artifactId": "heifwriter", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "recyclerview-selection", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.recyclerview", + "artifactId": "recyclerview-selection", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "webkit", + "version": "{oldSlVersion}" + }, + "to": { + "groupId": "androidx.webkit", + "artifactId": "webkit", + "version": "{newSlVersion}" + } + }, + { + "from": { + "groupId": "com.android.support", + "artifactId": "biometric", + "version": "{oldBiometricVersion}" + }, + "to": { + "groupId": "androidx.biometric", + "artifactId": "biometric", + "version": "{newBiometricVersion}" + } + } + ], + "versions": { + "latestReleased": { + "oldSlVersion": "28.0.0", + "oldMaterialVersion": "28.0.0", + "oldRoomVersion": "1.1.0", + "oldCarVersion": "28.0.0-alpha5", + "oldMediarouterVersion": "28.0.0-alpha5", + "oldMedia2Version": "28.0.0-alpha03", + "oldExoplayerVersion": "28.0.0-alpha01", + "oldBiometricVersion": "28.0.0-alpha03", + "oldNavigationVersion": "1.0.0", + "oldWorkManagerVersion": "1.0.0", + "newSlVersion": "1.0.0", + "newMaterialVersion": "1.0.0", + "newArchCoreVersion": "2.0.0", + "newLifecycleVersion": "2.0.0", + "newPagingVersion": "2.0.0", + "newRoomVersion": "2.0.0", + "newEspressoVersion": "3.1.0-alpha3", + "newTestsVersion": "1.1.0-alpha3", + "newJankTestHelperVersion": "1.0.1-alpha3", + "newUiAutomatorVersion": "2.2.0-alpha3", + "newCarVersion": "1.0.0-alpha5", + "newMediarouterVersion": "1.0.0-alpha5", + "newMedia2Version": "1.0.0-alpha03", + "newExoplayerVersion": "1.0.0-alpha01", + "newBiometricVersion": "1.0.0-alpha03", + "newDataBindingVersion": "undefined", + "newNavigationVersion": "2.0.0", + "newWorkManagerVersion": "2.0.0" + } + }, + "map": { + "types": { + "android/support/test/internal/runner/tracker/UsageTracker": "android/support/test/internal/runner/tracker/UsageTracker", + "android/support/test/internal/runner/tracker/UsageTrackerRegistry": "android/support/test/internal/runner/tracker/UsageTrackerRegistry", + "android/arch/core/executor/AppToolkitTaskExecutor": "androidx/arch/core/executor/AppToolkitTaskExecutor", + "android/arch/core/executor/ArchTaskExecutor": "androidx/arch/core/executor/ArchTaskExecutor", + "android/arch/core/executor/DefaultTaskExecutor": "androidx/arch/core/executor/DefaultTaskExecutor", + "android/arch/core/executor/JunitTaskExecutorRule": "androidx/arch/core/executor/JunitTaskExecutorRule", + "android/arch/core/executor/TaskExecutor": "androidx/arch/core/executor/TaskExecutor", + "android/arch/core/executor/TaskExecutorWithFakeMainThread": "androidx/arch/core/executor/TaskExecutorWithFakeMainThread", + "android/arch/core/executor/testing/CountingTaskExecutorRule": "androidx/arch/core/executor/testing/CountingTaskExecutorRule", + "android/arch/core/executor/testing/InstantTaskExecutorRule": "androidx/arch/core/executor/testing/InstantTaskExecutorRule", + "android/arch/core/internal/FastSafeIterableMap": "androidx/arch/core/internal/FastSafeIterableMap", + "android/arch/core/internal/SafeIterableMap": "androidx/arch/core/internal/SafeIterableMap", + "android/arch/core/util/Function": "androidx/arch/core/util/Function", + "android/arch/lifecycle/AndroidViewModel": "androidx/lifecycle/AndroidViewModel", + "android/arch/lifecycle/ClassesInfoCache": "androidx/lifecycle/ClassesInfoCache", + "android/arch/lifecycle/CompositeGeneratedAdaptersObserver": "androidx/lifecycle/CompositeGeneratedAdaptersObserver", + "android/arch/lifecycle/ComputableLiveData": "androidx/lifecycle/ComputableLiveData", + "android/arch/lifecycle/DefaultLifecycleObserver": "androidx/lifecycle/DefaultLifecycleObserver", + "android/arch/lifecycle/Elements_extKt": "androidx/lifecycle/Elements_extKt", + "android/arch/lifecycle/EmptyActivityLifecycleCallbacks": "androidx/lifecycle/EmptyActivityLifecycleCallbacks", + "android/arch/lifecycle/ErrorMessages": "androidx/lifecycle/ErrorMessages", + "android/arch/lifecycle/FullLifecycleObserver": "androidx/lifecycle/FullLifecycleObserver", + "android/arch/lifecycle/FullLifecycleObserverAdapter": "androidx/lifecycle/FullLifecycleObserverAdapter", + "android/arch/lifecycle/GeneratedAdapter": "androidx/lifecycle/GeneratedAdapter", + "android/arch/lifecycle/GenericLifecycleObserver": "androidx/lifecycle/GenericLifecycleObserver", + "android/arch/lifecycle/HolderFragment": "androidx/lifecycle/HolderFragment", + "android/arch/lifecycle/Input_collectorKt": "androidx/lifecycle/Input_collectorKt", + "android/arch/lifecycle/Lifecycle": "androidx/lifecycle/Lifecycle", + "android/arch/lifecycle/LifecycleDispatcher": "androidx/lifecycle/LifecycleDispatcher", + "android/arch/lifecycle/LifecycleObserver": "androidx/lifecycle/LifecycleObserver", + "android/arch/lifecycle/LifecycleOwner": "androidx/lifecycle/LifecycleOwner", + "android/arch/lifecycle/LifecycleProcessor": "androidx/lifecycle/LifecycleProcessor", + "android/arch/lifecycle/LifecycleRegistry": "androidx/lifecycle/LifecycleRegistry", + "android/arch/lifecycle/LifecycleRegistryOwner": "androidx/lifecycle/LifecycleRegistryOwner", + "android/arch/lifecycle/LifecycleService": "androidx/lifecycle/LifecycleService", + "android/arch/lifecycle/Lifecycling": "androidx/lifecycle/Lifecycling", + "android/arch/lifecycle/LiveData": "androidx/lifecycle/LiveData", + "android/arch/lifecycle/LiveDataReactiveStreams": "androidx/lifecycle/LiveDataReactiveStreams", + "android/arch/lifecycle/MediatorLiveData": "androidx/lifecycle/MediatorLiveData", + "android/arch/lifecycle/MethodCallsLogger": "androidx/lifecycle/MethodCallsLogger", + "android/arch/lifecycle/MutableLiveData": "androidx/lifecycle/MutableLiveData", + "android/arch/lifecycle/Observer": "androidx/lifecycle/Observer", + "android/arch/lifecycle/ObserversCollector": "androidx/lifecycle/ObserversCollector", + "android/arch/lifecycle/OnLifecycleEvent": "androidx/lifecycle/OnLifecycleEvent", + "android/arch/lifecycle/ProcessLifecycleOwner": "androidx/lifecycle/ProcessLifecycleOwner", + "android/arch/lifecycle/ProcessLifecycleOwnerInitializer": "androidx/lifecycle/ProcessLifecycleOwnerInitializer", + "android/arch/lifecycle/ReflectiveGenericLifecycleObserver": "androidx/lifecycle/ReflectiveGenericLifecycleObserver", + "android/arch/lifecycle/ReportFragment": "androidx/lifecycle/ReportFragment", + "android/arch/lifecycle/ServiceLifecycleDispatcher": "androidx/lifecycle/ServiceLifecycleDispatcher", + "android/arch/lifecycle/SingleGeneratedAdapterObserver": "androidx/lifecycle/SingleGeneratedAdapterObserver", + "android/arch/lifecycle/TransformationKt": "androidx/lifecycle/TransformationKt", + "android/arch/lifecycle/Transformations": "androidx/lifecycle/Transformations", + "android/arch/lifecycle/Validator": "androidx/lifecycle/Validator", + "android/arch/lifecycle/ViewModel": "androidx/lifecycle/ViewModel", + "android/arch/lifecycle/ViewModelProvider": "androidx/lifecycle/ViewModelProvider", + "android/arch/lifecycle/ViewModelProviders": "androidx/lifecycle/ViewModelProviders", + "android/arch/lifecycle/ViewModelStore": "androidx/lifecycle/ViewModelStore", + "android/arch/lifecycle/ViewModelStoreOwner": "androidx/lifecycle/ViewModelStoreOwner", + "android/arch/lifecycle/ViewModelStores": "androidx/lifecycle/ViewModelStores", + "android/arch/lifecycle/WriterKt": "androidx/lifecycle/WriterKt", + "android/arch/lifecycle/model/AdapterClass": "androidx/lifecycle/model/AdapterClass", + "android/arch/lifecycle/model/AdapterClassKt": "androidx/lifecycle/model/AdapterClassKt", + "android/arch/lifecycle/model/EventMethod": "androidx/lifecycle/model/EventMethod", + "android/arch/lifecycle/model/EventMethodCall": "androidx/lifecycle/model/EventMethodCall", + "android/arch/lifecycle/model/InputModel": "androidx/lifecycle/model/InputModel", + "android/arch/lifecycle/model/LifecycleObserverInfo": "androidx/lifecycle/model/LifecycleObserverInfo", + "android/arch/paging/AsyncPagedListDiffer": "androidx/paging/AsyncPagedListDiffer", + "android/arch/paging/ContiguousDataSource": "androidx/paging/ContiguousDataSource", + "android/arch/paging/ContiguousPagedList": "androidx/paging/ContiguousPagedList", + "android/arch/paging/DataSource": "androidx/paging/DataSource", + "android/arch/paging/ItemKeyedDataSource": "androidx/paging/ItemKeyedDataSource", + "android/arch/paging/ListDataSource": "androidx/paging/ListDataSource", + "android/arch/paging/LivePagedListBuilder": "androidx/paging/LivePagedListBuilder", + "android/arch/paging/LivePagedListProvider": "androidx/paging/LivePagedListProvider", + "android/arch/paging/PageKeyedDataSource": "androidx/paging/PageKeyedDataSource", + "android/arch/paging/PageResult": "androidx/paging/PageResult", + "android/arch/paging/PagedList": "androidx/paging/PagedList", + "android/arch/paging/PagedListAdapter": "androidx/paging/PagedListAdapter", + "android/arch/paging/PagedStorage": "androidx/paging/PagedStorage", + "android/arch/paging/PagedStorageDiffHelper": "androidx/paging/PagedStorageDiffHelper", + "android/arch/paging/PositionalDataSource": "androidx/paging/PositionalDataSource", + "android/arch/paging/RxPagedListBuilder": "androidx/paging/RxPagedListBuilder", + "android/arch/paging/SnapshotPagedList": "androidx/paging/SnapshotPagedList", + "android/arch/paging/TiledDataSource": "androidx/paging/TiledDataSource", + "android/arch/paging/TiledPagedList": "androidx/paging/TiledPagedList", + "android/arch/paging/WrapperItemKeyedDataSource": "androidx/paging/WrapperItemKeyedDataSource", + "android/arch/paging/WrapperPageKeyedDataSource": "androidx/paging/WrapperPageKeyedDataSource", + "android/arch/paging/WrapperPositionalDataSource": "androidx/paging/WrapperPositionalDataSource", + "android/arch/persistence/db/SimpleSQLiteQuery": "androidx/sqlite/db/SimpleSQLiteQuery", + "android/arch/persistence/db/SupportSQLiteDatabase": "androidx/sqlite/db/SupportSQLiteDatabase", + "android/arch/persistence/db/SupportSQLiteOpenHelper": "androidx/sqlite/db/SupportSQLiteOpenHelper", + "android/arch/persistence/db/SupportSQLiteProgram": "androidx/sqlite/db/SupportSQLiteProgram", + "android/arch/persistence/db/SupportSQLiteQuery": "androidx/sqlite/db/SupportSQLiteQuery", + "android/arch/persistence/db/SupportSQLiteQueryBuilder": "androidx/sqlite/db/SupportSQLiteQueryBuilder", + "android/arch/persistence/db/SupportSQLiteStatement": "androidx/sqlite/db/SupportSQLiteStatement", + "android/arch/persistence/db/framework/FrameworkSQLiteDatabase": "androidx/sqlite/db/framework/FrameworkSQLiteDatabase", + "android/arch/persistence/db/framework/FrameworkSQLiteOpenHelper": "androidx/sqlite/db/framework/FrameworkSQLiteOpenHelper", + "android/arch/persistence/db/framework/FrameworkSQLiteOpenHelperFactory": "androidx/sqlite/db/framework/FrameworkSQLiteOpenHelperFactory", + "android/arch/persistence/db/framework/FrameworkSQLiteProgram": "androidx/sqlite/db/framework/FrameworkSQLiteProgram", + "android/arch/persistence/db/framework/FrameworkSQLiteStatement": "androidx/sqlite/db/framework/FrameworkSQLiteStatement", + "android/arch/persistence/room/ColumnInfo": "androidx/room/ColumnInfo", + "android/arch/persistence/room/Dao": "androidx/room/Dao", + "android/arch/persistence/room/Database": "androidx/room/Database", + "android/arch/persistence/room/DatabaseConfiguration": "androidx/room/DatabaseConfiguration", + "android/arch/persistence/room/Delete": "androidx/room/Delete", + "android/arch/persistence/room/Embedded": "androidx/room/Embedded", + "android/arch/persistence/room/EmptyResultSetException": "androidx/room/EmptyResultSetException", + "android/arch/persistence/room/Entity": "androidx/room/Entity", + "android/arch/persistence/room/EntityDeletionOrUpdateAdapter": "androidx/room/EntityDeletionOrUpdateAdapter", + "android/arch/persistence/room/EntityInsertionAdapter": "androidx/room/EntityInsertionAdapter", + "android/arch/persistence/room/ForeignKey": "androidx/room/ForeignKey", + "android/arch/persistence/room/Ignore": "androidx/room/Ignore", + "android/arch/persistence/room/Index": "androidx/room/Index", + "android/arch/persistence/room/Insert": "androidx/room/Insert", + "android/arch/persistence/room/InvalidationTracker": "androidx/room/InvalidationTracker", + "android/arch/persistence/room/OnConflictStrategy": "androidx/room/OnConflictStrategy", + "android/arch/persistence/room/PrimaryKey": "androidx/room/PrimaryKey", + "android/arch/persistence/room/Query": "androidx/room/Query", + "android/arch/persistence/room/RawQuery": "androidx/room/RawQuery", + "android/arch/persistence/room/Relation": "androidx/room/Relation", + "android/arch/persistence/room/Room": "androidx/room/Room", + "android/arch/persistence/room/RoomDatabase": "androidx/room/RoomDatabase", + "android/arch/persistence/room/RoomMasterTable": "androidx/room/RoomMasterTable", + "android/arch/persistence/room/RoomOpenHelper": "androidx/room/RoomOpenHelper", + "android/arch/persistence/room/RoomProcessor": "androidx/room/RoomProcessor", + "android/arch/persistence/room/RoomSQLiteQuery": "androidx/room/RoomSQLiteQuery", + "android/arch/persistence/room/RoomWarnings": "androidx/room/RoomWarnings", + "android/arch/persistence/room/RxRoom": "androidx/room/RxRoom", + "android/arch/persistence/room/SharedSQLiteStatement": "androidx/room/SharedSQLiteStatement", + "android/arch/persistence/room/SkipQueryVerification": "androidx/room/SkipQueryVerification", + "android/arch/persistence/room/Transaction": "androidx/room/Transaction", + "android/arch/persistence/room/TypeConverter": "androidx/room/TypeConverter", + "android/arch/persistence/room/TypeConverters": "androidx/room/TypeConverters", + "android/arch/persistence/room/Update": "androidx/room/Update", + "android/arch/persistence/room/ext/AndroidTypeNames": "androidx/room/ext/AndroidTypeNames", + "android/arch/persistence/room/ext/ArchTypeNames": "androidx/room/ext/ArchTypeNames", + "android/arch/persistence/room/ext/CommonTypeNames": "androidx/room/ext/CommonTypeNames", + "android/arch/persistence/room/ext/Element_extKt": "androidx/room/ext/Element_extKt", + "android/arch/persistence/room/ext/GuavaBaseTypeNames": "androidx/room/ext/GuavaBaseTypeNames", + "android/arch/persistence/room/ext/GuavaUtilConcurrentTypeNames": "androidx/room/ext/GuavaUtilConcurrentTypeNames", + "android/arch/persistence/room/ext/Javapoet_extKt": "androidx/room/ext/Javapoet_extKt", + "android/arch/persistence/room/ext/KotlinMetadataProcessor": "androidx/room/ext/KotlinMetadataProcessor", + "android/arch/persistence/room/ext/LifecyclesTypeNames": "androidx/room/ext/LifecyclesTypeNames", + "android/arch/persistence/room/ext/PagingTypeNames": "androidx/room/ext/PagingTypeNames", + "android/arch/persistence/room/ext/ReactiveStreamsTypeNames": "androidx/room/ext/ReactiveStreamsTypeNames", + "android/arch/persistence/room/ext/RoomGuavaTypeNames": "androidx/room/ext/RoomGuavaTypeNames", + "android/arch/persistence/room/ext/RoomRxJava2TypeNames": "androidx/room/ext/RoomRxJava2TypeNames", + "android/arch/persistence/room/ext/RoomTypeNames": "androidx/room/ext/RoomTypeNames", + "android/arch/persistence/room/ext/RxJava2TypeNames": "androidx/room/ext/RxJava2TypeNames", + "android/arch/persistence/room/ext/SupportDbTypeNames": "androidx/room/ext/SupportDbTypeNames", + "android/arch/persistence/room/guava/GuavaRoom": "androidx/room/guava/GuavaRoom", + "android/arch/persistence/room/log/RLog": "androidx/room/log/RLog", + "android/arch/persistence/room/migration/Migration": "androidx/room/migration/Migration", + "android/arch/persistence/room/migration/bundle/BundleUtil": "androidx/room/migration/bundle/BundleUtil", + "android/arch/persistence/room/migration/bundle/DatabaseBundle": "androidx/room/migration/bundle/DatabaseBundle", + "android/arch/persistence/room/migration/bundle/EntityBundle": "androidx/room/migration/bundle/EntityBundle", + "android/arch/persistence/room/migration/bundle/FieldBundle": "androidx/room/migration/bundle/FieldBundle", + "android/arch/persistence/room/migration/bundle/ForeignKeyBundle": "androidx/room/migration/bundle/ForeignKeyBundle", + "android/arch/persistence/room/migration/bundle/IndexBundle": "androidx/room/migration/bundle/IndexBundle", + "android/arch/persistence/room/migration/bundle/PrimaryKeyBundle": "androidx/room/migration/bundle/PrimaryKeyBundle", + "android/arch/persistence/room/migration/bundle/SchemaBundle": "androidx/room/migration/bundle/SchemaBundle", + "android/arch/persistence/room/migration/bundle/SchemaEquality": "androidx/room/migration/bundle/SchemaEquality", + "android/arch/persistence/room/migration/bundle/SchemaEqualityUtil": "androidx/room/migration/bundle/SchemaEqualityUtil", + "android/arch/persistence/room/paging/LimitOffsetDataSource": "androidx/room/paging/LimitOffsetDataSource", + "android/arch/persistence/room/parser/Collate": "androidx/room/parser/Collate", + "android/arch/persistence/room/parser/ParsedQuery": "androidx/room/parser/ParsedQuery", + "android/arch/persistence/room/parser/ParserErrors": "androidx/room/parser/ParserErrors", + "android/arch/persistence/room/parser/QueryType": "androidx/room/parser/QueryType", + "android/arch/persistence/room/parser/QueryVisitor": "androidx/room/parser/QueryVisitor", + "android/arch/persistence/room/parser/SQLTypeAffinity": "androidx/room/parser/SQLTypeAffinity", + "android/arch/persistence/room/parser/SQLiteBaseListener": "androidx/room/parser/SQLiteBaseListener", + "android/arch/persistence/room/parser/SQLiteBaseVisitor": "androidx/room/parser/SQLiteBaseVisitor", + "android/arch/persistence/room/parser/SQLiteLexer": "androidx/room/parser/SQLiteLexer", + "android/arch/persistence/room/parser/SQLiteListener": "androidx/room/parser/SQLiteListener", + "android/arch/persistence/room/parser/SQLiteParser": "androidx/room/parser/SQLiteParser", + "android/arch/persistence/room/parser/SQLiteVisitor": "androidx/room/parser/SQLiteVisitor", + "android/arch/persistence/room/parser/Section": "androidx/room/parser/Section", + "android/arch/persistence/room/parser/SectionType": "androidx/room/parser/SectionType", + "android/arch/persistence/room/parser/SqlParser": "androidx/room/parser/SqlParser", + "android/arch/persistence/room/parser/Table": "androidx/room/parser/Table", + "android/arch/persistence/room/preconditions/Checks": "androidx/room/preconditions/Checks", + "android/arch/persistence/room/processor/Context": "androidx/room/processor/Context", + "android/arch/persistence/room/processor/CustomConverterProcessor": "androidx/room/processor/CustomConverterProcessor", + "android/arch/persistence/room/processor/DaoProcessor": "androidx/room/processor/DaoProcessor", + "android/arch/persistence/room/processor/DatabaseProcessor": "androidx/room/processor/DatabaseProcessor", + "android/arch/persistence/room/processor/DeletionMethodProcessor": "androidx/room/processor/DeletionMethodProcessor", + "android/arch/persistence/room/processor/EntityProcessor": "androidx/room/processor/EntityProcessor", + "android/arch/persistence/room/processor/FieldProcessor": "androidx/room/processor/FieldProcessor", + "android/arch/persistence/room/processor/InsertionMethodProcessor": "androidx/room/processor/InsertionMethodProcessor", + "android/arch/persistence/room/processor/OnConflictProcessor": "androidx/room/processor/OnConflictProcessor", + "android/arch/persistence/room/processor/PojoMethodProcessor": "androidx/room/processor/PojoMethodProcessor", + "android/arch/persistence/room/processor/PojoProcessor": "androidx/room/processor/PojoProcessor", + "android/arch/persistence/room/processor/ProcessorErrors": "androidx/room/processor/ProcessorErrors", + "android/arch/persistence/room/processor/QueryMethodProcessor": "androidx/room/processor/QueryMethodProcessor", + "android/arch/persistence/room/processor/QueryParameterProcessor": "androidx/room/processor/QueryParameterProcessor", + "android/arch/persistence/room/processor/RawQueryMethodProcessor": "androidx/room/processor/RawQueryMethodProcessor", + "android/arch/persistence/room/processor/ShortcutMethodProcessor": "androidx/room/processor/ShortcutMethodProcessor", + "android/arch/persistence/room/processor/ShortcutParameterProcessor": "androidx/room/processor/ShortcutParameterProcessor", + "android/arch/persistence/room/processor/SuppressWarningProcessor": "androidx/room/processor/SuppressWarningProcessor", + "android/arch/persistence/room/processor/TransactionMethodProcessor": "androidx/room/processor/TransactionMethodProcessor", + "android/arch/persistence/room/processor/UpdateMethodProcessor": "androidx/room/processor/UpdateMethodProcessor", + "android/arch/persistence/room/processor/cache/Cache": "androidx/room/processor/cache/Cache", + "android/arch/persistence/room/solver/CodeGenScope": "androidx/room/solver/CodeGenScope", + "android/arch/persistence/room/solver/ObservableQueryResultBinderProvider": "androidx/room/solver/ObservableQueryResultBinderProvider", + "android/arch/persistence/room/solver/QueryResultBinderProvider": "androidx/room/solver/QueryResultBinderProvider", + "android/arch/persistence/room/solver/TypeAdapterStore": "androidx/room/solver/TypeAdapterStore", + "android/arch/persistence/room/solver/binderprovider/CursorQueryResultBinderProvider": "androidx/room/solver/binderprovider/CursorQueryResultBinderProvider", + "android/arch/persistence/room/solver/binderprovider/DataSourceFactoryQueryResultBinderProvider": "androidx/room/solver/binderprovider/DataSourceFactoryQueryResultBinderProvider", + "android/arch/persistence/room/solver/binderprovider/DataSourceQueryResultBinderProvider": "androidx/room/solver/binderprovider/DataSourceQueryResultBinderProvider", + "android/arch/persistence/room/solver/binderprovider/FlowableQueryResultBinderProvider": "androidx/room/solver/binderprovider/FlowableQueryResultBinderProvider", + "android/arch/persistence/room/solver/binderprovider/GuavaListenableFutureQueryResultBinderProvider": "androidx/room/solver/binderprovider/GuavaListenableFutureQueryResultBinderProvider", + "android/arch/persistence/room/solver/binderprovider/InstantQueryResultBinderProvider": "androidx/room/solver/binderprovider/InstantQueryResultBinderProvider", + "android/arch/persistence/room/solver/binderprovider/LiveDataQueryResultBinderProvider": "androidx/room/solver/binderprovider/LiveDataQueryResultBinderProvider", + "android/arch/persistence/room/solver/binderprovider/RxCallableQueryResultBinderProvider": "androidx/room/solver/binderprovider/RxCallableQueryResultBinderProvider", + "android/arch/persistence/room/solver/binderprovider/RxMaybeQueryResultBinderProvider": "androidx/room/solver/binderprovider/RxMaybeQueryResultBinderProvider", + "android/arch/persistence/room/solver/binderprovider/RxSingleQueryResultBinderProvider": "androidx/room/solver/binderprovider/RxSingleQueryResultBinderProvider", + "android/arch/persistence/room/solver/query/parameter/ArrayQueryParameterAdapter": "androidx/room/solver/query/parameter/ArrayQueryParameterAdapter", + "android/arch/persistence/room/solver/query/parameter/BasicQueryParameterAdapter": "androidx/room/solver/query/parameter/BasicQueryParameterAdapter", + "android/arch/persistence/room/solver/query/parameter/CollectionQueryParameterAdapter": "androidx/room/solver/query/parameter/CollectionQueryParameterAdapter", + "android/arch/persistence/room/solver/query/parameter/QueryParameterAdapter": "androidx/room/solver/query/parameter/QueryParameterAdapter", + "android/arch/persistence/room/solver/query/result/ArrayQueryResultAdapter": "androidx/room/solver/query/result/ArrayQueryResultAdapter", + "android/arch/persistence/room/solver/query/result/BaseObservableQueryResultBinder": "androidx/room/solver/query/result/BaseObservableQueryResultBinder", + "android/arch/persistence/room/solver/query/result/CursorQueryResultBinder": "androidx/room/solver/query/result/CursorQueryResultBinder", + "android/arch/persistence/room/solver/query/result/DataSourceFactoryQueryResultBinder": "androidx/room/solver/query/result/DataSourceFactoryQueryResultBinder", + "android/arch/persistence/room/solver/query/result/EntityRowAdapter": "androidx/room/solver/query/result/EntityRowAdapter", + "android/arch/persistence/room/solver/query/result/FlowableQueryResultBinder": "androidx/room/solver/query/result/FlowableQueryResultBinder", + "android/arch/persistence/room/solver/query/result/GuavaListenableFutureQueryResultBinder": "androidx/room/solver/query/result/GuavaListenableFutureQueryResultBinder", + "android/arch/persistence/room/solver/query/result/GuavaOptionalQueryResultAdapter": "androidx/room/solver/query/result/GuavaOptionalQueryResultAdapter", + "android/arch/persistence/room/solver/query/result/InstantQueryResultBinder": "androidx/room/solver/query/result/InstantQueryResultBinder", + "android/arch/persistence/room/solver/query/result/ListQueryResultAdapter": "androidx/room/solver/query/result/ListQueryResultAdapter", + "android/arch/persistence/room/solver/query/result/LiveDataQueryResultBinder": "androidx/room/solver/query/result/LiveDataQueryResultBinder", + "android/arch/persistence/room/solver/query/result/OptionalQueryResultAdapter": "androidx/room/solver/query/result/OptionalQueryResultAdapter", + "android/arch/persistence/room/solver/query/result/PojoRowAdapter": "androidx/room/solver/query/result/PojoRowAdapter", + "android/arch/persistence/room/solver/query/result/PositionalDataSourceQueryResultBinder": "androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder", + "android/arch/persistence/room/solver/query/result/QueryResultAdapter": "androidx/room/solver/query/result/QueryResultAdapter", + "android/arch/persistence/room/solver/query/result/QueryResultBinder": "androidx/room/solver/query/result/QueryResultBinder", + "android/arch/persistence/room/solver/query/result/RowAdapter": "androidx/room/solver/query/result/RowAdapter", + "android/arch/persistence/room/solver/query/result/RxCallableQueryResultBinder": "androidx/room/solver/query/result/RxCallableQueryResultBinder", + "android/arch/persistence/room/solver/query/result/SingleColumnRowAdapter": "androidx/room/solver/query/result/SingleColumnRowAdapter", + "android/arch/persistence/room/solver/query/result/SingleEntityQueryResultAdapter": "androidx/room/solver/query/result/SingleEntityQueryResultAdapter", + "android/arch/persistence/room/solver/query/result/TransactionWrapper": "androidx/room/solver/query/result/TransactionWrapper", + "android/arch/persistence/room/solver/query/result/TransactionWrapperKt": "androidx/room/solver/query/result/TransactionWrapperKt", + "android/arch/persistence/room/solver/types/BoxedBooleanToBoxedIntConverter": "androidx/room/solver/types/BoxedBooleanToBoxedIntConverter", + "android/arch/persistence/room/solver/types/BoxedPrimitiveColumnTypeAdapter": "androidx/room/solver/types/BoxedPrimitiveColumnTypeAdapter", + "android/arch/persistence/room/solver/types/ByteArrayColumnTypeAdapter": "androidx/room/solver/types/ByteArrayColumnTypeAdapter", + "android/arch/persistence/room/solver/types/ColumnTypeAdapter": "androidx/room/solver/types/ColumnTypeAdapter", + "android/arch/persistence/room/solver/types/CompositeAdapter": "androidx/room/solver/types/CompositeAdapter", + "android/arch/persistence/room/solver/types/CompositeTypeConverter": "androidx/room/solver/types/CompositeTypeConverter", + "android/arch/persistence/room/solver/types/CursorValueReader": "androidx/room/solver/types/CursorValueReader", + "android/arch/persistence/room/solver/types/CustomTypeConverterWrapper": "androidx/room/solver/types/CustomTypeConverterWrapper", + "android/arch/persistence/room/solver/types/NoOpConverter": "androidx/room/solver/types/NoOpConverter", + "android/arch/persistence/room/solver/types/PrimitiveBooleanToIntConverter": "androidx/room/solver/types/PrimitiveBooleanToIntConverter", + "android/arch/persistence/room/solver/types/PrimitiveColumnTypeAdapter": "androidx/room/solver/types/PrimitiveColumnTypeAdapter", + "android/arch/persistence/room/solver/types/StatementValueBinder": "androidx/room/solver/types/StatementValueBinder", + "android/arch/persistence/room/solver/types/StringColumnTypeAdapter": "androidx/room/solver/types/StringColumnTypeAdapter", + "android/arch/persistence/room/solver/types/TypeConverter": "androidx/room/solver/types/TypeConverter", + "android/arch/persistence/room/testing/MigrationTestHelper": "androidx/room/testing/MigrationTestHelper", + "android/arch/persistence/room/util/StringUtil": "androidx/room/util/StringUtil", + "android/arch/persistence/room/util/TableInfo": "androidx/room/util/TableInfo", + "android/arch/persistence/room/verifier/ColumnInfo": "androidx/room/verifier/ColumnInfo", + "android/arch/persistence/room/verifier/DatabaseVerificaitonErrors": "androidx/room/verifier/DatabaseVerificaitonErrors", + "android/arch/persistence/room/verifier/DatabaseVerifier": "androidx/room/verifier/DatabaseVerifier", + "android/arch/persistence/room/verifier/QueryResultInfo": "androidx/room/verifier/QueryResultInfo", + "android/arch/persistence/room/vo/CallType": "androidx/room/vo/CallType", + "android/arch/persistence/room/vo/Constructor": "androidx/room/vo/Constructor", + "android/arch/persistence/room/vo/CustomTypeConverter": "androidx/room/vo/CustomTypeConverter", + "android/arch/persistence/room/vo/Dao": "androidx/room/vo/Dao", + "android/arch/persistence/room/vo/DaoMethod": "androidx/room/vo/DaoMethod", + "android/arch/persistence/room/vo/Database": "androidx/room/vo/Database", + "android/arch/persistence/room/vo/DeletionMethod": "androidx/room/vo/DeletionMethod", + "android/arch/persistence/room/vo/EmbeddedField": "androidx/room/vo/EmbeddedField", + "android/arch/persistence/room/vo/Entity": "androidx/room/vo/Entity", + "android/arch/persistence/room/vo/Field": "androidx/room/vo/Field", + "android/arch/persistence/room/vo/FieldGetter": "androidx/room/vo/FieldGetter", + "android/arch/persistence/room/vo/FieldSetter": "androidx/room/vo/FieldSetter", + "android/arch/persistence/room/vo/FieldWithIndex": "androidx/room/vo/FieldWithIndex", + "android/arch/persistence/room/vo/ForeignKey": "androidx/room/vo/ForeignKey", + "android/arch/persistence/room/vo/ForeignKeyAction": "androidx/room/vo/ForeignKeyAction", + "android/arch/persistence/room/vo/HasSchemaIdentity": "androidx/room/vo/HasSchemaIdentity", + "android/arch/persistence/room/vo/Index": "androidx/room/vo/Index", + "android/arch/persistence/room/vo/InsertionMethod": "androidx/room/vo/InsertionMethod", + "android/arch/persistence/room/vo/Pojo": "androidx/room/vo/Pojo", + "android/arch/persistence/room/vo/PojoMethod": "androidx/room/vo/PojoMethod", + "android/arch/persistence/room/vo/PrimaryKey": "androidx/room/vo/PrimaryKey", + "android/arch/persistence/room/vo/QueryMethod": "androidx/room/vo/QueryMethod", + "android/arch/persistence/room/vo/QueryParameter": "androidx/room/vo/QueryParameter", + "android/arch/persistence/room/vo/RawQueryMethod": "androidx/room/vo/RawQueryMethod", + "android/arch/persistence/room/vo/Relation": "androidx/room/vo/Relation", + "android/arch/persistence/room/vo/RelationCollector": "androidx/room/vo/RelationCollector", + "android/arch/persistence/room/vo/SchemaIdentityKey": "androidx/room/vo/SchemaIdentityKey", + "android/arch/persistence/room/vo/ShortcutMethod": "androidx/room/vo/ShortcutMethod", + "android/arch/persistence/room/vo/ShortcutQueryParameter": "androidx/room/vo/ShortcutQueryParameter", + "android/arch/persistence/room/vo/TransactionMethod": "androidx/room/vo/TransactionMethod", + "android/arch/persistence/room/vo/UpdateMethod": "androidx/room/vo/UpdateMethod", + "android/arch/persistence/room/vo/Warning": "androidx/room/vo/Warning", + "android/arch/persistence/room/writer/ClassWriter": "androidx/room/writer/ClassWriter", + "android/arch/persistence/room/writer/DaoWriter": "androidx/room/writer/DaoWriter", + "android/arch/persistence/room/writer/DatabaseWriter": "androidx/room/writer/DatabaseWriter", + "android/arch/persistence/room/writer/EntityCursorConverterWriter": "androidx/room/writer/EntityCursorConverterWriter", + "android/arch/persistence/room/writer/EntityDeleteComparator": "androidx/room/writer/EntityDeleteComparator", + "android/arch/persistence/room/writer/EntityDeletionAdapterWriter": "androidx/room/writer/EntityDeletionAdapterWriter", + "android/arch/persistence/room/writer/EntityInsertionAdapterWriter": "androidx/room/writer/EntityInsertionAdapterWriter", + "android/arch/persistence/room/writer/EntityUpdateAdapterWriter": "androidx/room/writer/EntityUpdateAdapterWriter", + "android/arch/persistence/room/writer/FieldReadWriteWriter": "androidx/room/writer/FieldReadWriteWriter", + "android/arch/persistence/room/writer/PreparedStatementWriter": "androidx/room/writer/PreparedStatementWriter", + "android/arch/persistence/room/writer/QueryWriter": "androidx/room/writer/QueryWriter", + "android/arch/persistence/room/writer/RelationCollectorMethodWriter": "androidx/room/writer/RelationCollectorMethodWriter", + "android/arch/persistence/room/writer/SQLiteOpenHelperWriter": "androidx/room/writer/SQLiteOpenHelperWriter", + "android/arch/persistence/room/writer/TableInfoValidationWriter": "androidx/room/writer/TableInfoValidationWriter", + "android/databinding/BaseObservable": "androidx/databinding/BaseObservable", + "android/databinding/BaseObservableField": "androidx/databinding/BaseObservableField", + "android/databinding/Bindable": "androidx/databinding/Bindable", + "android/databinding/BindingAdapter": "androidx/databinding/BindingAdapter", + "android/databinding/BindingBuildInfo": "androidx/databinding/BindingBuildInfo", + "android/databinding/BindingConversion": "androidx/databinding/BindingConversion", + "android/databinding/BindingMethod": "androidx/databinding/BindingMethod", + "android/databinding/BindingMethods": "androidx/databinding/BindingMethods", + "android/databinding/CallbackRegistry": "androidx/databinding/CallbackRegistry", + "android/databinding/DataBinderMapper": "androidx/databinding/DataBinderMapper", + "android/databinding/DataBinderMapperImpl": "androidx/databinding/DataBinderMapperImpl", + "android/databinding/DataBindingComponent": "androidx/databinding/DataBindingComponent", + "android/databinding/DataBindingUtil": "androidx/databinding/DataBindingUtil", + "android/databinding/InverseBindingAdapter": "androidx/databinding/InverseBindingAdapter", + "android/databinding/InverseBindingListener": "androidx/databinding/InverseBindingListener", + "android/databinding/InverseBindingMethod": "androidx/databinding/InverseBindingMethod", + "android/databinding/InverseBindingMethods": "androidx/databinding/InverseBindingMethods", + "android/databinding/InverseMethod": "androidx/databinding/InverseMethod", + "android/databinding/ListChangeRegistry": "androidx/databinding/ListChangeRegistry", + "android/databinding/MapChangeRegistry": "androidx/databinding/MapChangeRegistry", + "android/databinding/MergedDataBinderMapper": "androidx/databinding/MergedDataBinderMapper", + "android/databinding/Observable": "androidx/databinding/Observable", + "android/databinding/ObservableArrayList": "androidx/databinding/ObservableArrayList", + "android/databinding/ObservableArrayMap": "androidx/databinding/ObservableArrayMap", + "android/databinding/ObservableBoolean": "androidx/databinding/ObservableBoolean", + "android/databinding/ObservableByte": "androidx/databinding/ObservableByte", + "android/databinding/ObservableChar": "androidx/databinding/ObservableChar", + "android/databinding/ObservableDouble": "androidx/databinding/ObservableDouble", + "android/databinding/ObservableField": "androidx/databinding/ObservableField", + "android/databinding/ObservableFloat": "androidx/databinding/ObservableFloat", + "android/databinding/ObservableInt": "androidx/databinding/ObservableInt", + "android/databinding/ObservableList": "androidx/databinding/ObservableList", + "android/databinding/ObservableLong": "androidx/databinding/ObservableLong", + "android/databinding/ObservableMap": "androidx/databinding/ObservableMap", + "android/databinding/ObservableParcelable": "androidx/databinding/ObservableParcelable", + "android/databinding/ObservableShort": "androidx/databinding/ObservableShort", + "android/databinding/OnRebindCallback": "androidx/databinding/OnRebindCallback", + "android/databinding/PropertyChangeRegistry": "androidx/databinding/PropertyChangeRegistry", + "android/databinding/Untaggable": "androidx/databinding/Untaggable", + "android/databinding/ViewDataBinding": "androidx/databinding/ViewDataBinding", + "android/databinding/ViewStubProxy": "androidx/databinding/ViewStubProxy", + "android/databinding/adapters/AbsListViewBindingAdapter": "androidx/databinding/adapters/AbsListViewBindingAdapter", + "android/databinding/adapters/AbsSeekBarBindingAdapter": "androidx/databinding/adapters/AbsSeekBarBindingAdapter", + "android/databinding/adapters/AbsSpinnerBindingAdapter": "androidx/databinding/adapters/AbsSpinnerBindingAdapter", + "android/databinding/adapters/ActionMenuViewBindingAdapter": "androidx/databinding/adapters/ActionMenuViewBindingAdapter", + "android/databinding/adapters/AdapterViewBindingAdapter": "androidx/databinding/adapters/AdapterViewBindingAdapter", + "android/databinding/adapters/AutoCompleteTextViewBindingAdapter": "androidx/databinding/adapters/AutoCompleteTextViewBindingAdapter", + "android/databinding/adapters/CalendarViewBindingAdapter": "androidx/databinding/adapters/CalendarViewBindingAdapter", + "android/databinding/adapters/CardViewBindingAdapter": "androidx/databinding/adapters/CardViewBindingAdapter", + "android/databinding/adapters/CheckedTextViewBindingAdapter": "androidx/databinding/adapters/CheckedTextViewBindingAdapter", + "android/databinding/adapters/ChronometerBindingAdapter": "androidx/databinding/adapters/ChronometerBindingAdapter", + "android/databinding/adapters/CompoundButtonBindingAdapter": "androidx/databinding/adapters/CompoundButtonBindingAdapter", + "android/databinding/adapters/Converters": "androidx/databinding/adapters/Converters", + "android/databinding/adapters/DatePickerBindingAdapter": "androidx/databinding/adapters/DatePickerBindingAdapter", + "android/databinding/adapters/ExpandableListViewBindingAdapter": "androidx/databinding/adapters/ExpandableListViewBindingAdapter", + "android/databinding/adapters/FrameLayoutBindingAdapter": "androidx/databinding/adapters/FrameLayoutBindingAdapter", + "android/databinding/adapters/ImageViewBindingAdapter": "androidx/databinding/adapters/ImageViewBindingAdapter", + "android/databinding/adapters/LinearLayoutBindingAdapter": "androidx/databinding/adapters/LinearLayoutBindingAdapter", + "android/databinding/adapters/ListenerUtil": "androidx/databinding/adapters/ListenerUtil", + "android/databinding/adapters/NumberPickerBindingAdapter": "androidx/databinding/adapters/NumberPickerBindingAdapter", + "android/databinding/adapters/ObservableListAdapter": "androidx/databinding/adapters/ObservableListAdapter", + "android/databinding/adapters/ProgressBarBindingAdapter": "androidx/databinding/adapters/ProgressBarBindingAdapter", + "android/databinding/adapters/RadioGroupBindingAdapter": "androidx/databinding/adapters/RadioGroupBindingAdapter", + "android/databinding/adapters/RatingBarBindingAdapter": "androidx/databinding/adapters/RatingBarBindingAdapter", + "android/databinding/adapters/SearchViewBindingAdapter": "androidx/databinding/adapters/SearchViewBindingAdapter", + "android/databinding/adapters/SeekBarBindingAdapter": "androidx/databinding/adapters/SeekBarBindingAdapter", + "android/databinding/adapters/SpinnerBindingAdapter": "androidx/databinding/adapters/SpinnerBindingAdapter", + "android/databinding/adapters/SwitchBindingAdapter": "androidx/databinding/adapters/SwitchBindingAdapter", + "android/databinding/adapters/SwitchCompatBindingAdapter": "androidx/databinding/adapters/SwitchCompatBindingAdapter", + "android/databinding/adapters/TabHostBindingAdapter": "androidx/databinding/adapters/TabHostBindingAdapter", + "android/databinding/adapters/TabWidgetBindingAdapter": "androidx/databinding/adapters/TabWidgetBindingAdapter", + "android/databinding/adapters/TableLayoutBindingAdapter": "androidx/databinding/adapters/TableLayoutBindingAdapter", + "android/databinding/adapters/TextViewBindingAdapter": "androidx/databinding/adapters/TextViewBindingAdapter", + "android/databinding/adapters/TimePickerBindingAdapter": "androidx/databinding/adapters/TimePickerBindingAdapter", + "android/databinding/adapters/ToolbarBindingAdapter": "androidx/databinding/adapters/ToolbarBindingAdapter", + "android/databinding/adapters/VideoViewBindingAdapter": "androidx/databinding/adapters/VideoViewBindingAdapter", + "android/databinding/adapters/ViewBindingAdapter": "androidx/databinding/adapters/ViewBindingAdapter", + "android/databinding/adapters/ViewGroupBindingAdapter": "androidx/databinding/adapters/ViewGroupBindingAdapter", + "android/databinding/adapters/ViewStubBindingAdapter": "androidx/databinding/adapters/ViewStubBindingAdapter", + "android/databinding/adapters/ZoomControlsBindingAdapter": "androidx/databinding/adapters/ZoomControlsBindingAdapter", + "android/support/animation/AnimationHandler": "androidx/dynamicanimation/animation/AnimationHandler", + "android/support/animation/DynamicAnimation": "androidx/dynamicanimation/animation/DynamicAnimation", + "android/support/animation/FlingAnimation": "androidx/dynamicanimation/animation/FlingAnimation", + "android/support/animation/FloatPropertyCompat": "androidx/dynamicanimation/animation/FloatPropertyCompat", + "android/support/animation/FloatValueHolder": "androidx/dynamicanimation/animation/FloatValueHolder", + "android/support/animation/Force": "androidx/dynamicanimation/animation/Force", + "android/support/animation/SpringAnimation": "androidx/dynamicanimation/animation/SpringAnimation", + "android/support/animation/SpringForce": "androidx/dynamicanimation/animation/SpringForce", + "android/support/annotation/AnimRes": "androidx/annotation/AnimRes", + "android/support/annotation/AnimatorRes": "androidx/annotation/AnimatorRes", + "android/support/annotation/AnyRes": "androidx/annotation/AnyRes", + "android/support/annotation/AnyThread": "androidx/annotation/AnyThread", + "android/support/annotation/ArrayRes": "androidx/annotation/ArrayRes", + "android/support/annotation/AttrRes": "androidx/annotation/AttrRes", + "android/support/annotation/BinderThread": "androidx/annotation/BinderThread", + "android/support/annotation/BoolRes": "androidx/annotation/BoolRes", + "android/support/annotation/CallSuper": "androidx/annotation/CallSuper", + "android/support/annotation/CheckResult": "androidx/annotation/CheckResult", + "android/support/annotation/ColorInt": "androidx/annotation/ColorInt", + "android/support/annotation/ColorLong": "androidx/annotation/ColorLong", + "android/support/annotation/ColorRes": "androidx/annotation/ColorRes", + "android/support/annotation/DimenRes": "androidx/annotation/DimenRes", + "android/support/annotation/Dimension": "androidx/annotation/Dimension", + "android/support/annotation/DrawableRes": "androidx/annotation/DrawableRes", + "android/support/annotation/FloatRange": "androidx/annotation/FloatRange", + "android/support/annotation/FontRes": "androidx/annotation/FontRes", + "android/support/annotation/FractionRes": "androidx/annotation/FractionRes", + "android/support/annotation/GuardedBy": "androidx/annotation/GuardedBy", + "android/support/annotation/HalfFloat": "androidx/annotation/HalfFloat", + "android/support/annotation/IdRes": "androidx/annotation/IdRes", + "android/support/annotation/IntDef": "androidx/annotation/IntDef", + "android/support/annotation/IntRange": "androidx/annotation/IntRange", + "android/support/annotation/IntegerRes": "androidx/annotation/IntegerRes", + "android/support/annotation/InterpolatorRes": "androidx/annotation/InterpolatorRes", + "android/support/annotation/Keep": "androidx/annotation/Keep", + "android/support/annotation/LayoutRes": "androidx/annotation/LayoutRes", + "android/support/annotation/LongDef": "androidx/annotation/LongDef", + "android/support/annotation/MainThread": "androidx/annotation/MainThread", + "android/support/annotation/MenuRes": "androidx/annotation/MenuRes", + "android/support/annotation/NavigationRes": "androidx/annotation/NavigationRes", + "android/support/annotation/NonNull": "androidx/annotation/NonNull", + "android/support/annotation/Nullable": "androidx/annotation/Nullable", + "android/support/annotation/PluralsRes": "androidx/annotation/PluralsRes", + "android/support/annotation/Px": "androidx/annotation/Px", + "android/support/annotation/RawRes": "androidx/annotation/RawRes", + "android/support/annotation/RequiresApi": "androidx/annotation/RequiresApi", + "android/support/annotation/RequiresFeature": "androidx/annotation/RequiresFeature", + "android/support/annotation/RequiresPermission": "androidx/annotation/RequiresPermission", + "android/support/annotation/RestrictTo": "androidx/annotation/RestrictTo", + "android/support/annotation/Size": "androidx/annotation/Size", + "android/support/annotation/StringDef": "androidx/annotation/StringDef", + "android/support/annotation/StringRes": "androidx/annotation/StringRes", + "android/support/annotation/StyleRes": "androidx/annotation/StyleRes", + "android/support/annotation/StyleableRes": "androidx/annotation/StyleableRes", + "android/support/annotation/TransitionRes": "androidx/annotation/TransitionRes", + "android/support/annotation/UiThread": "androidx/annotation/UiThread", + "android/support/annotation/VisibleForTesting": "androidx/annotation/VisibleForTesting", + "android/support/annotation/WorkerThread": "androidx/annotation/WorkerThread", + "android/support/annotation/XmlRes": "androidx/annotation/XmlRes", + "android/support/app/recommendation/ContentRecommendation": "androidx/recommendation/app/ContentRecommendation", + "android/support/app/recommendation/RecommendationExtender": "androidx/recommendation/app/RecommendationExtender", + "android/support/compat/R": "androidx/core/R", + "android/support/constraint/Barrier": "androidx/constraintlayout/widget/Barrier", + "android/support/constraint/ConstraintHelper": "androidx/constraintlayout/widget/ConstraintHelper", + "android/support/constraint/ConstraintLayout": "androidx/constraintlayout/widget/ConstraintLayout", + "android/support/constraint/ConstraintSet": "androidx/constraintlayout/widget/ConstraintSet", + "android/support/constraint/Constraints": "androidx/constraintlayout/widget/Constraints", + "android/support/constraint/Group": "androidx/constraintlayout/widget/Group", + "android/support/constraint/Guideline": "androidx/constraintlayout/widget/Guideline", + "android/support/constraint/Placeholder": "androidx/constraintlayout/widget/Placeholder", + "android/support/constraint/R": "androidx/constraintlayout/widget/R", + "android/support/constraint/solver/ArrayLinkedVariables": "androidx/constraintlayout/solver/ArrayLinkedVariables", + "android/support/constraint/solver/ArrayRow": "androidx/constraintlayout/solver/ArrayRow", + "android/support/constraint/solver/Cache": "androidx/constraintlayout/solver/Cache", + "android/support/constraint/solver/GoalRow": "androidx/constraintlayout/solver/GoalRow", + "android/support/constraint/solver/LinearSystem": "androidx/constraintlayout/solver/LinearSystem", + "android/support/constraint/solver/Metrics": "androidx/constraintlayout/solver/Metrics", + "android/support/constraint/solver/Pools": "androidx/constraintlayout/solver/Pools", + "android/support/constraint/solver/SolverVariable": "androidx/constraintlayout/solver/SolverVariable", + "android/support/constraint/solver/widgets/Analyzer": "androidx/constraintlayout/solver/widgets/Analyzer", + "android/support/constraint/solver/widgets/Barrier": "androidx/constraintlayout/solver/widgets/Barrier", + "android/support/constraint/solver/widgets/Chain": "androidx/constraintlayout/solver/widgets/Chain", + "android/support/constraint/solver/widgets/ChainHead": "androidx/constraintlayout/solver/widgets/ChainHead", + "android/support/constraint/solver/widgets/ConstraintAnchor": "androidx/constraintlayout/solver/widgets/ConstraintAnchor", + "android/support/constraint/solver/widgets/ConstraintHorizontalLayout": "androidx/constraintlayout/solver/widgets/ConstraintHorizontalLayout", + "android/support/constraint/solver/widgets/ConstraintTableLayout": "androidx/constraintlayout/solver/widgets/ConstraintTableLayout", + "android/support/constraint/solver/widgets/ConstraintWidget": "androidx/constraintlayout/solver/widgets/ConstraintWidget", + "android/support/constraint/solver/widgets/ConstraintWidgetContainer": "androidx/constraintlayout/solver/widgets/ConstraintWidgetContainer", + "android/support/constraint/solver/widgets/ConstraintWidgetGroup": "androidx/constraintlayout/solver/widgets/ConstraintWidgetGroup", + "android/support/constraint/solver/widgets/Guideline": "androidx/constraintlayout/solver/widgets/Guideline", + "android/support/constraint/solver/widgets/Helper": "androidx/constraintlayout/solver/widgets/Helper", + "android/support/constraint/solver/widgets/Optimizer": "androidx/constraintlayout/solver/widgets/Optimizer", + "android/support/constraint/solver/widgets/Rectangle": "androidx/constraintlayout/solver/widgets/Rectangle", + "android/support/constraint/solver/widgets/ResolutionAnchor": "androidx/constraintlayout/solver/widgets/ResolutionAnchor", + "android/support/constraint/solver/widgets/ResolutionDimension": "androidx/constraintlayout/solver/widgets/ResolutionDimension", + "android/support/constraint/solver/widgets/ResolutionNode": "androidx/constraintlayout/solver/widgets/ResolutionNode", + "android/support/constraint/solver/widgets/Snapshot": "androidx/constraintlayout/solver/widgets/Snapshot", + "android/support/constraint/solver/widgets/WidgetContainer": "androidx/constraintlayout/solver/widgets/WidgetContainer", + "android/support/content/ContentPager": "androidx/contentpager/content/ContentPager", + "android/support/content/InMemoryCursor": "androidx/contentpager/content/InMemoryCursor", + "android/support/content/LoaderQueryRunner": "androidx/contentpager/content/LoaderQueryRunner", + "android/support/content/Query": "androidx/contentpager/content/Query", + "android/support/coordinatorlayout/R": "androidx/coordinatorlayout/R", + "android/support/customtabs/CustomTabsCallback": "androidx/browser/customtabs/CustomTabsCallback", + "android/support/customtabs/CustomTabsClient": "androidx/browser/customtabs/CustomTabsClient", + "android/support/customtabs/CustomTabsIntent": "androidx/browser/customtabs/CustomTabsIntent", + "android/support/customtabs/CustomTabsService": "androidx/browser/customtabs/CustomTabsService", + "android/support/customtabs/CustomTabsServiceConnection": "androidx/browser/customtabs/CustomTabsServiceConnection", + "android/support/customtabs/CustomTabsSession": "androidx/browser/customtabs/CustomTabsSession", + "android/support/customtabs/CustomTabsSessionToken": "androidx/browser/customtabs/CustomTabsSessionToken", + "android/support/customtabs/ICustomTabsCallback": "android/support/customtabs/ICustomTabsCallback", + "android/support/customtabs/ICustomTabsService": "android/support/customtabs/ICustomTabsService", + "android/support/customtabs/IPostMessageService": "android/support/customtabs/IPostMessageService", + "android/support/customtabs/PostMessageService": "androidx/browser/customtabs/PostMessageService", + "android/support/customtabs/PostMessageServiceConnection": "androidx/browser/customtabs/PostMessageServiceConnection", + "android/support/customtabs/R": "androidx/browser/R", + "android/support/customtabs/TrustedWebUtils": "androidx/browser/customtabs/TrustedWebUtils", + "android/support/design/R": "com/google/android/material/R", + "android/support/design/animation/AnimationUtils": "com/google/android/material/animation/AnimationUtils", + "android/support/design/animation/AnimatorSetCompat": "com/google/android/material/animation/AnimatorSetCompat", + "android/support/design/animation/ArgbEvaluatorCompat": "com/google/android/material/animation/ArgbEvaluatorCompat", + "android/support/design/animation/ChildrenAlphaProperty": "com/google/android/material/animation/ChildrenAlphaProperty", + "android/support/design/animation/DrawableAlphaProperty": "com/google/android/material/animation/DrawableAlphaProperty", + "android/support/design/animation/ImageMatrixProperty": "com/google/android/material/animation/ImageMatrixProperty", + "android/support/design/animation/MatrixEvaluator": "com/google/android/material/animation/MatrixEvaluator", + "android/support/design/animation/MotionSpec": "com/google/android/material/animation/MotionSpec", + "android/support/design/animation/MotionTiming": "com/google/android/material/animation/MotionTiming", + "android/support/design/animation/Positioning": "com/google/android/material/animation/Positioning", + "android/support/design/behavior/HideBottomViewOnScrollBehavior": "com/google/android/material/behavior/HideBottomViewOnScrollBehavior", + "android/support/design/bottomappbar/BottomAppBar": "com/google/android/material/bottomappbar/BottomAppBar", + "android/support/design/bottomappbar/BottomAppBarTopEdgeTreatment": "com/google/android/material/bottomappbar/BottomAppBarTopEdgeTreatment", + "android/support/design/bottomnavigation/LabelVisibilityMode": "com/google/android/material/bottomnavigation/LabelVisibilityMode", + "android/support/design/button/MaterialButton": "com/google/android/material/button/MaterialButton", + "android/support/design/button/MaterialButtonBackgroundDrawable": "com/google/android/material/button/MaterialButtonBackgroundDrawable", + "android/support/design/button/MaterialButtonHelper": "com/google/android/material/button/MaterialButtonHelper", + "android/support/design/canvas/CanvasCompat": "com/google/android/material/canvas/CanvasCompat", + "android/support/design/card/MaterialCardView": "com/google/android/material/card/MaterialCardView", + "android/support/design/card/MaterialCardViewHelper": "com/google/android/material/card/MaterialCardViewHelper", + "android/support/design/chip/Chip": "com/google/android/material/chip/Chip", + "android/support/design/chip/ChipDrawable": "com/google/android/material/chip/ChipDrawable", + "android/support/design/chip/ChipGroup": "com/google/android/material/chip/ChipGroup", + "android/support/design/circularreveal/CircularRevealCompat": "com/google/android/material/circularreveal/CircularRevealCompat", + "android/support/design/circularreveal/CircularRevealFrameLayout": "com/google/android/material/circularreveal/CircularRevealFrameLayout", + "android/support/design/circularreveal/CircularRevealGridLayout": "com/google/android/material/circularreveal/CircularRevealGridLayout", + "android/support/design/circularreveal/CircularRevealHelper": "com/google/android/material/circularreveal/CircularRevealHelper", + "android/support/design/circularreveal/CircularRevealLinearLayout": "com/google/android/material/circularreveal/CircularRevealLinearLayout", + "android/support/design/circularreveal/CircularRevealRelativeLayout": "com/google/android/material/circularreveal/CircularRevealRelativeLayout", + "android/support/design/circularreveal/CircularRevealWidget": "com/google/android/material/circularreveal/CircularRevealWidget", + "android/support/design/circularreveal/cardview/CircularRevealCardView": "com/google/android/material/circularreveal/cardview/CircularRevealCardView", + "android/support/design/circularreveal/coordinatorlayout/CircularRevealCoordinatorLayout": "com/google/android/material/circularreveal/coordinatorlayout/CircularRevealCoordinatorLayout", + "android/support/design/drawable/DrawableUtils": "com/google/android/material/drawable/DrawableUtils", + "android/support/design/expandable/ExpandableTransformationWidget": "com/google/android/material/expandable/ExpandableTransformationWidget", + "android/support/design/expandable/ExpandableWidget": "com/google/android/material/expandable/ExpandableWidget", + "android/support/design/expandable/ExpandableWidgetHelper": "com/google/android/material/expandable/ExpandableWidgetHelper", + "android/support/design/internal/BaselineLayout": "com/google/android/material/internal/BaselineLayout", + "android/support/design/internal/BottomNavigationItemView": "com/google/android/material/bottomnavigation/BottomNavigationItemView", + "android/support/design/internal/BottomNavigationMenu": "com/google/android/material/bottomnavigation/BottomNavigationMenu", + "android/support/design/internal/BottomNavigationMenuView": "com/google/android/material/bottomnavigation/BottomNavigationMenuView", + "android/support/design/internal/BottomNavigationPresenter": "com/google/android/material/bottomnavigation/BottomNavigationPresenter", + "android/support/design/internal/Experimental": "com/google/android/material/internal/Experimental", + "android/support/design/internal/FlowLayout": "com/google/android/material/internal/FlowLayout", + "android/support/design/internal/ForegroundLinearLayout": "com/google/android/material/internal/ForegroundLinearLayout", + "android/support/design/internal/NavigationMenu": "com/google/android/material/internal/NavigationMenu", + "android/support/design/internal/NavigationMenuItemView": "com/google/android/material/internal/NavigationMenuItemView", + "android/support/design/internal/NavigationMenuPresenter": "com/google/android/material/internal/NavigationMenuPresenter", + "android/support/design/internal/NavigationMenuView": "com/google/android/material/internal/NavigationMenuView", + "android/support/design/internal/NavigationSubMenu": "com/google/android/material/internal/NavigationSubMenu", + "android/support/design/internal/ParcelableSparseArray": "com/google/android/material/internal/ParcelableSparseArray", + "android/support/design/internal/ScrimInsetsFrameLayout": "com/google/android/material/internal/ScrimInsetsFrameLayout", + "android/support/design/internal/TextScale": "com/google/android/material/internal/TextScale", + "android/support/design/internal/ThemeEnforcement": "com/google/android/material/internal/ThemeEnforcement", + "android/support/design/internal/ViewUtils": "com/google/android/material/internal/ViewUtils", + "android/support/design/resources/MaterialResources": "com/google/android/material/resources/MaterialResources", + "android/support/design/resources/TextAppearance": "com/google/android/material/resources/TextAppearance", + "android/support/design/resources/TextAppearanceConfig": "com/google/android/material/resources/TextAppearanceConfig", + "android/support/design/ripple/RippleUtils": "com/google/android/material/ripple/RippleUtils", + "android/support/design/shape/CornerTreatment": "com/google/android/material/shape/CornerTreatment", + "android/support/design/shape/CutCornerTreatment": "com/google/android/material/shape/CutCornerTreatment", + "android/support/design/shape/EdgeTreatment": "com/google/android/material/shape/EdgeTreatment", + "android/support/design/shape/InterpolateOnScrollPositionChangeHelper": "com/google/android/material/shape/InterpolateOnScrollPositionChangeHelper", + "android/support/design/shape/MaterialShapeDrawable": "com/google/android/material/shape/MaterialShapeDrawable", + "android/support/design/shape/RoundedCornerTreatment": "com/google/android/material/shape/RoundedCornerTreatment", + "android/support/design/shape/ShapePath": "com/google/android/material/shape/ShapePath", + "android/support/design/shape/ShapePathModel": "com/google/android/material/shape/ShapePathModel", + "android/support/design/shape/TriangleEdgeTreatment": "com/google/android/material/shape/TriangleEdgeTreatment", + "android/support/design/snackbar/ContentViewCallback": "com/google/android/material/snackbar/ContentViewCallback", + "android/support/design/stateful/ExtendableSavedState": "com/google/android/material/stateful/ExtendableSavedState", + "android/support/design/theme/MaterialComponentsViewInflater": "com/google/android/material/theme/MaterialComponentsViewInflater", + "android/support/design/transformation/ExpandableBehavior": "com/google/android/material/transformation/ExpandableBehavior", + "android/support/design/transformation/ExpandableTransformationBehavior": "com/google/android/material/transformation/ExpandableTransformationBehavior", + "android/support/design/transformation/FabTransformationBehavior": "com/google/android/material/transformation/FabTransformationBehavior", + "android/support/design/transformation/FabTransformationScrimBehavior": "com/google/android/material/transformation/FabTransformationScrimBehavior", + "android/support/design/transformation/FabTransformationSheetBehavior": "com/google/android/material/transformation/FabTransformationSheetBehavior", + "android/support/design/transformation/TransformationChildCard": "com/google/android/material/transformation/TransformationChildCard", + "android/support/design/transformation/TransformationChildLayout": "com/google/android/material/transformation/TransformationChildLayout", + "android/support/design/widget/AppBarLayout": "com/google/android/material/appbar/AppBarLayout", + "android/support/design/widget/BaseTransientBottomBar": "com/google/android/material/snackbar/BaseTransientBottomBar", + "android/support/design/widget/BottomNavigationView": "com/google/android/material/bottomnavigation/BottomNavigationView", + "android/support/design/widget/BottomSheetBehavior": "com/google/android/material/bottomsheet/BottomSheetBehavior", + "android/support/design/widget/BottomSheetDialog": "com/google/android/material/bottomsheet/BottomSheetDialog", + "android/support/design/widget/BottomSheetDialogFragment": "com/google/android/material/bottomsheet/BottomSheetDialogFragment", + "android/support/design/widget/CheckableImageButton": "com/google/android/material/internal/CheckableImageButton", + "android/support/design/widget/CircularBorderDrawable": "com/google/android/material/internal/CircularBorderDrawable", + "android/support/design/widget/CircularBorderDrawableLollipop": "com/google/android/material/internal/CircularBorderDrawableLollipop", + "android/support/design/widget/CollapsingTextHelper": "com/google/android/material/internal/CollapsingTextHelper", + "android/support/design/widget/CollapsingToolbarLayout": "com/google/android/material/appbar/CollapsingToolbarLayout", + "android/support/design/widget/CoordinatorLayout": "androidx/coordinatorlayout/widget/CoordinatorLayout", + "android/support/design/widget/CutoutDrawable": "com/google/android/material/textfield/CutoutDrawable", + "android/support/design/widget/DescendantOffsetUtils": "com/google/android/material/internal/DescendantOffsetUtils", + "android/support/design/widget/DrawableUtils": "com/google/android/material/internal/DrawableUtils", + "android/support/design/widget/FloatingActionButton": "com/google/android/material/floatingactionbutton/FloatingActionButton", + "android/support/design/widget/FloatingActionButtonImpl": "com/google/android/material/floatingactionbutton/FloatingActionButtonImpl", + "android/support/design/widget/FloatingActionButtonImplLollipop": "com/google/android/material/floatingactionbutton/FloatingActionButtonImplLollipop", + "android/support/design/widget/HeaderBehavior": "com/google/android/material/appbar/HeaderBehavior", + "android/support/design/widget/HeaderScrollingViewBehavior": "com/google/android/material/appbar/HeaderScrollingViewBehavior", + "android/support/design/widget/IndicatorViewController": "com/google/android/material/textfield/IndicatorViewController", + "android/support/design/widget/MathUtils": "com/google/android/material/math/MathUtils", + "android/support/design/widget/NavigationView": "com/google/android/material/navigation/NavigationView", + "android/support/design/widget/ShadowDrawableWrapper": "com/google/android/material/shadow/ShadowDrawableWrapper", + "android/support/design/widget/ShadowViewDelegate": "com/google/android/material/shadow/ShadowViewDelegate", + "android/support/design/widget/Snackbar": "com/google/android/material/snackbar/Snackbar", + "android/support/design/widget/SnackbarContentLayout": "com/google/android/material/snackbar/SnackbarContentLayout", + "android/support/design/widget/SnackbarManager": "com/google/android/material/snackbar/SnackbarManager", + "android/support/design/widget/StateListAnimator": "com/google/android/material/internal/StateListAnimator", + "android/support/design/widget/SwipeDismissBehavior": "com/google/android/material/behavior/SwipeDismissBehavior", + "android/support/design/widget/TabItem": "com/google/android/material/tabs/TabItem", + "android/support/design/widget/TabLayout": "com/google/android/material/tabs/TabLayout", + "android/support/design/widget/TextInputEditText": "com/google/android/material/textfield/TextInputEditText", + "android/support/design/widget/TextInputLayout": "com/google/android/material/textfield/TextInputLayout", + "android/support/design/widget/ViewOffsetBehavior": "com/google/android/material/appbar/ViewOffsetBehavior", + "android/support/design/widget/ViewOffsetHelper": "com/google/android/material/appbar/ViewOffsetHelper", + "android/support/design/widget/ViewUtilsLollipop": "com/google/android/material/appbar/ViewUtilsLollipop", + "android/support/design/widget/VisibilityAwareImageButton": "com/google/android/material/internal/VisibilityAwareImageButton", + "android/support/graphics/drawable/AndroidResources": "androidx/vectordrawable/graphics/drawable/AndroidResources", + "android/support/graphics/drawable/Animatable2Compat": "androidx/vectordrawable/graphics/drawable/Animatable2Compat", + "android/support/graphics/drawable/AnimatedVectorDrawableCompat": "androidx/vectordrawable/graphics/drawable/AnimatedVectorDrawableCompat", + "android/support/graphics/drawable/AnimationUtilsCompat": "androidx/vectordrawable/graphics/drawable/AnimationUtilsCompat", + "android/support/graphics/drawable/AnimatorInflaterCompat": "androidx/vectordrawable/graphics/drawable/AnimatorInflaterCompat", + "android/support/graphics/drawable/ArgbEvaluator": "androidx/vectordrawable/graphics/drawable/ArgbEvaluator", + "android/support/graphics/drawable/PathInterpolatorCompat": "androidx/vectordrawable/graphics/drawable/PathInterpolatorCompat", + "android/support/graphics/drawable/VectorDrawableCommon": "androidx/vectordrawable/graphics/drawable/VectorDrawableCommon", + "android/support/graphics/drawable/VectorDrawableCompat": "androidx/vectordrawable/graphics/drawable/VectorDrawableCompat", + "android/support/media/ExifInterface": "androidx/exifinterface/media/ExifInterface", + "android/support/media/tv/BasePreviewProgram": "androidx/tvprovider/media/tv/BasePreviewProgram", + "android/support/media/tv/BaseProgram": "androidx/tvprovider/media/tv/BaseProgram", + "android/support/media/tv/Channel": "androidx/tvprovider/media/tv/Channel", + "android/support/media/tv/ChannelLogoUtils": "androidx/tvprovider/media/tv/ChannelLogoUtils", + "android/support/media/tv/CollectionUtils": "androidx/tvprovider/media/tv/CollectionUtils", + "android/support/media/tv/PreviewChannel": "androidx/tvprovider/media/tv/PreviewChannel", + "android/support/media/tv/PreviewChannelHelper": "androidx/tvprovider/media/tv/PreviewChannelHelper", + "android/support/media/tv/PreviewProgram": "androidx/tvprovider/media/tv/PreviewProgram", + "android/support/media/tv/Program": "androidx/tvprovider/media/tv/Program", + "android/support/media/tv/TvContractCompat": "androidx/tvprovider/media/tv/TvContractCompat", + "android/support/media/tv/TvContractUtils": "androidx/tvprovider/media/tv/TvContractUtils", + "android/support/media/tv/WatchNextProgram": "androidx/tvprovider/media/tv/WatchNextProgram", + "android/support/media2/BaseRemoteMediaPlayerConnector": "android/support/media2/BaseRemoteMediaPlayerConnector", + "android/support/media2/DataSourceDesc2": "android/support/media2/DataSourceDesc2", + "android/support/media2/MediaPlayerConnector": "android/support/media2/MediaPlayerConnector", + "android/support/media2/MediaPlaylistAgent": "android/support/media2/MediaPlaylistAgent", + "android/support/media2/MediaSession2": "android/support/media2/MediaSession2", + "android/support/mediacompat/R": "androidx/media/R", + "android/support/multidex/MultiDex": "androidx/multidex/MultiDex", + "android/support/multidex/MultiDexApplication": "androidx/multidex/MultiDexApplication", + "android/support/multidex/MultiDexExtractor": "androidx/multidex/MultiDexExtractor", + "android/support/multidex/ZipUtil": "androidx/multidex/ZipUtil", + "android/support/percent/PercentFrameLayout": "androidx/percentlayout/widget/PercentFrameLayout", + "android/support/percent/PercentLayoutHelper": "androidx/percentlayout/widget/PercentLayoutHelper", + "android/support/percent/PercentRelativeLayout": "androidx/percentlayout/widget/PercentRelativeLayout", + "android/support/percent/R": "androidx/percentlayout/R", + "android/support/test/InstrumentationRegistry": "androidx/test/InstrumentationRegistry", + "android/support/test/annotation/Beta": "androidx/test/annotation/Beta", + "android/support/test/annotation/UiThreadTest": "androidx/test/annotation/UiThreadTest", + "android/support/test/espresso/AmbiguousViewMatcherException": "androidx/test/espresso/AmbiguousViewMatcherException", + "android/support/test/espresso/AppNotIdleException": "androidx/test/espresso/AppNotIdleException", + "android/support/test/espresso/BaseLayerComponent": "androidx/test/espresso/BaseLayerComponent", + "android/support/test/espresso/DaggerBaseLayerComponent": "androidx/test/espresso/DaggerBaseLayerComponent", + "android/support/test/espresso/DataInteraction": "androidx/test/espresso/DataInteraction", + "android/support/test/espresso/DataInteractionRemote": "androidx/test/espresso/DataInteractionRemote", + "android/support/test/espresso/Espresso": "androidx/test/espresso/Espresso", + "android/support/test/espresso/EspressoException": "androidx/test/espresso/EspressoException", + "android/support/test/espresso/FailureHandler": "androidx/test/espresso/FailureHandler", + "android/support/test/espresso/GraphHolder": "androidx/test/espresso/GraphHolder", + "android/support/test/espresso/IdlingPolicies": "androidx/test/espresso/IdlingPolicies", + "android/support/test/espresso/IdlingPolicy": "androidx/test/espresso/IdlingPolicy", + "android/support/test/espresso/IdlingRegistry": "androidx/test/espresso/IdlingRegistry", + "android/support/test/espresso/IdlingResource": "androidx/test/espresso/IdlingResource", + "android/support/test/espresso/IdlingResourceTimeoutException": "androidx/test/espresso/IdlingResourceTimeoutException", + "android/support/test/espresso/InjectEventSecurityException": "androidx/test/espresso/InjectEventSecurityException", + "android/support/test/espresso/InteractionResultsHandler": "androidx/test/espresso/InteractionResultsHandler", + "android/support/test/espresso/NoActivityResumedException": "androidx/test/espresso/NoActivityResumedException", + "android/support/test/espresso/NoMatchingRootException": "androidx/test/espresso/NoMatchingRootException", + "android/support/test/espresso/NoMatchingViewException": "androidx/test/espresso/NoMatchingViewException", + "android/support/test/espresso/PerformException": "androidx/test/espresso/PerformException", + "android/support/test/espresso/Root": "androidx/test/espresso/Root", + "android/support/test/espresso/UiController": "androidx/test/espresso/UiController", + "android/support/test/espresso/ViewAction": "androidx/test/espresso/ViewAction", + "android/support/test/espresso/ViewAssertion": "androidx/test/espresso/ViewAssertion", + "android/support/test/espresso/ViewFinder": "androidx/test/espresso/ViewFinder", + "android/support/test/espresso/ViewInteraction": "androidx/test/espresso/ViewInteraction", + "android/support/test/espresso/ViewInteractionComponent": "androidx/test/espresso/ViewInteractionComponent", + "android/support/test/espresso/ViewInteractionModule": "androidx/test/espresso/ViewInteractionModule", + "android/support/test/espresso/ViewInteractionModule_ProvideNeedsActivityFactory": "androidx/test/espresso/ViewInteractionModule_ProvideNeedsActivityFactory", + "android/support/test/espresso/ViewInteractionModule_ProvideRemoteInteractionFactory": "androidx/test/espresso/ViewInteractionModule_ProvideRemoteInteractionFactory", + "android/support/test/espresso/ViewInteractionModule_ProvideRootMatcherFactory": "androidx/test/espresso/ViewInteractionModule_ProvideRootMatcherFactory", + "android/support/test/espresso/ViewInteractionModule_ProvideRootViewFactory": "androidx/test/espresso/ViewInteractionModule_ProvideRootViewFactory", + "android/support/test/espresso/ViewInteractionModule_ProvideViewFinderFactory": "androidx/test/espresso/ViewInteractionModule_ProvideViewFinderFactory", + "android/support/test/espresso/ViewInteractionModule_ProvideViewMatcherFactory": "androidx/test/espresso/ViewInteractionModule_ProvideViewMatcherFactory", + "android/support/test/espresso/ViewInteraction_Factory": "androidx/test/espresso/ViewInteraction_Factory", + "android/support/test/espresso/accessibility/AccessibilityChecks": "androidx/test/espresso/accessibility/AccessibilityChecks", + "android/support/test/espresso/action/AdapterDataLoaderAction": "androidx/test/espresso/action/AdapterDataLoaderAction", + "android/support/test/espresso/action/AdapterDataLoaderActionRemoteMsg": "androidx/test/espresso/action/AdapterDataLoaderActionRemoteMsg", + "android/support/test/espresso/action/AdapterViewProtocol": "androidx/test/espresso/action/AdapterViewProtocol", + "android/support/test/espresso/action/AdapterViewProtocols": "androidx/test/espresso/action/AdapterViewProtocols", + "android/support/test/espresso/action/CloseKeyboardAction": "androidx/test/espresso/action/CloseKeyboardAction", + "android/support/test/espresso/action/CoordinatesProvider": "androidx/test/espresso/action/CoordinatesProvider", + "android/support/test/espresso/action/EditorAction": "androidx/test/espresso/action/EditorAction", + "android/support/test/espresso/action/EspressoKey": "androidx/test/espresso/action/EspressoKey", + "android/support/test/espresso/action/GeneralClickAction": "androidx/test/espresso/action/GeneralClickAction", + "android/support/test/espresso/action/GeneralClickActionRemoteMessage": "androidx/test/espresso/action/GeneralClickActionRemoteMessage", + "android/support/test/espresso/action/GeneralLocation": "androidx/test/espresso/action/GeneralLocation", + "android/support/test/espresso/action/GeneralLocationRemoteMessage": "androidx/test/espresso/action/GeneralLocationRemoteMessage", + "android/support/test/espresso/action/GeneralSwipeAction": "androidx/test/espresso/action/GeneralSwipeAction", + "android/support/test/espresso/action/GeneralSwipeActionRemoteMessage": "androidx/test/espresso/action/GeneralSwipeActionRemoteMessage", + "android/support/test/espresso/action/KeyEventAction": "androidx/test/espresso/action/KeyEventAction", + "android/support/test/espresso/action/KeyEventActionBase": "androidx/test/espresso/action/KeyEventActionBase", + "android/support/test/espresso/action/MotionEvents": "androidx/test/espresso/action/MotionEvents", + "android/support/test/espresso/action/OpenLinkAction": "androidx/test/espresso/action/OpenLinkAction", + "android/support/test/espresso/action/PrecisionDescriber": "androidx/test/espresso/action/PrecisionDescriber", + "android/support/test/espresso/action/Press": "androidx/test/espresso/action/Press", + "android/support/test/espresso/action/PressBackAction": "androidx/test/espresso/action/PressBackAction", + "android/support/test/espresso/action/PressRemoteMessage": "androidx/test/espresso/action/PressRemoteMessage", + "android/support/test/espresso/action/RemoteViewActions": "androidx/test/espresso/action/RemoteViewActions", + "android/support/test/espresso/action/RepeatActionUntilViewState": "androidx/test/espresso/action/RepeatActionUntilViewState", + "android/support/test/espresso/action/ReplaceTextAction": "androidx/test/espresso/action/ReplaceTextAction", + "android/support/test/espresso/action/ScrollToAction": "androidx/test/espresso/action/ScrollToAction", + "android/support/test/espresso/action/Swipe": "androidx/test/espresso/action/Swipe", + "android/support/test/espresso/action/SwipeRemoteMessage": "androidx/test/espresso/action/SwipeRemoteMessage", + "android/support/test/espresso/action/Swiper": "androidx/test/espresso/action/Swiper", + "android/support/test/espresso/action/Tap": "androidx/test/espresso/action/Tap", + "android/support/test/espresso/action/TapRemoteMessage": "androidx/test/espresso/action/TapRemoteMessage", + "android/support/test/espresso/action/Tapper": "androidx/test/espresso/action/Tapper", + "android/support/test/espresso/action/TranslatedCoordinatesProvider": "androidx/test/espresso/action/TranslatedCoordinatesProvider", + "android/support/test/espresso/action/TranslatedCoordinatesProviderRemoteMessage": "androidx/test/espresso/action/TranslatedCoordinatesProviderRemoteMessage", + "android/support/test/espresso/action/TypeTextAction": "androidx/test/espresso/action/TypeTextAction", + "android/support/test/espresso/action/ViewActions": "androidx/test/espresso/action/ViewActions", + "android/support/test/espresso/assertion/LayoutAssertions": "androidx/test/espresso/assertion/LayoutAssertions", + "android/support/test/espresso/assertion/PositionAssertions": "androidx/test/espresso/assertion/PositionAssertions", + "android/support/test/espresso/assertion/RemoteViewAssertions": "androidx/test/espresso/assertion/RemoteViewAssertions", + "android/support/test/espresso/assertion/ViewAssertions": "androidx/test/espresso/assertion/ViewAssertions", + "android/support/test/espresso/base/ActiveRootLister": "androidx/test/espresso/base/ActiveRootLister", + "android/support/test/espresso/base/AsyncTaskPoolMonitor": "androidx/test/espresso/base/AsyncTaskPoolMonitor", + "android/support/test/espresso/base/BaseLayerModule": "androidx/test/espresso/base/BaseLayerModule", + "android/support/test/espresso/base/BaseLayerModule_FailureHandlerHolder_Factory": "androidx/test/espresso/base/BaseLayerModule_FailureHandlerHolder_Factory", + "android/support/test/espresso/base/BaseLayerModule_ProvideActiveRootListerFactory": "androidx/test/espresso/base/BaseLayerModule_ProvideActiveRootListerFactory", + "android/support/test/espresso/base/BaseLayerModule_ProvideCompatAsyncTaskMonitorFactory": "androidx/test/espresso/base/BaseLayerModule_ProvideCompatAsyncTaskMonitorFactory", + "android/support/test/espresso/base/BaseLayerModule_ProvideDynamicNotiferFactory": "androidx/test/espresso/base/BaseLayerModule_ProvideDynamicNotiferFactory", + "android/support/test/espresso/base/BaseLayerModule_ProvideEventInjectorFactory": "androidx/test/espresso/base/BaseLayerModule_ProvideEventInjectorFactory", + "android/support/test/espresso/base/BaseLayerModule_ProvideFailureHanderFactory": "androidx/test/espresso/base/BaseLayerModule_ProvideFailureHanderFactory", + "android/support/test/espresso/base/BaseLayerModule_ProvideFailureHandlerFactory": "androidx/test/espresso/base/BaseLayerModule_ProvideFailureHandlerFactory", + "android/support/test/espresso/base/BaseLayerModule_ProvideLifecycleMonitorFactory": "androidx/test/espresso/base/BaseLayerModule_ProvideLifecycleMonitorFactory", + "android/support/test/espresso/base/BaseLayerModule_ProvideMainLooperFactory": "androidx/test/espresso/base/BaseLayerModule_ProvideMainLooperFactory", + "android/support/test/espresso/base/BaseLayerModule_ProvideMainThreadExecutorFactory": "androidx/test/espresso/base/BaseLayerModule_ProvideMainThreadExecutorFactory", + "android/support/test/espresso/base/BaseLayerModule_ProvideRemoteExecutorFactory": "androidx/test/espresso/base/BaseLayerModule_ProvideRemoteExecutorFactory", + "android/support/test/espresso/base/BaseLayerModule_ProvideSdkAsyncTaskMonitorFactory": "androidx/test/espresso/base/BaseLayerModule_ProvideSdkAsyncTaskMonitorFactory", + "android/support/test/espresso/base/BaseLayerModule_ProvideTargetContextFactory": "androidx/test/espresso/base/BaseLayerModule_ProvideTargetContextFactory", + "android/support/test/espresso/base/CompatAsyncTask": "androidx/test/espresso/base/CompatAsyncTask", + "android/support/test/espresso/base/Default": "androidx/test/espresso/base/Default", + "android/support/test/espresso/base/DefaultFailureHandler": "androidx/test/espresso/base/DefaultFailureHandler", + "android/support/test/espresso/base/EventInjectionStrategy": "androidx/test/espresso/base/EventInjectionStrategy", + "android/support/test/espresso/base/EventInjector": "androidx/test/espresso/base/EventInjector", + "android/support/test/espresso/base/IdleNotifier": "androidx/test/espresso/base/IdleNotifier", + "android/support/test/espresso/base/IdlingResourceRegistry": "androidx/test/espresso/base/IdlingResourceRegistry", + "android/support/test/espresso/base/IdlingResourceRegistry_Factory": "androidx/test/espresso/base/IdlingResourceRegistry_Factory", + "android/support/test/espresso/base/IdlingUiController": "androidx/test/espresso/base/IdlingUiController", + "android/support/test/espresso/base/InputManagerEventInjectionStrategy": "androidx/test/espresso/base/InputManagerEventInjectionStrategy", + "android/support/test/espresso/base/Interrogator": "androidx/test/espresso/base/Interrogator", + "android/support/test/espresso/base/InterruptableUiController": "androidx/test/espresso/base/InterruptableUiController", + "android/support/test/espresso/base/LooperIdlingResourceInterrogationHandler": "androidx/test/espresso/base/LooperIdlingResourceInterrogationHandler", + "android/support/test/espresso/base/MainThread": "androidx/test/espresso/base/MainThread", + "android/support/test/espresso/base/NoopIdleNotificationCallbackIdleNotifierProvider": "androidx/test/espresso/base/NoopIdleNotificationCallbackIdleNotifierProvider", + "android/support/test/espresso/base/NoopRunnableIdleNotifier": "androidx/test/espresso/base/NoopRunnableIdleNotifier", + "android/support/test/espresso/base/RootViewPicker": "androidx/test/espresso/base/RootViewPicker", + "android/support/test/espresso/base/RootViewPickerScope": "androidx/test/espresso/base/RootViewPickerScope", + "android/support/test/espresso/base/RootViewPicker_Factory": "androidx/test/espresso/base/RootViewPicker_Factory", + "android/support/test/espresso/base/RootViewPicker_RootResultFetcher_Factory": "androidx/test/espresso/base/RootViewPicker_RootResultFetcher_Factory", + "android/support/test/espresso/base/RootsOracle": "androidx/test/espresso/base/RootsOracle", + "android/support/test/espresso/base/RootsOracle_Factory": "androidx/test/espresso/base/RootsOracle_Factory", + "android/support/test/espresso/base/SdkAsyncTask": "androidx/test/espresso/base/SdkAsyncTask", + "android/support/test/espresso/base/ThreadPoolExecutorExtractor": "androidx/test/espresso/base/ThreadPoolExecutorExtractor", + "android/support/test/espresso/base/ThreadPoolExecutorExtractor_Factory": "androidx/test/espresso/base/ThreadPoolExecutorExtractor_Factory", + "android/support/test/espresso/base/UiControllerImpl": "androidx/test/espresso/base/UiControllerImpl", + "android/support/test/espresso/base/UiControllerImpl_Factory": "androidx/test/espresso/base/UiControllerImpl_Factory", + "android/support/test/espresso/base/UiControllerModule": "androidx/test/espresso/base/UiControllerModule", + "android/support/test/espresso/base/ViewFinderImpl": "androidx/test/espresso/base/ViewFinderImpl", + "android/support/test/espresso/base/ViewFinderImpl_Factory": "androidx/test/espresso/base/ViewFinderImpl_Factory", + "android/support/test/espresso/base/WindowManagerEventInjectionStrategy": "androidx/test/espresso/base/WindowManagerEventInjectionStrategy", + "android/support/test/espresso/contrib/AccessibilityChecks": "androidx/test/espresso/contrib/AccessibilityChecks", + "android/support/test/espresso/contrib/ActivityResultMatchers": "androidx/test/espresso/contrib/ActivityResultMatchers", + "android/support/test/espresso/contrib/Checks": "androidx/test/espresso/contrib/Checks", + "android/support/test/espresso/contrib/DrawerActions": "androidx/test/espresso/contrib/DrawerActions", + "android/support/test/espresso/contrib/DrawerMatchers": "androidx/test/espresso/contrib/DrawerMatchers", + "android/support/test/espresso/contrib/NavigationViewActions": "androidx/test/espresso/contrib/NavigationViewActions", + "android/support/test/espresso/contrib/PickerActions": "androidx/test/espresso/contrib/PickerActions", + "android/support/test/espresso/contrib/RecyclerViewActions": "androidx/test/espresso/contrib/RecyclerViewActions", + "android/support/test/espresso/contrib/ViewPagerActions": "androidx/test/espresso/contrib/ViewPagerActions", + "android/support/test/espresso/idling/CountingIdlingResource": "androidx/test/espresso/idling/CountingIdlingResource", + "android/support/test/espresso/idling/concurrent/IdlingScheduledThreadPoolExecutor": "androidx/test/espresso/idling/concurrent/IdlingScheduledThreadPoolExecutor", + "android/support/test/espresso/idling/concurrent/IdlingThreadPoolExecutor": "androidx/test/espresso/idling/concurrent/IdlingThreadPoolExecutor", + "android/support/test/espresso/idling/net/UriIdlingResource": "androidx/test/espresso/idling/net/UriIdlingResource", + "android/support/test/espresso/intent/ActivityResultFunction": "androidx/test/espresso/intent/ActivityResultFunction", + "android/support/test/espresso/intent/Checks": "androidx/test/espresso/intent/Checks", + "android/support/test/espresso/intent/Intents": "androidx/test/espresso/intent/Intents", + "android/support/test/espresso/intent/OngoingStubbing": "androidx/test/espresso/intent/OngoingStubbing", + "android/support/test/espresso/intent/ResettingStubber": "androidx/test/espresso/intent/ResettingStubber", + "android/support/test/espresso/intent/ResettingStubberImpl": "androidx/test/espresso/intent/ResettingStubberImpl", + "android/support/test/espresso/intent/ResolvedIntent": "androidx/test/espresso/intent/ResolvedIntent", + "android/support/test/espresso/intent/ResolvedIntentImpl": "androidx/test/espresso/intent/ResolvedIntentImpl", + "android/support/test/espresso/intent/VerifiableIntent": "androidx/test/espresso/intent/VerifiableIntent", + "android/support/test/espresso/intent/VerifiableIntentImpl": "androidx/test/espresso/intent/VerifiableIntentImpl", + "android/support/test/espresso/intent/VerificationMode": "androidx/test/espresso/intent/VerificationMode", + "android/support/test/espresso/intent/VerificationModes": "androidx/test/espresso/intent/VerificationModes", + "android/support/test/espresso/intent/matcher/BundleMatchers": "androidx/test/espresso/intent/matcher/BundleMatchers", + "android/support/test/espresso/intent/matcher/ComponentNameMatchers": "androidx/test/espresso/intent/matcher/ComponentNameMatchers", + "android/support/test/espresso/intent/matcher/IntentMatchers": "androidx/test/espresso/intent/matcher/IntentMatchers", + "android/support/test/espresso/intent/matcher/UriMatchers": "androidx/test/espresso/intent/matcher/UriMatchers", + "android/support/test/espresso/intent/rule/IntentsTestRule": "androidx/test/espresso/intent/rule/IntentsTestRule", + "android/support/test/espresso/matcher/BoundedMatcher": "androidx/test/espresso/matcher/BoundedMatcher", + "android/support/test/espresso/matcher/CursorMatchers": "androidx/test/espresso/matcher/CursorMatchers", + "android/support/test/espresso/matcher/HasBackgroundMatcher": "androidx/test/espresso/matcher/HasBackgroundMatcher", + "android/support/test/espresso/matcher/LayoutMatchers": "androidx/test/espresso/matcher/LayoutMatchers", + "android/support/test/espresso/matcher/PreferenceMatchers": "androidx/test/espresso/matcher/PreferenceMatchers", + "android/support/test/espresso/matcher/RemoteHamcrestCoreMatchers13": "androidx/test/espresso/matcher/RemoteHamcrestCoreMatchers13", + "android/support/test/espresso/matcher/RemoteRootMatchers": "androidx/test/espresso/matcher/RemoteRootMatchers", + "android/support/test/espresso/matcher/RemoteViewMatchers": "androidx/test/espresso/matcher/RemoteViewMatchers", + "android/support/test/espresso/matcher/RootMatchers": "androidx/test/espresso/matcher/RootMatchers", + "android/support/test/espresso/matcher/ViewMatchers": "androidx/test/espresso/matcher/ViewMatchers", + "android/support/test/espresso/proto/UiInteraction": "androidx/test/espresso/proto/UiInteraction", + "android/support/test/espresso/proto/action/ViewActions": "androidx/test/espresso/proto/action/ViewActions", + "android/support/test/espresso/proto/assertion/ViewAssertions": "androidx/test/espresso/proto/assertion/ViewAssertions", + "android/support/test/espresso/proto/matcher/RootMatchers": "androidx/test/espresso/proto/matcher/RootMatchers", + "android/support/test/espresso/proto/matcher/ViewMatchers": "androidx/test/espresso/proto/matcher/ViewMatchers", + "android/support/test/espresso/proto/matcher13/HamcrestMatchersv13": "androidx/test/espresso/proto/matcher13/HamcrestMatchersv13", + "android/support/test/espresso/remote/AnyToTypeConverter": "androidx/test/espresso/remote/AnyToTypeConverter", + "android/support/test/espresso/remote/Bindable": "androidx/test/espresso/remote/Bindable", + "android/support/test/espresso/remote/BuilderReflector": "androidx/test/espresso/remote/BuilderReflector", + "android/support/test/espresso/remote/ByteStringToParcelableConverter": "androidx/test/espresso/remote/ByteStringToParcelableConverter", + "android/support/test/espresso/remote/ByteStringToTypeConverter": "androidx/test/espresso/remote/ByteStringToTypeConverter", + "android/support/test/espresso/remote/ConstructorInvocation": "androidx/test/espresso/remote/ConstructorInvocation", + "android/support/test/espresso/remote/Converter": "androidx/test/espresso/remote/Converter", + "android/support/test/espresso/remote/EspressoRemote": "androidx/test/espresso/remote/EspressoRemote", + "android/support/test/espresso/remote/EspressoRemoteMessage": "androidx/test/espresso/remote/EspressoRemoteMessage", + "android/support/test/espresso/remote/FieldDescriptor": "androidx/test/espresso/remote/FieldDescriptor", + "android/support/test/espresso/remote/GenericRemoteMessage": "androidx/test/espresso/remote/GenericRemoteMessage", + "android/support/test/espresso/remote/IInteractionExecutionStatus": "androidx/test/espresso/remote/IInteractionExecutionStatus", + "android/support/test/espresso/remote/InteractionRequest": "androidx/test/espresso/remote/InteractionRequest", + "android/support/test/espresso/remote/InteractionResponse": "androidx/test/espresso/remote/InteractionResponse", + "android/support/test/espresso/remote/MethodInvocation": "androidx/test/espresso/remote/MethodInvocation", + "android/support/test/espresso/remote/NoRemoteEspressoInstanceException": "androidx/test/espresso/remote/NoRemoteEspressoInstanceException", + "android/support/test/espresso/remote/NoopRemoteInteraction": "androidx/test/espresso/remote/NoopRemoteInteraction", + "android/support/test/espresso/remote/ParcelableToByteStringConverter": "androidx/test/espresso/remote/ParcelableToByteStringConverter", + "android/support/test/espresso/remote/ProtoReflector": "androidx/test/espresso/remote/ProtoReflector", + "android/support/test/espresso/remote/ProtoUtils": "androidx/test/espresso/remote/ProtoUtils", + "android/support/test/espresso/remote/RemoteDescriptor": "androidx/test/espresso/remote/RemoteDescriptor", + "android/support/test/espresso/remote/RemoteDescriptorRegistry": "androidx/test/espresso/remote/RemoteDescriptorRegistry", + "android/support/test/espresso/remote/RemoteEspressoException": "androidx/test/espresso/remote/RemoteEspressoException", + "android/support/test/espresso/remote/RemoteInteraction": "androidx/test/espresso/remote/RemoteInteraction", + "android/support/test/espresso/remote/RemoteInteractionRegistry": "androidx/test/espresso/remote/RemoteInteractionRegistry", + "android/support/test/espresso/remote/RemoteMessageDeserializer": "androidx/test/espresso/remote/RemoteMessageDeserializer", + "android/support/test/espresso/remote/RemoteMessageSerializer": "androidx/test/espresso/remote/RemoteMessageSerializer", + "android/support/test/espresso/remote/RemoteProtocolException": "androidx/test/espresso/remote/RemoteProtocolException", + "android/support/test/espresso/remote/TypeProtoConverters": "androidx/test/espresso/remote/TypeProtoConverters", + "android/support/test/espresso/remote/TypeToAnyConverter": "androidx/test/espresso/remote/TypeToAnyConverter", + "android/support/test/espresso/remote/TypeToByteStringConverter": "androidx/test/espresso/remote/TypeToByteStringConverter", + "android/support/test/espresso/remote/annotation/RemoteMsgConstructor": "androidx/test/espresso/remote/annotation/RemoteMsgConstructor", + "android/support/test/espresso/remote/annotation/RemoteMsgField": "androidx/test/espresso/remote/annotation/RemoteMsgField", + "android/support/test/espresso/util/ActivityLifecycles": "androidx/test/espresso/util/ActivityLifecycles", + "android/support/test/espresso/util/EspressoOptional": "androidx/test/espresso/util/EspressoOptional", + "android/support/test/espresso/util/HumanReadables": "androidx/test/espresso/util/HumanReadables", + "android/support/test/espresso/util/TreeIterables": "androidx/test/espresso/util/TreeIterables", + "android/support/test/espresso/web/action/AtomAction": "androidx/test/espresso/web/action/AtomAction", + "android/support/test/espresso/web/action/AtomActionRemoteMessage": "androidx/test/espresso/web/action/AtomActionRemoteMessage", + "android/support/test/espresso/web/action/EnableJavascriptAction": "androidx/test/espresso/web/action/EnableJavascriptAction", + "android/support/test/espresso/web/action/EvaluationAtom": "androidx/test/espresso/web/action/EvaluationAtom", + "android/support/test/espresso/web/action/IAtomActionResultPropagator": "androidx/test/espresso/web/action/IAtomActionResultPropagator", + "android/support/test/espresso/web/action/JavascriptEvaluation": "androidx/test/espresso/web/action/JavascriptEvaluation", + "android/support/test/espresso/web/action/RemoteWebActions": "androidx/test/espresso/web/action/RemoteWebActions", + "android/support/test/espresso/web/assertion/ByteStringToDocumentConverter": "androidx/test/espresso/web/assertion/ByteStringToDocumentConverter", + "android/support/test/espresso/web/assertion/CheckResultWebAssertionRemoteMessage": "androidx/test/espresso/web/assertion/CheckResultWebAssertionRemoteMessage", + "android/support/test/espresso/web/assertion/CompressorDecompressor": "androidx/test/espresso/web/assertion/CompressorDecompressor", + "android/support/test/espresso/web/assertion/DocumentProtoConverters": "androidx/test/espresso/web/assertion/DocumentProtoConverters", + "android/support/test/espresso/web/assertion/DocumentToByteStringConverter": "androidx/test/espresso/web/assertion/DocumentToByteStringConverter", + "android/support/test/espresso/web/assertion/RemoteWebViewAssertions": "androidx/test/espresso/web/assertion/RemoteWebViewAssertions", + "android/support/test/espresso/web/assertion/TagSoupDocumentParser": "androidx/test/espresso/web/assertion/TagSoupDocumentParser", + "android/support/test/espresso/web/assertion/WebAssertion": "androidx/test/espresso/web/assertion/WebAssertion", + "android/support/test/espresso/web/assertion/WebViewAssertions": "androidx/test/espresso/web/assertion/WebViewAssertions", + "android/support/test/espresso/web/matcher/AmbiguousElementMatcherException": "androidx/test/espresso/web/matcher/AmbiguousElementMatcherException", + "android/support/test/espresso/web/matcher/DomMatchers": "androidx/test/espresso/web/matcher/DomMatchers", + "android/support/test/espresso/web/matcher/RemoteWebMatchers": "androidx/test/espresso/web/matcher/RemoteWebMatchers", + "android/support/test/espresso/web/model/Atom": "androidx/test/espresso/web/model/Atom", + "android/support/test/espresso/web/model/Atoms": "androidx/test/espresso/web/model/Atoms", + "android/support/test/espresso/web/model/ElementReference": "androidx/test/espresso/web/model/ElementReference", + "android/support/test/espresso/web/model/Evaluation": "androidx/test/espresso/web/model/Evaluation", + "android/support/test/espresso/web/model/JSONAble": "androidx/test/espresso/web/model/JSONAble", + "android/support/test/espresso/web/model/ModelCodec": "androidx/test/espresso/web/model/ModelCodec", + "android/support/test/espresso/web/model/RemoteWebModelAtoms": "androidx/test/espresso/web/model/RemoteWebModelAtoms", + "android/support/test/espresso/web/model/ScriptWithArgsSimpleAtomRemoteMessage": "androidx/test/espresso/web/model/ScriptWithArgsSimpleAtomRemoteMessage", + "android/support/test/espresso/web/model/SimpleAtom": "androidx/test/espresso/web/model/SimpleAtom", + "android/support/test/espresso/web/model/TransformingAtom": "androidx/test/espresso/web/model/TransformingAtom", + "android/support/test/espresso/web/model/WindowReference": "androidx/test/espresso/web/model/WindowReference", + "android/support/test/espresso/web/proto/action/WebActions": "androidx/test/espresso/web/proto/action/WebActions", + "android/support/test/espresso/web/proto/assertion/WebAssertions": "androidx/test/espresso/web/proto/assertion/WebAssertions", + "android/support/test/espresso/web/proto/matcher/RemoteWebMatchers": "androidx/test/espresso/web/proto/matcher/RemoteWebMatchers", + "android/support/test/espresso/web/proto/model/WebModelAtoms": "androidx/test/espresso/web/proto/model/WebModelAtoms", + "android/support/test/espresso/web/proto/sugar/WebSugar": "androidx/test/espresso/web/proto/sugar/WebSugar", + "android/support/test/espresso/web/proto/webdriver/WebWebdriverAtoms": "androidx/test/espresso/web/proto/webdriver/WebWebdriverAtoms", + "android/support/test/espresso/web/sugar/RemoteWebSugar": "androidx/test/espresso/web/sugar/RemoteWebSugar", + "android/support/test/espresso/web/sugar/Web": "androidx/test/espresso/web/sugar/Web", + "android/support/test/espresso/web/webdriver/DriverAtoms": "androidx/test/espresso/web/webdriver/DriverAtoms", + "android/support/test/espresso/web/webdriver/Locator": "androidx/test/espresso/web/webdriver/Locator", + "android/support/test/espresso/web/webdriver/RemoteWebDriverAtoms": "androidx/test/espresso/web/webdriver/RemoteWebDriverAtoms", + "android/support/test/espresso/web/webdriver/WebDriverAtomScripts": "androidx/test/espresso/web/webdriver/WebDriverAtomScripts", + "android/support/test/filters/FlakyTest": "androidx/test/filters/FlakyTest", + "android/support/test/filters/LargeTest": "androidx/test/filters/LargeTest", + "android/support/test/filters/MediumTest": "androidx/test/filters/MediumTest", + "android/support/test/filters/RequiresDevice": "androidx/test/filters/RequiresDevice", + "android/support/test/filters/SdkSuppress": "androidx/test/filters/SdkSuppress", + "android/support/test/filters/SmallTest": "androidx/test/filters/SmallTest", + "android/support/test/filters/Suppress": "androidx/test/filters/Suppress", + "android/support/test/jank/GfxMonitor": "androidx/test/jank/GfxMonitor", + "android/support/test/jank/JankTest": "androidx/test/jank/JankTest", + "android/support/test/jank/JankTestBase": "androidx/test/jank/JankTestBase", + "android/support/test/jank/WindowAnimationFrameStatsMonitor": "androidx/test/jank/WindowAnimationFrameStatsMonitor", + "android/support/test/jank/WindowContentFrameStatsMonitor": "androidx/test/jank/WindowContentFrameStatsMonitor", + "android/support/test/orchestrator/callback/OrchestratorCallback": "androidx/test/orchestrator/callback/OrchestratorCallback", + "android/support/test/orchestrator/instrumentationlistener/OrchestratedInstrumentationListener": "androidx/test/orchestrator/instrumentationlistener/OrchestratedInstrumentationListener", + "android/support/test/orchestrator/junit/BundleJUnitUtils": "androidx/test/orchestrator/junit/BundleJUnitUtils", + "android/support/test/orchestrator/junit/ParcelableDescription": "androidx/test/orchestrator/junit/ParcelableDescription", + "android/support/test/orchestrator/junit/ParcelableFailure": "androidx/test/orchestrator/junit/ParcelableFailure", + "android/support/test/orchestrator/junit/ParcelableResult": "androidx/test/orchestrator/junit/ParcelableResult", + "android/support/test/orchestrator/listeners/OrchestrationListenerManager": "androidx/test/orchestrator/listeners/OrchestrationListenerManager", + "android/support/test/orchestrator/listeners/OrchestrationRunListener": "androidx/test/orchestrator/listeners/OrchestrationRunListener", + "android/support/test/orchestrator/listeners/result/ITestRunListener": "androidx/test/orchestrator/listeners/result/ITestRunListener", + "android/support/test/orchestrator/listeners/result/TestIdentifier": "androidx/test/orchestrator/listeners/result/TestIdentifier", + "android/support/test/orchestrator/listeners/result/TestResult": "androidx/test/orchestrator/listeners/result/TestResult", + "android/support/test/orchestrator/listeners/result/TestRunResult": "androidx/test/orchestrator/listeners/result/TestRunResult", + "android/support/test/rule/ActivityTestRule": "androidx/test/rule/ActivityTestRule", + "android/support/test/rule/DisableOnAndroidDebug": "androidx/test/rule/DisableOnAndroidDebug", + "android/support/test/rule/GrantPermissionRule": "androidx/test/rule/GrantPermissionRule", + "android/support/test/rule/PortForwardingRule": "androidx/test/rule/PortForwardingRule", + "android/support/test/rule/ServiceTestRule": "androidx/test/rule/ServiceTestRule", + "android/support/test/rule/UiThreadTestRule": "androidx/test/rule/UiThreadTestRule", + "android/support/test/rule/logging/AtraceLogger": "androidx/test/rule/logging/AtraceLogger", + "android/support/test/rule/provider/DatabaseArgs": "androidx/test/rule/provider/DatabaseArgs", + "android/support/test/rule/provider/DelegatingContext": "androidx/test/rule/provider/DelegatingContext", + "android/support/test/rule/provider/ProviderArgs": "androidx/test/rule/provider/ProviderArgs", + "android/support/test/rule/provider/ProviderTestRule": "androidx/test/rule/provider/ProviderTestRule", + "android/support/test/runner/AndroidJUnit4": "androidx/test/runner/AndroidJUnit4", + "android/support/test/runner/AndroidJUnitRunner": "androidx/test/runner/AndroidJUnitRunner", + "android/support/test/runner/MonitoringInstrumentation": "androidx/test/runner/MonitoringInstrumentation", + "android/support/test/runner/UsageTrackerFacilitator": "androidx/test/runner/UsageTrackerFacilitator", + "android/support/test/runner/intent/IntentCallback": "androidx/test/runner/intent/IntentCallback", + "android/support/test/runner/intent/IntentMonitor": "androidx/test/runner/intent/IntentMonitor", + "android/support/test/runner/intent/IntentMonitorRegistry": "androidx/test/runner/intent/IntentMonitorRegistry", + "android/support/test/runner/intent/IntentStubber": "androidx/test/runner/intent/IntentStubber", + "android/support/test/runner/intent/IntentStubberRegistry": "androidx/test/runner/intent/IntentStubberRegistry", + "android/support/test/runner/intercepting/InterceptingActivityFactory": "androidx/test/runner/intercepting/InterceptingActivityFactory", + "android/support/test/runner/intercepting/SingleActivityFactory": "androidx/test/runner/intercepting/SingleActivityFactory", + "android/support/test/runner/lifecycle/ActivityLifecycleCallback": "androidx/test/runner/lifecycle/ActivityLifecycleCallback", + "android/support/test/runner/lifecycle/ActivityLifecycleMonitor": "androidx/test/runner/lifecycle/ActivityLifecycleMonitor", + "android/support/test/runner/lifecycle/ActivityLifecycleMonitorRegistry": "androidx/test/runner/lifecycle/ActivityLifecycleMonitorRegistry", + "android/support/test/runner/lifecycle/ApplicationLifecycleCallback": "androidx/test/runner/lifecycle/ApplicationLifecycleCallback", + "android/support/test/runner/lifecycle/ApplicationLifecycleMonitor": "androidx/test/runner/lifecycle/ApplicationLifecycleMonitor", + "android/support/test/runner/lifecycle/ApplicationLifecycleMonitorRegistry": "androidx/test/runner/lifecycle/ApplicationLifecycleMonitorRegistry", + "android/support/test/runner/lifecycle/ApplicationStage": "androidx/test/runner/lifecycle/ApplicationStage", + "android/support/test/runner/lifecycle/Stage": "androidx/test/runner/lifecycle/Stage", + "android/support/test/runner/permission/GrantPermissionCallable": "androidx/test/runner/permission/GrantPermissionCallable", + "android/support/test/runner/permission/PermissionRequester": "androidx/test/runner/permission/PermissionRequester", + "android/support/test/runner/permission/RequestPermissionCallable": "androidx/test/runner/permission/RequestPermissionCallable", + "android/support/test/runner/permission/ShellCommand": "androidx/test/runner/permission/ShellCommand", + "android/support/test/runner/permission/UiAutomationShellCommand": "androidx/test/runner/permission/UiAutomationShellCommand", + "android/support/test/runner/screenshot/BasicScreenCaptureProcessor": "androidx/test/runner/screenshot/BasicScreenCaptureProcessor", + "android/support/test/runner/screenshot/ScreenCapture": "androidx/test/runner/screenshot/ScreenCapture", + "android/support/test/runner/screenshot/ScreenCaptureProcessor": "androidx/test/runner/screenshot/ScreenCaptureProcessor", + "android/support/test/runner/screenshot/Screenshot": "androidx/test/runner/screenshot/Screenshot", + "android/support/test/runner/screenshot/TakeScreenshotCallable": "androidx/test/runner/screenshot/TakeScreenshotCallable", + "android/support/test/runner/screenshot/UiAutomationWrapper": "androidx/test/runner/screenshot/UiAutomationWrapper", + "android/support/test/uiautomator/AccessibilityNodeInfoDumper": "androidx/test/uiautomator/AccessibilityNodeInfoDumper", + "android/support/test/uiautomator/AccessibilityNodeInfoHelper": "androidx/test/uiautomator/AccessibilityNodeInfoHelper", + "android/support/test/uiautomator/By": "androidx/test/uiautomator/By", + "android/support/test/uiautomator/ByMatcher": "androidx/test/uiautomator/ByMatcher", + "android/support/test/uiautomator/BySelector": "androidx/test/uiautomator/BySelector", + "android/support/test/uiautomator/Condition": "androidx/test/uiautomator/Condition", + "android/support/test/uiautomator/Configurator": "androidx/test/uiautomator/Configurator", + "android/support/test/uiautomator/Direction": "androidx/test/uiautomator/Direction", + "android/support/test/uiautomator/EventCondition": "androidx/test/uiautomator/EventCondition", + "android/support/test/uiautomator/GestureController": "androidx/test/uiautomator/GestureController", + "android/support/test/uiautomator/Gestures": "androidx/test/uiautomator/Gestures", + "android/support/test/uiautomator/IAutomationSupport": "androidx/test/uiautomator/IAutomationSupport", + "android/support/test/uiautomator/InstrumentationAutomationSupport": "androidx/test/uiautomator/InstrumentationAutomationSupport", + "android/support/test/uiautomator/InteractionController": "androidx/test/uiautomator/InteractionController", + "android/support/test/uiautomator/PointerGesture": "androidx/test/uiautomator/PointerGesture", + "android/support/test/uiautomator/QueryController": "androidx/test/uiautomator/QueryController", + "android/support/test/uiautomator/SearchCondition": "androidx/test/uiautomator/SearchCondition", + "android/support/test/uiautomator/Searchable": "androidx/test/uiautomator/Searchable", + "android/support/test/uiautomator/StaleObjectException": "androidx/test/uiautomator/StaleObjectException", + "android/support/test/uiautomator/Tracer": "androidx/test/uiautomator/Tracer", + "android/support/test/uiautomator/UiAutomatorInstrumentationTestRunner": "androidx/test/uiautomator/UiAutomatorInstrumentationTestRunner", + "android/support/test/uiautomator/UiAutomatorTestCase": "androidx/test/uiautomator/UiAutomatorTestCase", + "android/support/test/uiautomator/UiCollection": "androidx/test/uiautomator/UiCollection", + "android/support/test/uiautomator/UiDevice": "androidx/test/uiautomator/UiDevice", + "android/support/test/uiautomator/UiObject": "androidx/test/uiautomator/UiObject", + "android/support/test/uiautomator/UiObject2": "androidx/test/uiautomator/UiObject2", + "android/support/test/uiautomator/UiObject2Condition": "androidx/test/uiautomator/UiObject2Condition", + "android/support/test/uiautomator/UiObjectNotFoundException": "androidx/test/uiautomator/UiObjectNotFoundException", + "android/support/test/uiautomator/UiScrollable": "androidx/test/uiautomator/UiScrollable", + "android/support/test/uiautomator/UiSelector": "androidx/test/uiautomator/UiSelector", + "android/support/test/uiautomator/UiWatcher": "androidx/test/uiautomator/UiWatcher", + "android/support/test/uiautomator/Until": "androidx/test/uiautomator/Until", + "android/support/test/uiautomator/WaitMixin": "androidx/test/uiautomator/WaitMixin", + "android/support/text/emoji/EmojiCompat": "androidx/emoji/text/EmojiCompat", + "android/support/text/emoji/EmojiMetadata": "androidx/emoji/text/EmojiMetadata", + "android/support/text/emoji/EmojiProcessor": "androidx/emoji/text/EmojiProcessor", + "android/support/text/emoji/EmojiSpan": "androidx/emoji/text/EmojiSpan", + "android/support/text/emoji/FontRequestEmojiCompatConfig": "androidx/emoji/text/FontRequestEmojiCompatConfig", + "android/support/text/emoji/MetadataListReader": "androidx/emoji/text/MetadataListReader", + "android/support/text/emoji/MetadataRepo": "androidx/emoji/text/MetadataRepo", + "android/support/text/emoji/R": "androidx/emoji/R", + "android/support/text/emoji/TypefaceEmojiSpan": "androidx/emoji/text/TypefaceEmojiSpan", + "android/support/text/emoji/bundled/BundledEmojiCompatConfig": "androidx/emoji/bundled/BundledEmojiCompatConfig", + "android/support/text/emoji/widget/EditTextAttributeHelper": "androidx/emoji/widget/EditTextAttributeHelper", + "android/support/text/emoji/widget/EmojiAppCompatButton": "androidx/emoji/widget/EmojiAppCompatButton", + "android/support/text/emoji/widget/EmojiAppCompatEditText": "androidx/emoji/widget/EmojiAppCompatEditText", + "android/support/text/emoji/widget/EmojiAppCompatTextView": "androidx/emoji/widget/EmojiAppCompatTextView", + "android/support/text/emoji/widget/EmojiButton": "androidx/emoji/widget/EmojiButton", + "android/support/text/emoji/widget/EmojiEditText": "androidx/emoji/widget/EmojiEditText", + "android/support/text/emoji/widget/EmojiEditTextHelper": "androidx/emoji/widget/EmojiEditTextHelper", + "android/support/text/emoji/widget/EmojiEditableFactory": "androidx/emoji/widget/EmojiEditableFactory", + "android/support/text/emoji/widget/EmojiExtractEditText": "androidx/emoji/widget/EmojiExtractEditText", + "android/support/text/emoji/widget/EmojiExtractTextLayout": "androidx/emoji/widget/EmojiExtractTextLayout", + "android/support/text/emoji/widget/EmojiInputConnection": "androidx/emoji/widget/EmojiInputConnection", + "android/support/text/emoji/widget/EmojiInputFilter": "androidx/emoji/widget/EmojiInputFilter", + "android/support/text/emoji/widget/EmojiKeyListener": "androidx/emoji/widget/EmojiKeyListener", + "android/support/text/emoji/widget/EmojiTextView": "androidx/emoji/widget/EmojiTextView", + "android/support/text/emoji/widget/EmojiTextViewHelper": "androidx/emoji/widget/EmojiTextViewHelper", + "android/support/text/emoji/widget/EmojiTextWatcher": "androidx/emoji/widget/EmojiTextWatcher", + "android/support/text/emoji/widget/EmojiTransformationMethod": "androidx/emoji/widget/EmojiTransformationMethod", + "android/support/text/emoji/widget/ExtractButtonCompat": "androidx/emoji/widget/ExtractButtonCompat", + "android/support/text/emoji/widget/SpannableBuilder": "androidx/emoji/widget/SpannableBuilder", + "android/support/transition/AnimatorUtils": "androidx/transition/AnimatorUtils", + "android/support/transition/ArcMotion": "androidx/transition/ArcMotion", + "android/support/transition/AutoTransition": "androidx/transition/AutoTransition", + "android/support/transition/ChangeBounds": "androidx/transition/ChangeBounds", + "android/support/transition/ChangeClipBounds": "androidx/transition/ChangeClipBounds", + "android/support/transition/ChangeImageTransform": "androidx/transition/ChangeImageTransform", + "android/support/transition/ChangeScroll": "androidx/transition/ChangeScroll", + "android/support/transition/ChangeTransform": "androidx/transition/ChangeTransform", + "android/support/transition/CircularPropagation": "androidx/transition/CircularPropagation", + "android/support/transition/Explode": "androidx/transition/Explode", + "android/support/transition/Fade": "androidx/transition/Fade", + "android/support/transition/FloatArrayEvaluator": "androidx/transition/FloatArrayEvaluator", + "android/support/transition/FragmentTransitionSupport": "androidx/transition/FragmentTransitionSupport", + "android/support/transition/GhostViewApi14": "androidx/transition/GhostViewApi14", + "android/support/transition/GhostViewApi21": "androidx/transition/GhostViewApi21", + "android/support/transition/GhostViewImpl": "androidx/transition/GhostViewImpl", + "android/support/transition/GhostViewUtils": "androidx/transition/GhostViewUtils", + "android/support/transition/ImageViewUtils": "androidx/transition/ImageViewUtils", + "android/support/transition/MatrixUtils": "androidx/transition/MatrixUtils", + "android/support/transition/ObjectAnimatorUtils": "androidx/transition/ObjectAnimatorUtils", + "android/support/transition/PathMotion": "androidx/transition/PathMotion", + "android/support/transition/PathProperty": "androidx/transition/PathProperty", + "android/support/transition/PatternPathMotion": "androidx/transition/PatternPathMotion", + "android/support/transition/PropertyValuesHolderUtils": "androidx/transition/PropertyValuesHolderUtils", + "android/support/transition/R": "androidx/transition/R", + "android/support/transition/RectEvaluator": "androidx/transition/RectEvaluator", + "android/support/transition/Scene": "androidx/transition/Scene", + "android/support/transition/SidePropagation": "androidx/transition/SidePropagation", + "android/support/transition/Slide": "androidx/transition/Slide", + "android/support/transition/Styleable": "androidx/transition/Styleable", + "android/support/transition/Transition": "androidx/transition/Transition", + "android/support/transition/TransitionInflater": "androidx/transition/TransitionInflater", + "android/support/transition/TransitionListenerAdapter": "androidx/transition/TransitionListenerAdapter", + "android/support/transition/TransitionManager": "androidx/transition/TransitionManager", + "android/support/transition/TransitionPropagation": "androidx/transition/TransitionPropagation", + "android/support/transition/TransitionSet": "androidx/transition/TransitionSet", + "android/support/transition/TransitionUtils": "androidx/transition/TransitionUtils", + "android/support/transition/TransitionValues": "androidx/transition/TransitionValues", + "android/support/transition/TransitionValuesMaps": "androidx/transition/TransitionValuesMaps", + "android/support/transition/TranslationAnimationCreator": "androidx/transition/TranslationAnimationCreator", + "android/support/transition/ViewGroupOverlayApi14": "androidx/transition/ViewGroupOverlayApi14", + "android/support/transition/ViewGroupOverlayApi18": "androidx/transition/ViewGroupOverlayApi18", + "android/support/transition/ViewGroupOverlayImpl": "androidx/transition/ViewGroupOverlayImpl", + "android/support/transition/ViewGroupUtils": "androidx/transition/ViewGroupUtils", + "android/support/transition/ViewGroupUtilsApi14": "androidx/transition/ViewGroupUtilsApi14", + "android/support/transition/ViewGroupUtilsApi18": "androidx/transition/ViewGroupUtilsApi18", + "android/support/transition/ViewOverlayApi14": "androidx/transition/ViewOverlayApi14", + "android/support/transition/ViewOverlayApi18": "androidx/transition/ViewOverlayApi18", + "android/support/transition/ViewOverlayImpl": "androidx/transition/ViewOverlayImpl", + "android/support/transition/ViewUtils": "androidx/transition/ViewUtils", + "android/support/transition/ViewUtilsApi19": "androidx/transition/ViewUtilsApi19", + "android/support/transition/ViewUtilsApi21": "androidx/transition/ViewUtilsApi21", + "android/support/transition/ViewUtilsApi22": "androidx/transition/ViewUtilsApi22", + "android/support/transition/ViewUtilsBase": "androidx/transition/ViewUtilsBase", + "android/support/transition/Visibility": "androidx/transition/Visibility", + "android/support/transition/VisibilityPropagation": "androidx/transition/VisibilityPropagation", + "android/support/transition/WindowIdApi14": "androidx/transition/WindowIdApi14", + "android/support/transition/WindowIdApi18": "androidx/transition/WindowIdApi18", + "android/support/transition/WindowIdImpl": "androidx/transition/WindowIdImpl", + "android/support/v13/app/ActivityCompat": "androidx/legacy/app/ActivityCompat", + "android/support/v13/app/FragmentCompat": "androidx/legacy/app/FragmentCompat", + "android/support/v13/app/FragmentPagerAdapter": "androidx/legacy/app/FragmentPagerAdapter", + "android/support/v13/app/FragmentStatePagerAdapter": "androidx/legacy/app/FragmentStatePagerAdapter", + "android/support/v13/app/FragmentTabHost": "androidx/legacy/app/FragmentTabHost", + "android/support/v13/view/DragAndDropPermissionsCompat": "androidx/core/view/DragAndDropPermissionsCompat", + "android/support/v13/view/DragStartHelper": "androidx/core/view/DragStartHelper", + "android/support/v13/view/ViewCompat": "androidx/legacy/view/ViewCompat", + "android/support/v13/view/inputmethod/EditorInfoCompat": "androidx/core/view/inputmethod/EditorInfoCompat", + "android/support/v13/view/inputmethod/InputConnectionCompat": "androidx/core/view/inputmethod/InputConnectionCompat", + "android/support/v13/view/inputmethod/InputContentInfoCompat": "androidx/core/view/inputmethod/InputContentInfoCompat", + "android/support/v14/preference/EditTextPreferenceDialogFragment": "androidx/preference/EditTextPreferenceDialogFragment", + "android/support/v14/preference/ListPreferenceDialogFragment": "androidx/preference/ListPreferenceDialogFragment", + "android/support/v14/preference/MultiSelectListPreference": "androidx/preference/MultiSelectListPreference", + "android/support/v14/preference/MultiSelectListPreferenceDialogFragment": "androidx/preference/MultiSelectListPreferenceDialogFragment", + "android/support/v14/preference/PreferenceDialogFragment": "androidx/preference/PreferenceDialogFragment", + "android/support/v14/preference/PreferenceFragment": "androidx/preference/PreferenceFragment", + "android/support/v14/preference/SwitchPreference": "androidx/preference/SwitchPreference", + "android/support/v17/internal/widget/OutlineOnlyWithChildrenFrameLayout": "androidx/leanback/preference/internal/OutlineOnlyWithChildrenFrameLayout", + "android/support/v17/leanback/R": "androidx/leanback/R", + "android/support/v17/leanback/animation/LogAccelerateInterpolator": "androidx/leanback/animation/LogAccelerateInterpolator", + "android/support/v17/leanback/animation/LogDecelerateInterpolator": "androidx/leanback/animation/LogDecelerateInterpolator", + "android/support/v17/leanback/app/BackgroundFragment": "androidx/leanback/app/BackgroundFragment", + "android/support/v17/leanback/app/BackgroundManager": "androidx/leanback/app/BackgroundManager", + "android/support/v17/leanback/app/BaseFragment": "androidx/leanback/app/BaseFragment", + "android/support/v17/leanback/app/BaseRowFragment": "androidx/leanback/app/BaseRowFragment", + "android/support/v17/leanback/app/BaseRowSupportFragment": "androidx/leanback/app/BaseRowSupportFragment", + "android/support/v17/leanback/app/BaseSupportFragment": "androidx/leanback/app/BaseSupportFragment", + "android/support/v17/leanback/app/BrandedFragment": "androidx/leanback/app/BrandedFragment", + "android/support/v17/leanback/app/BrandedSupportFragment": "androidx/leanback/app/BrandedSupportFragment", + "android/support/v17/leanback/app/BrowseFragment": "androidx/leanback/app/BrowseFragment", + "android/support/v17/leanback/app/BrowseSupportFragment": "androidx/leanback/app/BrowseSupportFragment", + "android/support/v17/leanback/app/DetailsBackgroundVideoHelper": "androidx/leanback/app/DetailsBackgroundVideoHelper", + "android/support/v17/leanback/app/DetailsFragment": "androidx/leanback/app/DetailsFragment", + "android/support/v17/leanback/app/DetailsFragmentBackgroundController": "androidx/leanback/app/DetailsFragmentBackgroundController", + "android/support/v17/leanback/app/DetailsSupportFragment": "androidx/leanback/app/DetailsSupportFragment", + "android/support/v17/leanback/app/DetailsSupportFragmentBackgroundController": "androidx/leanback/app/DetailsSupportFragmentBackgroundController", + "android/support/v17/leanback/app/ErrorFragment": "androidx/leanback/app/ErrorFragment", + "android/support/v17/leanback/app/ErrorSupportFragment": "androidx/leanback/app/ErrorSupportFragment", + "android/support/v17/leanback/app/FragmentUtil": "androidx/leanback/app/FragmentUtil", + "android/support/v17/leanback/app/GuidedStepFragment": "androidx/leanback/app/GuidedStepFragment", + "android/support/v17/leanback/app/GuidedStepRootLayout": "androidx/leanback/app/GuidedStepRootLayout", + "android/support/v17/leanback/app/GuidedStepSupportFragment": "androidx/leanback/app/GuidedStepSupportFragment", + "android/support/v17/leanback/app/HeadersFragment": "androidx/leanback/app/HeadersFragment", + "android/support/v17/leanback/app/HeadersSupportFragment": "androidx/leanback/app/HeadersSupportFragment", + "android/support/v17/leanback/app/ListRowDataAdapter": "androidx/leanback/app/ListRowDataAdapter", + "android/support/v17/leanback/app/OnboardingFragment": "androidx/leanback/app/OnboardingFragment", + "android/support/v17/leanback/app/OnboardingSupportFragment": "androidx/leanback/app/OnboardingSupportFragment", + "android/support/v17/leanback/app/PermissionHelper": "androidx/leanback/app/PermissionHelper", + "android/support/v17/leanback/app/PlaybackFragment": "androidx/leanback/app/PlaybackFragment", + "android/support/v17/leanback/app/PlaybackFragmentGlueHost": "androidx/leanback/app/PlaybackFragmentGlueHost", + "android/support/v17/leanback/app/PlaybackSupportFragment": "androidx/leanback/app/PlaybackSupportFragment", + "android/support/v17/leanback/app/PlaybackSupportFragmentGlueHost": "androidx/leanback/app/PlaybackSupportFragmentGlueHost", + "android/support/v17/leanback/app/ProgressBarManager": "androidx/leanback/app/ProgressBarManager", + "android/support/v17/leanback/app/RowsFragment": "androidx/leanback/app/RowsFragment", + "android/support/v17/leanback/app/RowsSupportFragment": "androidx/leanback/app/RowsSupportFragment", + "android/support/v17/leanback/app/SearchFragment": "androidx/leanback/app/SearchFragment", + "android/support/v17/leanback/app/SearchSupportFragment": "androidx/leanback/app/SearchSupportFragment", + "android/support/v17/leanback/app/VerticalGridFragment": "androidx/leanback/app/VerticalGridFragment", + "android/support/v17/leanback/app/VerticalGridSupportFragment": "androidx/leanback/app/VerticalGridSupportFragment", + "android/support/v17/leanback/app/VideoFragment": "androidx/leanback/app/VideoFragment", + "android/support/v17/leanback/app/VideoFragmentGlueHost": "androidx/leanback/app/VideoFragmentGlueHost", + "android/support/v17/leanback/app/VideoSupportFragment": "androidx/leanback/app/VideoSupportFragment", + "android/support/v17/leanback/app/VideoSupportFragmentGlueHost": "androidx/leanback/app/VideoSupportFragmentGlueHost", + "android/support/v17/leanback/database/CursorMapper": "androidx/leanback/database/CursorMapper", + "android/support/v17/leanback/graphics/BoundsRule": "androidx/leanback/graphics/BoundsRule", + "android/support/v17/leanback/graphics/ColorFilterCache": "androidx/leanback/graphics/ColorFilterCache", + "android/support/v17/leanback/graphics/ColorFilterDimmer": "androidx/leanback/graphics/ColorFilterDimmer", + "android/support/v17/leanback/graphics/ColorOverlayDimmer": "androidx/leanback/graphics/ColorOverlayDimmer", + "android/support/v17/leanback/graphics/CompositeDrawable": "androidx/leanback/graphics/CompositeDrawable", + "android/support/v17/leanback/graphics/FitWidthBitmapDrawable": "androidx/leanback/graphics/FitWidthBitmapDrawable", + "android/support/v17/leanback/media/MediaControllerAdapter": "androidx/leanback/media/MediaControllerAdapter", + "android/support/v17/leanback/media/MediaControllerGlue": "androidx/leanback/media/MediaControllerGlue", + "android/support/v17/leanback/media/MediaPlayerAdapter": "androidx/leanback/media/MediaPlayerAdapter", + "android/support/v17/leanback/media/MediaPlayerGlue": "androidx/leanback/media/MediaPlayerGlue", + "android/support/v17/leanback/media/PlaybackBannerControlGlue": "androidx/leanback/media/PlaybackBannerControlGlue", + "android/support/v17/leanback/media/PlaybackBaseControlGlue": "androidx/leanback/media/PlaybackBaseControlGlue", + "android/support/v17/leanback/media/PlaybackControlGlue": "androidx/leanback/media/PlaybackControlGlue", + "android/support/v17/leanback/media/PlaybackGlue": "androidx/leanback/media/PlaybackGlue", + "android/support/v17/leanback/media/PlaybackGlueHost": "androidx/leanback/media/PlaybackGlueHost", + "android/support/v17/leanback/media/PlaybackTransportControlGlue": "androidx/leanback/media/PlaybackTransportControlGlue", + "android/support/v17/leanback/media/PlayerAdapter": "androidx/leanback/media/PlayerAdapter", + "android/support/v17/leanback/media/SurfaceHolderGlueHost": "androidx/leanback/media/SurfaceHolderGlueHost", + "android/support/v17/leanback/system/Settings": "androidx/leanback/system/Settings", + "android/support/v17/leanback/transition/CustomChangeBounds": "androidx/leanback/transition/CustomChangeBounds", + "android/support/v17/leanback/transition/FadeAndShortSlide": "androidx/leanback/transition/FadeAndShortSlide", + "android/support/v17/leanback/transition/LeanbackTransitionHelper": "androidx/leanback/transition/LeanbackTransitionHelper", + "android/support/v17/leanback/transition/ParallaxTransition": "androidx/leanback/transition/ParallaxTransition", + "android/support/v17/leanback/transition/Scale": "androidx/leanback/transition/Scale", + "android/support/v17/leanback/transition/SlideKitkat": "androidx/leanback/transition/SlideKitkat", + "android/support/v17/leanback/transition/SlideNoPropagation": "androidx/leanback/transition/SlideNoPropagation", + "android/support/v17/leanback/transition/TransitionEpicenterCallback": "androidx/leanback/transition/TransitionEpicenterCallback", + "android/support/v17/leanback/transition/TransitionHelper": "androidx/leanback/transition/TransitionHelper", + "android/support/v17/leanback/transition/TransitionListener": "androidx/leanback/transition/TransitionListener", + "android/support/v17/leanback/transition/TranslationAnimationCreator": "androidx/leanback/transition/TranslationAnimationCreator", + "android/support/v17/leanback/util/MathUtil": "androidx/leanback/util/MathUtil", + "android/support/v17/leanback/util/StateMachine": "androidx/leanback/util/StateMachine", + "android/support/v17/leanback/widget/AbstractDetailsDescriptionPresenter": "androidx/leanback/widget/AbstractDetailsDescriptionPresenter", + "android/support/v17/leanback/widget/AbstractMediaItemPresenter": "androidx/leanback/widget/AbstractMediaItemPresenter", + "android/support/v17/leanback/widget/AbstractMediaListHeaderPresenter": "androidx/leanback/widget/AbstractMediaListHeaderPresenter", + "android/support/v17/leanback/widget/Action": "androidx/leanback/widget/Action", + "android/support/v17/leanback/widget/ActionPresenterSelector": "androidx/leanback/widget/ActionPresenterSelector", + "android/support/v17/leanback/widget/ArrayObjectAdapter": "androidx/leanback/widget/ArrayObjectAdapter", + "android/support/v17/leanback/widget/BackgroundHelper": "androidx/leanback/widget/BackgroundHelper", + "android/support/v17/leanback/widget/BaseCardView": "androidx/leanback/widget/BaseCardView", + "android/support/v17/leanback/widget/BaseGridView": "androidx/leanback/widget/BaseGridView", + "android/support/v17/leanback/widget/BaseOnItemViewClickedListener": "androidx/leanback/widget/BaseOnItemViewClickedListener", + "android/support/v17/leanback/widget/BaseOnItemViewSelectedListener": "androidx/leanback/widget/BaseOnItemViewSelectedListener", + "android/support/v17/leanback/widget/BrowseFrameLayout": "androidx/leanback/widget/BrowseFrameLayout", + "android/support/v17/leanback/widget/BrowseRowsFrameLayout": "androidx/leanback/widget/BrowseRowsFrameLayout", + "android/support/v17/leanback/widget/CheckableImageView": "androidx/leanback/widget/CheckableImageView", + "android/support/v17/leanback/widget/ClassPresenterSelector": "androidx/leanback/widget/ClassPresenterSelector", + "android/support/v17/leanback/widget/ControlBar": "androidx/leanback/widget/ControlBar", + "android/support/v17/leanback/widget/ControlBarPresenter": "androidx/leanback/widget/ControlBarPresenter", + "android/support/v17/leanback/widget/ControlButtonPresenterSelector": "androidx/leanback/widget/ControlButtonPresenterSelector", + "android/support/v17/leanback/widget/CursorObjectAdapter": "androidx/leanback/widget/CursorObjectAdapter", + "android/support/v17/leanback/widget/DetailsOverviewLogoPresenter": "androidx/leanback/widget/DetailsOverviewLogoPresenter", + "android/support/v17/leanback/widget/DetailsOverviewRow": "androidx/leanback/widget/DetailsOverviewRow", + "android/support/v17/leanback/widget/DetailsOverviewRowPresenter": "androidx/leanback/widget/DetailsOverviewRowPresenter", + "android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper": "androidx/leanback/widget/DetailsOverviewSharedElementHelper", + "android/support/v17/leanback/widget/DetailsParallax": "androidx/leanback/widget/DetailsParallax", + "android/support/v17/leanback/widget/DetailsParallaxDrawable": "androidx/leanback/widget/DetailsParallaxDrawable", + "android/support/v17/leanback/widget/DiffCallback": "androidx/leanback/widget/DiffCallback", + "android/support/v17/leanback/widget/DividerPresenter": "androidx/leanback/widget/DividerPresenter", + "android/support/v17/leanback/widget/DividerRow": "androidx/leanback/widget/DividerRow", + "android/support/v17/leanback/widget/FacetProvider": "androidx/leanback/widget/FacetProvider", + "android/support/v17/leanback/widget/FacetProviderAdapter": "androidx/leanback/widget/FacetProviderAdapter", + "android/support/v17/leanback/widget/FocusHighlight": "androidx/leanback/widget/FocusHighlight", + "android/support/v17/leanback/widget/FocusHighlightHandler": "androidx/leanback/widget/FocusHighlightHandler", + "android/support/v17/leanback/widget/FocusHighlightHelper": "androidx/leanback/widget/FocusHighlightHelper", + "android/support/v17/leanback/widget/ForegroundHelper": "androidx/leanback/widget/ForegroundHelper", + "android/support/v17/leanback/widget/FragmentAnimationProvider": "androidx/leanback/widget/FragmentAnimationProvider", + "android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter": "androidx/leanback/widget/FullWidthDetailsOverviewRowPresenter", + "android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper": "androidx/leanback/widget/FullWidthDetailsOverviewSharedElementHelper", + "android/support/v17/leanback/widget/Grid": "androidx/leanback/widget/Grid", + "android/support/v17/leanback/widget/GridLayoutManager": "androidx/leanback/widget/GridLayoutManager", + "android/support/v17/leanback/widget/GuidanceStylingRelativeLayout": "androidx/leanback/widget/GuidanceStylingRelativeLayout", + "android/support/v17/leanback/widget/GuidanceStylist": "androidx/leanback/widget/GuidanceStylist", + "android/support/v17/leanback/widget/GuidedAction": "androidx/leanback/widget/GuidedAction", + "android/support/v17/leanback/widget/GuidedActionAdapter": "androidx/leanback/widget/GuidedActionAdapter", + "android/support/v17/leanback/widget/GuidedActionAdapterGroup": "androidx/leanback/widget/GuidedActionAdapterGroup", + "android/support/v17/leanback/widget/GuidedActionAutofillSupport": "androidx/leanback/widget/GuidedActionAutofillSupport", + "android/support/v17/leanback/widget/GuidedActionDiffCallback": "androidx/leanback/widget/GuidedActionDiffCallback", + "android/support/v17/leanback/widget/GuidedActionEditText": "androidx/leanback/widget/GuidedActionEditText", + "android/support/v17/leanback/widget/GuidedActionItemContainer": "androidx/leanback/widget/GuidedActionItemContainer", + "android/support/v17/leanback/widget/GuidedActionsRelativeLayout": "androidx/leanback/widget/GuidedActionsRelativeLayout", + "android/support/v17/leanback/widget/GuidedActionsStylist": "androidx/leanback/widget/GuidedActionsStylist", + "android/support/v17/leanback/widget/GuidedDatePickerAction": "androidx/leanback/widget/GuidedDatePickerAction", + "android/support/v17/leanback/widget/HeaderItem": "androidx/leanback/widget/HeaderItem", + "android/support/v17/leanback/widget/HorizontalGridView": "androidx/leanback/widget/HorizontalGridView", + "android/support/v17/leanback/widget/HorizontalHoverCardSwitcher": "androidx/leanback/widget/HorizontalHoverCardSwitcher", + "android/support/v17/leanback/widget/ImageCardView": "androidx/leanback/widget/ImageCardView", + "android/support/v17/leanback/widget/ImeKeyMonitor": "androidx/leanback/widget/ImeKeyMonitor", + "android/support/v17/leanback/widget/InvisibleRowPresenter": "androidx/leanback/widget/InvisibleRowPresenter", + "android/support/v17/leanback/widget/ItemAlignment": "androidx/leanback/widget/ItemAlignment", + "android/support/v17/leanback/widget/ItemAlignmentFacet": "androidx/leanback/widget/ItemAlignmentFacet", + "android/support/v17/leanback/widget/ItemAlignmentFacetHelper": "androidx/leanback/widget/ItemAlignmentFacetHelper", + "android/support/v17/leanback/widget/ItemBridgeAdapter": "androidx/leanback/widget/ItemBridgeAdapter", + "android/support/v17/leanback/widget/ItemBridgeAdapterShadowOverlayWrapper": "androidx/leanback/widget/ItemBridgeAdapterShadowOverlayWrapper", + "android/support/v17/leanback/widget/ListRow": "androidx/leanback/widget/ListRow", + "android/support/v17/leanback/widget/ListRowHoverCardView": "androidx/leanback/widget/ListRowHoverCardView", + "android/support/v17/leanback/widget/ListRowPresenter": "androidx/leanback/widget/ListRowPresenter", + "android/support/v17/leanback/widget/ListRowView": "androidx/leanback/widget/ListRowView", + "android/support/v17/leanback/widget/MediaItemActionPresenter": "androidx/leanback/widget/MediaItemActionPresenter", + "android/support/v17/leanback/widget/MediaNowPlayingView": "androidx/leanback/widget/MediaNowPlayingView", + "android/support/v17/leanback/widget/MediaRowFocusView": "androidx/leanback/widget/MediaRowFocusView", + "android/support/v17/leanback/widget/MultiActionsProvider": "androidx/leanback/widget/MultiActionsProvider", + "android/support/v17/leanback/widget/NonOverlappingFrameLayout": "androidx/leanback/widget/NonOverlappingFrameLayout", + "android/support/v17/leanback/widget/NonOverlappingLinearLayout": "androidx/leanback/widget/NonOverlappingLinearLayout", + "android/support/v17/leanback/widget/NonOverlappingLinearLayoutWithForeground": "androidx/leanback/widget/NonOverlappingLinearLayoutWithForeground", + "android/support/v17/leanback/widget/NonOverlappingRelativeLayout": "androidx/leanback/widget/NonOverlappingRelativeLayout", + "android/support/v17/leanback/widget/NonOverlappingView": "androidx/leanback/widget/NonOverlappingView", + "android/support/v17/leanback/widget/ObjectAdapter": "androidx/leanback/widget/ObjectAdapter", + "android/support/v17/leanback/widget/OnActionClickedListener": "androidx/leanback/widget/OnActionClickedListener", + "android/support/v17/leanback/widget/OnChildLaidOutListener": "androidx/leanback/widget/OnChildLaidOutListener", + "android/support/v17/leanback/widget/OnChildSelectedListener": "androidx/leanback/widget/OnChildSelectedListener", + "android/support/v17/leanback/widget/OnChildViewHolderSelectedListener": "androidx/leanback/widget/OnChildViewHolderSelectedListener", + "android/support/v17/leanback/widget/OnItemViewClickedListener": "androidx/leanback/widget/OnItemViewClickedListener", + "android/support/v17/leanback/widget/OnItemViewSelectedListener": "androidx/leanback/widget/OnItemViewSelectedListener", + "android/support/v17/leanback/widget/PageRow": "androidx/leanback/widget/PageRow", + "android/support/v17/leanback/widget/PagingIndicator": "androidx/leanback/widget/PagingIndicator", + "android/support/v17/leanback/widget/Parallax": "androidx/leanback/widget/Parallax", + "android/support/v17/leanback/widget/ParallaxEffect": "androidx/leanback/widget/ParallaxEffect", + "android/support/v17/leanback/widget/ParallaxTarget": "androidx/leanback/widget/ParallaxTarget", + "android/support/v17/leanback/widget/PersistentFocusWrapper": "androidx/leanback/widget/PersistentFocusWrapper", + "android/support/v17/leanback/widget/PlaybackControlsPresenter": "androidx/leanback/widget/PlaybackControlsPresenter", + "android/support/v17/leanback/widget/PlaybackControlsRow": "androidx/leanback/widget/PlaybackControlsRow", + "android/support/v17/leanback/widget/PlaybackControlsRowPresenter": "androidx/leanback/widget/PlaybackControlsRowPresenter", + "android/support/v17/leanback/widget/PlaybackControlsRowView": "androidx/leanback/widget/PlaybackControlsRowView", + "android/support/v17/leanback/widget/PlaybackRowPresenter": "androidx/leanback/widget/PlaybackRowPresenter", + "android/support/v17/leanback/widget/PlaybackSeekDataProvider": "androidx/leanback/widget/PlaybackSeekDataProvider", + "android/support/v17/leanback/widget/PlaybackSeekUi": "androidx/leanback/widget/PlaybackSeekUi", + "android/support/v17/leanback/widget/PlaybackTransportRowPresenter": "androidx/leanback/widget/PlaybackTransportRowPresenter", + "android/support/v17/leanback/widget/PlaybackTransportRowView": "androidx/leanback/widget/PlaybackTransportRowView", + "android/support/v17/leanback/widget/Presenter": "androidx/leanback/widget/Presenter", + "android/support/v17/leanback/widget/PresenterSelector": "androidx/leanback/widget/PresenterSelector", + "android/support/v17/leanback/widget/PresenterSwitcher": "androidx/leanback/widget/PresenterSwitcher", + "android/support/v17/leanback/widget/RecyclerViewParallax": "androidx/leanback/widget/RecyclerViewParallax", + "android/support/v17/leanback/widget/ResizingTextView": "androidx/leanback/widget/ResizingTextView", + "android/support/v17/leanback/widget/RoundedRectHelper": "androidx/leanback/widget/RoundedRectHelper", + "android/support/v17/leanback/widget/RoundedRectHelperApi21": "androidx/leanback/widget/RoundedRectHelperApi21", + "android/support/v17/leanback/widget/Row": "androidx/leanback/widget/Row", + "android/support/v17/leanback/widget/RowContainerView": "androidx/leanback/widget/RowContainerView", + "android/support/v17/leanback/widget/RowHeaderPresenter": "androidx/leanback/widget/RowHeaderPresenter", + "android/support/v17/leanback/widget/RowHeaderView": "androidx/leanback/widget/RowHeaderView", + "android/support/v17/leanback/widget/RowPresenter": "androidx/leanback/widget/RowPresenter", + "android/support/v17/leanback/widget/ScaleFrameLayout": "androidx/leanback/widget/ScaleFrameLayout", + "android/support/v17/leanback/widget/SearchBar": "androidx/leanback/widget/SearchBar", + "android/support/v17/leanback/widget/SearchEditText": "androidx/leanback/widget/SearchEditText", + "android/support/v17/leanback/widget/SearchOrbView": "androidx/leanback/widget/SearchOrbView", + "android/support/v17/leanback/widget/SectionRow": "androidx/leanback/widget/SectionRow", + "android/support/v17/leanback/widget/SeekBar": "androidx/leanback/widget/SeekBar", + "android/support/v17/leanback/widget/ShadowHelper": "androidx/leanback/widget/ShadowHelper", + "android/support/v17/leanback/widget/ShadowHelperApi21": "androidx/leanback/widget/ShadowHelperApi21", + "android/support/v17/leanback/widget/ShadowOverlayContainer": "androidx/leanback/widget/ShadowOverlayContainer", + "android/support/v17/leanback/widget/ShadowOverlayHelper": "androidx/leanback/widget/ShadowOverlayHelper", + "android/support/v17/leanback/widget/SinglePresenterSelector": "androidx/leanback/widget/SinglePresenterSelector", + "android/support/v17/leanback/widget/SingleRow": "androidx/leanback/widget/SingleRow", + "android/support/v17/leanback/widget/SparseArrayObjectAdapter": "androidx/leanback/widget/SparseArrayObjectAdapter", + "android/support/v17/leanback/widget/SpeechOrbView": "androidx/leanback/widget/SpeechOrbView", + "android/support/v17/leanback/widget/SpeechRecognitionCallback": "androidx/leanback/widget/SpeechRecognitionCallback", + "android/support/v17/leanback/widget/StaggeredGrid": "androidx/leanback/widget/StaggeredGrid", + "android/support/v17/leanback/widget/StaggeredGridDefault": "androidx/leanback/widget/StaggeredGridDefault", + "android/support/v17/leanback/widget/StaticShadowHelper": "androidx/leanback/widget/StaticShadowHelper", + "android/support/v17/leanback/widget/StreamingTextView": "androidx/leanback/widget/StreamingTextView", + "android/support/v17/leanback/widget/ThumbsBar": "androidx/leanback/widget/ThumbsBar", + "android/support/v17/leanback/widget/TitleHelper": "androidx/leanback/widget/TitleHelper", + "android/support/v17/leanback/widget/TitleView": "androidx/leanback/widget/TitleView", + "android/support/v17/leanback/widget/TitleViewAdapter": "androidx/leanback/widget/TitleViewAdapter", + "android/support/v17/leanback/widget/Util": "androidx/leanback/widget/Util", + "android/support/v17/leanback/widget/VerticalGridPresenter": "androidx/leanback/widget/VerticalGridPresenter", + "android/support/v17/leanback/widget/VerticalGridView": "androidx/leanback/widget/VerticalGridView", + "android/support/v17/leanback/widget/VideoSurfaceView": "androidx/leanback/widget/VideoSurfaceView", + "android/support/v17/leanback/widget/ViewHolderTask": "androidx/leanback/widget/ViewHolderTask", + "android/support/v17/leanback/widget/ViewsStateBundle": "androidx/leanback/widget/ViewsStateBundle", + "android/support/v17/leanback/widget/Visibility": "androidx/leanback/widget/Visibility", + "android/support/v17/leanback/widget/WindowAlignment": "androidx/leanback/widget/WindowAlignment", + "android/support/v17/leanback/widget/picker/DatePicker": "androidx/leanback/widget/picker/DatePicker", + "android/support/v17/leanback/widget/picker/Picker": "androidx/leanback/widget/picker/Picker", + "android/support/v17/leanback/widget/picker/PickerColumn": "androidx/leanback/widget/picker/PickerColumn", + "android/support/v17/leanback/widget/picker/PickerUtility": "androidx/leanback/widget/picker/PickerUtility", + "android/support/v17/leanback/widget/picker/TimePicker": "androidx/leanback/widget/picker/TimePicker", + "android/support/v17/preference/BaseLeanbackPreferenceFragment": "androidx/leanback/preference/BaseLeanbackPreferenceFragment", + "android/support/v17/preference/LeanbackListPreferenceDialogFragment": "androidx/leanback/preference/LeanbackListPreferenceDialogFragment", + "android/support/v17/preference/LeanbackPreferenceDialogFragment": "androidx/leanback/preference/LeanbackPreferenceDialogFragment", + "android/support/v17/preference/LeanbackPreferenceFragment": "androidx/leanback/preference/LeanbackPreferenceFragment", + "android/support/v17/preference/LeanbackPreferenceFragmentTransitionHelperApi21": "androidx/leanback/preference/LeanbackPreferenceFragmentTransitionHelperApi21", + "android/support/v17/preference/LeanbackSettingsFragment": "androidx/leanback/preference/LeanbackSettingsFragment", + "android/support/v17/preference/LeanbackSettingsRootView": "androidx/leanback/preference/LeanbackSettingsRootView", + "android/support/v17/preference/R": "androidx/leanback/preference/R", + "android/support/v4/accessibilityservice/AccessibilityServiceInfoCompat": "androidx/core/accessibilityservice/AccessibilityServiceInfoCompat", + "android/support/v4/app/ActionBarDrawerToggle": "androidx/legacy/app/ActionBarDrawerToggle", + "android/support/v4/app/ActivityCompat": "androidx/core/app/ActivityCompat", + "android/support/v4/app/ActivityManagerCompat": "androidx/core/app/ActivityManagerCompat", + "android/support/v4/app/ActivityOptionsCompat": "androidx/core/app/ActivityOptionsCompat", + "android/support/v4/app/AlarmManagerCompat": "androidx/core/app/AlarmManagerCompat", + "android/support/v4/app/AppComponentFactory": "androidx/core/app/AppComponentFactory", + "android/support/v4/app/AppLaunchChecker": "androidx/core/app/AppLaunchChecker", + "android/support/v4/app/AppOpsManagerCompat": "androidx/core/app/AppOpsManagerCompat", + "android/support/v4/app/BackStackRecord": "androidx/fragment/app/BackStackRecord", + "android/support/v4/app/BackStackState": "androidx/fragment/app/BackStackState", + "android/support/v4/app/BundleCompat": "androidx/core/app/BundleCompat", + "android/support/v4/app/CoreComponentFactory": "androidx/core/app/CoreComponentFactory", + "android/support/v4/app/DialogFragment": "androidx/fragment/app/DialogFragment", + "android/support/v4/app/Fragment": "androidx/fragment/app/Fragment", + "android/support/v4/app/FragmentActivity": "androidx/fragment/app/FragmentActivity", + "android/support/v4/app/FragmentContainer": "androidx/fragment/app/FragmentContainer", + "android/support/v4/app/FragmentController": "androidx/fragment/app/FragmentController", + "android/support/v4/app/FragmentHostCallback": "androidx/fragment/app/FragmentHostCallback", + "android/support/v4/app/FragmentManager": "androidx/fragment/app/FragmentManager", + "android/support/v4/app/FragmentManagerImpl": "androidx/fragment/app/FragmentManagerImpl", + "android/support/v4/app/FragmentManagerNonConfig": "androidx/fragment/app/FragmentManagerNonConfig", + "android/support/v4/app/FragmentManagerState": "androidx/fragment/app/FragmentManagerState", + "android/support/v4/app/FragmentPagerAdapter": "androidx/fragment/app/FragmentPagerAdapter", + "android/support/v4/app/FragmentState": "androidx/fragment/app/FragmentState", + "android/support/v4/app/FragmentStatePagerAdapter": "androidx/fragment/app/FragmentStatePagerAdapter", + "android/support/v4/app/FragmentTabHost": "androidx/fragment/app/FragmentTabHost", + "android/support/v4/app/FragmentTransaction": "androidx/fragment/app/FragmentTransaction", + "android/support/v4/app/FragmentTransition": "androidx/fragment/app/FragmentTransition", + "android/support/v4/app/FragmentTransitionCompat21": "androidx/fragment/app/FragmentTransitionCompat21", + "android/support/v4/app/FragmentTransitionImpl": "androidx/fragment/app/FragmentTransitionImpl", + "android/support/v4/app/FrameMetricsAggregator": "androidx/core/app/FrameMetricsAggregator", + "android/support/v4/app/INotificationSideChannel": "androidx/core/app/INotificationSideChannel", + "android/support/v4/app/JobIntentService": "androidx/core/app/JobIntentService", + "android/support/v4/app/ListFragment": "androidx/fragment/app/ListFragment", + "android/support/v4/app/LoaderManager": "androidx/loader/app/LoaderManager", + "android/support/v4/app/LoaderManagerImpl": "androidx/loader/app/LoaderManagerImpl", + "android/support/v4/app/NavUtils": "androidx/core/app/NavUtils", + "android/support/v4/app/NotificationBuilderWithBuilderAccessor": "androidx/core/app/NotificationBuilderWithBuilderAccessor", + "android/support/v4/app/NotificationCompat": "androidx/core/app/NotificationCompat", + "android/support/v4/app/NotificationCompatBuilder": "androidx/core/app/NotificationCompatBuilder", + "android/support/v4/app/NotificationCompatExtras": "androidx/core/app/NotificationCompatExtras", + "android/support/v4/app/NotificationCompatJellybean": "androidx/core/app/NotificationCompatJellybean", + "android/support/v4/app/NotificationCompatSideChannelService": "androidx/core/app/NotificationCompatSideChannelService", + "android/support/v4/app/NotificationManagerCompat": "androidx/core/app/NotificationManagerCompat", + "android/support/v4/app/OneShotPreDrawListener": "androidx/fragment/app/OneShotPreDrawListener", + "android/support/v4/app/Person": "androidx/core/app/Person", + "android/support/v4/app/RemoteInput": "androidx/core/app/RemoteInput", + "android/support/v4/app/ServiceCompat": "androidx/core/app/ServiceCompat", + "android/support/v4/app/ShareCompat": "androidx/core/app/ShareCompat", + "android/support/v4/app/SharedElementCallback": "androidx/core/app/SharedElementCallback", + "android/support/v4/app/SuperNotCalledException": "androidx/fragment/app/SuperNotCalledException", + "android/support/v4/app/SupportActivity": "androidx/core/app/ComponentActivity", + "android/support/v4/app/TaskStackBuilder": "androidx/core/app/TaskStackBuilder", + "android/support/v4/content/AsyncTaskLoader": "androidx/loader/content/AsyncTaskLoader", + "android/support/v4/content/ContentResolverCompat": "androidx/core/content/ContentResolverCompat", + "android/support/v4/content/ContextCompat": "androidx/core/content/ContextCompat", + "android/support/v4/content/CursorLoader": "androidx/loader/content/CursorLoader", + "android/support/v4/content/FileProvider": "androidx/core/content/FileProvider", + "android/support/v4/content/IntentCompat": "androidx/core/content/IntentCompat", + "android/support/v4/content/Loader": "androidx/loader/content/Loader", + "android/support/v4/content/LocalBroadcastManager": "androidx/localbroadcastmanager/content/LocalBroadcastManager", + "android/support/v4/content/MimeTypeFilter": "androidx/core/content/MimeTypeFilter", + "android/support/v4/content/ModernAsyncTask": "androidx/loader/content/ModernAsyncTask", + "android/support/v4/content/PermissionChecker": "androidx/core/content/PermissionChecker", + "android/support/v4/content/SharedPreferencesCompat": "androidx/core/content/SharedPreferencesCompat", + "android/support/v4/content/WakefulBroadcastReceiver": "androidx/legacy/content/WakefulBroadcastReceiver", + "android/support/v4/content/pm/ActivityInfoCompat": "androidx/core/content/pm/ActivityInfoCompat", + "android/support/v4/content/pm/PackageInfoCompat": "androidx/core/content/pm/PackageInfoCompat", + "android/support/v4/content/pm/PermissionInfoCompat": "androidx/core/content/pm/PermissionInfoCompat", + "android/support/v4/content/pm/ShortcutInfoCompat": "androidx/core/content/pm/ShortcutInfoCompat", + "android/support/v4/content/pm/ShortcutManagerCompat": "androidx/core/content/pm/ShortcutManagerCompat", + "android/support/v4/content/res/ColorStateListInflaterCompat": "androidx/core/content/res/ColorStateListInflaterCompat", + "android/support/v4/content/res/ComplexColorCompat": "androidx/core/content/res/ComplexColorCompat", + "android/support/v4/content/res/ConfigurationHelper": "androidx/core/content/res/ConfigurationHelper", + "android/support/v4/content/res/FontResourcesParserCompat": "androidx/core/content/res/FontResourcesParserCompat", + "android/support/v4/content/res/GradientColorInflaterCompat": "androidx/core/content/res/GradientColorInflaterCompat", + "android/support/v4/content/res/GrowingArrayUtils": "androidx/core/content/res/GrowingArrayUtils", + "android/support/v4/content/res/ResourcesCompat": "androidx/core/content/res/ResourcesCompat", + "android/support/v4/content/res/TypedArrayUtils": "androidx/core/content/res/TypedArrayUtils", + "android/support/v4/database/CursorWindowCompat": "androidx/core/database/CursorWindowCompat", + "android/support/v4/database/DatabaseUtilsCompat": "androidx/core/database/DatabaseUtilsCompat", + "android/support/v4/database/sqlite/SQLiteCursorCompat": "androidx/core/database/sqlite/SQLiteCursorCompat", + "android/support/v4/graphics/BitmapCompat": "androidx/core/graphics/BitmapCompat", + "android/support/v4/graphics/ColorUtils": "androidx/core/graphics/ColorUtils", + "android/support/v4/graphics/PaintCompat": "androidx/core/graphics/PaintCompat", + "android/support/v4/graphics/PathParser": "androidx/core/graphics/PathParser", + "android/support/v4/graphics/PathSegment": "androidx/core/graphics/PathSegment", + "android/support/v4/graphics/PathUtils": "androidx/core/graphics/PathUtils", + "android/support/v4/graphics/TypefaceCompat": "androidx/core/graphics/TypefaceCompat", + "android/support/v4/graphics/TypefaceCompatApi21Impl": "androidx/core/graphics/TypefaceCompatApi21Impl", + "android/support/v4/graphics/TypefaceCompatApi24Impl": "androidx/core/graphics/TypefaceCompatApi24Impl", + "android/support/v4/graphics/TypefaceCompatApi26Impl": "androidx/core/graphics/TypefaceCompatApi26Impl", + "android/support/v4/graphics/TypefaceCompatApi28Impl": "androidx/core/graphics/TypefaceCompatApi28Impl", + "android/support/v4/graphics/TypefaceCompatBaseImpl": "androidx/core/graphics/TypefaceCompatBaseImpl", + "android/support/v4/graphics/TypefaceCompatUtil": "androidx/core/graphics/TypefaceCompatUtil", + "android/support/v4/graphics/drawable/DrawableCompat": "androidx/core/graphics/drawable/DrawableCompat", + "android/support/v4/graphics/drawable/IconCompat": "androidx/core/graphics/drawable/IconCompat", + "android/support/v4/graphics/drawable/IconCompatParcelizer": "android/support/v4/graphics/drawable/IconCompatParcelizer", + "android/support/v4/graphics/drawable/RoundedBitmapDrawable": "androidx/core/graphics/drawable/RoundedBitmapDrawable", + "android/support/v4/graphics/drawable/RoundedBitmapDrawable21": "androidx/core/graphics/drawable/RoundedBitmapDrawable21", + "android/support/v4/graphics/drawable/RoundedBitmapDrawableFactory": "androidx/core/graphics/drawable/RoundedBitmapDrawableFactory", + "android/support/v4/graphics/drawable/TintAwareDrawable": "androidx/core/graphics/drawable/TintAwareDrawable", + "android/support/v4/graphics/drawable/WrappedDrawable": "androidx/core/graphics/drawable/WrappedDrawable", + "android/support/v4/graphics/drawable/WrappedDrawableApi14": "androidx/core/graphics/drawable/WrappedDrawableApi14", + "android/support/v4/graphics/drawable/WrappedDrawableApi21": "androidx/core/graphics/drawable/WrappedDrawableApi21", + "android/support/v4/hardware/display/DisplayManagerCompat": "androidx/core/hardware/display/DisplayManagerCompat", + "android/support/v4/hardware/fingerprint/FingerprintManagerCompat": "androidx/core/hardware/fingerprint/FingerprintManagerCompat", + "android/support/v4/internal/view/SupportMenu": "androidx/core/internal/view/SupportMenu", + "android/support/v4/internal/view/SupportMenuItem": "androidx/core/internal/view/SupportMenuItem", + "android/support/v4/internal/view/SupportSubMenu": "androidx/core/internal/view/SupportSubMenu", + "android/support/v4/math/MathUtils": "androidx/core/math/MathUtils", + "android/support/v4/media/AudioAttributesCompat": "androidx/media/AudioAttributesCompat", + "android/support/v4/media/AudioAttributesImpl": "android/support/v4/media/AudioAttributesImpl", + "android/support/v4/media/AudioAttributesImplApi21": "android/support/v4/media/AudioAttributesImplApi21", + "android/support/v4/media/AudioAttributesImplBase": "android/support/v4/media/AudioAttributesImplBase", + "android/support/v4/media/MediaBrowserCompat": "android/support/v4/media/MediaBrowserCompat", + "android/support/v4/media/MediaBrowserCompatApi21": "android/support/v4/media/MediaBrowserCompatApi21", + "android/support/v4/media/MediaBrowserCompatApi23": "android/support/v4/media/MediaBrowserCompatApi23", + "android/support/v4/media/MediaBrowserCompatApi26": "android/support/v4/media/MediaBrowserCompatApi26", + "android/support/v4/media/MediaBrowserCompatUtils": "androidx/media/MediaBrowserCompatUtils", + "android/support/v4/media/MediaBrowserProtocol": "androidx/media/MediaBrowserProtocol", + "android/support/v4/media/MediaBrowserServiceCompat": "androidx/media/MediaBrowserServiceCompat", + "android/support/v4/media/MediaBrowserServiceCompatApi21": "androidx/media/MediaBrowserServiceCompatApi21", + "android/support/v4/media/MediaBrowserServiceCompatApi23": "androidx/media/MediaBrowserServiceCompatApi23", + "android/support/v4/media/MediaBrowserServiceCompatApi26": "androidx/media/MediaBrowserServiceCompatApi26", + "android/support/v4/media/MediaDescriptionCompat": "android/support/v4/media/MediaDescriptionCompat", + "android/support/v4/media/MediaDescriptionCompatApi21": "android/support/v4/media/MediaDescriptionCompatApi21", + "android/support/v4/media/MediaDescriptionCompatApi23": "android/support/v4/media/MediaDescriptionCompatApi23", + "android/support/v4/media/MediaMetadataCompat": "android/support/v4/media/MediaMetadataCompat", + "android/support/v4/media/MediaMetadataCompatApi21": "android/support/v4/media/MediaMetadataCompatApi21", + "android/support/v4/media/MediaSessionManager": "androidx/media/MediaSessionManager", + "android/support/v4/media/MediaSessionManagerImplApi21": "androidx/media/MediaSessionManagerImplApi21", + "android/support/v4/media/MediaSessionManagerImplApi28": "androidx/media/MediaSessionManagerImplApi28", + "android/support/v4/media/MediaSessionManagerImplBase": "androidx/media/MediaSessionManagerImplBase", + "android/support/v4/media/ParceledListSliceAdapterApi21": "android/support/v4/media/ParceledListSliceAdapterApi21", + "android/support/v4/media/RatingCompat": "android/support/v4/media/RatingCompat", + "android/support/v4/media/VolumeProviderCompat": "androidx/media/VolumeProviderCompat", + "android/support/v4/media/VolumeProviderCompatApi21": "androidx/media/VolumeProviderCompatApi21", + "android/support/v4/media/app/NotificationCompat": "androidx/media/app/NotificationCompat", + "android/support/v4/media/session/IMediaControllerCallback": "android/support/v4/media/session/IMediaControllerCallback", + "android/support/v4/media/session/IMediaSession": "android/support/v4/media/session/IMediaSession", + "android/support/v4/media/session/MediaButtonReceiver": "androidx/media/session/MediaButtonReceiver", + "android/support/v4/media/session/MediaControllerCompat": "android/support/v4/media/session/MediaControllerCompat", + "android/support/v4/media/session/MediaControllerCompatApi21": "android/support/v4/media/session/MediaControllerCompatApi21", + "android/support/v4/media/session/MediaControllerCompatApi23": "android/support/v4/media/session/MediaControllerCompatApi23", + "android/support/v4/media/session/MediaControllerCompatApi24": "android/support/v4/media/session/MediaControllerCompatApi24", + "android/support/v4/media/session/MediaSessionCompat": "android/support/v4/media/session/MediaSessionCompat", + "android/support/v4/media/session/MediaSessionCompatApi21": "android/support/v4/media/session/MediaSessionCompatApi21", + "android/support/v4/media/session/MediaSessionCompatApi22": "android/support/v4/media/session/MediaSessionCompatApi22", + "android/support/v4/media/session/MediaSessionCompatApi23": "android/support/v4/media/session/MediaSessionCompatApi23", + "android/support/v4/media/session/MediaSessionCompatApi24": "android/support/v4/media/session/MediaSessionCompatApi24", + "android/support/v4/media/session/ParcelableVolumeInfo": "android/support/v4/media/session/ParcelableVolumeInfo", + "android/support/v4/media/session/PlaybackStateCompat": "android/support/v4/media/session/PlaybackStateCompat", + "android/support/v4/media/session/PlaybackStateCompatApi21": "android/support/v4/media/session/PlaybackStateCompatApi21", + "android/support/v4/media/session/PlaybackStateCompatApi22": "android/support/v4/media/session/PlaybackStateCompatApi22", + "android/support/v4/net/ConnectivityManagerCompat": "androidx/core/net/ConnectivityManagerCompat", + "android/support/v4/net/DatagramSocketWrapper": "androidx/core/net/DatagramSocketWrapper", + "android/support/v4/net/TrafficStatsCompat": "androidx/core/net/TrafficStatsCompat", + "android/support/v4/os/BuildCompat": "androidx/core/os/BuildCompat", + "android/support/v4/os/CancellationSignal": "androidx/core/os/CancellationSignal", + "android/support/v4/os/ConfigurationCompat": "androidx/core/os/ConfigurationCompat", + "android/support/v4/os/EnvironmentCompat": "androidx/core/os/EnvironmentCompat", + "android/support/v4/os/HandlerCompat": "androidx/core/os/HandlerCompat", + "android/support/v4/os/IResultReceiver": "androidx/core/os/IResultReceiver", + "android/support/v4/os/LocaleHelper": "androidx/core/os/LocaleHelper", + "android/support/v4/os/LocaleListCompat": "androidx/core/os/LocaleListCompat", + "android/support/v4/os/LocaleListHelper": "androidx/core/os/LocaleListHelper", + "android/support/v4/os/LocaleListInterface": "androidx/core/os/LocaleListInterface", + "android/support/v4/os/OperationCanceledException": "androidx/core/os/OperationCanceledException", + "android/support/v4/os/ParcelCompat": "androidx/core/os/ParcelCompat", + "android/support/v4/os/ParcelableCompat": "androidx/core/os/ParcelableCompat", + "android/support/v4/os/ParcelableCompatCreatorCallbacks": "androidx/core/os/ParcelableCompatCreatorCallbacks", + "android/support/v4/os/TraceCompat": "androidx/core/os/TraceCompat", + "android/support/v4/os/UserManagerCompat": "androidx/core/os/UserManagerCompat", + "android/support/v4/print/PrintHelper": "androidx/print/PrintHelper", + "android/support/v4/provider/DocumentFile": "androidx/documentfile/provider/DocumentFile", + "android/support/v4/provider/DocumentsContractApi19": "androidx/documentfile/provider/DocumentsContractApi19", + "android/support/v4/provider/FontRequest": "androidx/core/provider/FontRequest", + "android/support/v4/provider/FontsContractCompat": "androidx/core/provider/FontsContractCompat", + "android/support/v4/provider/RawDocumentFile": "androidx/documentfile/provider/RawDocumentFile", + "android/support/v4/provider/SelfDestructiveThread": "androidx/core/provider/SelfDestructiveThread", + "android/support/v4/provider/SingleDocumentFile": "androidx/documentfile/provider/SingleDocumentFile", + "android/support/v4/provider/TreeDocumentFile": "androidx/documentfile/provider/TreeDocumentFile", + "android/support/v4/text/BidiFormatter": "androidx/core/text/BidiFormatter", + "android/support/v4/text/HtmlCompat": "androidx/core/text/HtmlCompat", + "android/support/v4/text/ICUCompat": "androidx/core/text/ICUCompat", + "android/support/v4/text/PrecomputedTextCompat": "androidx/core/text/PrecomputedTextCompat", + "android/support/v4/text/TextDirectionHeuristicCompat": "androidx/core/text/TextDirectionHeuristicCompat", + "android/support/v4/text/TextDirectionHeuristicsCompat": "androidx/core/text/TextDirectionHeuristicsCompat", + "android/support/v4/text/TextUtilsCompat": "androidx/core/text/TextUtilsCompat", + "android/support/v4/text/util/FindAddress": "androidx/core/text/util/FindAddress", + "android/support/v4/text/util/LinkifyCompat": "androidx/core/text/util/LinkifyCompat", + "android/support/v4/util/ArrayMap": "androidx/collection/ArrayMap", + "android/support/v4/util/ArraySet": "androidx/collection/ArraySet", + "android/support/v4/util/AtomicFile": "androidx/core/util/AtomicFile", + "android/support/v4/util/CircularArray": "androidx/collection/CircularArray", + "android/support/v4/util/CircularIntArray": "androidx/collection/CircularIntArray", + "android/support/v4/util/Consumer": "androidx/core/util/Consumer", + "android/support/v4/util/ContainerHelpers": "androidx/collection/ContainerHelpers", + "android/support/v4/util/DebugUtils": "androidx/core/util/DebugUtils", + "android/support/v4/util/LogWriter": "androidx/core/util/LogWriter", + "android/support/v4/util/LongSparseArray": "androidx/collection/LongSparseArray", + "android/support/v4/util/LruCache": "androidx/collection/LruCache", + "android/support/v4/util/MapCollections": "androidx/collection/MapCollections", + "android/support/v4/util/ObjectsCompat": "androidx/core/util/ObjectsCompat", + "android/support/v4/util/Pair": "androidx/core/util/Pair", + "android/support/v4/util/PatternsCompat": "androidx/core/util/PatternsCompat", + "android/support/v4/util/Pools": "androidx/core/util/Pools", + "android/support/v4/util/Preconditions": "androidx/core/util/Preconditions", + "android/support/v4/util/SimpleArrayMap": "androidx/collection/SimpleArrayMap", + "android/support/v4/util/SparseArrayCompat": "androidx/collection/SparseArrayCompat", + "android/support/v4/util/TimeUtils": "androidx/core/util/TimeUtils", + "android/support/v4/view/AbsSavedState": "androidx/customview/view/AbsSavedState", + "android/support/v4/view/AccessibilityDelegateCompat": "androidx/core/view/AccessibilityDelegateCompat", + "android/support/v4/view/ActionProvider": "androidx/core/view/ActionProvider", + "android/support/v4/view/AsyncLayoutInflater": "androidx/asynclayoutinflater/view/AsyncLayoutInflater", + "android/support/v4/view/DisplayCutoutCompat": "androidx/core/view/DisplayCutoutCompat", + "android/support/v4/view/GestureDetectorCompat": "androidx/core/view/GestureDetectorCompat", + "android/support/v4/view/GravityCompat": "androidx/core/view/GravityCompat", + "android/support/v4/view/InputDeviceCompat": "androidx/core/view/InputDeviceCompat", + "android/support/v4/view/KeyEventDispatcher": "androidx/core/view/KeyEventDispatcher", + "android/support/v4/view/LayoutInflaterCompat": "androidx/core/view/LayoutInflaterCompat", + "android/support/v4/view/LayoutInflaterFactory": "androidx/core/view/LayoutInflaterFactory", + "android/support/v4/view/MarginLayoutParamsCompat": "androidx/core/view/MarginLayoutParamsCompat", + "android/support/v4/view/MenuCompat": "androidx/core/view/MenuCompat", + "android/support/v4/view/MenuItemCompat": "androidx/core/view/MenuItemCompat", + "android/support/v4/view/MotionEventCompat": "androidx/core/view/MotionEventCompat", + "android/support/v4/view/NestedScrollingChild": "androidx/core/view/NestedScrollingChild", + "android/support/v4/view/NestedScrollingChild2": "androidx/core/view/NestedScrollingChild2", + "android/support/v4/view/NestedScrollingChildHelper": "androidx/core/view/NestedScrollingChildHelper", + "android/support/v4/view/NestedScrollingParent": "androidx/core/view/NestedScrollingParent", + "android/support/v4/view/NestedScrollingParent2": "androidx/core/view/NestedScrollingParent2", + "android/support/v4/view/NestedScrollingParentHelper": "androidx/core/view/NestedScrollingParentHelper", + "android/support/v4/view/OnApplyWindowInsetsListener": "androidx/core/view/OnApplyWindowInsetsListener", + "android/support/v4/view/PagerAdapter": "androidx/viewpager/widget/PagerAdapter", + "android/support/v4/view/PagerTabStrip": "androidx/viewpager/widget/PagerTabStrip", + "android/support/v4/view/PagerTitleStrip": "androidx/viewpager/widget/PagerTitleStrip", + "android/support/v4/view/PointerIconCompat": "androidx/core/view/PointerIconCompat", + "android/support/v4/view/ScaleGestureDetectorCompat": "androidx/core/view/ScaleGestureDetectorCompat", + "android/support/v4/view/ScrollingView": "androidx/core/view/ScrollingView", + "android/support/v4/view/TintableBackgroundView": "androidx/core/view/TintableBackgroundView", + "android/support/v4/view/VelocityTrackerCompat": "androidx/core/view/VelocityTrackerCompat", + "android/support/v4/view/ViewCompat": "androidx/core/view/ViewCompat", + "android/support/v4/view/ViewConfigurationCompat": "androidx/core/view/ViewConfigurationCompat", + "android/support/v4/view/ViewGroupCompat": "androidx/core/view/ViewGroupCompat", + "android/support/v4/view/ViewPager": "androidx/viewpager/widget/ViewPager", + "android/support/v4/view/ViewParentCompat": "androidx/core/view/ViewParentCompat", + "android/support/v4/view/ViewPropertyAnimatorCompat": "androidx/core/view/ViewPropertyAnimatorCompat", + "android/support/v4/view/ViewPropertyAnimatorListener": "androidx/core/view/ViewPropertyAnimatorListener", + "android/support/v4/view/ViewPropertyAnimatorListenerAdapter": "androidx/core/view/ViewPropertyAnimatorListenerAdapter", + "android/support/v4/view/ViewPropertyAnimatorUpdateListener": "androidx/core/view/ViewPropertyAnimatorUpdateListener", + "android/support/v4/view/WindowCompat": "androidx/core/view/WindowCompat", + "android/support/v4/view/WindowInsetsCompat": "androidx/core/view/WindowInsetsCompat", + "android/support/v4/view/accessibility/AccessibilityEventCompat": "androidx/core/view/accessibility/AccessibilityEventCompat", + "android/support/v4/view/accessibility/AccessibilityManagerCompat": "androidx/core/view/accessibility/AccessibilityManagerCompat", + "android/support/v4/view/accessibility/AccessibilityNodeInfoCompat": "androidx/core/view/accessibility/AccessibilityNodeInfoCompat", + "android/support/v4/view/accessibility/AccessibilityNodeProviderCompat": "androidx/core/view/accessibility/AccessibilityNodeProviderCompat", + "android/support/v4/view/accessibility/AccessibilityRecordCompat": "androidx/core/view/accessibility/AccessibilityRecordCompat", + "android/support/v4/view/accessibility/AccessibilityWindowInfoCompat": "androidx/core/view/accessibility/AccessibilityWindowInfoCompat", + "android/support/v4/view/animation/FastOutLinearInInterpolator": "androidx/interpolator/view/animation/FastOutLinearInInterpolator", + "android/support/v4/view/animation/FastOutSlowInInterpolator": "androidx/interpolator/view/animation/FastOutSlowInInterpolator", + "android/support/v4/view/animation/LinearOutSlowInInterpolator": "androidx/interpolator/view/animation/LinearOutSlowInInterpolator", + "android/support/v4/view/animation/LookupTableInterpolator": "androidx/interpolator/view/animation/LookupTableInterpolator", + "android/support/v4/view/animation/PathInterpolatorApi14": "androidx/core/view/animation/PathInterpolatorApi14", + "android/support/v4/view/animation/PathInterpolatorCompat": "androidx/core/view/animation/PathInterpolatorCompat", + "android/support/v4/widget/AutoScrollHelper": "androidx/core/widget/AutoScrollHelper", + "android/support/v4/widget/AutoSizeableTextView": "androidx/core/widget/AutoSizeableTextView", + "android/support/v4/widget/CircleImageView": "androidx/swiperefreshlayout/widget/CircleImageView", + "android/support/v4/widget/CircularProgressDrawable": "androidx/swiperefreshlayout/widget/CircularProgressDrawable", + "android/support/v4/widget/CompoundButtonCompat": "androidx/core/widget/CompoundButtonCompat", + "android/support/v4/widget/ContentLoadingProgressBar": "androidx/core/widget/ContentLoadingProgressBar", + "android/support/v4/widget/CursorAdapter": "androidx/cursoradapter/widget/CursorAdapter", + "android/support/v4/widget/CursorFilter": "androidx/cursoradapter/widget/CursorFilter", + "android/support/v4/widget/DirectedAcyclicGraph": "androidx/coordinatorlayout/widget/DirectedAcyclicGraph", + "android/support/v4/widget/DrawerLayout": "androidx/drawerlayout/widget/DrawerLayout", + "android/support/v4/widget/EdgeEffectCompat": "androidx/core/widget/EdgeEffectCompat", + "android/support/v4/widget/ExploreByTouchHelper": "androidx/customview/widget/ExploreByTouchHelper", + "android/support/v4/widget/FocusStrategy": "androidx/customview/widget/FocusStrategy", + "android/support/v4/widget/ImageViewCompat": "androidx/core/widget/ImageViewCompat", + "android/support/v4/widget/ListPopupWindowCompat": "androidx/core/widget/ListPopupWindowCompat", + "android/support/v4/widget/ListViewAutoScrollHelper": "androidx/core/widget/ListViewAutoScrollHelper", + "android/support/v4/widget/ListViewCompat": "androidx/core/widget/ListViewCompat", + "android/support/v4/widget/NestedScrollView": "androidx/core/widget/NestedScrollView", + "android/support/v4/widget/PopupMenuCompat": "androidx/core/widget/PopupMenuCompat", + "android/support/v4/widget/PopupWindowCompat": "androidx/core/widget/PopupWindowCompat", + "android/support/v4/widget/ResourceCursorAdapter": "androidx/cursoradapter/widget/ResourceCursorAdapter", + "android/support/v4/widget/ScrollerCompat": "androidx/core/widget/ScrollerCompat", + "android/support/v4/widget/SimpleCursorAdapter": "androidx/cursoradapter/widget/SimpleCursorAdapter", + "android/support/v4/widget/SlidingPaneLayout": "androidx/slidingpanelayout/widget/SlidingPaneLayout", + "android/support/v4/widget/Space": "androidx/legacy/widget/Space", + "android/support/v4/widget/SwipeRefreshLayout": "androidx/swiperefreshlayout/widget/SwipeRefreshLayout", + "android/support/v4/widget/TextViewCompat": "androidx/core/widget/TextViewCompat", + "android/support/v4/widget/TintableCompoundButton": "androidx/core/widget/TintableCompoundButton", + "android/support/v4/widget/TintableImageSourceView": "androidx/core/widget/TintableImageSourceView", + "android/support/v4/widget/ViewDragHelper": "androidx/customview/widget/ViewDragHelper", + "android/support/v4/widget/ViewGroupUtils": "androidx/coordinatorlayout/widget/ViewGroupUtils", + "android/support/v7/app/ActionBar": "androidx/appcompat/app/ActionBar", + "android/support/v7/app/ActionBarDrawerToggle": "androidx/appcompat/app/ActionBarDrawerToggle", + "android/support/v7/app/ActionBarDrawerToggleHoneycomb": "androidx/appcompat/app/ActionBarDrawerToggleHoneycomb", + "android/support/v7/app/AlertController": "androidx/appcompat/app/AlertController", + "android/support/v7/app/AlertDialog": "androidx/appcompat/app/AlertDialog", + "android/support/v7/app/AppCompatActivity": "androidx/appcompat/app/AppCompatActivity", + "android/support/v7/app/AppCompatCallback": "androidx/appcompat/app/AppCompatCallback", + "android/support/v7/app/AppCompatDelegate": "androidx/appcompat/app/AppCompatDelegate", + "android/support/v7/app/AppCompatDelegateImpl": "androidx/appcompat/app/AppCompatDelegateImpl", + "android/support/v7/app/AppCompatDialog": "androidx/appcompat/app/AppCompatDialog", + "android/support/v7/app/AppCompatDialogFragment": "androidx/appcompat/app/AppCompatDialogFragment", + "android/support/v7/app/AppCompatViewInflater": "androidx/appcompat/app/AppCompatViewInflater", + "android/support/v7/app/MediaRouteActionProvider": "androidx/mediarouter/app/MediaRouteActionProvider", + "android/support/v7/app/MediaRouteButton": "androidx/mediarouter/app/MediaRouteButton", + "android/support/v7/app/MediaRouteCastDialog": "androidx/mediarouter/app/MediaRouteCastDialog", + "android/support/v7/app/MediaRouteChooserDialog": "androidx/mediarouter/app/MediaRouteChooserDialog", + "android/support/v7/app/MediaRouteChooserDialogFragment": "androidx/mediarouter/app/MediaRouteChooserDialogFragment", + "android/support/v7/app/MediaRouteControllerDialog": "androidx/mediarouter/app/MediaRouteControllerDialog", + "android/support/v7/app/MediaRouteControllerDialogFragment": "androidx/mediarouter/app/MediaRouteControllerDialogFragment", + "android/support/v7/app/MediaRouteDevicePickerDialog": "androidx/mediarouter/app/MediaRouteDevicePickerDialog", + "android/support/v7/app/MediaRouteDialogFactory": "androidx/mediarouter/app/MediaRouteDialogFactory", + "android/support/v7/app/MediaRouteDialogHelper": "androidx/mediarouter/app/MediaRouteDialogHelper", + "android/support/v7/app/MediaRouteDiscoveryFragment": "androidx/mediarouter/app/MediaRouteDiscoveryFragment", + "android/support/v7/app/MediaRouteExpandCollapseButton": "androidx/mediarouter/app/MediaRouteExpandCollapseButton", + "android/support/v7/app/MediaRouteVolumeSlider": "androidx/mediarouter/app/MediaRouteVolumeSlider", + "android/support/v7/app/MediaRouterThemeHelper": "androidx/mediarouter/app/MediaRouterThemeHelper", + "android/support/v7/app/NavItemSelectedListener": "androidx/appcompat/app/NavItemSelectedListener", + "android/support/v7/app/OverlayListView": "androidx/mediarouter/app/OverlayListView", + "android/support/v7/app/ResourcesFlusher": "androidx/appcompat/app/ResourcesFlusher", + "android/support/v7/app/ToolbarActionBar": "androidx/appcompat/app/ToolbarActionBar", + "android/support/v7/app/TwilightCalculator": "androidx/appcompat/app/TwilightCalculator", + "android/support/v7/app/TwilightManager": "androidx/appcompat/app/TwilightManager", + "android/support/v7/app/WindowDecorActionBar": "androidx/appcompat/app/WindowDecorActionBar", + "android/support/v7/appcompat/R": "androidx/appcompat/R", + "android/support/v7/cardview/R": "androidx/cardview/R", + "android/support/v7/content/res/AppCompatResources": "androidx/appcompat/content/res/AppCompatResources", + "android/support/v7/graphics/ColorCutQuantizer": "androidx/palette/graphics/ColorCutQuantizer", + "android/support/v7/graphics/Palette": "androidx/palette/graphics/Palette", + "android/support/v7/graphics/Target": "androidx/palette/graphics/Target", + "android/support/v7/graphics/drawable/AnimatedStateListDrawableCompat": "androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompat", + "android/support/v7/graphics/drawable/DrawableContainer": "androidx/appcompat/graphics/drawable/DrawableContainer", + "android/support/v7/graphics/drawable/DrawableWrapper": "androidx/appcompat/graphics/drawable/DrawableWrapper", + "android/support/v7/graphics/drawable/DrawerArrowDrawable": "androidx/appcompat/graphics/drawable/DrawerArrowDrawable", + "android/support/v7/graphics/drawable/StateListDrawable": "androidx/appcompat/graphics/drawable/StateListDrawable", + "android/support/v7/gridlayout/R": "androidx/gridlayout/R", + "android/support/v7/internal/widget/PreferenceImageView": "androidx/preference/internal/PreferenceImageView", + "android/support/v7/media/MediaControlIntent": "androidx/mediarouter/media/MediaControlIntent", + "android/support/v7/media/MediaItemMetadata": "androidx/mediarouter/media/MediaItemMetadata", + "android/support/v7/media/MediaItemStatus": "androidx/mediarouter/media/MediaItemStatus", + "android/support/v7/media/MediaRouteDescriptor": "androidx/mediarouter/media/MediaRouteDescriptor", + "android/support/v7/media/MediaRouteDiscoveryRequest": "androidx/mediarouter/media/MediaRouteDiscoveryRequest", + "android/support/v7/media/MediaRouteProvider": "androidx/mediarouter/media/MediaRouteProvider", + "android/support/v7/media/MediaRouteProviderDescriptor": "androidx/mediarouter/media/MediaRouteProviderDescriptor", + "android/support/v7/media/MediaRouteProviderProtocol": "androidx/mediarouter/media/MediaRouteProviderProtocol", + "android/support/v7/media/MediaRouteProviderService": "androidx/mediarouter/media/MediaRouteProviderService", + "android/support/v7/media/MediaRouteSelector": "androidx/mediarouter/media/MediaRouteSelector", + "android/support/v7/media/MediaRouter": "androidx/mediarouter/media/MediaRouter", + "android/support/v7/media/MediaRouterApi24": "androidx/mediarouter/media/MediaRouterApi24", + "android/support/v7/media/MediaRouterJellybean": "androidx/mediarouter/media/MediaRouterJellybean", + "android/support/v7/media/MediaRouterJellybeanMr1": "androidx/mediarouter/media/MediaRouterJellybeanMr1", + "android/support/v7/media/MediaRouterJellybeanMr2": "androidx/mediarouter/media/MediaRouterJellybeanMr2", + "android/support/v7/media/MediaSessionStatus": "androidx/mediarouter/media/MediaSessionStatus", + "android/support/v7/media/RegisteredMediaRouteProvider": "androidx/mediarouter/media/RegisteredMediaRouteProvider", + "android/support/v7/media/RegisteredMediaRouteProviderWatcher": "androidx/mediarouter/media/RegisteredMediaRouteProviderWatcher", + "android/support/v7/media/RemoteControlClientCompat": "androidx/mediarouter/media/RemoteControlClientCompat", + "android/support/v7/media/RemotePlaybackClient": "androidx/mediarouter/media/RemotePlaybackClient", + "android/support/v7/media/RouteMediaPlayerConnector": "androidx/mediarouter/media/RouteMediaPlayerConnector", + "android/support/v7/media/SystemMediaRouteProvider": "androidx/mediarouter/media/SystemMediaRouteProvider", + "android/support/v7/mediarouter/R": "androidx/mediarouter/R", + "android/support/v7/preference/AndroidResources": "androidx/preference/AndroidResources", + "android/support/v7/preference/CheckBoxPreference": "androidx/preference/CheckBoxPreference", + "android/support/v7/preference/CollapsiblePreferenceGroupController": "androidx/preference/CollapsiblePreferenceGroupController", + "android/support/v7/preference/DialogPreference": "androidx/preference/DialogPreference", + "android/support/v7/preference/DropDownPreference": "androidx/preference/DropDownPreference", + "android/support/v7/preference/EditTextPreference": "androidx/preference/EditTextPreference", + "android/support/v7/preference/EditTextPreferenceDialogFragmentCompat": "androidx/preference/EditTextPreferenceDialogFragmentCompat", + "android/support/v7/preference/ListPreference": "androidx/preference/ListPreference", + "android/support/v7/preference/ListPreferenceDialogFragmentCompat": "androidx/preference/ListPreferenceDialogFragmentCompat", + "android/support/v7/preference/MultiSelectListPreferenceDialogFragmentCompat": "androidx/preference/MultiSelectListPreferenceDialogFragmentCompat", + "android/support/v7/preference/Preference": "androidx/preference/Preference", + "android/support/v7/preference/PreferenceCategory": "androidx/preference/PreferenceCategory", + "android/support/v7/preference/PreferenceDataStore": "androidx/preference/PreferenceDataStore", + "android/support/v7/preference/PreferenceDialogFragmentCompat": "androidx/preference/PreferenceDialogFragmentCompat", + "android/support/v7/preference/PreferenceFragmentCompat": "androidx/preference/PreferenceFragmentCompat", + "android/support/v7/preference/PreferenceGroup": "androidx/preference/PreferenceGroup", + "android/support/v7/preference/PreferenceGroupAdapter": "androidx/preference/PreferenceGroupAdapter", + "android/support/v7/preference/PreferenceInflater": "androidx/preference/PreferenceInflater", + "android/support/v7/preference/PreferenceManager": "androidx/preference/PreferenceManager", + "android/support/v7/preference/PreferenceRecyclerViewAccessibilityDelegate": "androidx/preference/PreferenceRecyclerViewAccessibilityDelegate", + "android/support/v7/preference/PreferenceScreen": "androidx/preference/PreferenceScreen", + "android/support/v7/preference/PreferenceViewHolder": "androidx/preference/PreferenceViewHolder", + "android/support/v7/preference/R": "androidx/preference/R", + "android/support/v7/preference/SeekBarPreference": "androidx/preference/SeekBarPreference", + "android/support/v7/preference/SwitchPreferenceCompat": "androidx/preference/SwitchPreferenceCompat", + "android/support/v7/preference/TwoStatePreference": "androidx/preference/TwoStatePreference", + "android/support/v7/preference/UnPressableLinearLayout": "androidx/preference/UnPressableLinearLayout", + "android/support/v7/preference/internal/AbstractMultiSelectListPreference": "androidx/preference/internal/AbstractMultiSelectListPreference", + "android/support/v7/recyclerview/R": "androidx/recyclerview/R", + "android/support/v7/recyclerview/extensions/AsyncDifferConfig": "androidx/recyclerview/widget/AsyncDifferConfig", + "android/support/v7/recyclerview/extensions/AsyncListDiffer": "androidx/recyclerview/widget/AsyncListDiffer", + "android/support/v7/recyclerview/extensions/ListAdapter": "androidx/recyclerview/widget/ListAdapter", + "android/support/v7/text/AllCapsTransformationMethod": "androidx/appcompat/text/AllCapsTransformationMethod", + "android/support/v7/util/AdapterListUpdateCallback": "androidx/recyclerview/widget/AdapterListUpdateCallback", + "android/support/v7/util/AsyncListUtil": "androidx/recyclerview/widget/AsyncListUtil", + "android/support/v7/util/BatchingListUpdateCallback": "androidx/recyclerview/widget/BatchingListUpdateCallback", + "android/support/v7/util/DiffUtil": "androidx/recyclerview/widget/DiffUtil", + "android/support/v7/util/ListUpdateCallback": "androidx/recyclerview/widget/ListUpdateCallback", + "android/support/v7/util/MessageThreadUtil": "androidx/recyclerview/widget/MessageThreadUtil", + "android/support/v7/util/SortedList": "androidx/recyclerview/widget/SortedList", + "android/support/v7/util/ThreadUtil": "androidx/recyclerview/widget/ThreadUtil", + "android/support/v7/util/TileList": "androidx/recyclerview/widget/TileList", + "android/support/v7/view/ActionBarPolicy": "androidx/appcompat/view/ActionBarPolicy", + "android/support/v7/view/ActionMode": "androidx/appcompat/view/ActionMode", + "android/support/v7/view/CollapsibleActionView": "androidx/appcompat/view/CollapsibleActionView", + "android/support/v7/view/ContextThemeWrapper": "androidx/appcompat/view/ContextThemeWrapper", + "android/support/v7/view/StandaloneActionMode": "androidx/appcompat/view/StandaloneActionMode", + "android/support/v7/view/SupportActionModeWrapper": "androidx/appcompat/view/SupportActionModeWrapper", + "android/support/v7/view/SupportMenuInflater": "androidx/appcompat/view/SupportMenuInflater", + "android/support/v7/view/ViewPropertyAnimatorCompatSet": "androidx/appcompat/view/ViewPropertyAnimatorCompatSet", + "android/support/v7/view/WindowCallbackWrapper": "androidx/appcompat/view/WindowCallbackWrapper", + "android/support/v7/view/menu/ActionMenuItem": "androidx/appcompat/view/menu/ActionMenuItem", + "android/support/v7/view/menu/ActionMenuItemView": "androidx/appcompat/view/menu/ActionMenuItemView", + "android/support/v7/view/menu/BaseMenuPresenter": "androidx/appcompat/view/menu/BaseMenuPresenter", + "android/support/v7/view/menu/BaseMenuWrapper": "androidx/appcompat/view/menu/BaseMenuWrapper", + "android/support/v7/view/menu/BaseWrapper": "androidx/appcompat/view/menu/BaseWrapper", + "android/support/v7/view/menu/CascadingMenuPopup": "androidx/appcompat/view/menu/CascadingMenuPopup", + "android/support/v7/view/menu/ExpandedMenuView": "androidx/appcompat/view/menu/ExpandedMenuView", + "android/support/v7/view/menu/ListMenuItemView": "androidx/appcompat/view/menu/ListMenuItemView", + "android/support/v7/view/menu/ListMenuPresenter": "androidx/appcompat/view/menu/ListMenuPresenter", + "android/support/v7/view/menu/MenuAdapter": "androidx/appcompat/view/menu/MenuAdapter", + "android/support/v7/view/menu/MenuBuilder": "androidx/appcompat/view/menu/MenuBuilder", + "android/support/v7/view/menu/MenuDialogHelper": "androidx/appcompat/view/menu/MenuDialogHelper", + "android/support/v7/view/menu/MenuHelper": "androidx/appcompat/view/menu/MenuHelper", + "android/support/v7/view/menu/MenuItemImpl": "androidx/appcompat/view/menu/MenuItemImpl", + "android/support/v7/view/menu/MenuItemWrapperICS": "androidx/appcompat/view/menu/MenuItemWrapperICS", + "android/support/v7/view/menu/MenuItemWrapperJB": "androidx/appcompat/view/menu/MenuItemWrapperJB", + "android/support/v7/view/menu/MenuPopup": "androidx/appcompat/view/menu/MenuPopup", + "android/support/v7/view/menu/MenuPopupHelper": "androidx/appcompat/view/menu/MenuPopupHelper", + "android/support/v7/view/menu/MenuPresenter": "androidx/appcompat/view/menu/MenuPresenter", + "android/support/v7/view/menu/MenuView": "androidx/appcompat/view/menu/MenuView", + "android/support/v7/view/menu/MenuWrapperFactory": "androidx/appcompat/view/menu/MenuWrapperFactory", + "android/support/v7/view/menu/MenuWrapperICS": "androidx/appcompat/view/menu/MenuWrapperICS", + "android/support/v7/view/menu/ShowableListMenu": "androidx/appcompat/view/menu/ShowableListMenu", + "android/support/v7/view/menu/StandardMenuPopup": "androidx/appcompat/view/menu/StandardMenuPopup", + "android/support/v7/view/menu/SubMenuBuilder": "androidx/appcompat/view/menu/SubMenuBuilder", + "android/support/v7/view/menu/SubMenuWrapperICS": "androidx/appcompat/view/menu/SubMenuWrapperICS", + "android/support/v7/widget/AbsActionBarView": "androidx/appcompat/widget/AbsActionBarView", + "android/support/v7/widget/ActionBarBackgroundDrawable": "androidx/appcompat/widget/ActionBarBackgroundDrawable", + "android/support/v7/widget/ActionBarContainer": "androidx/appcompat/widget/ActionBarContainer", + "android/support/v7/widget/ActionBarContextView": "androidx/appcompat/widget/ActionBarContextView", + "android/support/v7/widget/ActionBarOverlayLayout": "androidx/appcompat/widget/ActionBarOverlayLayout", + "android/support/v7/widget/ActionMenuPresenter": "androidx/appcompat/widget/ActionMenuPresenter", + "android/support/v7/widget/ActionMenuView": "androidx/appcompat/widget/ActionMenuView", + "android/support/v7/widget/ActivityChooserModel": "androidx/appcompat/widget/ActivityChooserModel", + "android/support/v7/widget/ActivityChooserView": "androidx/appcompat/widget/ActivityChooserView", + "android/support/v7/widget/AdapterHelper": "androidx/recyclerview/widget/AdapterHelper", + "android/support/v7/widget/AlertDialogLayout": "androidx/appcompat/widget/AlertDialogLayout", + "android/support/v7/widget/AppCompatAutoCompleteTextView": "androidx/appcompat/widget/AppCompatAutoCompleteTextView", + "android/support/v7/widget/AppCompatBackgroundHelper": "androidx/appcompat/widget/AppCompatBackgroundHelper", + "android/support/v7/widget/AppCompatButton": "androidx/appcompat/widget/AppCompatButton", + "android/support/v7/widget/AppCompatCheckBox": "androidx/appcompat/widget/AppCompatCheckBox", + "android/support/v7/widget/AppCompatCheckedTextView": "androidx/appcompat/widget/AppCompatCheckedTextView", + "android/support/v7/widget/AppCompatCompoundButtonHelper": "androidx/appcompat/widget/AppCompatCompoundButtonHelper", + "android/support/v7/widget/AppCompatDrawableManager": "androidx/appcompat/widget/AppCompatDrawableManager", + "android/support/v7/widget/AppCompatEditText": "androidx/appcompat/widget/AppCompatEditText", + "android/support/v7/widget/AppCompatHintHelper": "androidx/appcompat/widget/AppCompatHintHelper", + "android/support/v7/widget/AppCompatImageButton": "androidx/appcompat/widget/AppCompatImageButton", + "android/support/v7/widget/AppCompatImageHelper": "androidx/appcompat/widget/AppCompatImageHelper", + "android/support/v7/widget/AppCompatImageView": "androidx/appcompat/widget/AppCompatImageView", + "android/support/v7/widget/AppCompatMultiAutoCompleteTextView": "androidx/appcompat/widget/AppCompatMultiAutoCompleteTextView", + "android/support/v7/widget/AppCompatPopupWindow": "androidx/appcompat/widget/AppCompatPopupWindow", + "android/support/v7/widget/AppCompatProgressBarHelper": "androidx/appcompat/widget/AppCompatProgressBarHelper", + "android/support/v7/widget/AppCompatRadioButton": "androidx/appcompat/widget/AppCompatRadioButton", + "android/support/v7/widget/AppCompatRatingBar": "androidx/appcompat/widget/AppCompatRatingBar", + "android/support/v7/widget/AppCompatSeekBar": "androidx/appcompat/widget/AppCompatSeekBar", + "android/support/v7/widget/AppCompatSeekBarHelper": "androidx/appcompat/widget/AppCompatSeekBarHelper", + "android/support/v7/widget/AppCompatSpinner": "androidx/appcompat/widget/AppCompatSpinner", + "android/support/v7/widget/AppCompatTextHelper": "androidx/appcompat/widget/AppCompatTextHelper", + "android/support/v7/widget/AppCompatTextView": "androidx/appcompat/widget/AppCompatTextView", + "android/support/v7/widget/AppCompatTextViewAutoSizeHelper": "androidx/appcompat/widget/AppCompatTextViewAutoSizeHelper", + "android/support/v7/widget/ButtonBarLayout": "androidx/appcompat/widget/ButtonBarLayout", + "android/support/v7/widget/CardView": "androidx/cardview/widget/CardView", + "android/support/v7/widget/CardViewApi17Impl": "androidx/cardview/widget/CardViewApi17Impl", + "android/support/v7/widget/CardViewApi21Impl": "androidx/cardview/widget/CardViewApi21Impl", + "android/support/v7/widget/CardViewBaseImpl": "androidx/cardview/widget/CardViewBaseImpl", + "android/support/v7/widget/CardViewDelegate": "androidx/cardview/widget/CardViewDelegate", + "android/support/v7/widget/CardViewImpl": "androidx/cardview/widget/CardViewImpl", + "android/support/v7/widget/ChildHelper": "androidx/recyclerview/widget/ChildHelper", + "android/support/v7/widget/ContentFrameLayout": "androidx/appcompat/widget/ContentFrameLayout", + "android/support/v7/widget/DecorContentParent": "androidx/appcompat/widget/DecorContentParent", + "android/support/v7/widget/DecorToolbar": "androidx/appcompat/widget/DecorToolbar", + "android/support/v7/widget/DefaultItemAnimator": "androidx/recyclerview/widget/DefaultItemAnimator", + "android/support/v7/widget/DialogTitle": "androidx/appcompat/widget/DialogTitle", + "android/support/v7/widget/DividerItemDecoration": "androidx/recyclerview/widget/DividerItemDecoration", + "android/support/v7/widget/DrawableUtils": "androidx/appcompat/widget/DrawableUtils", + "android/support/v7/widget/DropDownListView": "androidx/appcompat/widget/DropDownListView", + "android/support/v7/widget/FastScroller": "androidx/recyclerview/widget/FastScroller", + "android/support/v7/widget/FitWindowsFrameLayout": "androidx/appcompat/widget/FitWindowsFrameLayout", + "android/support/v7/widget/FitWindowsLinearLayout": "androidx/appcompat/widget/FitWindowsLinearLayout", + "android/support/v7/widget/FitWindowsViewGroup": "androidx/appcompat/widget/FitWindowsViewGroup", + "android/support/v7/widget/ForwardingListener": "androidx/appcompat/widget/ForwardingListener", + "android/support/v7/widget/GapWorker": "androidx/recyclerview/widget/GapWorker", + "android/support/v7/widget/GridLayout": "androidx/gridlayout/widget/GridLayout", + "android/support/v7/widget/GridLayoutManager": "androidx/recyclerview/widget/GridLayoutManager", + "android/support/v7/widget/LayoutState": "androidx/recyclerview/widget/LayoutState", + "android/support/v7/widget/LinearLayoutCompat": "androidx/appcompat/widget/LinearLayoutCompat", + "android/support/v7/widget/LinearLayoutManager": "androidx/recyclerview/widget/LinearLayoutManager", + "android/support/v7/widget/LinearSmoothScroller": "androidx/recyclerview/widget/LinearSmoothScroller", + "android/support/v7/widget/LinearSnapHelper": "androidx/recyclerview/widget/LinearSnapHelper", + "android/support/v7/widget/ListPopupWindow": "androidx/appcompat/widget/ListPopupWindow", + "android/support/v7/widget/MenuItemHoverListener": "androidx/appcompat/widget/MenuItemHoverListener", + "android/support/v7/widget/MenuPopupWindow": "androidx/appcompat/widget/MenuPopupWindow", + "android/support/v7/widget/OpReorderer": "androidx/recyclerview/widget/OpReorderer", + "android/support/v7/widget/OrientationHelper": "androidx/recyclerview/widget/OrientationHelper", + "android/support/v7/widget/PagerSnapHelper": "androidx/recyclerview/widget/PagerSnapHelper", + "android/support/v7/widget/PopupMenu": "androidx/appcompat/widget/PopupMenu", + "android/support/v7/widget/RecyclerView": "androidx/recyclerview/widget/RecyclerView", + "android/support/v7/widget/RecyclerViewAccessibilityDelegate": "androidx/recyclerview/widget/RecyclerViewAccessibilityDelegate", + "android/support/v7/widget/ResourcesWrapper": "androidx/appcompat/widget/ResourcesWrapper", + "android/support/v7/widget/RoundRectDrawable": "androidx/cardview/widget/RoundRectDrawable", + "android/support/v7/widget/RoundRectDrawableWithShadow": "androidx/cardview/widget/RoundRectDrawableWithShadow", + "android/support/v7/widget/RtlSpacingHelper": "androidx/appcompat/widget/RtlSpacingHelper", + "android/support/v7/widget/ScrollbarHelper": "androidx/recyclerview/widget/ScrollbarHelper", + "android/support/v7/widget/ScrollingTabContainerView": "androidx/appcompat/widget/ScrollingTabContainerView", + "android/support/v7/widget/SearchView": "androidx/appcompat/widget/SearchView", + "android/support/v7/widget/ShareActionProvider": "androidx/appcompat/widget/ShareActionProvider", + "android/support/v7/widget/SimpleItemAnimator": "androidx/recyclerview/widget/SimpleItemAnimator", + "android/support/v7/widget/SnapHelper": "androidx/recyclerview/widget/SnapHelper", + "android/support/v7/widget/StaggeredGridLayoutManager": "androidx/recyclerview/widget/StaggeredGridLayoutManager", + "android/support/v7/widget/SuggestionsAdapter": "androidx/appcompat/widget/SuggestionsAdapter", + "android/support/v7/widget/SwitchCompat": "androidx/appcompat/widget/SwitchCompat", + "android/support/v7/widget/ThemeUtils": "androidx/appcompat/widget/ThemeUtils", + "android/support/v7/widget/ThemedSpinnerAdapter": "androidx/appcompat/widget/ThemedSpinnerAdapter", + "android/support/v7/widget/TintContextWrapper": "androidx/appcompat/widget/TintContextWrapper", + "android/support/v7/widget/TintInfo": "androidx/appcompat/widget/TintInfo", + "android/support/v7/widget/TintResources": "androidx/appcompat/widget/TintResources", + "android/support/v7/widget/TintTypedArray": "androidx/appcompat/widget/TintTypedArray", + "android/support/v7/widget/Toolbar": "androidx/appcompat/widget/Toolbar", + "android/support/v7/widget/ToolbarWidgetWrapper": "androidx/appcompat/widget/ToolbarWidgetWrapper", + "android/support/v7/widget/TooltipCompat": "androidx/appcompat/widget/TooltipCompat", + "android/support/v7/widget/TooltipCompatHandler": "androidx/appcompat/widget/TooltipCompatHandler", + "android/support/v7/widget/TooltipPopup": "androidx/appcompat/widget/TooltipPopup", + "android/support/v7/widget/VectorEnabledTintResources": "androidx/appcompat/widget/VectorEnabledTintResources", + "android/support/v7/widget/ViewBoundsCheck": "androidx/recyclerview/widget/ViewBoundsCheck", + "android/support/v7/widget/ViewInfoStore": "androidx/recyclerview/widget/ViewInfoStore", + "android/support/v7/widget/ViewStubCompat": "androidx/appcompat/widget/ViewStubCompat", + "android/support/v7/widget/ViewUtils": "androidx/appcompat/widget/ViewUtils", + "android/support/v7/widget/WithHint": "androidx/appcompat/widget/WithHint", + "android/support/v7/widget/helper/ItemTouchHelper": "androidx/recyclerview/widget/ItemTouchHelper", + "android/support/v7/widget/helper/ItemTouchUIUtil": "androidx/recyclerview/widget/ItemTouchUIUtil", + "android/support/v7/widget/helper/ItemTouchUIUtilImpl": "androidx/recyclerview/widget/ItemTouchUIUtilImpl", + "android/support/v7/widget/util/SortedListAdapterCallback": "androidx/recyclerview/widget/SortedListAdapterCallback", + "android/support/wear/R": "androidx/wear/R", + "android/support/wear/activity/ConfirmationActivity": "androidx/wear/activity/ConfirmationActivity", + "android/support/wear/ambient/AmbientDelegate": "androidx/wear/ambient/AmbientDelegate", + "android/support/wear/ambient/AmbientMode": "androidx/wear/ambient/AmbientMode", + "android/support/wear/ambient/AmbientModeSupport": "androidx/wear/ambient/AmbientModeSupport", + "android/support/wear/ambient/SharedLibraryVersion": "androidx/wear/ambient/SharedLibraryVersion", + "android/support/wear/ambient/WearableControllerProvider": "androidx/wear/ambient/WearableControllerProvider", + "android/support/wear/internal/widget/ResourcesUtil": "androidx/wear/internal/widget/ResourcesUtil", + "android/support/wear/internal/widget/drawer/MultiPagePresenter": "androidx/wear/internal/widget/drawer/MultiPagePresenter", + "android/support/wear/internal/widget/drawer/MultiPageUi": "androidx/wear/internal/widget/drawer/MultiPageUi", + "android/support/wear/internal/widget/drawer/SinglePagePresenter": "androidx/wear/internal/widget/drawer/SinglePagePresenter", + "android/support/wear/internal/widget/drawer/SinglePageUi": "androidx/wear/internal/widget/drawer/SinglePageUi", + "android/support/wear/internal/widget/drawer/WearableNavigationDrawerPresenter": "androidx/wear/internal/widget/drawer/WearableNavigationDrawerPresenter", + "android/support/wear/utils/MetadataConstants": "androidx/wear/utils/MetadataConstants", + "android/support/wear/widget/BezierSCurveInterpolator": "androidx/wear/widget/BezierSCurveInterpolator", + "android/support/wear/widget/BoxInsetLayout": "androidx/wear/widget/BoxInsetLayout", + "android/support/wear/widget/CircledImageView": "androidx/wear/widget/CircledImageView", + "android/support/wear/widget/CircularProgressLayout": "androidx/wear/widget/CircularProgressLayout", + "android/support/wear/widget/CircularProgressLayoutController": "androidx/wear/widget/CircularProgressLayoutController", + "android/support/wear/widget/ConfirmationOverlay": "androidx/wear/widget/ConfirmationOverlay", + "android/support/wear/widget/CurvingLayoutCallback": "androidx/wear/widget/CurvingLayoutCallback", + "android/support/wear/widget/ProgressDrawable": "androidx/wear/widget/ProgressDrawable", + "android/support/wear/widget/ResourcesUtil": "androidx/wear/widget/ResourcesUtil", + "android/support/wear/widget/RoundedDrawable": "androidx/wear/widget/RoundedDrawable", + "android/support/wear/widget/ScrollManager": "androidx/wear/widget/ScrollManager", + "android/support/wear/widget/SimpleAnimatorListener": "androidx/wear/widget/SimpleAnimatorListener", + "android/support/wear/widget/SwipeDismissFrameLayout": "androidx/wear/widget/SwipeDismissFrameLayout", + "android/support/wear/widget/SwipeDismissLayout": "androidx/wear/widget/SwipeDismissLayout", + "android/support/wear/widget/WearableLinearLayoutManager": "androidx/wear/widget/WearableLinearLayoutManager", + "android/support/wear/widget/WearableRecyclerView": "androidx/wear/widget/WearableRecyclerView", + "android/support/wear/widget/drawer/AbsListViewFlingWatcher": "androidx/wear/widget/drawer/AbsListViewFlingWatcher", + "android/support/wear/widget/drawer/FlingWatcherFactory": "androidx/wear/widget/drawer/FlingWatcherFactory", + "android/support/wear/widget/drawer/NestedScrollViewFlingWatcher": "androidx/wear/widget/drawer/NestedScrollViewFlingWatcher", + "android/support/wear/widget/drawer/PageIndicatorView": "androidx/wear/widget/drawer/PageIndicatorView", + "android/support/wear/widget/drawer/RecyclerViewFlingWatcher": "androidx/wear/widget/drawer/RecyclerViewFlingWatcher", + "android/support/wear/widget/drawer/ScrollViewFlingWatcher": "androidx/wear/widget/drawer/ScrollViewFlingWatcher", + "android/support/wear/widget/drawer/WearableActionDrawerMenu": "androidx/wear/widget/drawer/WearableActionDrawerMenu", + "android/support/wear/widget/drawer/WearableActionDrawerView": "androidx/wear/widget/drawer/WearableActionDrawerView", + "android/support/wear/widget/drawer/WearableDrawerController": "androidx/wear/widget/drawer/WearableDrawerController", + "android/support/wear/widget/drawer/WearableDrawerLayout": "androidx/wear/widget/drawer/WearableDrawerLayout", + "android/support/wear/widget/drawer/WearableDrawerView": "androidx/wear/widget/drawer/WearableDrawerView", + "android/support/wear/widget/drawer/WearableNavigationDrawerView": "androidx/wear/widget/drawer/WearableNavigationDrawerView", + "com/android/databinding/library/baseAdapters/DataBinderMapperImpl": "androidx/databinding/library/baseAdapters/DataBinderMapperImpl", + "com/android/databinding/library/baseAdapters/R": "androidx/databinding/library/baseAdapters/R" + } + }, + "proGuardMap": { + "rules": { + "androidx/{any}Parcelizer": [ + "androidx/{any}Parcelizer" + ], + "android/support/{any}": [ + "android/support/{any}", + "androidx/{any}" + ], + "android/support{any}": [ + "android/support{any}", + "androidx{any}" + ], + "android/support/v*/{any}": [ + "android/support/**", + "androidx/{any}" + ], + "android/support/v4/{any}": [ + "android/support/v4/{any}", + "androidx/{any}" + ], + "android/support/v4/view/{any}": [ + "androidx/customview/view/{any}", + "androidx/core/view/{any}", + "androidx/asynclayoutinflater/{any}", + "androidx/viewpager/{any}", + "androidx/interpolator/{any}" + ], + "android/support/v4/media/{any}": [ + "androidx/media/{any}", + "android/support/v4/{any}" + ], + "android/support/v7/{any}": [ + "androidx/appcompat/{any}", + "androidx/mediarouter/{any}", + "androidx/cardview/{any}", + "androidx/palette/{any}", + "androidx/gridlayout/{any}", + "androidx/preference/{any}" + ], + "android/support/v7/widget/{any}": [ + "androidx/appcompat/widget/{any}", + "androidx/recyclerview/widget/{any}", + "androidx/cardview/widget/{any}" + ], + "android/support/v7/preference/{any}": [ + "androidx/preference/{any}" + ], + "android/support/v14/preference/{any}": [ + "androidx/preference/{any}" + ], + "android/support/v17/preference/{any}": [ + "androidx/leanback/preference/{any}" + ], + "android/support/v17/leanback/{any}": [ + "androidx/leanback/{any}" + ], + "android/support/design/widget/{any}": [ + "androidx/coordinatorlayout/widget/{any}", + "com/google/android/material/**" + ], + "android/support/design/{any}": [ + "androidx/coordinatorlayout/**", + "com/google/android/material/**" + ], + "android/support/design/internal/{any}": [ + "com/google/android/material/{any}" + ], + "android/support/car/{any}": [ + "androidx/car/{any}" + ], + "android/arch/persistence/room/paging/{any}": [ + "androidx/room/paging/{any}" + ], + "android/support/v7/internal/widget/ActionBarView${any}": [ + "androidx/appcompat/widget/AbsActionBarView${any}" + ], + "android/support/v4/view/MenuItemCompat/*": [ + "androidx/core/view/MenuItemCompat/*" + ], + "Android{any}": [ + "Android{any}" + ] + } + }, + "stringsMap": { + "types": { + "android/support/v13/view/inputmethod/EditorInfoCompat/CONTENT_MIME_TYPES": "android/support/v13/view/inputmethod/EditorInfoCompat/CONTENT_MIME_TYPES", + "android/support/v13/view/inputmethod/InputConnectionCompat/COMMIT_CONTENT": "android/support/v13/view/inputmethod/InputConnectionCompat/COMMIT_CONTENT", + "android/support/v13/view/inputmethod/InputConnectionCompat/CONTENT_DESCRIPTION": "android/support/v13/view/inputmethod/InputConnectionCompat/CONTENT_DESCRIPTION", + "android/support/v13/view/inputmethod/InputConnectionCompat/CONTENT_FLAGS": "android/support/v13/view/inputmethod/InputConnectionCompat/CONTENT_FLAGS", + "android/support/v13/view/inputmethod/InputConnectionCompat/CONTENT_LINK_URI": "android/support/v13/view/inputmethod/InputConnectionCompat/CONTENT_LINK_URI", + "android/support/v13/view/inputmethod/InputConnectionCompat/CONTENT_OPTS": "android/support/v13/view/inputmethod/InputConnectionCompat/CONTENT_OPTS", + "android/support/v13/view/inputmethod/InputConnectionCompat/CONTENT_RESULT_RECEIVER": "android/support/v13/view/inputmethod/InputConnectionCompat/CONTENT_RESULT_RECEIVER", + "android/support/v13/view/inputmethod/InputConnectionCompat/CONTENT_URI": "android/support/v13/view/inputmethod/InputConnectionCompat/CONTENT_URI", + "android/support/v4/app/EXTRA_CALLING_ACTIVITY": "android/support/v4/app/EXTRA_CALLING_ACTIVITY", + "android/support/v4/app/EXTRA_CALLING_PACKAGE": "android/support/v4/app/EXTRA_CALLING_PACKAGE", + "androidx/contentpager/content/wakelockid": "androidx/contentpager/content/wakelockid", + "androidx/core/app/EXTRA_CALLING_ACTIVITY": "androidx/core/app/EXTRA_CALLING_ACTIVITY", + "androidx/core/app/EXTRA_CALLING_PACKAGE": "androidx/core/app/EXTRA_CALLING_PACKAGE", + "androidx/core/view/inputmethod/EditorInfoCompat/CONTENT_MIME_TYPES": "androidx/core/view/inputmethod/EditorInfoCompat/CONTENT_MIME_TYPES", + "androidx/support/content/wakelockid": "androidx/support/content/wakelockid", + "androidx/core/view/inputmethod/InputConnectionCompat/COMMIT_CONTENT": "androidx/core/view/inputmethod/InputConnectionCompat/COMMIT_CONTENT", + "androidx/core/view/inputmethod/InputConnectionCompat/CONTENT_URI": "androidx/core/view/inputmethod/InputConnectionCompat/CONTENT_URI", + "androidx/core/view/inputmethod/InputConnectionCompat/CONTENT_DESCRIPTION": "androidx/core/view/inputmethod/InputConnectionCompat/CONTENT_DESCRIPTION", + "androidx/core/view/inputmethod/InputConnectionCompat/CONTENT_LINK_URI": "androidx/core/view/inputmethod/InputConnectionCompat/CONTENT_LINK_URI", + "androidx/core/view/inputmethod/InputConnectionCompat/CONTENT_OPTS": "androidx/core/view/inputmethod/InputConnectionCompat/CONTENT_OPTS", + "androidx/core/view/inputmethod/InputConnectionCompat/CONTENT_FLAGS": "androidx/core/view/inputmethod/InputConnectionCompat/CONTENT_FLAGS", + "androidx/core/view/inputmethod/InputConnectionCompat/CONTENT_RESULT_RECEIVER": "androidx/core/view/inputmethod/InputConnectionCompat/CONTENT_RESULT_RECEIVER" + } + } +}
diff --git a/third_party/google_benchmark/buildconfig.gni b/third_party/google_benchmark/buildconfig.gni deleted file mode 100644 index 6203a4e..0000000 --- a/third_party/google_benchmark/buildconfig.gni +++ /dev/null
@@ -1,12 +0,0 @@ -# Copyright 2019 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# TODO(https://crbug.com/1404759): Remove this whole file. - -import("//build/config/gclient_args.gni") -import("//build_overrides/build.gni") - -declare_args() { - enable_google_benchmarks = true -}
diff --git a/third_party/nearby/BUILD.gn b/third_party/nearby/BUILD.gn index f4e35e39..2505fd8 100644 --- a/third_party/nearby/BUILD.gn +++ b/third_party/nearby/BUILD.gn
@@ -26,20 +26,6 @@ } ############################################################################### -# src/internal/analytics -############################################################################### -# src/internal/analytics -source_set("analytics_event_logger") { - public_configs = [ - ":nearby_include_config", - ":nearby_defines", - ] - public = [ "src/internal/analytics/event_logger.h" ] - configs -= [ "//build/config/compiler:chromium_code" ] - configs += [ "//build/config/compiler:no_chromium_code" ] -} - -############################################################################### # src/connections ############################################################################### # src/connections @@ -190,6 +176,7 @@ public_deps = [ ":connections_enums_proto", ":connections_implementation_analytics", + ":connections_implementation_flags", ":connections_implementation_mediums", ":connections_implementation_mediums_webrtc", ":connections_types", @@ -253,6 +240,27 @@ configs += [ "//build/config/compiler:no_chromium_code" ] } +# src/connections/implementation/flags +source_set("connections_implementation_flags") { + public_configs = [ + ":nearby_include_config", + ":nearby_defines", + ] + sources = [ "src/connections/implementation/flags/connections_flags.cc" ] + public = [ + "src/connections/implementation/flags/connections_flags.h", + "src/connections/implementation/flags/nearby_connections_feature_flags.h", + ] + public_deps = [ + ":flags", + ":platform_base", + ":platform_public_types", + "//third_party/abseil-cpp:absl", + ] + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] +} + # src/connections/implementation/mediums source_set("connections_implementation_mediums") { public_configs = [ @@ -298,6 +306,8 @@ "src/connections/implementation/mediums/wifi_lan.h", ] public_deps = [ + ":ble_frames_proto", + ":connections_implementation_flags", ":connections_implementation_mediums_webrtc", ":connections_types", ":platform_base", @@ -306,7 +316,6 @@ ":platform_public_comm", ":platform_public_logging", ":platform_public_types", - ":ble_frames_proto", ":web_rtc_signaling_frames_proto", "//third_party/abseil-cpp:absl", "//third_party/smhasher:murmurhash3", @@ -390,6 +399,36 @@ } ############################################################################### +# src/internal +############################################################################### +# src/internal/analytics +source_set("analytics_event_logger") { + public_configs = [ + ":nearby_include_config", + ":nearby_defines", + ] + public = [ "src/internal/analytics/event_logger.h" ] + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] +} + +# src/internal/flags +source_set("flags") { + public_configs = [ + ":nearby_include_config", + ":nearby_defines", + ] + public = [ + "src/internal/flags/default_flag_reader.h", + "src/internal/flags/flag.h", + "src/internal/flags/flag_reader.h", + ] + public_deps = [ "//third_party/abseil-cpp:absl" ] + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] +} + +############################################################################### # src/internal/platform ############################################################################### # src/internal/platform/implementation
diff --git a/third_party/nearby/README.chromium b/third_party/nearby/README.chromium index a06189d..c7a9d99 100644 --- a/third_party/nearby/README.chromium +++ b/third_party/nearby/README.chromium
@@ -1,7 +1,7 @@ Name: Nearby Connections Library Short Name: Nearby URL: https://github.com/google/nearby -Version: 9c3b5fb3773b8111268093559c81bc2228a78282 +Version: acf0f09a3f4477abefd77be746fbf79a68fa38ca License: Apache 2.0 License File: LICENSE Security Critical: yes
diff --git a/third_party/updater/chrome_linux64/3pp/3pp.pb b/third_party/updater/chrome_linux64/3pp/3pp.pb new file mode 100644 index 0000000..1bf6f45 --- /dev/null +++ b/third_party/updater/chrome_linux64/3pp/3pp.pb
@@ -0,0 +1,14 @@ +create { + source { + script { name: "fetch.py" } + unpack_archive: true + no_archive_prune: true + } + build { + } +} + +upload { + universal: true + pkg_prefix: "chromium/third_party/updater" +}
diff --git a/third_party/updater/chrome_linux64/3pp/fetch.py b/third_party/updater/chrome_linux64/3pp/fetch.py new file mode 100755 index 0000000..5793d664 --- /dev/null +++ b/third_party/updater/chrome_linux64/3pp/fetch.py
@@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# Copyright 2023 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import argparse +import datetime +import json +import os +import sys +import urllib.request + +# TODO(crbug.com/1268555): This is compared lexically. Remove it before M1000. +MIN_VERSION = '113.0.5639.0' + +def fetch(): + """ + Queries the VersionHistory API to determine the version of the updater that + was serving on Monday. + """ + # TODO(crbug.com/1293206): Once this script is python3 only, use + # datetime.timezone.utc to make it consistent regardless of local timezone. + datum = datetime.datetime.now() + datum = (datum - datetime.timedelta(days=datum.weekday())).replace( + hour=0, minute=0, second=0, microsecond=0) + datum = datum.isoformat() + 'Z' + + return json.load( + urllib.request.urlopen( + 'https://versionhistory.googleapis.com/v1/chromium_updater/' + 'platforms/linux/channels/all/versions/all/releases?' + 'filter=starttime%%3C%s,endtime%%3E%s' % + (datum, datum)))['releases'][0]['version'] + + +def print_latest(): + print(max(fetch(), MIN_VERSION)) + + +def get_url(): + print( + json.dumps({ + 'url': [ + 'https://edgedl.me.gvt1.com/edgedl/release2/182l0/latest/' + '%s_linux_updater.zip' % os.environ['_3PP_VERSION'] + ], + 'ext': + '.zip', + 'name': ['updater.zip'] + })) + + +def main(): + ap = argparse.ArgumentParser() + sub = ap.add_subparsers() + sub.add_parser('latest').set_defaults(func=lambda _opts: print_latest()) + sub.add_parser('get_url').set_defaults(func=lambda _opts: get_url()) + opts = ap.parse_args() + return opts.func(opts) + + +if __name__ == '__main__': + sys.exit(main())
diff --git a/third_party/updater/chrome_linux64/3pp/install.sh b/third_party/updater/chrome_linux64/3pp/install.sh new file mode 100755 index 0000000..4ba2b25 --- /dev/null +++ b/third_party/updater/chrome_linux64/3pp/install.sh
@@ -0,0 +1,14 @@ +#!/bin/bash +# Copyright 2023 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e +set -x +set -o pipefail + +# An auto-created directory whose content will ultimately be uploaded to CIPD. +# The commands below should output the built product to this directory. +PREFIX="$1" + +mv updater_test "$PREFIX"
diff --git a/third_party/wpt_tools/README.chromium b/third_party/wpt_tools/README.chromium index da4d9e58..e556afd 100644 --- a/third_party/wpt_tools/README.chromium +++ b/third_party/wpt_tools/README.chromium
@@ -1,7 +1,7 @@ Name: web-platform-tests - Test Suites for Web Platform specifications Short Name: wpt URL: https://github.com/web-platform-tests/wpt/ -Version: 69982c63f56787fd56947e9a53ad188514af757f +Version: f791e89cb111b15467d28ef4d9d436af7bc004a3 License: LICENSES FOR W3C TEST SUITES (https://www.w3.org/Consortium/Legal/2008/03-bsd-license.html) License File: NOT_SHIPPED Security Critical: no
diff --git a/third_party/wpt_tools/WPTIncludeList b/third_party/wpt_tools/WPTIncludeList index 200c7e0..dc1f447 100644 --- a/third_party/wpt_tools/WPTIncludeList +++ b/third_party/wpt_tools/WPTIncludeList
@@ -289,7 +289,7 @@ ./tools/wptrunner/wptrunner/executors/executorservodriver.py ./tools/wptrunner/wptrunner/executors/process.py ./tools/wptrunner/wptrunner/executors/executorsafari.py -./tools/wptrunner/wptrunner/print_reftest_runner.html +./tools/wptrunner/wptrunner/print_pdf_runner.html ./tools/wptrunner/wptrunner/metadata.py ./tools/wptrunner/wptrunner/wptlogging.py ./tools/wptrunner/wptrunner/vcs.py
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/environment.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/environment.py index 7edc68f..2bf72448 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/environment.py +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/environment.py
@@ -223,7 +223,7 @@ for path, format_args, content_type, route in [ ("testharness_runner.html", {}, "text/html", "/testharness_runner.html"), - ("print_reftest_runner.html", {}, "text/html", "/print_reftest_runner.html"), + ("print_pdf_runner.html", {}, "text/html", "/print_pdf_runner.html"), (os.path.join(here, "..", "..", "third_party", "pdf_js", "pdf.js"), None, "text/javascript", "/_pdf_js/pdf.js"), (os.path.join(here, "..", "..", "third_party", "pdf_js", "pdf.worker.js"), None,
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorchrome.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorchrome.py index e5f5615..030578c 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorchrome.py +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorchrome.py
@@ -18,7 +18,7 @@ self.runner_handle = None def load_runner(self): - url = urljoin(self.parent.executor.server_url("http"), "/print_reftest_runner.html") + url = urljoin(self.parent.executor.server_url("http"), "/print_pdf_runner.html") self.logger.debug("Loading %s" % url) try: self.webdriver.url = url
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executormarionette.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executormarionette.py index 6fafca87..aa6d50b 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executormarionette.py +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executormarionette.py
@@ -628,7 +628,7 @@ self.runner_handle = None def load_runner(self): - url = urljoin(self.parent.executor.server_url("http"), "/print_reftest_runner.html") + url = urljoin(self.parent.executor.server_url("http"), "/print_pdf_runner.html") self.logger.debug("Loading %s" % url) try: self.marionette.navigate(url) @@ -655,7 +655,7 @@ "bottom": margin, }, "shrinkToFit": False, - "printBackground": True, + "background": True, } return self.marionette._send_message("WebDriver:Print", body, key="value")
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/print_reftest_runner.html b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/print_pdf_runner.html similarity index 100% rename from third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/print_reftest_runner.html rename to third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/print_pdf_runner.html
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testharnessreport-content-shell.js b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testharnessreport-content-shell.js index e4693f9b..191218d 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testharnessreport-content-shell.js +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testharnessreport-content-shell.js
@@ -1,6 +1,4 @@ var props = {output:%(output)d, debug: %(debug)s}; -var start_loc = document.createElement('a'); -start_loc.href = location.href; setup(props); testRunner.dumpAsText(); @@ -9,7 +7,7 @@ testRunner.setDumpJavaScriptDialogs(false); add_completion_callback(function (tests, harness_status) { - var id = decodeURIComponent(start_loc.pathname) + decodeURIComponent(start_loc.search) + decodeURIComponent(start_loc.hash); + var id = decodeURIComponent(location.pathname) + decodeURIComponent(location.search) + decodeURIComponent(location.hash); var result_string = JSON.stringify([ id, harness_status.status,
diff --git a/tools/android/modularization/convenience/lookup_dep.py b/tools/android/modularization/convenience/lookup_dep.py index af98dbf0..61cfdd3 100755 --- a/tools/android/modularization/convenience/lookup_dep.py +++ b/tools/android/modularization/convenience/lookup_dep.py
@@ -15,6 +15,8 @@ tools/android/modularization/convenience/lookup_dep.py FooUtil ''' +from __future__ import annotations + import argparse import collections import dataclasses @@ -25,7 +27,7 @@ import subprocess import sys import zipfile -from typing import Dict, List, Set +from typing import Dict, Iterator, List, Set _SRC_DIR = pathlib.Path(__file__).resolve().parents[4] @@ -102,18 +104,51 @@ @dataclasses.dataclass(frozen=True) -class TargetInfo: - """Container for information about a build target.""" - target_name: str - low_classpath_priority: bool - - -@dataclasses.dataclass(frozen=True) class ClassEntry: """An assignment of a Java class to a build target.""" full_class_name: str target: str - low_classpath_priority: bool + preferred_dep: bool + + def __lt__(self, other: 'ClassEntry'): + # Prefer canonical targets first. + if self.preferred_dep and not other.preferred_dep: + return True + # Prefer targets without __ in the name. Usually double underscores are used + # for internal subtargets and not top level targets. + if '__' not in self.target and '__' in other.target: + return True + # Prefer shorter target names first since they are usually the correct ones. + if len(self.target) < len(other.target): + return True + elif len(self.target) > len(other.target): + return False + # Use string comparison to get a stable ordering of equal-length names. + return self.target < other.target + + +@dataclasses.dataclass +class BuildConfig: + """Container for information from a build config.""" + target_name: str + relpath: str + is_group: bool + preferred_dep: bool + dependent_config_paths: List[str] + full_class_names: Set[str] + + def all_dependent_configs( + self, + path_to_configs: Dict[str, 'BuildConfig'], + ) -> Iterator['BuildConfig']: + for path in self.dependent_config_paths: + dep_build_config = path_to_configs.get(path) + # This can happen when a java group depends on non-java targets. + if dep_build_config is None: + continue + yield dep_build_config + if dep_build_config.is_group: + yield from dep_build_config.all_dependent_configs(path_to_configs) class ClassLookupIndex: @@ -155,13 +190,11 @@ return matches def _entries_for(self, class_name) -> List[ClassEntry]: - return [ - ClassEntry(class_name, target_info.target_name, - target_info.low_classpath_priority) - for target_info in self._class_index.get(class_name) - ] + class_entries = self._class_index.get(class_name) + assert class_entries is not None + return sorted(class_entries) - def _index_root(self) -> Dict[str, List[TargetInfo]]: + def _index_root(self) -> Dict[str, List[ClassEntry]]: """Create the class to target index.""" logging.debug('Running list_java_targets.py...') list_java_targets_command = [ @@ -179,57 +212,67 @@ check=True) logging.debug('... done.') - # Parse output of list_java_targets.py with mapping of build_target to - # build_config - root_build_targets = list_java_targets_run.stdout.split('\n') - class_index = collections.defaultdict(list) - for target_line in root_build_targets: + # Parse output of list_java_targets.py into BuildConfig objects. + path_to_build_config: Dict[str, BuildConfig] = {} + target_lines = list_java_targets_run.stdout.splitlines() + for target_line in target_lines: # Skip empty lines if not target_line: continue target_line_parts = target_line.split(': ') assert len(target_line_parts) == 2, target_line_parts - target, build_config_path = target_line_parts + target_name, build_config_path = target_line_parts if not os.path.exists(build_config_path): assert not self._should_build continue with open(build_config_path) as build_config_contents: - build_config: Dict = json.load(build_config_contents) - deps_info = build_config['deps_info'] + build_config_json: Dict = json.load(build_config_contents) + deps_info = build_config_json['deps_info'] + # Checking the library type here instead of in list_java_targets.py avoids # reading each .build_config file twice. - if deps_info['type'] != 'java_library': + if deps_info['type'] not in ('java_library', 'group'): continue - target = self._compute_toplevel_target(target) - low_classpath_priority = bool(deps_info.get('low_classpath_priority')) - target_info = TargetInfo(target, low_classpath_priority) + relpath = os.path.relpath(build_config_path, self._abs_build_output_dir) + preferred_dep = bool(deps_info.get('preferred_dep')) + is_group = bool(deps_info.get('type') == 'group') + dependent_config_paths = deps_info.get('deps_configs', []) full_class_names = self._compute_full_class_names_for_build_config( deps_info) - for full_class_name in full_class_names: - class_index[full_class_name].append(target_info) + build_config = BuildConfig(relpath=relpath, + target_name=target_name, + is_group=is_group, + preferred_dep=preferred_dep, + dependent_config_paths=dependent_config_paths, + full_class_names=full_class_names) + path_to_build_config[relpath] = build_config + + # From GN's perspective, depending on a java group is the same as depending + # on all of its deps directly, since groups are collapsed in + # write_build_config.py. Thus, collect all the java files in a java group's + # deps (recursing into other java groups) and set that as the java group's + # list of classes. + for build_config in path_to_build_config.values(): + if build_config.is_group: + for dep_build_config in build_config.all_dependent_configs( + path_to_build_config): + build_config.full_class_names.update( + dep_build_config.full_class_names) + + class_index = collections.defaultdict(list) + for build_config in path_to_build_config.values(): + for full_class_name in build_config.full_class_names: + class_index[full_class_name].append( + ClassEntry(full_class_name=full_class_name, + target=build_config.target_name, + preferred_dep=build_config.preferred_dep)) return class_index - @staticmethod - def _compute_toplevel_target(target: str) -> str: - """Computes top level target from the passed-in sub-target.""" - if target.endswith('_java'): - return target - - # Handle android_aar_prebuilt() sub targets. - index = target.find('_java__subjar') - if index >= 0: - return target[0:index + 5] - index = target.find('_java__classes') - if index >= 0: - return target[0:index + 5] - - return target - def _compute_full_class_names_for_build_config(self, deps_info: Dict) -> Set[str]: """Returns set of fully qualified class names for build config.""" @@ -334,7 +377,7 @@ """Guess the fully qualified class name from the path to the source file.""" if source_path.suffix not in ('.java', '.kt'): logging.warning(f'"{source_path}" does not end in .java or .kt.') - return None + return '' directory_path: pathlib.Path = source_path.parent package_list_reversed = [] @@ -347,7 +390,7 @@ else: logging.debug(f'File {source_path} not in a subdir of "org" or "com", ' 'cannot detect package heuristically.') - return None + return '' package = '.'.join(reversed(package_list_reversed)) class_name = source_path.stem
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index 1bebd7a..0046246 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -2737,7 +2737,7 @@ 'gpu_tests_release_trybot_no_symbols_mac_code_coverage_reclient': [ 'gpu_tests', 'release_trybot_reclient', 'no_symbols', 'use_clang_coverage', 'partial_code_coverage_instrumentation', - 'enable_dangling_raw_ptr_checks', 'enable_backup_ref_ptr_feature_flag', + 'enable_dangling_raw_ptr_feature_flag', ], # Keep in sync with
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.mac.json b/tools/mb/mb_config_expectations/tryserver.chromium.mac.json index 43ce100..41e7853 100644 --- a/tools/mb/mb_config_expectations/tryserver.chromium.mac.json +++ b/tools/mb/mb_config_expectations/tryserver.chromium.mac.json
@@ -543,6 +543,7 @@ "dcheck_always_on": true, "enable_backup_ref_ptr_feature_flag": true, "enable_dangling_raw_ptr_checks": true, + "enable_dangling_raw_ptr_feature_flag": true, "ffmpeg_branding": "Chrome", "is_component_build": false, "is_debug": false,
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml index 4835aca..0adb42a 100644 --- a/tools/metrics/actions/actions.xml +++ b/tools/metrics/actions/actions.xml
@@ -30625,6 +30625,13 @@ </description> </action> +<action name="Signin_Impression_FromCreatorFeedFollow"> + <owner>wry@chromium.org</owner> + <description> + Recorded when showing sign-in entry in Creator Feed on Android only. + </description> +</action> + <action name="Signin_Impression_FromDevicesPage" not_user_triggered="true"> <owner>gogerald@chromium.org</owner> <description> @@ -31241,6 +31248,15 @@ </description> </action> +<action name="Signin_Signin_FromCreatorFeedFollow"> + <owner>wry@chromium.org</owner> + <description> + Recorded on sign in start from access point + signin_metrics::AccessPoint::ACCESS_POINT_CREATOR_FEED_FOLLOW on Android + Only. + </description> +</action> + <action name="Signin_Signin_FromDevicesPage"> <owner>gogerald@chromium.org</owner> <description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 183e486e..316c375eb 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -32945,6 +32945,7 @@ <int value="1088" label="ClientSidePhishingProtectionAllowed"/> <int value="1089" label="UserAvatarCustomizationSelectorsEnabled"/> <int value="1090" label="DefaultThirdPartyStoragePartitioningSetting"/> + <int value="1091" label="ThirdPartyStoragePartitioningBlockedForOrigins"/> </enum> <enum name="EnterprisePoliciesSources"> @@ -36182,6 +36183,7 @@ <int value="1768" label="SMARTCARDPROVIDERPRIVATE_REPORTCONNECTRESULT"/> <int value="1769" label="SMARTCARDPROVIDERPRIVATE_REPORTDISCONNECTRESULT"/> <int value="1770" label="WMDESKSPRIVATE_GETDESKBYID"/> + <int value="1771" label="AUTOFILLPRIVATE_ISVALIDIBAN"/> </enum> <enum name="ExtensionIconState"> @@ -82382,6 +82384,7 @@ <int value="44" label="BrowserIdle"/> <int value="45" label="PrivacySandbox"/> <int value="46" label="KerberosPrefilledConfig"/> + <int value="47" label="ThirdPartyStoragePartitioningSettings"/> </enum> <enum name="PolicyLoadStatus"> @@ -95010,6 +95013,7 @@ <int value="44" label="Automatic reset to match account cookies, on Desktop only."/> <int value="45" label="'For You' First Run Experience on Desktop"/> + <int value="46" label="Creator Feed Follow Button on Android only"/> </enum> <enum name="SigninAccountReconcilorState"> @@ -99455,6 +99459,286 @@ <int value="14" label="Added to menu (deprecated)"/> </enum> +<enum name="SyncablePref"> + <int value="1" label="AutofillCreditCardEnabled"/> + <int value="2" label="AutofillEnabledDeprecated"/> + <int value="3" label="AutofillHasSeenIban"/> + <int value="4" label="AutofillIBANEnabled"/> + <int value="5" label="AutofillLastVersionDeduped"/> + <int value="6" label="AutofillLastVersionDisusedAddressesDeleted"/> + <int value="7" label="AutofillProfileEnabled"/> + <int value="8" label="ShowAppsShortcutInBookmarkBar"/> + <int value="9" label="ShowBookmarkBar"/> + <int value="10" label="ShowManagedBookmarksInBookmarkBar"/> + <int value="11" label="ClearBrowsingDataHistoryNoticeShownTimes"/> + <int value="12" label="DeleteBrowsingHistory"/> + <int value="13" label="DeleteBrowsingHistoryBasic"/> + <int value="14" label="DeleteCache"/> + <int value="15" label="DeleteCacheBasic"/> + <int value="16" label="DeleteCookies"/> + <int value="17" label="DeleteCookiesBasic"/> + <int value="18" label="DeleteDownloadHistory"/> + <int value="19" label="DeleteFormData"/> + <int value="20" label="DeleteHostedAppsData"/> + <int value="21" label="DeletePasswords"/> + <int value="22" label="DeleteSiteSettings"/> + <int value="23" label="DeleteTimePeriod"/> + <int value="24" label="DeleteTimePeriodBasic"/> + <int value="25" label="LastClearBrowsingDataTime"/> + <int value="26" label="PreferencesMigratedToBasic"/> + <int value="27" label="PriceEmailNotificationsEnabled"/> + <int value="28" label="Font"/> + <int value="29" label="OfferReaderMode"/> + <int value="30" label="ReaderForAccessibility"/> + <int value="31" label="Theme"/> + <int value="32" label="AcceptLanguages"/> + <int value="33" label="ApplicationLocale"/> + <int value="34" label="SelectedLanguages"/> + <int value="35" label="SyncDemographicsPrefName"/> + <int value="36" label="CustomLinksInitialized"/> + <int value="37" label="CustomLinksList"/> + <int value="38" label="KeywordSpaceTriggeringEnabled"/> + <int value="39" label="CredentialsEnableAutosignin"/> + <int value="40" label="CredentialsEnableService"/> + <int value="41" label="PasswordDismissCompromisedAlertEnabled"/> + <int value="42" label="PasswordLeakDetectionEnabled"/> + <int value="43" label="SyncedLastTimePasswordCheckCompleted"/> + <int value="44" label="WasAutoSignInFirstRunExperienceShown"/> + <int value="45" label="CanMakePaymentEnabled"/> + <int value="46" label="AccountTailoredSecurityUpdateTimestamp"/> + <int value="47" label="CookieControlsMode"/> + <int value="48" label="SafeBrowsingEnabled"/> + <int value="49" label="SyncedDefaultSearchProviderGUID"/> + <int value="50" label="PrefForceTriggerTranslateCount"/> + <int value="51" label="PrefNeverPromptSitesDeprecated"/> + <int value="52" label="PrefTranslateAcceptedCount"/> + <int value="53" label="PrefTranslateAutoAlwaysCount"/> + <int value="54" label="PrefTranslateAutoNeverCount"/> + <int value="55" label="PrefTranslateDeniedCount"/> + <int value="56" label="PrefTranslateIgnoredCount"/> + <int value="57" label="BlockedLanguages"/> + <int value="58" label="OfferTranslateEnabled"/> + <int value="59" label="PrefAlwaysTranslateList"/> + <int value="60" label="PrefNeverPromptSitesWithTime"/> + <int value="61" label="PrefTranslateRecentTarget"/> + <int value="100000" label="AppLanguagePromptShown"/> + <int value="100001" label="PrefExplicitLanguageAskShown"/> + <int value="100002" label="ContextualSearchEnabled_Android"/> + <int value="100003" label="ContextualSearchWasFullyPrivacyEnabled"/> + <int value="100004" label="AccessibilityImageLabelsEnabledAndroid"/> + <int value="100005" label="AccessibilityImageLabelsOnlyOnWifi"/> + <int value="100006" label="PromptForDownloadAndroid"/> + <int value="100007" label="AccessibilityReadAnythingFontName"/> + <int value="100008" label="AccessibilityReadAnythingFontScale"/> + <int value="100009" label="AccessibilityReadAnythingColorInfo"/> + <int value="100010" label="AccessibilityReadAnythingLineSpacing"/> + <int value="100011" label="AccessibilityReadAnythingLetterSpacing"/> + <int value="100012" label="LensRegionSearchEnabled"/> + <int value="100013" label="HatsSurveyMetadata"/> + <int value="100014" label="HomePage"/> + <int value="100015" label="HomePageIsNewTabPage"/> + <int value="100016" label="NtpCustomBackgroundDict"/> + <int value="100017" label="LiveCaptionBubbleExpanded"/> + <int value="100018" label="LiveCaptionBubblePinned"/> + <int value="100019" label="LiveCaptionEnabled"/> + <int value="100020" label="LiveCaptionLanguageCode"/> + <int value="100021" label="LiveCaptionMediaFoundationRendererErrorSilenced"/> + <int value="100022" label="ShowHomeButton"/> + <int value="100023" label="PinnedExtensions"/> + <int value="100024" label="SupervisedUserApprovedExtensions"/> + <int value="100025" label="IsDefaultPageColorsOnHighContrast"/> + <int value="100026" label="ShowFullscreenToolbar"/> + <int value="100027" label="AllowJavascriptAppleEvents"/> + <int value="100028" label="RestoreAppsAndPagesPrefName"/> + <int value="100029" label="AccessibilityAutoclickDelayMs"/> + <int value="100030" label="AccessibilityAutoclickEventType"/> + <int value="100031" label="AccessibilityAutoclickMenuPosition"/> + <int value="100032" label="AccessibilityAutoclickMovementThreshold"/> + <int value="100033" label="AccessibilityAutoclickRevertToLeftClick"/> + <int value="100034" label="AccessibilityAutoclickStabilizePosition"/> + <int value="100035" label="AccessibilityCursorColor"/> + <int value="100036" + label="AccessibilityEnhancedNetworkVoicesInSelectToSpeakAllowed"/> + <int value="100037" label="AccessibilityFloatingMenuPosition"/> + <int value="100038" label="AccessibilityGreyscaleAmount"/> + <int value="100039" label="AccessibilityHueRotationAmount"/> + <int value="100040" label="AccessibilitySaturationAmount"/> + <int value="100041" label="AccessibilityScreenMagnifierCenterFocus"/> + <int value="100042" + label="AccessibilityScreenMagnifierFocusFollowingEnabled"/> + <int value="100043" label="AccessibilityScreenMagnifierMouseFollowingMode"/> + <int value="100044" label="AccessibilitySelectToSpeakBackgroundShading"/> + <int value="100045" label="AccessibilitySelectToSpeakEnhancedNetworkVoices"/> + <int value="100046" label="AccessibilitySelectToSpeakEnhancedVoiceName"/> + <int value="100047" + label="AccessibilitySelectToSpeakEnhancedVoicesDialogShown"/> + <int value="100048" label="AccessibilitySelectToSpeakHighlightColor"/> + <int value="100049" label="AccessibilitySelectToSpeakNavigationControls"/> + <int value="100050" label="AccessibilitySelectToSpeakVoiceName"/> + <int value="100051" label="AccessibilitySelectToSpeakVoiceSwitching"/> + <int value="100052" label="AccessibilitySelectToSpeakWordHighlight"/> + <int value="100053" label="AccessibilitySepiaAmount"/> + <int value="100054" label="AccessibilitySwitchAccessAutoScanEnabled"/> + <int value="100055" label="AccessibilitySwitchAccessAutoScanKeyboardSpeedMs"/> + <int value="100056" label="AccessibilitySwitchAccessAutoScanSpeedMs"/> + <int value="100057" label="AccessibilitySwitchAccessNextDeviceKeyCodes"/> + <int value="100058" + label="AccessibilitySwitchAccessPointScanSpeedDipsPerSecond"/> + <int value="100059" label="AccessibilitySwitchAccessPreviousDeviceKeyCodes"/> + <int value="100060" label="AccessibilitySwitchAccessSelectDeviceKeyCodes"/> + <int value="100061" label="AppNotificationBadgingEnabled"/> + <int value="100062" label="EnableAutoScreenLock"/> + <int value="100063" label="EnableStylusTools"/> + <int value="100064" label="FilesAppFolderShortcuts"/> + <int value="100065" label="FilesAppTrashEnabled"/> + <int value="100066" label="FilesAppUIPrefsMigrated"/> + <int value="100067" label="LaunchPaletteOnEjectEvent"/> + <int value="100068" label="LauncherContinueSectionHidden"/> + <int value="100069" label="LauncherFeedbackOnContinueSectionSent"/> + <int value="100070" label="LauncherResultEverLaunched"/> + <int value="100071" label="MessageCenterLockScreenMode"/> + <int value="100072" label="MouseAcceleration"/> + <int value="100073" label="MouseReverseScroll"/> + <int value="100074" label="MouseScrollAcceleration"/> + <int value="100075" label="MouseScrollSensitivity"/> + <int value="100076" label="MouseSensitivity"/> + <int value="100077" label="NaturalScroll"/> + <int value="100078" label="OobeMarketingOptInChoice"/> + <int value="100079" label="OobeMarketingOptInScreenFinished"/> + <int value="100080" label="PointingStickAcceleration"/> + <int value="100081" label="PointingStickSensitivity"/> + <int value="100082" label="PowerAdaptiveChargingEnabled"/> + <int value="100083" label="PowerAdaptiveChargingNudgeShown"/> + <int value="100084" label="PrimaryMouseButtonRight"/> + <int value="100085" label="PrimaryPointingStickButtonRight"/> + <int value="100086" label="ProjectorAnnotatorLastUsedMarkerColor"/> + <int value="100087" label="ProjectorCreationFlowEnabled"/> + <int value="100088" label="ProjectorCreationFlowLanguage"/> + <int value="100089" label="ProjectorGalleryOnboardingShowCount"/> + <int value="100090" label="ProjectorViewerOnboardingShowCount"/> + <int value="100091" label="ShelfAlignment"/> + <int value="100092" label="ShelfAutoHideBehavior"/> + <int value="100093" label="SuggestedContentEnabled"/> + <int value="100094" label="SyncableWallpaperInfo"/> + <int value="100095" label="TapDraggingEnabled"/> + <int value="100096" label="TapToClickEnabled"/> + <int value="100097" label="TouchpadAcceleration"/> + <int value="100098" label="TouchpadHapticClickSensitivity"/> + <int value="100099" label="TouchpadHapticFeedback"/> + <int value="100100" label="TouchpadScrollAcceleration"/> + <int value="100101" label="TouchpadScrollSensitivity"/> + <int value="100102" label="TouchpadSensitivity"/> + <int value="100103" label="XkbAutoRepeatDelay"/> + <int value="100104" label="XkbAutoRepeatEnabled"/> + <int value="100105" label="XkbAutoRepeatInterval"/> + <int value="100106" label="MostRecentConnectTetheringResponderIds"/> + <int value="100107" label="MostRecentTetherAvailablilityResponderIds"/> + <int value="100108" label="GuestOsTerminalSettings"/> + <int value="100109" label="PreferredLanguagesSyncable"/> + <int value="100110" label="SyncOsDemographicsPrefName"/> + <int value="100111" label="AppListPreferredOrder"/> + <int value="100112" label="ChromeOSReleaseNotesVersion"/> + <int value="100113" label="LanguageEnabledImesSyncable"/> + <int value="100114" label="LanguagePreloadEnginesSyncable"/> + <int value="100115" label="LanguageRemapAltKeyTo"/> + <int value="100116" label="LanguageRemapAssistantKeyTo"/> + <int value="100117" label="LanguageRemapBackspaceKeyTo"/> + <int value="100118" label="LanguageRemapCapsLockKeyTo"/> + <int value="100119" label="LanguageRemapControlKeyTo"/> + <int value="100120" label="LanguageRemapEscapeKeyTo"/> + <int value="100121" label="LanguageRemapExternalCommandKeyTo"/> + <int value="100122" label="LanguageRemapExternalMetaKeyTo"/> + <int value="100123" label="LanguageRemapSearchKeyTo"/> + <int value="100124" label="MultiProfileNeverShowIntro"/> + <int value="100125" label="MultiProfileWarningShowDismissed"/> + <int value="100126" label="OfficeSetupComplete"/> + <int value="100127" label="ResolveTimezoneByGeolocationMethod"/> + <int value="100128" label="ResolveTimezoneByGeolocationMigratedToMethod"/> + <int value="100129" label="ShelfDefaultPinLayoutRolls"/> + <int value="100130" label="TextToSpeechLangToVoiceName"/> + <int value="100131" label="TextToSpeechPitch"/> + <int value="100132" label="TextToSpeechRate"/> + <int value="100133" label="TextToSpeechVolume"/> + <int value="100134" label="Use24HourClock"/> + <int value="100135" label="UserPrintersAllowed"/> + <int value="100136" label="ProximityAuthIsChromeOSLoginEnabled"/> + <int value="100137" label="UserImageInfo"/> + <int value="100138" label="GdataDisabled"/> + <int value="100139" label="GdataCellularDisabled"/> + <int value="100140" label="AlternateErrorPagesEnabled"/> + <int value="100141" label="TabDiscardingExceptions"/> + <int value="100142" label="AccessibilityImageLabelsEnabled"/> + <int value="100143" label="AccessibilityImageLabelsOptInAccepted"/> + <int value="100144" label="AccessibilityPdfOcrAlwaysActive"/> + <int value="100145" label="ApplyPageColorsOnlyOnIncreasedContrast"/> + <int value="100146" label="DefaultCharset"/> + <int value="100147" label="DefaultTasksByMimeType"/> + <int value="100148" label="DefaultTasksBySuffix"/> + <int value="100149" label="DevToolsSyncPreferences"/> + <int value="100150" label="DevToolsSyncedPreferencesSyncEnabled"/> + <int value="100151" label="DownloadBubbleIphSuppression"/> + <int value="100152" label="EnableDoNotTrack"/> + <int value="100153" label="ExtensionCommands"/> + <int value="100154" label="ExtensionsUIDeveloperMode"/> + <int value="100155" label="HttpsOnlyModeEnabled"/> + <int value="100156" label="LiveTranslateEnabled"/> + <int value="100157" label="LiveTranslateTargetLanguageCode"/> + <int value="100158" label="NetworkEasterEggHighScore"/> + <int value="100159" label="NetworkPredictionOptions"/> + <int value="100160" label="NetworkQualities"/> + <int value="100161" label="NtpAppPageNames"/> + <int value="100162" label="PageColors"/> + <int value="100163" label="PerformanceTracingEnabled"/> + <int value="100164" label="PluginsAlwaysOpenPdfExternally"/> + <int value="100165" label="PrivacySandboxApisEnabled"/> + <int value="100166" label="PrivacySandboxFirstPartySetsEnabled"/> + <int value="100167" label="PrivacySandboxManuallyControlled"/> + <int value="100168" label="PromptForDownload"/> + <int value="100169" label="ProtectedContentDefault"/> + <int value="100170" label="RestoreOnStartup"/> + <int value="100171" label="SearchSuggestEnabled"/> + <int value="100172" label="SharingVapidKey"/> + <int value="100173" label="URLsToRestoreOnStartup"/> + <int value="100174" label="SpellCheckEnable"/> + <int value="100175" label="ProfileContentSettingsExceptionsAntiAbuse"/> + <int value="100176" + label="ProfileContentSettingsExceptionsAutomaticDownloads"/> + <int value="100177" label="ProfileContentSettingsExceptionsCookies"/> + <int value="100178" + label="ProfileContentSettingsExceptionsGetDisplayMediaSetSelectAllScreens"/> + <int value="100179" label="ProfileContentSettingsExceptionsImages"/> + <int value="100180" label="ProfileContentSettingsExceptionsJavascript"/> + <int value="100181" label="ProfileContentSettingsExceptionsLocalFonts"/> + <int value="100182" label="ProfileContentSettingsExceptionsMouselock"/> + <int value="100183" label="ProfileContentSettingsExceptionsPopups"/> + <int value="100184" label="ProfileContentSettingsExceptionsWindowPlacement"/> + <int value="100185" label="ProfileDefaultContentSettingValuesAntiAbuse"/> + <int value="100186" + label="ProfileDefaultContentSettingValuesAutomaticDownloads"/> + <int value="100187" label="ProfileDefaultContentSettingValuesCookies"/> + <int value="100188" + label="ProfileDefaultContentSettingValuesGetDisplayMediaSetSelectAllScreens"/> + <int value="100189" label="ProfileDefaultContentSettingValuesImages"/> + <int value="100190" label="ProfileDefaultContentSettingValuesJavascript"/> + <int value="100191" label="ProfileDefaultContentSettingValuesLocalFonts"/> + <int value="100192" label="ProfileDefaultContentSettingValuesMouselock"/> + <int value="100193" label="ProfileDefaultContentSettingValuesPopups"/> + <int value="100194" + label="ProfileDefaultContentSettingValuesWindowPlacement"/> + <int value="100195" label="WebauthnCablev2Pairings"/> + <int value="200000" label="ArticlesForYouEnabled"/> + <int value="200001" label="ContextualSearchEnabled_IOS"/> + <int value="200002" label="DefaultCharset_IOS"/> + <int value="200003" label="EnableDoNotTrack_IOS"/> + <int value="200004" label="IosHandoffToOtherDevices"/> + <int value="200005" label="NetworkPredictionSetting"/> + <int value="200006" label="NTPContentSuggestionsEnabled"/> + <int value="200007" label="NTPContentSuggestionsForSupervisedUserEnabled"/> + <int value="200008" label="SearchSuggestEnabled_IOS"/> + <int value="200009" label="TrackPricesOnTabsEnabled"/> + <int value="200010" label="VoiceSearchLocale"/> +</enum> + <enum name="SyncAttachmentStoreResult"> <int value="0" label="SUCCESS"/> <int value="1" label="UNSPECIFIED_ERROR"/>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml index 1e881b82f..c06fd79 100644 --- a/tools/metrics/histograms/metadata/android/histograms.xml +++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -1344,7 +1344,7 @@ <histogram name="Android.FamilyLinkUser.LocalWebApprovalParentAuthenticationError" - units="Status error codes raised in GMS Core" expires_after="2023-03-26"> + units="Status error codes raised in GMS Core" expires_after="2023-06-13"> <owner>anthie@google.com</owner> <owner>ljjlee@google.com</owner> <owner>chrome-kids-eng@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/arc/histograms.xml b/tools/metrics/histograms/metadata/arc/histograms.xml index c6893dc..33d4dbb 100644 --- a/tools/metrics/histograms/metadata/arc/histograms.xml +++ b/tools/metrics/histograms/metadata/arc/histograms.xml
@@ -2353,6 +2353,43 @@ </summary> </histogram> +<histogram name="Arc.VmDataMigration.NotificationShownForTheFirstTime" + enum="BooleanEnabled" expires_after="2024-01-01"> + <owner>momohatt@google.com</owner> + <owner>youkichihosoi@google.com</owner> + <owner>arc-storage@google.com</owner> + <summary> + Counts the number of events that the notification for ARCVM /data migration + is shown for the first first time for the user. + </summary> +</histogram> + +<histogram + name="Arc.VmDataMigration.RemainingDays.{ArcVmDataMigrationFrontEndEvents}" + units="days" expires_after="2024-01-01"> + <owner>momohatt@google.com</owner> + <owner>youkichihosoi@google.com</owner> + <owner>arc-storage@google.com</owner> + <summary> + The number of days until the deadline displayed in the notification or the + confirmation dialog of ARCVM /data migration. The value is initialized as 30 + when the notification is shown for the first time, and decremented until it + reaches the minimum value 1. When it reaches the minimum value, it means + that the migration should be performed in the same day and should not be + dismissed, so the notification and the dialog become nondismissible. + Reported when {ArcVmDataMigrationFrontEndEvents}. + </summary> + <token key="ArcVmDataMigrationFrontEndEvents"> + <variant name="ConfirmationDialogAccepted" + summary="the accept button of the confirmation dialog is clicked"/> + <variant name="ConfirmationDialogCanceled" + summary="the cancel button of the confirmation dialog is clicked"/> + <variant name="ConfirmationDialogShown" + summary="the confirmation dialog is shown"/> + <variant name="NotificationShown" summary="the notification is shown"/> + </token> +</histogram> + <histogram name="Arc.VmDataMigration.SetupResult" enum="ArcVmDataMigrationSetupResult" expires_after="2024-01-01"> <owner>momohatt@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/browser/histograms.xml b/tools/metrics/histograms/metadata/browser/histograms.xml index 1ff4f39..3a2198be 100644 --- a/tools/metrics/histograms/metadata/browser/histograms.xml +++ b/tools/metrics/histograms/metadata/browser/histograms.xml
@@ -544,13 +544,13 @@ </histogram> <histogram name="Browser.Responsiveness.JankyIntervalsPerThirtySeconds3{Phase}" - units="janks" expires_after="never"> -<!-- expires-never: guiding metric (internal: go/chrome-browser-guiding-metrics) --> - + units="janks" expires_after="M111"> <owner>etienneb@chromium.org</owner> <owner>gab@chromium.org</owner> <owner>chrome-analysis-team@google.com</owner> <summary> + Replaced by Browser.MainThreadsCongestion as a guardrail metric. + This metric is emitted every 30 seconds after main message loop start, when there is user activity. Each 30 second duration is divided into 100ms intervals. This metric counts the number of these intervals that were @@ -576,12 +576,14 @@ <histogram name="Browser.Responsiveness.JankyIntervalsPerThirtySeconds{Phase}" units="janks" expires_after="M111"> -<!-- to be replaced by Browser.MainThreadsCongestion as a go/chrome-browser-guiding-metrics) --> - <owner>gab@chromium.org</owner> <owner>etienneb@chromium.org</owner> <owner>chrome-analysis-team@google.com</owner> <summary> + Replaced by Browser.MainThreadsCongestion as a guardrail metric. + Browser.MainThreadsCongestion.RunningOnly still represents what this used + to. + This metric is emitted every 30 seconds [when there is user activity]. Each 30 second duration is divided into 100ms intervals. This metric counts the number of these intervals that were "janky". See
diff --git a/tools/metrics/histograms/metadata/chromeos_settings/histograms.xml b/tools/metrics/histograms/metadata/chromeos_settings/histograms.xml index 6ad53cb4..35d847a 100644 --- a/tools/metrics/histograms/metadata/chromeos_settings/histograms.xml +++ b/tools/metrics/histograms/metadata/chromeos_settings/histograms.xml
@@ -231,7 +231,7 @@ </histogram> <histogram name="ChromeOS.Settings.LoadCompletedTime" units="ms" - expires_after="2023-04-17"> + expires_after="2024-03-14"> <owner>xiaohuic@chromium.org</owner> <owner>cros-settings@google.com</owner> <summary> @@ -242,7 +242,7 @@ </histogram> <histogram name="ChromeOS.Settings.LoadDocumentTime" units="ms" - expires_after="2023-04-17"> + expires_after="2024-03-14"> <owner>xiaohuic@chromium.org</owner> <owner>cros-settings@google.com</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/network/histograms.xml b/tools/metrics/histograms/metadata/network/histograms.xml index a35f354..abe5417f 100644 --- a/tools/metrics/histograms/metadata/network/histograms.xml +++ b/tools/metrics/histograms/metadata/network/histograms.xml
@@ -569,6 +569,10 @@ <histogram name="Network.CacheTransparency.MarkedUnusable" units="list index" expires_after="2023-09-10"> + <obsolete> + Removed as of 03/2023, as it is replaced by Network.CacheTransparency2. + MarkedUnusable which has a higher range. + </obsolete> <owner>nidhijaju@chromium.org</owner> <owner>ricea@chromium.org</owner> <summary> @@ -580,6 +584,10 @@ <histogram name="Network.CacheTransparency.MismatchedChecksums" units="list index" expires_after="2023-09-10"> + <obsolete> + Removed as of 03/2023, as it is replaced by Network.CacheTransparency2. + MismatchedChecksums which has a higher range. + </obsolete> <owner>nidhijaju@chromium.org</owner> <owner>ricea@chromium.org</owner> <summary> @@ -592,6 +600,10 @@ <histogram name="Network.CacheTransparency.SingleKeyedCacheIsUsed" units="list index" expires_after="2023-09-10"> + <obsolete> + Removed as of 03/2023, as it is replaced by Network.CacheTransparency2. + SingleKeyedCacheIsUsed which has a higher range. + </obsolete> <owner>nidhijaju@chromium.org</owner> <owner>ricea@chromium.org</owner> <summary> @@ -605,6 +617,57 @@ <histogram name="Network.CacheTransparency.URLMatched" units="list index" expires_after="2023-09-10"> + <obsolete> + Removed as of 03/2023, as it is replaced by + Network.CacheTransparency2.URLMatched which has a higher range. + </obsolete> + <owner>nidhijaju@chromium.org</owner> + <owner>ricea@chromium.org</owner> + <summary> + Records whether a requested resource's URL matched with any of the URLs in + the pervasive payloads list when a URL request is made. The list index is + the index of the URL in the pervasive payloads list. + </summary> +</histogram> + +<histogram name="Network.CacheTransparency2.MarkedUnusable" units="list index" + expires_after="2023-11-01"> + <owner>nidhijaju@chromium.org</owner> + <owner>ricea@chromium.org</owner> + <summary> + Records whether a URL in the pervasive payloads list is marked as + "unusable" when the cached response is read. The list index is the + index of the URL in the pervasive payloads list. + </summary> +</histogram> + +<histogram name="Network.CacheTransparency2.MismatchedChecksums" + units="list index" expires_after="2023-11-01"> + <owner>nidhijaju@chromium.org</owner> + <owner>ricea@chromium.org</owner> + <summary> + Records whether a URL in the pervasive payloads list has a different + checksum than what is expected when the response checksums are compared in + HttpCache::Transaction. The list index is the index of the URL in the + pervasive payloads list. + </summary> +</histogram> + +<histogram name="Network.CacheTransparency2.SingleKeyedCacheIsUsed" + units="list index" expires_after="2023-11-01"> + <owner>nidhijaju@chromium.org</owner> + <owner>ricea@chromium.org</owner> + <summary> + Records whether the single-keyed cache is used for a URL in the pervasive + payloads list (not just when a cache entry is hit because there could be an + "unusable" flag set). This is recorded when the response checksum + matches the expected checksum in HttpCache::Transaction. The list index is + the index of the URL in the pervasive payloads list. + </summary> +</histogram> + +<histogram name="Network.CacheTransparency2.URLMatched" units="list index" + expires_after="2023-11-01"> <owner>nidhijaju@chromium.org</owner> <owner>ricea@chromium.org</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/profile/histograms.xml b/tools/metrics/histograms/metadata/profile/histograms.xml index 6cc68f44..02c2daf0 100644 --- a/tools/metrics/histograms/metadata/profile/histograms.xml +++ b/tools/metrics/histograms/metadata/profile/histograms.xml
@@ -814,27 +814,6 @@ </summary> </histogram> -<histogram name="ProfilePicker.NewProfileCreateShortcut" enum="BooleanCreated" - expires_after="M90"> - <owner>msalama@chromium.org</owner> - <owner>chrome-signin-team@google.com</owner> - <summary> - Whether the user picked to create a desktop shortcut for the new profile - created through the local profile creation in the profile picker. Recorded - on platforms where desktop shortcut is enabled. - </summary> -</histogram> - -<histogram name="ProfilePicker.NewProfileTheme" enum="ChromeColorsInfo" - expires_after="2021-10-25"> - <owner>msalama@chromium.org</owner> - <owner>chrome-signin-team@google.com</owner> - <summary> - Records the theme color of a new profile just created through the profile - picker local profile creation. - </summary> -</histogram> - <histogram name="ProfilePicker.Shown" enum="ProfilePickerEntryPoint" expires_after="2023-08-08"> <owner>msalama@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/sync/histograms.xml b/tools/metrics/histograms/metadata/sync/histograms.xml index 7fd1dec..609bb698 100644 --- a/tools/metrics/histograms/metadata/sync/histograms.xml +++ b/tools/metrics/histograms/metadata/sync/histograms.xml
@@ -1294,6 +1294,17 @@ </summary> </histogram> +<histogram name="Sync.SyncablePrefValueChanged" enum="SyncablePref" + expires_after="2024-01-31"> + <owner>ankushkush@google.com</owner> + <owner>treib@chromium.org</owner> + <component>Services>Sync</component> + <summary> + This is recorded everytime a syncable pref is changed locally. This does not + include changes sent during the initial sync/merge. + </summary> +</histogram> + <histogram name="Sync.SyncedHistoryFaviconAvailability.{RequestOrigin}" enum="FaviconAvailabilityStatus" expires_after="2023-07-10"> <owner>victorvianna@google.com</owner>
diff --git a/tools/metrics/structured/structured.xml b/tools/metrics/structured/structured.xml index a7e51ae..96416cc 100644 --- a/tools/metrics/structured/structured.xml +++ b/tools/metrics/structured/structured.xml
@@ -10,6 +10,27 @@ Metrics for the ChromeOS Fast Pair implementation. </summary> + <event name="DiscoveryNotificationShown"> + <summary> + The user is shown a notification which can start the pairing process. + </summary> + <metric name="Protocol" type="int"> + <summary> + The pairing protocol of the discovered device. + </summary> + </metric> + <metric name="FastPairVersion" type="int"> + <summary> + The Fast Pair spec version the device implements. + </summary> + </metric> + <metric name="ModelId" type="int"> + <summary> + The model ID of the discovered device. + </summary> + </metric> + </event> + <event name="PairingStart"> <summary> The starting of the pairing process.
diff --git a/tools/perf/chrome-health-presets.yaml b/tools/perf/chrome-health-presets.yaml index a576def..ce1dcbff 100644 --- a/tools/perf/chrome-health-presets.yaml +++ b/tools/perf/chrome-health-presets.yaml
@@ -13,7 +13,7 @@ - mac-m1_mini_2020-perf - linux-perf - win-10-perf - - android-pixel4-perf + - android-pixel6-perf-pgo stories: - Speedometer2 - benchmark: rendering.desktop @@ -34,7 +34,7 @@ - motionmark_ramp_suits - benchmark: rendering.mobile configs: - - android-pixel4-perf + - android-pixel6-perf-pgo stories: - motionmark_ramp_canvas_arcs - motionmark_ramp_canvas_lines @@ -55,7 +55,7 @@ - motionmark_ramp_composite - benchmark: rendering.mobile.notracing configs: - - android-pixel4-perf + - android-pixel6-perf-pgo stories: - motionmark_ramp_composite - benchmark: jetstream2 @@ -64,7 +64,7 @@ - mac-m1_mini_2020-perf - linux-perf - win-10-perf - - android-pixel4-perf + - android-pixel6-perf-pgo stories: - JetStream2 chrome_health_pgo: @@ -75,7 +75,7 @@ - mac-m1_mini_2020-perf-pgo - linux-perf-pgo - win-10-perf-pgo - - android-pixel4-perf + - android-pixel6-perf-pgo stories: - Speedometer2 - benchmark: rendering.desktop @@ -95,7 +95,7 @@ - motionmark_ramp_suits - benchmark: rendering.mobile configs: - - android-pixel4-perf + - android-pixel6-perf-pgo stories: - motionmark_ramp_canvas_arcs - motionmark_ramp_canvas_lines @@ -115,7 +115,7 @@ - motionmark_ramp_composite - benchmark: rendering.mobile.notracing configs: - - android-pixel4-perf + - android-pixel6-perf-pgo stories: - motionmark_ramp_composite - benchmark: jetstream2 @@ -124,18 +124,47 @@ - mac-m1_mini_2020-perf-pgo - linux-perf-pgo - win-10-perf-pgo - - android-pixel4-perf + - android-pixel6-perf-pgo + stories: + - JetStream2 + android_only: + telemetry_batch_experiment: + - benchmark: speedometer2 + configs: + - android-pixel6-perf-pgo + stories: + - Speedometer2 + - benchmark: rendering.mobile + configs: + - android-pixel6-perf-pgo + stories: + - motionmark_ramp_canvas_arcs + - motionmark_ramp_canvas_lines + - motionmark_ramp_design + - motionmark_ramp_images + - motionmark_ramp_leaves + - motionmark_ramp_multiply + - motionmark_ramp_paths + - motionmark_ramp_suits + - benchmark: rendering.mobile.notracing + configs: + - android-pixel6-perf-pgo + stories: + - motionmark_ramp_composite + - benchmark: jetstream2 + configs: + - android-pixel6-perf-pgo stories: - JetStream2 speedometer2: telemetry_batch_experiment: - - benchmark: speedometer2-chrome-health + - benchmark: speedometer2 configs: - mac-laptop_low_end-perf - mac-m1_mini_2020-perf - linux-perf - win-10-perf - - android-pixel4-perf + - android-pixel6-perf-pgo stories: - Speedometer2 speedometer2_pgo: @@ -146,9 +175,53 @@ - mac-m1_mini_2020-perf-pgo - linux-perf-pgo - win-10-perf-pgo - - android-pixel4-perf + - android-pixel6-perf-pgo stories: - Speedometer2 + motionmark_pgo: + telemetry_batch_experiment: + - benchmark: rendering.desktop + configs: + - mac-laptop_low_end-perf-pgo + - mac-m1_mini_2020-perf-pgo + - linux-perf-pgo + - win-10-perf-pgo + stories: + - motionmark_ramp_canvas_arcs + - motionmark_ramp_canvas_lines + - motionmark_ramp_design + - motionmark_ramp_focus + - motionmark_ramp_images + - motionmark_ramp_leaves + - motionmark_ramp_multiply + - motionmark_ramp_paths + - motionmark_ramp_suits + - benchmark: rendering.mobile + configs: + - android-pixel6-perf-pgo + stories: + - motionmark_ramp_canvas_arcs + - motionmark_ramp_canvas_lines + - motionmark_ramp_design + - motionmark_ramp_focus + - motionmark_ramp_images + - motionmark_ramp_leaves + - motionmark_ramp_multiply + - motionmark_ramp_paths + - motionmark_ramp_suits + - benchmark: rendering.desktop.notracing + configs: + - mac-laptop_low_end-perf-pgo + - mac-m1_mini_2020-perf-pgo + - linux-perf-pgo + - win-10-perf-pgo + stories: + - motionmark_ramp_composite + - benchmark: rendering.mobile.notracing + configs: + - android-pixel6-perf-pgo + stories: + - motionmark_ramp_composite motionmark: telemetry_batch_experiment: - benchmark: rendering.desktop @@ -169,7 +242,7 @@ - motionmark_ramp_suits - benchmark: rendering.mobile configs: - - android-pixel4-perf + - android-pixel6-perf-pgo stories: - motionmark_ramp_canvas_arcs - motionmark_ramp_canvas_lines @@ -190,7 +263,7 @@ - motionmark_ramp_composite - benchmark: rendering.mobile.notracing configs: - - android-pixel4-perf + - android-pixel6-perf-pgo stories: - motionmark_ramp_composite motionmark-composite-pgo: @@ -205,7 +278,7 @@ - motionmark_ramp_composite - benchmark: rendering.mobile.notracing configs: - - android-pixel4-perf + - android-pixel6-perf-pgo stories: - motionmark_ramp_composite jetstream2: @@ -216,7 +289,7 @@ - mac-m1_mini_2020-perf - linux-perf - win-10-perf - - android-pixel4-perf + - android-pixel6-perf-pgo stories: - JetStream2 jetstream2-pgo: @@ -227,182 +300,91 @@ - mac-m1_mini_2020-perf-pgo - linux-perf-pgo - win-10-perf-pgo - - android-pixel4-perf + - android-pixel6-perf-pgo stories: - JetStream2 - cwv-ch: + mac-laptop-pgo: telemetry_batch_experiment: - - benchmark: loading.desktop + - benchmark: speedometer2 configs: - - mac-laptop_low_end-perf - - mac-m1_mini_2020-perf - - linux-perf - - win-10-perf + - mac-laptop_low_end-perf-pgo stories: - - AirBnB_cold - - AllRecipes_cold - - benchmark: loading.mobile + - Speedometer2 + - benchmark: jetstream2 configs: - - android-pixel4-perf + - mac-laptop_low_end-perf-pgo stories: - - Amazon - - Dailymotion - cwv-all: + - JetStream2 + - benchmark: rendering.desktop.notracing + configs: + - mac-laptop_low_end-perf-pgo + stories: + - motionmark_ramp_composite + mac-m1-pgo: telemetry_batch_experiment: - - benchmark: loading.desktop + - benchmark: speedometer2 configs: - - mac-laptop_low_end-perf - - mac-m1_mini_2020-perf - - win-10-perf + - mac-m1_mini_2020-perf-pgo stories: - - 24h_cold - - 24h_warm - - AirBnB_cold - - AirBnB_warm - - Aljayyash_cold - - Aljayyash_warm - - AllRecipes_cold - - AllRecipes_warm - - ArsTechnica_cold - - ArsTechnica_warm - - Baidu_cold - - Baidu_warm - - Bhaskar_cold - - Bhaskar_warm - - Chosun_cold - - Chosun_warm - - Colorado.edu_cold - - Colorado.edu_warm - - Danawa_cold - - Danawa_warm - - Daum_cold - - Daum_warm - - Donga_cold - - Donga_warm - - Economist_cold - - Economist_warm - - Elmundo_cold - - Elmundo_warm - - FC2Blog_cold - - FC2Blog_warm - - FIFA_cold - - FIFA_warm - - FarsNews_cold - - FarsNews_warm - - Flickr_cold - - Flickr_warm - - FlipKart_cold - - FlipKart_warm - - Free.fr_cold - - Free.fr_warm - - HTML5Rocks_cold - - HTML5Rocks_warm - - Haraj_cold - - Haraj_warm - - HatenaBookmark_cold - - HatenaBookmark_warm - - IGN_cold - - IGN_warm - - IMDB_cold - - IMDB_warm - - IndiaTimes_cold - - IndiaTimes_warm - - Kakaku_cold - - Kakaku_warm - - Kenh14_cold - - Kenh14_warm - - Mercadolivre_cold - - Mercadolivre_warm - - Naver_cold - - Naver_warm - - Orange_cold - - Orange_warm - - Pantip_cold - - Pantip_warm - - PremierLeague_cold - - PremierLeague_warm - - QQ_cold - - QQ_warm - - REI_cold - - REI_warm - - Ruten_cold - - Ruten_warm - - Sina_cold - - Sina_warm - - Taobao_cold - - Taobao_warm - - TheOnion_cold - - TheOnion_warm - - TheVerge_cold - - TheVerge_warm - - TicketMaster_cold - - TicketMaster_warm - - Vietnamnet_cold - - Vietnamnet_warm - - Vnexpress_cold - - Vnexpress_warm - - Yandex_cold - - Yandex_warm - - amazon.co.jp_cold - - amazon.co.jp_warm - - ja.wikipedia_cold - - ja.wikipedia_warm - - money.cnn_cold - - money.cnn_warm - - ru.wikipedia_cold - - ru.wikipedia_warm - - uol.com.br_cold - - uol.com.br_warm - - yahoo.co.jp_cold - - yahoo.co.jp_warm - - benchmark: loading.mobile + - Speedometer2 + - benchmark: jetstream2 configs: - - android-pixel4-perf + - mac-m1_mini_2020-perf-pgo stories: - - 58Pic - - Amazon - - BOLNoticias - - Baidu - - Bradesco - - Dailymotion - - Dawn - - DevOpera_cold - - DevOpera_hot - - DevOpera_warm - - Dramaq - - EnquiryIndianRail - - Facebook - - FlipBoard_cold - - FlipBoard_hot - - FlipBoard_warm - - FlipKart_cold - - FlipKart_hot - - FlipKart_warm - - FranceTVInfo - - G1 - - GSShop - - GoogleBrazil - - GoogleIndia - - GoogleIndonesia - - GoogleRedirectToGoogleJapan - - Hongkiat - - KapanLagi - - Kaskus - - LocalMoxie - - Locanto - - OLX - - QQNews - - SlideShare - - Suumo_cold - - Suumo_hot - - Suumo_warm - - Thairath - - TheStar - - TribunNews - - Twitter - - VoiceMemos_cold - - VoiceMemos_hot - - VoiceMemos_warm - - Wikipedia - - YahooNews - - Youtube + - JetStream2 + - benchmark: rendering.desktop.notracing + configs: + - mac-m1_mini_2020-perf-pgo + stories: + - motionmark_ramp_composite + linux-pgo: + telemetry_batch_experiment: + - benchmark: speedometer2 + configs: + - linux-perf-pgo + stories: + - Speedometer2 + - benchmark: jetstream2 + configs: + - linux-perf-pgo + stories: + - JetStream2 + - benchmark: rendering.desktop.notracing + configs: + - linux-perf-pgo + stories: + - motionmark_ramp_composite + win-10-pgo: + telemetry_batch_experiment: + - benchmark: speedometer2 + configs: + - win-10-perf-pgo + stories: + - Speedometer2 + - benchmark: jetstream2 + configs: + - win-10-perf-pgo + stories: + - JetStream2 + - benchmark: rendering.desktop.notracing + configs: + - win-10-perf-pgo + stories: + - motionmark_ramp_composite + pixel6: + telemetry_batch_experiment: + - benchmark: speedometer2 + configs: + - android-pixel6-perf-pgo + stories: + - Speedometer2 + - benchmark: jetstream2 + configs: + - android-pixel6-perf-pgo + stories: + - JetStream2 + - benchmark: rendering.mobile.notracing + configs: + - android-pixel6-perf-pgo + stories: + - motionmark_ramp_composite
diff --git a/tools/typescript/definitions/autofill_private.d.ts b/tools/typescript/definitions/autofill_private.d.ts index cada1071..4a9c9dd 100644 --- a/tools/typescript/definitions/autofill_private.d.ts +++ b/tools/typescript/definitions/autofill_private.d.ts
@@ -123,6 +123,7 @@ params: ValidatePhoneParams): Promise<string[]>; export function getCreditCardList(): Promise<CreditCardEntry[]>; export function getIbanList(): Promise<IbanEntry[]>; + export function isValidIban(ibanValue: string): Promise<boolean>; export function maskCreditCard(guid: string): void; export function migrateCreditCards(): void; export function logServerCardLinkClicked(): void;
diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc index cb13ebf7..fb5e6bf3 100644 --- a/ui/accessibility/ax_tree.cc +++ b/ui/accessibility/ax_tree.cc
@@ -14,7 +14,6 @@ #include "base/command_line.h" #include "base/containers/adapters.h" #include "base/containers/contains.h" -#include "base/debug/dump_without_crashing.h" #include "base/functional/callback_helpers.h" #include "base/memory/ptr_util.h" #include "base/memory/raw_ptr.h" @@ -2747,18 +2746,13 @@ // In fast-failing-builds, crash immediately with a full message, otherwise // rely on AccessibilityFatalError(), which will not crash until multiple // errors occur. - // TODO(accessibility) Make AXTree errors fatal in all builds, as they - // indicate fundamental problems in part of the engine. They are much less - // frequent than in the past -- it should not be high impact on users. + // TODO(accessibility) Make AXTree errors fatal in Canary and Dev builds, as + // they indicate fundamental problems in part of the engine. They are much + // less frequent than in the past -- it should not be highimpact on users. #if defined(AX_FAIL_FAST_BUILD) is_fatal = true; #endif - ++accessibility_error_count_; - if (accessibility_error_count_ == kMaxAccessibilityErrorsBeforeDeath) { - is_fatal = true; - } - std::ostringstream verbose_error; verbose_error << new_error << "\n** Pending tree update **\n" << update_state.pending_tree_update->ToString( @@ -2771,17 +2765,24 @@ LOG(FATAL) << verbose_error.str(); } - // Only send crash report first time -- don't skew crash stats too much. - if (accessibility_error_count_ == 1) { - SCOPED_CRASH_KEY_STRING256("ax-tree", "error", new_error); - SCOPED_CRASH_KEY_STRING1024("ax-tree", "update", - update_state.pending_tree_update->ToString()); - SCOPED_CRASH_KEY_STRING1024("ax-tree", "nodes", - TreeToStringHelper(root_, 0, false)); - SCOPED_CRASH_KEY_STRING256("ax-tree", "data", data_.ToString()); - base::debug::DumpWithoutCrashing(); - } + // If this is the first error, will dump without crashing in + // RenderAccessibilityImpl::OnFatalError(). + static auto* const ax_tree_error_key = base::debug::AllocateCrashKeyString( + "ax_tree_error", base::debug::CrashKeySize::Size256); + static auto* const ax_tree_update_key = base::debug::AllocateCrashKeyString( + "ax_tree_update", base::debug::CrashKeySize::Size256); + static auto* const ax_tree_key = base::debug::AllocateCrashKeyString( + "ax_tree", base::debug::CrashKeySize::Size256); + static auto* const ax_tree_data_key = base::debug::AllocateCrashKeyString( + "ax_tree_data", base::debug::CrashKeySize::Size256); + // Log additional crash keys so we can debug bad tree updates. + base::debug::SetCrashKeyString(ax_tree_error_key, new_error); + base::debug::SetCrashKeyString(ax_tree_update_key, + update_state.pending_tree_update->ToString()); + base::debug::SetCrashKeyString(ax_tree_key, + TreeToStringHelper(root_, 0, false)); + base::debug::SetCrashKeyString(ax_tree_data_key, data_.ToString()); LOG(ERROR) << verbose_error.str(); }
diff --git a/ui/accessibility/ax_tree.h b/ui/accessibility/ax_tree.h index fca58e4..085bf7e1 100644 --- a/ui/accessibility/ax_tree.h +++ b/ui/accessibility/ax_tree.h
@@ -495,10 +495,6 @@ // Indicates if the tree represents a paginated document bool has_pagination_support_ = false; - // The number of times an a11y error occurred in this AXTree. - int accessibility_error_count_ = 0; - static constexpr int kMaxAccessibilityErrorsBeforeDeath = 5; - std::unique_ptr<AXEvent> event_data_; };
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn index 46aee20..971468b 100644 --- a/ui/android/BUILD.gn +++ b/ui/android/BUILD.gn
@@ -275,6 +275,7 @@ "java/src/org/chromium/ui/base/TouchDevice.java", "java/src/org/chromium/ui/base/ViewAndroidDelegate.java", "java/src/org/chromium/ui/base/ViewUtils.java", + "java/src/org/chromium/ui/base/ViewportInsets.java", "java/src/org/chromium/ui/base/WindowAndroid.java", "java/src/org/chromium/ui/base/WindowDelegate.java", "java/src/org/chromium/ui/display/DisplayAndroid.java", @@ -395,6 +396,7 @@ "//third_party/androidx:androidx_core_core_java", "//third_party/androidx:androidx_vectordrawable_vectordrawable_animated_java", "//ui/base/cursor/mojom:cursor_type_java", + "//ui/base/ime/mojom:mojom_java", "//url:gurl_java", ] annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] @@ -554,6 +556,7 @@ "//third_party/androidx:androidx_test_runner_java", "//third_party/hamcrest:hamcrest_java", "//third_party/mockito:mockito_java", + "//ui/base/ime/mojom:mojom_java", "//url:gurl_java", "//url:gurl_junit_shadows", "//url:gurl_junit_test_support",
diff --git a/ui/android/java/src/org/chromium/ui/base/ApplicationViewportInsetSupplier.java b/ui/android/java/src/org/chromium/ui/base/ApplicationViewportInsetSupplier.java index ac6ade6..e56487b 100644 --- a/ui/android/java/src/org/chromium/ui/base/ApplicationViewportInsetSupplier.java +++ b/ui/android/java/src/org/chromium/ui/base/ApplicationViewportInsetSupplier.java
@@ -10,63 +10,61 @@ import org.chromium.base.lifetime.Destroyable; import org.chromium.base.supplier.ObservableSupplier; import org.chromium.base.supplier.ObservableSupplierImpl; - -import java.util.HashSet; -import java.util.Set; +import org.chromium.ui.mojom.VirtualKeyboardMode; /** - * A class responsible for managing multiple users of UI insets affecting - * the visual viewport. + * A class responsible for managing multiple users of UI insets over the application viewport. * - * Insetting the visual viewport overlays web content without resizing its - * container (meaning it doesn't affect page layout); however, the user can - * always scroll within the visual viewport to reveal overlaid content and - * authors can respond to changes in the visual viewport. + * UI insets are complicated. The application viewport is provided by CompositorViewHolder but the + * browser provides various UI controls which overlay the viewport. How these UI controls interact + * with the underlying WebContents varies between controls and can depend on web-APIs. * - * Features needing to know if something is obscuring part of the screen listen - * to this class via {@link #addObserver(Callback)}. UI that wishes to inset - * the visual viewport can attach an inset supplier via {@link - * #addSupplier(ObservableSupplier)}. + * For example, browser controls cause the WebContents to resize so that the web page reflows in + * response to showing/hiding (although the timing of when this happens is non-straightforward). On + * the other hand, the virtual keyboard should resize only the page's visualViewport, without + * affecting layout. Chrome provides "keyboard accessories" that appear to the user to be part of + * the keyboard but are actually separate UI components. To make matters even more complicated, the + * page can change how the virtual keyboard affects the page (to affect page layout). * - * This class supports two kinds of inset suppliers: overlapping and stacking. + * This class aims to centralize and encapsulate all these complex interactions so clients don't + * have to worry about the details. This class currently handles only the keyboard and keyboard + * accessory but there are plans to move browser controls into here as well + * (https://crbug.com/1211066). * - * Stacking suppliers are assumed to be presented "stacked", one of top (in the - * y-axis) of the other. For example, the autofill keyboard accessory and the - * on-screen keyboard are presented with the accessory appearing directly above - * the keyboard. In this case, the keyboard inset is added to the accessory - * inset to compute the total "stacking inset". - * - * Overlapping suppliers assume each supplier is attached to the viewport - * bottom and don't take each other into account. For example, if a bottom info - * bar is showing but a contextual search panel slides in from below, obscuring - * the info bar. In this case, both the info bar and search panel provide their - * own overlapping inset. The total "overlapping inset" is computed by taking - * the largest value of all overlap suppliers. - * - * The final inset (the one provided to observers) is the largest between the - * stacking and overlapping insets. + * Features needing to know if anything is obscuring part of the screen listen to this class via + * {@link #addObserver(Callback)} which observes changes to a {@link ViewportInsets} object which + * has various inset types clients can use. See that class for more detials about the inset types. * * In general: - * - Features that want to modify the inset should pass around the - * {@link ApplicationViewportInsetSupplier} object. - * - Features only interested in what the current inset is should pass around an - * {@link ObservableSupplier<Integer>} object. + * - Features that want to modify the inset should pass around the {@link + * ApplicationViewportInsetSupplier} object. + * - Features only interested in what the current inset is should pass around an {@link + * ObservableSupplier<ViewportInsets>} object. */ public class ApplicationViewportInsetSupplier - extends ObservableSupplierImpl<Integer> implements Destroyable { - /** The lists of inset providers that this class manages. */ - private final Set<ObservableSupplier<Integer>> mOverlappingInsetSuppliers = new HashSet<>(); - private final Set<ObservableSupplier<Integer>> mStackingInsetSuppliers = new HashSet<>(); + extends ObservableSupplierImpl<ViewportInsets> implements Destroyable { + /** Keyboard related suppliers */ + private ObservableSupplier<Integer> mKeyboardInsetSupplier; + private ObservableSupplier<Integer> mKeyboardAccessoryInsetSupplier; - /** The observer that gets attached to all inset providers. */ - private final Callback<Integer> mInsetSupplierObserver = (inset) -> computeInset(); + /** The observer that gets attached to all keyboard inset suppliers. */ + private final Callback<Integer> mInsetSupplierObserver = (unused) -> computeInsets(); + + /** + * By default, the virtual keyboard overlays content, only resizing the visual viewport. + * + * Web content has APIs that change how the virtual keyboard interacts with content. This class + * needs to know which mode we're in to determine how different kinds of insets are computed. + */ + @VirtualKeyboardMode.EnumType + private int mVirtualKeyboardMode = VirtualKeyboardMode.RESIZES_VISUAL; /** Default constructor. */ ApplicationViewportInsetSupplier() { super(); // Make sure this is initialized to 0 since "Integer" is an object and would be null // otherwise. - super.set(0); + super.set(new ViewportInsets()); } @VisibleForTesting @@ -74,80 +72,110 @@ return new ApplicationViewportInsetSupplier(); } - /** Clean up observers and suppliers. */ @Override - public void destroy() { - for (ObservableSupplier<Integer> os : mOverlappingInsetSuppliers) { - os.removeObserver(mInsetSupplierObserver); - } - for (ObservableSupplier<Integer> os : mStackingInsetSuppliers) { - os.removeObserver(mInsetSupplierObserver); - } - - mOverlappingInsetSuppliers.clear(); - mStackingInsetSuppliers.clear(); - } - - /** Compute the new inset based on the current registered providers. */ - private void computeInset() { - int stackingInset = 0; - for (ObservableSupplier<Integer> os : mStackingInsetSuppliers) { - // Similarly to the constructor, check that the Integer object isn't null as the - // supplier may not yet have supplied the initial value. - stackingInset += os.get() == null ? 0 : os.get(); - } - - int overlappingInset = 0; - for (ObservableSupplier<Integer> os : mOverlappingInsetSuppliers) { - // Similarly to the constructor, check that the Integer object isn't null as the - // supplier may not yet have supplied the initial value. - overlappingInset = Math.max(overlappingInset, os.get() == null ? 0 : os.get()); - } - - super.set(Math.max(stackingInset, overlappingInset)); - } - - @Override - public void set(Integer value) { + public void set(ViewportInsets value) { throw new IllegalStateException( "#set(...) should not be called directly on ApplicationViewportInsetSupplier."); } - /** - * Adds a supplier of viewport insets that overlap. - * - * Of all overlap insets, only the largest is applied to the final inset. - * - * @param insetSupplier A supplier of bottom insets to be added. - */ - public void addOverlappingSupplier(ObservableSupplier<Integer> insetSupplier) { - mOverlappingInsetSuppliers.add(insetSupplier); - insetSupplier.addObserver(mInsetSupplierObserver); + /** Clean up observers and suppliers. */ + @Override + public void destroy() { + setKeyboardInsetSupplier(null); + setKeyboardAccessoryInsetSupplier(null); } /** - * Adds a supplier of viewport insets that stack. + * Notifies this object when the VirtualKeyboardMode of the currently active WebContents is + * changed. * - * Stacking insets are added together when applied to the final inset. - * - * @param insetSupplier A supplier of bottom insets to be added. + * This can happen as a result of a web content API call or swapping a WebContents or Tab. */ - public void addStackingSupplier(ObservableSupplier<Integer> insetSupplier) { - mStackingInsetSuppliers.add(insetSupplier); - insetSupplier.addObserver(mInsetSupplierObserver); + public void setVirtualKeyboardMode(@VirtualKeyboardMode.EnumType int mode) { + if (mVirtualKeyboardMode == mode) return; + + @VirtualKeyboardMode.EnumType + int oldMode = mVirtualKeyboardMode; + mVirtualKeyboardMode = mode; + + // The VirtualKeyboardMode affects only the visual viewport inset and only if moving to or + // from RESIZES_VISUAL. + if (oldMode == VirtualKeyboardMode.RESIZES_VISUAL + || mode == VirtualKeyboardMode.RESIZES_VISUAL) { + computeInsets(); + } + } + + // TODO(bokan): Temporarily needed for ManualFillingMediator#hasSufficientSpace, do not use + // elsewhere. Once this class also includes top/bottom browser controls hasSufficientSpace can + // use CompositorViewHolder's size instead of WebContents size and apply the inset from this + // class without reference to the virtual keyboard mode. https://crbug.com/1211066. + public @VirtualKeyboardMode.EnumType int getVirtualKeyboardMode() { + return mVirtualKeyboardMode; } /** - * Removes a previously added supplier. + * Sets the inset supplier for the soft keyboard itself. * - * The given supplier is removed regardless of whether it was overlapping or stacking. - * - * @param insetSupplier A supplier of bottom insets to be removed. + * Pass null to unset the current supplier. */ - public void removeSupplier(ObservableSupplier<Integer> insetSupplier) { - mOverlappingInsetSuppliers.remove(insetSupplier); - mStackingInsetSuppliers.remove(insetSupplier); - insetSupplier.removeObserver(mInsetSupplierObserver); - computeInset(); + public void setKeyboardInsetSupplier(ObservableSupplier<Integer> insetSupplier) { + boolean didRemove = false; + + if (mKeyboardInsetSupplier != null) { + mKeyboardInsetSupplier.removeObserver(mInsetSupplierObserver); + didRemove = true; + } + + mKeyboardInsetSupplier = insetSupplier; + + if (mKeyboardInsetSupplier != null) { + mKeyboardInsetSupplier.addObserver(mInsetSupplierObserver); + } else if (didRemove) { + // If a supplier was removed, removeObserver will not have notified observers (unlike + // addObserver) so make sure insets get recomputed in this case. + computeInsets(); + } + } + + /** + * Sets the inset supplier for the keyboard accessory. + * + * Pass null to unset the current supplier. + */ + public void setKeyboardAccessoryInsetSupplier(ObservableSupplier<Integer> insetSupplier) { + boolean didRemove = false; + if (mKeyboardAccessoryInsetSupplier != null) { + mKeyboardAccessoryInsetSupplier.removeObserver(mInsetSupplierObserver); + didRemove = true; + } + + mKeyboardAccessoryInsetSupplier = insetSupplier; + + if (mKeyboardAccessoryInsetSupplier != null) { + mKeyboardAccessoryInsetSupplier.addObserver(mInsetSupplierObserver); + } else if (didRemove) { + // If a supplier was removed, removeObserver will not have notified observers (unlike + // addObserver) so make sure insets get recomputed in this case. + computeInsets(); + } + } + + /** Compute the new total inset based on all registered suppliers. */ + private void computeInsets() { + int totalKeyboardInset = intFromSupplier(mKeyboardInsetSupplier) + + intFromSupplier(mKeyboardAccessoryInsetSupplier); + + ViewportInsets newValues = new ViewportInsets(); + newValues.viewVisibleHeightInset = intFromSupplier(mKeyboardAccessoryInsetSupplier); + newValues.visualViewportBottomInset = + mVirtualKeyboardMode == VirtualKeyboardMode.RESIZES_VISUAL ? totalKeyboardInset : 0; + + super.set(newValues); + } + + private int intFromSupplier(ObservableSupplier<Integer> supplier) { + if (supplier == null || supplier.get() == null) return 0; + return supplier.get(); } }
diff --git a/ui/android/java/src/org/chromium/ui/base/ViewportInsets.java b/ui/android/java/src/org/chromium/ui/base/ViewportInsets.java new file mode 100644 index 0000000..dc585ab --- /dev/null +++ b/ui/android/java/src/org/chromium/ui/base/ViewportInsets.java
@@ -0,0 +1,24 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.ui.base; + +/** + * Information about various kinds of insets on the application viewport. + * + * Objects of this class are vended by ApplicationViewportInsetSupplier. + */ +public class ViewportInsets { + /** + * The total vertical inset on the application viewport coming from all visible UI controls. + * TODO(bokan): This will have to be split up into top/bottom when browser controls are + * added. + */ + public int viewVisibleHeightInset; + + /** + * The bottom inset applied to the WebContents to get the visual viewport rect. + */ + public int visualViewportBottomInset; +}
diff --git a/ui/android/junit/src/org/chromium/ui/base/ApplicationViewportInsetSupplierTest.java b/ui/android/junit/src/org/chromium/ui/base/ApplicationViewportInsetSupplierTest.java index 9578fd04..dc56c52 100644 --- a/ui/android/junit/src/org/chromium/ui/base/ApplicationViewportInsetSupplierTest.java +++ b/ui/android/junit/src/org/chromium/ui/base/ApplicationViewportInsetSupplierTest.java
@@ -5,6 +5,8 @@ package org.chromium.ui.base; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import org.junit.Before; import org.junit.Test; @@ -14,6 +16,7 @@ import org.chromium.base.Callback; import org.chromium.base.supplier.ObservableSupplierImpl; import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.ui.mojom.VirtualKeyboardMode; /** Unit tests for the ApplicationViewportInsetSupplier. */ @RunWith(BaseRobolectricTestRunner.class) @@ -34,102 +37,261 @@ } private ApplicationViewportInsetSupplier mWindowApplicationInsetSupplier; - private ObservableSupplierImpl<Integer> mFeatureInsetSupplier; - private CapturingCallback<Integer> mWindowInsetObserver; + private ObservableSupplierImpl<Integer> mKeyboardInsetSupplier; + private CapturingCallback<ViewportInsets> mInsetObserver; @Before public void setUp() { mWindowApplicationInsetSupplier = new ApplicationViewportInsetSupplier(); - mFeatureInsetSupplier = new ObservableSupplierImpl<>(); - mWindowInsetObserver = new CapturingCallback<>(); + mKeyboardInsetSupplier = new ObservableSupplierImpl<>(); - mWindowApplicationInsetSupplier.addOverlappingSupplier(mFeatureInsetSupplier); - mWindowApplicationInsetSupplier.addObserver(mWindowInsetObserver); + mInsetObserver = new CapturingCallback<>(); + + mWindowApplicationInsetSupplier.setVirtualKeyboardMode(VirtualKeyboardMode.RESIZES_VISUAL); + mWindowApplicationInsetSupplier.setKeyboardInsetSupplier(mKeyboardInsetSupplier); + mWindowApplicationInsetSupplier.addObserver(mInsetObserver); + + // Clear the observer initially so tests can check whether it was called. + mInsetObserver.onResult(null); } @Test public void testSupplierDidNotSetValue() { - assertEquals("Observed value from supplier is incorrect.", (Integer) 0, + assertNotNull("Supplier should provide a non-null value even when unset.", mWindowApplicationInsetSupplier.get()); + assertEquals("Initial value for viewVisibleHeightInset is incorrect.", 0, + mWindowApplicationInsetSupplier.get().viewVisibleHeightInset); + assertEquals("Initial value for visualViewportBottomInset is incorrect.", 0, + mWindowApplicationInsetSupplier.get().visualViewportBottomInset); } @Test - public void testSupplierTriggersObserver() { - mFeatureInsetSupplier.set(5); - assertEquals("Observed value from supplier is incorrect.", (Integer) 5, - mWindowInsetObserver.getCapturedValue()); + public void testKeyboardTriggersObserver() { + mKeyboardInsetSupplier.set(5); + assertEquals("Keyboard does not insets the View's visible height.", 0, + mInsetObserver.getCapturedValue().viewVisibleHeightInset); + assertEquals("Keyboard insets the visual viewport in RESIZES_VISUAL mode.", 5, + mInsetObserver.getCapturedValue().visualViewportBottomInset); + + mWindowApplicationInsetSupplier.setVirtualKeyboardMode( + VirtualKeyboardMode.OVERLAYS_CONTENT); + + assertEquals("Keyboard does not inset the View's visible height.", 0, + mInsetObserver.getCapturedValue().viewVisibleHeightInset); + assertEquals("Keyboard does not inset the visual viewport in OVERLAYS_CONTENT mode.", 0, + mInsetObserver.getCapturedValue().visualViewportBottomInset); + + mWindowApplicationInsetSupplier.setVirtualKeyboardMode(VirtualKeyboardMode.RESIZES_CONTENT); + + assertEquals("Keyboard does not insetsthe View's visible height.", 0, + mInsetObserver.getCapturedValue().viewVisibleHeightInset); + assertEquals("Keyboard does not inset the visual viewport in RESIZES_CONTENT mode.", 0, + mInsetObserver.getCapturedValue().visualViewportBottomInset); + + mKeyboardInsetSupplier.set(10); + + assertEquals("Keyboard does not inset the View's visible height.", 0, + mInsetObserver.getCapturedValue().viewVisibleHeightInset); + assertEquals("Keyboard does not inset the visual viewport in RESIZES_CONTENT mode.", 0, + mInsetObserver.getCapturedValue().visualViewportBottomInset); } @Test - public void testSupplierTriggersObserver_multipleSuppliers_2() { - ObservableSupplierImpl<Integer> secondSupplier = new ObservableSupplierImpl<>(); - mWindowApplicationInsetSupplier.addOverlappingSupplier(secondSupplier); + public void testKeyboardWithAccessory() { + ObservableSupplierImpl<Integer> accessorySupplier = new ObservableSupplierImpl<>(); + mWindowApplicationInsetSupplier.setKeyboardAccessoryInsetSupplier(accessorySupplier); - mFeatureInsetSupplier.set(5); - secondSupplier.set(10); + mKeyboardInsetSupplier.set(10); + accessorySupplier.set(5); - assertEquals("Observed value should be the max of the two supplied.", (Integer) 10, - mWindowInsetObserver.getCapturedValue()); + assertEquals("Only accessory insets the View's visible height.", 5, + mInsetObserver.getCapturedValue().viewVisibleHeightInset); + assertEquals("Both keyboard and accessory inset the visual viewport in RESIZES_VISUAL.", 15, + mInsetObserver.getCapturedValue().visualViewportBottomInset); + + mWindowApplicationInsetSupplier.setVirtualKeyboardMode(VirtualKeyboardMode.RESIZES_CONTENT); + + assertEquals("Only accessory insets the View's visible height.", 5, + mInsetObserver.getCapturedValue().viewVisibleHeightInset); + assertEquals( + "Neither keyboard nor accessory resize the visual viewport in RESIZES_CONTENT mode.", + 0, mInsetObserver.getCapturedValue().visualViewportBottomInset); } @Test - public void testSupplierTriggersObserver_multipleSuppliers_3() { - ObservableSupplierImpl<Integer> secondSupplier = new ObservableSupplierImpl<>(); - mWindowApplicationInsetSupplier.addOverlappingSupplier(secondSupplier); - - ObservableSupplierImpl<Integer> thirdSupplier = new ObservableSupplierImpl<>(); - mWindowApplicationInsetSupplier.addOverlappingSupplier(thirdSupplier); - - mFeatureInsetSupplier.set(5); - secondSupplier.set(20); - thirdSupplier.set(10); - - assertEquals("Observed value should be the max of the three supplied.", (Integer) 20, - mWindowInsetObserver.getCapturedValue()); - } - - @Test - public void testSupplierTriggersObserver_setBeforeAdded() { + public void testSupplierSetBeforeAddingTriggersObserverOnAdd() { ObservableSupplierImpl<Integer> supplier = new ObservableSupplierImpl<>(); supplier.set(20); - mWindowApplicationInsetSupplier.addOverlappingSupplier(supplier); + mWindowApplicationInsetSupplier.setKeyboardAccessoryInsetSupplier(supplier); - assertEquals("The observer should have been triggered after the supplier was added.", - (Integer) 20, mWindowInsetObserver.getCapturedValue()); + assertEquals("The observer should have been triggered when the supplier was added.", 20, + mInsetObserver.getCapturedValue().viewVisibleHeightInset); } @Test - public void testSupplierRemoveTriggersEvent() { - ObservableSupplierImpl<Integer> secondSupplier = new ObservableSupplierImpl<>(); - mWindowApplicationInsetSupplier.addOverlappingSupplier(secondSupplier); + public void testRemovingSupplierTriggersObservers() { + ObservableSupplierImpl<Integer> accessorySupplier = new ObservableSupplierImpl<>(); + mWindowApplicationInsetSupplier.setKeyboardAccessoryInsetSupplier(accessorySupplier); - ObservableSupplierImpl<Integer> thirdSupplier = new ObservableSupplierImpl<>(); - mWindowApplicationInsetSupplier.addOverlappingSupplier(thirdSupplier); + mKeyboardInsetSupplier.set(20); + accessorySupplier.set(5); - mFeatureInsetSupplier.set(5); - secondSupplier.set(20); - thirdSupplier.set(10); + assertEquals("Observed value should come from accessory.", 5, + mInsetObserver.getCapturedValue().viewVisibleHeightInset); - assertEquals("Observed value should be the max of the three supplied.", (Integer) 20, - mWindowInsetObserver.getCapturedValue()); + mWindowApplicationInsetSupplier.setKeyboardAccessoryInsetSupplier(null); - mWindowApplicationInsetSupplier.removeSupplier(secondSupplier); - - assertEquals("Observed value should be the max of the two remaining.", (Integer) 10, - mWindowInsetObserver.getCapturedValue()); + assertEquals("Observed value should be 0 when accessory supplier removed.", 0, + mInsetObserver.getCapturedValue().viewVisibleHeightInset); } @Test public void testAllSuppliersRemoved() { - mFeatureInsetSupplier.set(5); + ObservableSupplierImpl<Integer> accessorySupplier = new ObservableSupplierImpl<>(); + mWindowApplicationInsetSupplier.setKeyboardAccessoryInsetSupplier(accessorySupplier); - assertEquals("Observed value from supplier is incorrect.", (Integer) 5, - mWindowInsetObserver.getCapturedValue()); + mKeyboardInsetSupplier.set(10); + accessorySupplier.set(5); - mWindowApplicationInsetSupplier.removeSupplier(mFeatureInsetSupplier); + assertEquals("View inset is correct.", 5, + mInsetObserver.getCapturedValue().viewVisibleHeightInset); + assertEquals("VisualViewport inset is correct.", 15, + mInsetObserver.getCapturedValue().visualViewportBottomInset); - assertEquals("Observed value should be 0 with no suppliers attached.", (Integer) 0, - mWindowInsetObserver.getCapturedValue()); + mWindowApplicationInsetSupplier.setKeyboardInsetSupplier(null); + mWindowApplicationInsetSupplier.setKeyboardAccessoryInsetSupplier(null); + + assertEquals("View inset should be 0 with no suppliers attached.", 0, + mInsetObserver.getCapturedValue().viewVisibleHeightInset); + assertEquals("VisualViewport inset should be 0 with no suppliers attached.", 0, + mInsetObserver.getCapturedValue().visualViewportBottomInset); + } + + @Test + public void testVisualViewportBottomInset() { + ObservableSupplierImpl<Integer> accessorySupplier = new ObservableSupplierImpl<>(); + mWindowApplicationInsetSupplier.setKeyboardAccessoryInsetSupplier(accessorySupplier); + + mWindowApplicationInsetSupplier.setVirtualKeyboardMode(VirtualKeyboardMode.RESIZES_VISUAL); + + assertEquals("VisualViewport inset initially 0", 0, + mWindowApplicationInsetSupplier.get().visualViewportBottomInset); + + mKeyboardInsetSupplier.set(10); + + assertEquals("Keyboard insets visual viewport", 10, + mWindowApplicationInsetSupplier.get().visualViewportBottomInset); + + accessorySupplier.set(20); + + assertEquals("Accessory insets visual viewport", 30, + mWindowApplicationInsetSupplier.get().visualViewportBottomInset); + + mWindowApplicationInsetSupplier.setKeyboardAccessoryInsetSupplier(null); + + assertEquals("Removing accessory removes visual viewport inset", 10, + mWindowApplicationInsetSupplier.get().visualViewportBottomInset); + + mWindowApplicationInsetSupplier.setKeyboardInsetSupplier(null); + + assertEquals("Removing keyboard removes visual viewport inset", 0, + mWindowApplicationInsetSupplier.get().visualViewportBottomInset); + } + + @Test + public void testVisualViewportInsetWithVirtualKeyboardModes() { + ObservableSupplierImpl<Integer> accessorySupplier = new ObservableSupplierImpl<>(); + mWindowApplicationInsetSupplier.setKeyboardAccessoryInsetSupplier(accessorySupplier); + + mWindowApplicationInsetSupplier.setVirtualKeyboardMode(VirtualKeyboardMode.RESIZES_VISUAL); + + assertEquals("VisualViewport inset initially 0", 0, + mWindowApplicationInsetSupplier.get().visualViewportBottomInset); + + mWindowApplicationInsetSupplier.setVirtualKeyboardMode(VirtualKeyboardMode.RESIZES_CONTENT); + + mKeyboardInsetSupplier.set(10); + assertEquals("Keyboard doesn't inset visual viewport with RESIZES_CONTENT", 0, + mWindowApplicationInsetSupplier.get().visualViewportBottomInset); + + accessorySupplier.set(20); + assertEquals("Accessory doesn't inset visual viewport with RESIZES_CONTENT", 0, + mWindowApplicationInsetSupplier.get().visualViewportBottomInset); + + mWindowApplicationInsetSupplier.setVirtualKeyboardMode( + VirtualKeyboardMode.OVERLAYS_CONTENT); + + mKeyboardInsetSupplier.set(12); + assertEquals("Keyboard doesn't inset visual viewport with OVERLAYS_CONTENT", 0, + mWindowApplicationInsetSupplier.get().visualViewportBottomInset); + + accessorySupplier.set(25); + assertEquals("Accessory doesn't inset visual viewport with OVERLAYS_CONTENT", 0, + mWindowApplicationInsetSupplier.get().visualViewportBottomInset); + + mWindowApplicationInsetSupplier.setVirtualKeyboardMode(VirtualKeyboardMode.RESIZES_VISUAL); + assertEquals("Accessory and keyboard inset visual viewport with RESIZES_VISUAL", 37, + mWindowApplicationInsetSupplier.get().visualViewportBottomInset); + } + + @Test + public void testTriggerVisualViewportObserver() { + assertNull("Observer initially uncalled", mInsetObserver.getCapturedValue()); + + ObservableSupplierImpl<Integer> accessorySupplier = new ObservableSupplierImpl<>(); + mWindowApplicationInsetSupplier.setKeyboardAccessoryInsetSupplier(accessorySupplier); + mWindowApplicationInsetSupplier.setVirtualKeyboardMode(VirtualKeyboardMode.RESIZES_VISUAL); + + mKeyboardInsetSupplier.set(10); + + assertEquals("Keyboard triggers visual viewport observer", 10, + mInsetObserver.getCapturedValue().visualViewportBottomInset); + + accessorySupplier.set(20); + + assertEquals("Accessory triggers visual viewport observer", 30, + mInsetObserver.getCapturedValue().visualViewportBottomInset); + + mWindowApplicationInsetSupplier.setKeyboardAccessoryInsetSupplier(null); + + assertEquals("Removing accessory supplier triggers visual viewport observer", 10, + mInsetObserver.getCapturedValue().visualViewportBottomInset); + + mWindowApplicationInsetSupplier.setKeyboardInsetSupplier(null); + assertEquals("Removing keyboard supplier triggers visual viewport observer", 0, + mInsetObserver.getCapturedValue().visualViewportBottomInset); + } + + @Test + public void testTriggerObserverWithVirtualKeyboardModes() { + ObservableSupplierImpl<Integer> accessorySupplier = new ObservableSupplierImpl<>(); + mWindowApplicationInsetSupplier.setKeyboardAccessoryInsetSupplier(accessorySupplier); + mKeyboardInsetSupplier.set(30); + accessorySupplier.set(15); + + mWindowApplicationInsetSupplier.setVirtualKeyboardMode(VirtualKeyboardMode.RESIZES_CONTENT); + + // Clear the observer so we can tell if it gets called. + mInsetObserver.onResult(null); + + mWindowApplicationInsetSupplier.setVirtualKeyboardMode( + VirtualKeyboardMode.OVERLAYS_CONTENT); + + assertNull("Changing to OVERLAYS_CONTENT doesn't trigger observer", + mInsetObserver.getCapturedValue()); + + mWindowApplicationInsetSupplier.setVirtualKeyboardMode(VirtualKeyboardMode.RESIZES_VISUAL); + + assertEquals("Changing to RESIZES_VISUAL triggers observer", 45, + mInsetObserver.getCapturedValue().visualViewportBottomInset); + + // Clear the observer so we can tell if it gets called. + mInsetObserver.onResult(null); + + mWindowApplicationInsetSupplier.setVirtualKeyboardMode(VirtualKeyboardMode.RESIZES_CONTENT); + + assertEquals("Changing from RESIZES_VISUAL triggers observer", 0, + mInsetObserver.getCapturedValue().visualViewportBottomInset); } }
diff --git a/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc b/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc index 3de81ab2..3dd139a 100644 --- a/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc +++ b/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc
@@ -282,8 +282,12 @@ ui::DeviceDataManager::CreateInstance(); } - void TearDown() override { + void TearDown() override { TearDownDevice(); } + + void TearDownDevice() { device_.reset(); + dispatcher_.reset(); + shared_palm_state_.reset(); } void UpdateTime(struct input_event* queue, long count, timeval time) const { @@ -751,6 +755,7 @@ TEST_F(TouchEventConverterEvdevTest, ToolTypePalmNotCancelTouch) { // By default, we use TOOL_TYPE_PALM as a cancellation signal for all touches. // We disable that behavior and want to see all touches registered as usual. + TearDownDevice(); scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>(); scoped_feature_list_->InitWithFeatures( {}, {kEnablePalmOnMaxTouchMajor, kEnablePalmOnToolTypePalm, @@ -847,6 +852,7 @@ // By default, tests disable single-cancel and enable palm on touch_major == // major_max. So we disable that behavior: and expect to see a RELEASED rather // than cancelled. + TearDownDevice(); scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>(); scoped_feature_list_->InitWithFeatures( {}, {kEnablePalmOnMaxTouchMajor, kEnableSingleCancelTouch});
diff --git a/ui/gfx/switches.cc b/ui/gfx/switches.cc index cbdfb60..78781a2 100644 --- a/ui/gfx/switches.cc +++ b/ui/gfx/switches.cc
@@ -69,7 +69,7 @@ #if BUILDFLAG(IS_CHROMEOS) BASE_FEATURE(kVariableGoogleSansFont, "VariableGoogleSansFont", - base::FEATURE_DISABLED_BY_DEFAULT); + base::FEATURE_ENABLED_BY_DEFAULT); GFX_SWITCHES_EXPORT bool UseVariableGoogleSansFont() { return base::FeatureList::IsEnabled(kVariableGoogleSansFont);
diff --git a/ui/views/controls/combobox/combobox.cc b/ui/views/controls/combobox/combobox.cc index d99c6d3..84321f9 100644 --- a/ui/views/controls/combobox/combobox.cc +++ b/ui/views/controls/combobox/combobox.cc
@@ -80,28 +80,7 @@ views::InstallRoundRectHighlightPathGenerator(this, gfx::Insets(), GetCornerRadius()); } - InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::ON); - SetHasInkDropActionOnClick(true); - InkDrop::UseInkDropForSquareRipple(InkDrop::Get(this), - /*highlight_on_hover=*/false); - views::InkDrop::Get(this)->SetBaseColorCallback(base::BindRepeating( - [](Button* host) { - // This button will be used like a LabelButton, so use the same - // foreground base color as a label button. - return color_utils::DeriveDefaultIconColor( - host->GetColorProvider()->GetColor(views::style::GetColorId( - views::style::CONTEXT_BUTTON, views::style::STYLE_PRIMARY))); - }, - this)); - InkDrop::Get(this)->SetCreateRippleCallback(base::BindRepeating( - [](Button* host) -> std::unique_ptr<views::InkDropRipple> { - return std::make_unique<views::FloodFillInkDropRipple>( - InkDrop::Get(host), host->size(), - InkDrop::Get(host)->GetInkDropCenterBasedOnLastEvent(), - host->GetColorProvider()->GetColor(ui::kColorLabelForeground), - InkDrop::Get(host)->GetVisibleOpacity()); - }, - this)); + ConfigureComboboxButtonInkDrop(this); } TransparentButton(const TransparentButton&) = delete; TransparentButton& operator&=(const TransparentButton&) = delete;
diff --git a/ui/views/controls/combobox/combobox_util.cc b/ui/views/controls/combobox/combobox_util.cc index d050086..e231e43 100644 --- a/ui/views/controls/combobox/combobox_util.cc +++ b/ui/views/controls/combobox/combobox_util.cc
@@ -4,6 +4,8 @@ #include "ui/views/controls/combobox/combobox_util.h" +#include <memory> + #include "cc/paint/paint_flags.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/base/ui_base_features.h" @@ -11,7 +13,12 @@ #include "ui/gfx/geometry/point_f.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size_f.h" +#include "ui/views/animation/flood_fill_ink_drop_ripple.h" +#include "ui/views/animation/ink_drop.h" +#include "ui/views/animation/ink_drop_host.h" +#include "ui/views/controls/button/button.h" #include "ui/views/layout/layout_provider.h" +#include "ui/views/style/typography.h" namespace views { @@ -61,4 +68,28 @@ canvas->DrawPath(path, flags); } +void ConfigureComboboxButtonInkDrop(Button* host_view) { + InkDrop::Get(host_view)->SetMode(views::InkDropHost::InkDropMode::ON); + host_view->SetHasInkDropActionOnClick(true); + InkDrop::UseInkDropForSquareRipple(InkDrop::Get(host_view), + /*highlight_on_hover=*/false); + views::InkDrop::Get(host_view)->SetBaseColorCallback(base::BindRepeating( + [](Button* host) { + // Use the same foreground base color as a label button. + return color_utils::DeriveDefaultIconColor(views::style::GetColor( + *host, views::style::CONTEXT_BUTTON, views::style::STYLE_PRIMARY)); + }, + host_view)); + InkDrop::Get(host_view)->SetCreateRippleCallback(base::BindRepeating( + [](Button* host) -> std::unique_ptr<views::InkDropRipple> { + return std::make_unique<views::FloodFillInkDropRipple>( + InkDrop::Get(host), host->size(), + InkDrop::Get(host)->GetInkDropCenterBasedOnLastEvent(), + host->GetColorProvider()->GetColor(style::GetColorId( + style::CONTEXT_TEXTFIELD, style::STYLE_PRIMARY)), + InkDrop::Get(host)->GetVisibleOpacity()); + }, + host_view)); +} + } // namespace views
diff --git a/ui/views/controls/combobox/combobox_util.h b/ui/views/controls/combobox/combobox_util.h index 5d035cd..8002cf2 100644 --- a/ui/views/controls/combobox/combobox_util.h +++ b/ui/views/controls/combobox/combobox_util.h
@@ -17,6 +17,7 @@ } // namespace gfx namespace views { +class Button; // Constants for the size of the combobox arrow. constexpr gfx::Size ComboboxArrowSize() { @@ -34,6 +35,8 @@ const gfx::Rect& bounds, gfx::Canvas* canvas); +void ConfigureComboboxButtonInkDrop(Button* host_view); + } // namespace views #endif // UI_VIEWS_CONTROLS_COMBOBOX_COMBOBOX_UTIL_H_
diff --git a/ui/views/controls/editable_combobox/editable_combobox.cc b/ui/views/controls/editable_combobox/editable_combobox.cc index b1eeec5..d75ff249 100644 --- a/ui/views/controls/editable_combobox/editable_combobox.cc +++ b/ui/views/controls/editable_combobox/editable_combobox.cc
@@ -80,23 +80,7 @@ button_controller()->set_notify_action( ButtonController::NotifyAction::kOnPress); - // TODO(pbos): Share ink-drop configuration code between here and - // Combobox's TransparentButton. - // Similar to Combobox's TransparentButton. - InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::ON); - SetHasInkDropActionOnClick(true); - InkDrop::UseInkDropForSquareRipple(InkDrop::Get(this), - /*highlight_on_hover=*/false); - InkDrop::Get(this)->SetCreateRippleCallback(base::BindRepeating( - [](Button* host) -> std::unique_ptr<views::InkDropRipple> { - return std::make_unique<views::FloodFillInkDropRipple>( - InkDrop::Get(host), host->size(), - InkDrop::Get(host)->GetInkDropCenterBasedOnLastEvent(), - host->GetColorProvider()->GetColor(style::GetColorId( - style::CONTEXT_TEXTFIELD, style::STYLE_PRIMARY)), - InkDrop::Get(host)->GetVisibleOpacity()); - }, - this)); + ConfigureComboboxButtonInkDrop(this); } Arrow(const Arrow&) = delete; Arrow& operator=(const Arrow&) = delete; @@ -421,6 +405,14 @@ HandleNewContent(text); } +std::u16string EditableCombobox::GetPlaceholderText() const { + return textfield_->GetPlaceholderText(); +} + +void EditableCombobox::SetPlaceholderText(const std::u16string& text) { + textfield_->SetPlaceholderText(text); +} + const gfx::FontList& EditableCombobox::GetFontList() const { return style::GetFont(text_context_, text_style_); } @@ -616,6 +608,8 @@ } BEGIN_METADATA(EditableCombobox, View) +ADD_PROPERTY_METADATA(std::u16string, Text) +ADD_PROPERTY_METADATA(std::u16string, PlaceholderText) END_METADATA } // namespace views
diff --git a/ui/views/controls/editable_combobox/editable_combobox.h b/ui/views/controls/editable_combobox/editable_combobox.h index 1f9d969..5b22707 100644 --- a/ui/views/controls/editable_combobox/editable_combobox.h +++ b/ui/views/controls/editable_combobox/editable_combobox.h
@@ -95,6 +95,9 @@ const std::u16string& GetText() const; void SetText(const std::u16string& text); + std::u16string GetPlaceholderText() const; + void SetPlaceholderText(const std::u16string& text); + const gfx::FontList& GetFontList() const; void SetCallback(base::RepeatingClosure callback) {
diff --git a/ui/views/examples/vector_example.cc b/ui/views/examples/vector_example.cc index d3de609..7c4456de 100644 --- a/ui/views/examples/vector_example.cc +++ b/ui/views/examples/vector_example.cc
@@ -7,21 +7,24 @@ #include <memory> #include <string> #include <utility> +#include <vector> -#include "base/files/file_path.h" +#include "base/files/file_enumerator.h" #include "base/files/file_util.h" #include "base/memory/raw_ptr.h" +#include "base/path_service.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_restrictions.h" #include "build/build_config.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/models/simple_combobox_model.h" #include "ui/gfx/color_palette.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/views/border.h" -#include "ui/views/controls/button/button.h" #include "ui/views/controls/button/md_text_button.h" +#include "ui/views/controls/editable_combobox/editable_combobox.h" #include "ui/views/controls/image_view.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/controls/textfield/textfield_controller.h" @@ -29,6 +32,7 @@ #include "ui/views/examples/grit/views_examples_resources.h" #include "ui/views/layout/box_layout.h" #include "ui/views/layout/fill_layout.h" +#include "ui/views/layout/table_layout.h" #include "ui/views/view.h" using l10n_util::GetStringUTF16; @@ -44,38 +48,61 @@ size_input_ = AddChildView(std::make_unique<Textfield>()); color_input_ = AddChildView(std::make_unique<Textfield>()); - auto image_view_container = std::make_unique<views::View>(); - image_view_ = - image_view_container->AddChildView(std::make_unique<ImageView>()); - auto image_layout = - std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal); - image_layout->set_cross_axis_alignment( - BoxLayout::CrossAxisAlignment::kCenter); - image_layout->set_main_axis_alignment( - BoxLayout::MainAxisAlignment::kCenter); - image_view_container->SetLayoutManager(std::move(image_layout)); - image_view_->SetBorder(CreateThemedSolidBorder( - 1, ExamplesColorIds::kColorVectorExampleImageBorder)); - image_view_container_ = AddChildView(std::move(image_view_container)); + image_view_container_ = AddChildView(std::make_unique<views::View>()); BoxLayout* box = SetLayoutManager(std::make_unique<BoxLayout>( BoxLayout::Orientation::kVertical, gfx::Insets(10), 10)); box->SetFlexForView(image_view_container_, 1); - auto file_chooser = std::make_unique<Textfield>(); - file_chooser->SetPlaceholderText( + base::FilePath test_dir; + base::PathService::Get(base::DIR_SOURCE_ROOT, &test_dir); + std::u16string base_path = test_dir.AsUTF16Unsafe(); + std::vector<std::u16string> icon_dir = { + base::FilePath(test_dir.AppendASCII("ash") + .AppendASCII("resources") + .AppendASCII("vector_icons")) + .AsUTF16Unsafe(), + base::FilePath( + test_dir.AppendASCII("chrome").AppendASCII("app").AppendASCII( + "vector_icons")) + .AsUTF16Unsafe(), + base::FilePath(test_dir.AppendASCII("chromeos") + .AppendASCII("ui") + .AppendASCII("vector_icons")) + .AsUTF16Unsafe(), + base::FilePath( + test_dir.AppendASCII("components").AppendASCII("vector_icons")) + .AsUTF16Unsafe(), + base::FilePath(test_dir.AppendASCII("components") + .AppendASCII("omnibox") + .AppendASCII("browser") + .AppendASCII("vector_icons")) + .AsUTF16Unsafe(), + base::FilePath( + test_dir.AppendASCII("ui").AppendASCII("views").AppendASCII( + "vector_icons")) + .AsUTF16Unsafe(), + }; + auto editable_combobox = std::make_unique<views::EditableCombobox>(); + editable_combobox->SetModel(std::make_unique<ui::SimpleComboboxModel>( + std::vector<ui::SimpleComboboxModel::Item>(icon_dir.begin(), + icon_dir.end()))); + editable_combobox->SetPlaceholderText( GetStringUTF16(IDS_VECTOR_FILE_SELECT_LABEL)); + auto file_container = std::make_unique<View>(); BoxLayout* file_box = file_container->SetLayoutManager(std::make_unique<BoxLayout>( BoxLayout::Orientation::kHorizontal, gfx::Insets(10), 10)); - file_chooser_ = file_container->AddChildView(std::move(file_chooser)); + file_chooser_combobox_ = + file_container->AddChildView(std::move(editable_combobox)); + file_go_button_ = file_container->AddChildView(std::make_unique<MdTextButton>( base::BindRepeating(&VectorIconGallery::FileGoButtonPressed, base::Unretained(this)), GetStringUTF16(IDS_VECTOR_RENDER_LABEL))); - file_box->SetFlexForView(file_chooser_, 1); + file_box->SetFlexForView(file_chooser_combobox_, 1); AddChildView(std::move(file_container)); size_input_->SetPlaceholderText( @@ -116,12 +143,36 @@ private: void FileGoButtonPressed() { - base::ScopedAllowBlockingForTesting allow_blocking; #if BUILDFLAG(IS_WIN) - base::FilePath path(base::UTF16ToWide(file_chooser_->GetText())); + base::FilePath path(base::UTF16ToWide(file_chooser_combobox_->GetText())); #else - base::FilePath path(base::UTF16ToUTF8(file_chooser_->GetText())); + base::FilePath path(base::UTF16ToUTF8(file_chooser_combobox_->GetText())); #endif + + // If there is an extension, then it would not be a folder. + if (path.Extension().size() == 0) { + GenerateAllIconInFolder(path); + } else { + GenerateSingleIcon(path); + } + } + + void GenerateSingleIcon(const base::FilePath& path) { + image_view_container_->RemoveAllChildViews(); + image_view_ = + image_view_container_->AddChildView(std::make_unique<ImageView>()); + image_view_->SetBorder(CreateThemedSolidBorder( + 1, ExamplesColorIds::kColorVectorExampleImageBorder)); + + auto image_layout = + std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal); + image_layout->set_cross_axis_alignment( + BoxLayout::CrossAxisAlignment::kCenter); + image_layout->set_main_axis_alignment( + BoxLayout::MainAxisAlignment::kCenter); + image_view_container_->SetLayoutManager(std::move(image_layout)); + + base::ScopedAllowBlockingForTesting allow_blocking; base::ReadFileToString(path, &contents_); // Skip over comments. for (size_t slashes = contents_.find("//"); slashes != std::string::npos; @@ -132,8 +183,62 @@ Update(); } + void GenerateAllIconInFolder(const base::FilePath& path) { + image_view_container_->RemoveAllChildViews(); + + int nCols = 18; + int kColumnWidth = size_; + views::TableLayout* layout = image_view_container_->SetLayoutManager( + std::make_unique<views::TableLayout>()); + for (int i = 0; i < nCols; ++i) { + layout->AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStretch, + TableLayout::kFixedSize, + TableLayout::ColumnSize::kFixed, kColumnWidth, + kColumnWidth); + } + + int nRows = 10; + for (int i = 0; i < nRows; ++i) { + layout->AddRows(1, TableLayout::kFixedSize); + } + size_t max = nCols * nRows; + size_t count = 0; + + base::FileEnumerator file_iter(path, false, base::FileEnumerator::FILES, + FILE_PATH_LITERAL("*.icon")); + base::FilePath file = file_iter.Next(); + + while (!file.empty() && count < max) { + count++; + std::string file_content; + base::ReadFileToString(file, &file_content); + // Skip over comments. + // This handles very basic cases of // and /*. More complicated edge + // cases such as /* /* */ */ are not handled. + for (size_t slashes = file_content.find("//"); + slashes != std::string::npos; slashes = file_content.find("//")) { + size_t eol = file_content.find("\n", slashes); + file_content.erase(slashes, eol - slashes); + } + + for (size_t slashes = file_content.find("/*"); + slashes != std::string::npos; slashes = file_content.find("/*")) { + size_t eol = file_content.find("*/", slashes); + file_content.erase(slashes, eol - slashes + 2); + } + + ImageView* icon_view = + image_view_container_->AddChildView(std::make_unique<ImageView>()); + icon_view->SetImage( + gfx::CreateVectorIconFromSource(file_content, size_, color_)); + icon_view->SetTooltipText(file.BaseName().AsUTF16Unsafe()); + file = file_iter.Next(); + } + InvalidateLayout(); + } + void Update() { - if (!contents_.empty()) { + if (!contents_.empty() && image_view_ != nullptr) { image_view_->SetImage( gfx::CreateVectorIconFromSource(contents_, size_, color_)); } @@ -149,7 +254,7 @@ raw_ptr<View> image_view_container_; raw_ptr<Textfield> size_input_; raw_ptr<Textfield> color_input_; - raw_ptr<Textfield> file_chooser_; + raw_ptr<EditableCombobox> file_chooser_combobox_; raw_ptr<Button> file_go_button_; std::string contents_; };
diff --git a/ui/views/examples/views_examples_resources.grd b/ui/views/examples/views_examples_resources.grd index 10ccfd91..49c5d0a 100644 --- a/ui/views/examples/views_examples_resources.grd +++ b/ui/views/examples/views_examples_resources.grd
@@ -525,7 +525,7 @@ <!-- vector example --> <message translateable="false" name="IDS_VECTOR_FILE_SELECT_LABEL"> - Enter a file to read + Enter a file or folder to read </message> <message translateable="false" name="IDS_VECTOR_RENDER_LABEL"> Render
diff --git a/ui/webui/resources/cr_elements/chromeos/cros_color_overrides.css b/ui/webui/resources/cr_elements/chromeos/cros_color_overrides.css index e93e4059..62a5286 100644 --- a/ui/webui/resources/cr_elements/chromeos/cros_color_overrides.css +++ b/ui/webui/resources/cr_elements/chromeos/cros_color_overrides.css
@@ -118,6 +118,7 @@ /* Input and Textarea */ :host-context(body.jelly-enabled) cr-input, +:host-context(body.jelly-enabled) cr-search-field::part(searchInput), :host-context(body.jelly-enabled) cr-textarea { --cr-input-background-color: var(--cros-sys-input_field_dark); --cr-input-error-color: var(--cros-sys-error); @@ -135,6 +136,20 @@ var(--cros-sys-ripple_neutral_on_subtle); } +/* Search field */ +:host-context(body.jelly-enabled) cr-search-field { + --cr-search-field-clear-icon-fill: var(--cros-sys-primary); + --cr-search-field-clear-icon-margin-end: 6px; + --cr-search-field-input-border-bottom: none; + --cr-search-field-input-padding-start: 8px; + --cr-search-field-input-underline-border-radius: 4px; + --cr-search-field-search-icon-display: none; + --cr-search-field-search-icon-fill: var(--cros-sys-primary); + --cr-search-field-search-icon-inline-display: block; + --cr-search-field-search-icon-inline-margin-start: 6px; + border-radius: 4px; +} + /* Toggle */ :host-context(body.jelly-enabled) cr-toggle { --cr-toggle-checked-bar-color: var(--cros-sys-primary_container);
diff --git a/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.html b/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.html index a05bc091..70a4044b 100644 --- a/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.html +++ b/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.html
@@ -65,7 +65,7 @@ } </style> <iron-icon id="searchIcon" icon="cr:search" part="searchIcon"></iron-icon> - <cr-input id="searchInput" on-search="onSearchTermSearch" + <cr-input id="searchInput" part="searchInput" on-search="onSearchTermSearch" on-input="onSearchTermInput" aria-label$="[[label]]" type="search" autofocus="[[autofocus]]" placeholder="[[label]]" spellcheck="false"> <iron-icon id="searchIconInline" slot="inline-prefix" icon="cr:search"></iron-icon>