diff --git a/DEPS b/DEPS index 03e9d975..5046abc 100644 --- a/DEPS +++ b/DEPS
@@ -306,15 +306,15 @@ # 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': 'b0e0442cd021833f02a6bd92f3e2e2a172818b43', + 'skia_revision': 'b5b35f8dc919376c000feb1c7c7176fd0cc7b3de', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': '18e4c7416a6f4bdc5b825f9c3257edbafa7cd730', + 'v8_revision': '1e3c2d12fd42ec18fc0bbb19a97e728941e6c4c8', # 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': 'f37a32f0701e948b5180cd0b6af79acfef290a43', + 'angle_revision': '356b2a590e11be18482696ea1d283e975fd612d2', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -329,11 +329,11 @@ # # Note this revision should be updated with # third_party/boringssl/roll_boringssl.py, not roll-dep. - 'boringssl_revision': '1ee71185a2322dc354bee5e5a0abfb1810a27dc6', + 'boringssl_revision': 'f0518d45119dd4dd322a884669daf8247bc3c992', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Fuchsia sdk # and whatever else without interference from each other. - 'fuchsia_version': 'version:10.20221121.0.1', + 'fuchsia_version': 'version:10.20221121.2.1', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling google-toolbox-for-mac # and whatever else without interference from each other. @@ -377,7 +377,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': 'b898eb3e06f5f67172dfbdc19e1451d52638dbd4', + 'catapult_revision': '39d570c940b94f4179fa345573355455ffb4ab64', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -421,7 +421,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': '95df1efa5a7521614907b3c7200c3a1a4ab3127d', + 'dawn_revision': 'f5eec817de0df46d5aa2a7205f5b2f9c02e98dd4', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -816,7 +816,7 @@ 'src/clank': { 'url': 'https://chrome-internal.googlesource.com/clank/internal/apps.git' + '@' + - '19d35b9fd53210fb5cf1af9d6d32929136a060ad', + '709b59b8dbb560a3d544b4ddea303fd2b5a13a72', 'condition': 'checkout_android and checkout_src_internal and not checkout_clank_via_src_internal', }, @@ -910,7 +910,7 @@ 'packages': [ { 'package': 'chromium/rts/model/linux-amd64', - 'version': '0Spv3S8B9GHYWht5RMKVErkK8uMA87ASY076aOTgRVwC', + 'version': 'xB-0wbx4vRW3qCqhlfnA7s8p91c-ud-RnG8CiPoqimgC', }, ], 'dep_type': 'cipd', @@ -932,7 +932,7 @@ 'packages': [ { 'package': 'chromium/rts/model/windows-amd64', - 'version': '2ASyp7Dyws79jFos8NF-oCBCc_GW2GwNKUEdk31pqiQC', + 'version': 'cfeCqvGAqJxTQDu6lW0DhLdZFtE0dU4Fm4c81E_F_FYC', }, ], 'dep_type': 'cipd', @@ -1000,7 +1000,7 @@ 'packages': [ { 'package': 'chromium/third_party/androidx', - 'version': 'QywWz1_m6w-DIah4IXQ3I27LDjKgn9kUMTP1SEJgigUC', + 'version': 'QQCBJDbCcTJd-GDm4CHpjKdcXKjbHP9ebzyHDuU2x-4C', }, ], 'condition': 'checkout_android', @@ -1215,7 +1215,7 @@ # Tools used when building Chrome for Chrome OS. This affects both the Simple # Chrome workflow, as well as the chromeos-chrome ebuild. 'src/third_party/chromite': { - 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '7ce0019ef66b5c970850870b65100b76cae15a47', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '06404255922ceb03054273b5c35fe2b67cea41f4', 'condition': 'checkout_chromeos', }, @@ -1534,7 +1534,7 @@ Var('chromium_git') + '/webm/libwebp.git' + '@' + '7366f7f394af26de814296152c50e673ed0a832f', 'src/third_party/libyuv': - Var('chromium_git') + '/libyuv/libyuv.git' + '@' + 'fe9ced6e3c8ae6c69bcc3ebb8505a650d2df30e0', + Var('chromium_git') + '/libyuv/libyuv.git' + '@' + '8713ba3f0bddfa19943559981acd5aad2d703d5d', 'src/third_party/lighttpd': { 'url': Var('chromium_git') + '/chromium/deps/lighttpd.git' + '@' + Var('lighttpd_revision'), @@ -1623,7 +1623,7 @@ Var('chromium_git') + '/external/github.com/intel/ARM_NEON_2_x86_SSE.git' + '@' + 'a15b489e1222b2087007546b4912e21293ea86ff', 'src/third_party/netty-tcnative/src': { - 'url': Var('chromium_git') + '/external/netty-tcnative.git' + '@' + '7eeb50be90c9ba0f6afa3375132df63942a0f32d', + 'url': Var('chromium_git') + '/external/netty-tcnative.git' + '@' + '7b6c4ac18e823ba463085d1e44968773e2a82c02', 'condition': 'checkout_android', }, @@ -1664,7 +1664,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + 'd8f0dc3d20c814236d97a042f06ffacd8f2d6b1f', + Var('android_git') + '/platform/external/perfetto.git' + '@' + 'f5b6c4ed868219aefe172ac2d5bd683814a5f001', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1704,7 +1704,7 @@ 'packages': [ { 'package': 'chromium/third_party/r8', - 'version': 'vqsrb5_6fg9u-aSAqrcJiG7q2GOG66_39vdYTKgHmHgC', + 'version': 'zJXokrYtEiaa-jjRGetxUuWf3Zkv_G7Fvl5oCXkhBAsC', }, ], 'condition': 'checkout_android', @@ -1919,7 +1919,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@84f51a9106fa52bd9dfffcc13df752839dc93dc8', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@7a06af5537b39890c9d465509667116ad8b3aaf4', 'condition': 'checkout_src_internal', }, @@ -1960,7 +1960,7 @@ 'packages': [ { 'package': 'chromeos_internal/apps/media_app/app', - 'version': 'UUYSYs5_Q0QL4-I974AWD-A4ETSwXD9bmrOHP99vO1AC', + 'version': 'dPtbu2LkFG30wC5QbPnPVuVRjP7mZG6gadfnS2XzVqcC', }, ], 'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/android_webview/expectations/system_webview_bundle.AndroidManifest.expected b/android_webview/expectations/system_webview_bundle.AndroidManifest.expected index 8c2eda2..b18b638 100644 --- a/android_webview/expectations/system_webview_bundle.AndroidManifest.expected +++ b/android_webview/expectations/system_webview_bundle.AndroidManifest.expected
@@ -14,7 +14,7 @@ <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="33"/> <application android:name="org.chromium.android_webview.nonembedded.WebViewApkApplication" - android:extractNativeLibs="True" + android:extractNativeLibs="false" android:icon="@$PACKAGE:drawable/icon_webview" android:label="Android System WebView" android:multiArch="true"
diff --git a/android_webview/expectations/trichrome_webview_bundle.AndroidManifest.expected b/android_webview/expectations/trichrome_webview_bundle.AndroidManifest.expected index 8d7414d1..2c7b74ca5 100644 --- a/android_webview/expectations/trichrome_webview_bundle.AndroidManifest.expected +++ b/android_webview/expectations/trichrome_webview_bundle.AndroidManifest.expected
@@ -14,7 +14,7 @@ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="33"/> <application android:name="org.chromium.android_webview.nonembedded.WebViewApkApplication" - android:extractNativeLibs="False" + android:extractNativeLibs="false" android:icon="@$PACKAGE:drawable/icon_webview" android:label="Android System WebView" android:multiArch="true"
diff --git a/android_webview/nonembedded/java/AndroidManifest.xml b/android_webview/nonembedded/java/AndroidManifest.xml index f31a77f8f..a6ec0a7 100644 --- a/android_webview/nonembedded/java/AndroidManifest.xml +++ b/android_webview/nonembedded/java/AndroidManifest.xml
@@ -38,7 +38,7 @@ android:name="{{ application_name|default('org.chromium.android_webview.nonembedded.WebViewApkApplication') }}" android:multiArch="true" {{ use32bitAbi|default('android:use32bitAbi="true"') }} - android:extractNativeLibs="{{ trichrome_library is not defined }}"> + android:extractNativeLibs="false"> {# This part is shared between stand-alone WebView and Monochrome #} {% macro common(manifest_package, webview_lib) %} <meta-data android:name="com.android.webview.WebViewLibrary"
diff --git a/android_webview/system_webview_apk_tmpl.gni b/android_webview/system_webview_apk_tmpl.gni index 3d60cf9..52dca0a 100644 --- a/android_webview/system_webview_apk_tmpl.gni +++ b/android_webview/system_webview_apk_tmpl.gni
@@ -140,6 +140,7 @@ _include_primary_support = false _include_secondary_support = false + uncompress_shared_libraries = true if (!_use_trichrome_library) { shared_resources = true @@ -154,7 +155,6 @@ } deps += [ "//third_party/icu:icu_assets" ] } else { - uncompress_shared_libraries = true app_as_shared_lib = true # Include placeholder libraries to ensure we are treated as the desired
diff --git a/ash/clipboard/views/clipboard_history_item_view.cc b/ash/clipboard/views/clipboard_history_item_view.cc index 085ac69..460fc77 100644 --- a/ash/clipboard/views/clipboard_history_item_view.cc +++ b/ash/clipboard/views/clipboard_history_item_view.cc
@@ -45,10 +45,11 @@ void ClipboardHistoryItemView::ContentsView::OnHostPseudoFocusUpdated() { delete_button_->SetVisible(container_->ShouldShowDeleteButton()); - const bool focused = - (container_->pseudo_focus_ == PseudoFocus::kDeleteButton); - views::InkDrop::Get(delete_button_)->GetInkDrop()->SetFocused(focused); - if (focused) { + const bool delete_button_focused = container_->IsDeleteButtonPseudoFocused(); + views::InkDrop::Get(delete_button_) + ->GetInkDrop() + ->SetFocused(delete_button_focused); + if (delete_button_focused) { delete_button_->NotifyAccessibilityEvent(ax::mojom::Event::kHover, /*send_native_event*/ true); } @@ -108,13 +109,12 @@ // When the menu item is disabled, only the delete button is able to work. if (!container_->GetEnabled()) { - DCHECK_EQ(PseudoFocus::kDeleteButton, pseudo_focus_); + DCHECK(IsDeleteButtonPseudoFocused()); SetPseudoFocus(PseudoFocus::kEmpty); return false; } - DCHECK(pseudo_focus_ == PseudoFocus::kMainButton || - pseudo_focus_ == PseudoFocus::kDeleteButton); + DCHECK(IsMainButtonPseudoFocused() || IsDeleteButtonPseudoFocused()); int new_pseudo_focus = pseudo_focus_; bool move_focus_out = false; if (reverse) { @@ -229,10 +229,14 @@ InitiatePseudoFocus(/*reverse=*/false); } -bool ClipboardHistoryItemView::ShouldHighlight() const { +bool ClipboardHistoryItemView::IsMainButtonPseudoFocused() const { return pseudo_focus_ == PseudoFocus::kMainButton; } +bool ClipboardHistoryItemView::IsDeleteButtonPseudoFocused() const { + return pseudo_focus_ == PseudoFocus::kDeleteButton; +} + void ClipboardHistoryItemView::OnMouseClickOnDescendantCanceled() { // When mouse click is canceled, mouse may hover a different menu item from // the one where the click event started. A typical way is to move the mouse @@ -271,6 +275,12 @@ // via AXNodeData::SetName. data->role = ax::mojom::Role::kMenuItem; data->SetNameChecked(GetAccessibleName()); + + // In fitting with existing conventions for menu items, we treat clipboard + // history items as "selected" from an accessibility standpoint if pressing + // Enter will perform the item's default expected action: pasting. + data->AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, + IsMainButtonPseudoFocused()); } void ClipboardHistoryItemView::Activate(Action action, int event_flags) { @@ -308,19 +318,14 @@ } bool ClipboardHistoryItemView::ShouldShowDeleteButton() const { - return (pseudo_focus_ == PseudoFocus::kMainButton && IsMouseHovered()) || - pseudo_focus_ == PseudoFocus::kDeleteButton || - under_gesture_long_press_; + return (IsMainButtonPseudoFocused() && IsMouseHovered()) || + IsDeleteButtonPseudoFocused() || under_gesture_long_press_; } void ClipboardHistoryItemView::InitiatePseudoFocus(bool reverse) { - PseudoFocus target_pseudo_focus; - if (!container_->GetEnabled() || reverse) - target_pseudo_focus = PseudoFocus::kDeleteButton; - else - target_pseudo_focus = PseudoFocus::kMainButton; - - SetPseudoFocus(target_pseudo_focus); + SetPseudoFocus(reverse || !container_->GetEnabled() + ? PseudoFocus::kDeleteButton + : PseudoFocus::kMainButton); } void ClipboardHistoryItemView::SetPseudoFocus(PseudoFocus new_pseudo_focus) { @@ -329,7 +334,7 @@ return; pseudo_focus_ = new_pseudo_focus; - if (pseudo_focus_ == PseudoFocus::kMainButton) { + if (IsMainButtonPseudoFocused()) { NotifyAccessibilityEvent(ax::mojom::Event::kSelection, /*send_native_event=*/true); }
diff --git a/ash/clipboard/views/clipboard_history_item_view.h b/ash/clipboard/views/clipboard_history_item_view.h index 13177bb..9134d73 100644 --- a/ash/clipboard/views/clipboard_history_item_view.h +++ b/ash/clipboard/views/clipboard_history_item_view.h
@@ -5,6 +5,7 @@ #ifndef ASH_CLIPBOARD_VIEWS_CLIPBOARD_HISTORY_ITEM_VIEW_H_ #define ASH_CLIPBOARD_VIEWS_CLIPBOARD_HISTORY_ITEM_VIEW_H_ +#include "ash/ash_export.h" #include "ash/clipboard/clipboard_history_util.h" #include "ui/views/view.h" #include "ui/views/view_targeter_delegate.h" @@ -20,7 +21,7 @@ class ClipboardHistoryResourceManager; // The base class for menu items of the clipboard history menu. -class ClipboardHistoryItemView : public views::View { +class ASH_EXPORT ClipboardHistoryItemView : public views::View { public: static std::unique_ptr<ClipboardHistoryItemView> CreateFromClipboardHistoryItem( @@ -50,8 +51,15 @@ // Called when the selection state has changed. void OnSelectionChanged(); - // Returns whether the highlight background should show. - bool ShouldHighlight() const; + // Returns whether the item's main button has pseudo focus, meaning the item's + // contents will be pasted if the user presses Enter. An item's background is + // highlighted when its main button has pseudo focus. + bool IsMainButtonPseudoFocused() const; + + // Returns whether the item's delete button has pseudo focus, meaning the item + // will be removed from clipboard history if the user presses Enter. An item's + // background is not highlighted when its delete button has pseudo focus. + bool IsDeleteButtonPseudoFocused() const; // Called when the mouse click on descendants (such as the main button or // the delete button) gets canceled.
diff --git a/ash/clipboard/views/clipboard_history_main_button.cc b/ash/clipboard/views/clipboard_history_main_button.cc index 2c0bbd0b..72daa37 100644 --- a/ash/clipboard/views/clipboard_history_main_button.cc +++ b/ash/clipboard/views/clipboard_history_main_button.cc
@@ -55,7 +55,7 @@ ClipboardHistoryMainButton::~ClipboardHistoryMainButton() = default; void ClipboardHistoryMainButton::OnHostPseudoFocusUpdated() { - SetShouldHighlight(container_->ShouldHighlight()); + SetShouldHighlight(container_->IsMainButtonPseudoFocused()); } void ClipboardHistoryMainButton::SetShouldHighlight(bool should_highlight) {
diff --git a/ash/webui/shortcut_customization_ui/backend/BUILD.gn b/ash/webui/shortcut_customization_ui/backend/BUILD.gn index e6227c6d..d86bf626 100644 --- a/ash/webui/shortcut_customization_ui/backend/BUILD.gn +++ b/ash/webui/shortcut_customization_ui/backend/BUILD.gn
@@ -10,6 +10,7 @@ sources = [ "accelerator_configuration_provider.cc", "accelerator_configuration_provider.h", + "shortcut_customization_delegate.h", ] deps = [
diff --git a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.cc b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.cc index fbf51cb..c2c39a5 100644 --- a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.cc +++ b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.cc
@@ -4,6 +4,8 @@ #include "ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h" +#include <memory> +#include <utility> #include <vector> #include "ash/accelerators/accelerator_layout_table.h" @@ -12,6 +14,7 @@ #include "ash/public/cpp/accelerators_util.h" #include "ash/session/session_controller_impl.h" #include "ash/shell.h" +#include "ash/webui/shortcut_customization_ui/backend/shortcut_customization_delegate.h" #include "ash/webui/shortcut_customization_ui/mojom/shortcut_customization.mojom.h" #include "base/containers/fixed_flat_map.h" #include "base/containers/flat_map.h" @@ -132,9 +135,13 @@ namespace shortcut_ui { -AcceleratorConfigurationProvider::AcceleratorConfigurationProvider() +AcceleratorConfigurationProvider::AcceleratorConfigurationProvider( + std::unique_ptr<ShortcutCustomizationDelegate> + shortcut_customization_delegate) : ash_accelerator_configuration_( - Shell::Get()->ash_accelerator_configuration()) { + Shell::Get()->ash_accelerator_configuration()), + shortcut_customization_delegate_( + std::move(shortcut_customization_delegate)) { // Observe connected keyboard events. ui::DeviceDataManager::GetInstance()->AddObserver(this);
diff --git a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h index c5d9927..5187b42b 100644 --- a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h +++ b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h
@@ -5,6 +5,8 @@ #ifndef ASH_WEBUI_SHORTCUT_CUSTOMIZATION_UI_BACKEND_ACCELERATOR_CONFIGURATION_PROVIDER_H_ #define ASH_WEBUI_SHORTCUT_CUSTOMIZATION_UI_BACKEND_ACCELERATOR_CONFIGURATION_PROVIDER_H_ +#include <memory> + #include "ash/public/cpp/accelerator_configuration.h" #include "ash/public/mojom/accelerator_keys.mojom.h" #include "ash/webui/shortcut_customization_ui/mojom/shortcut_customization.mojom.h" @@ -20,6 +22,8 @@ namespace ash { namespace shortcut_ui { +class ShortcutCustomizationDelegate; + class AcceleratorConfigurationProvider : public shortcut_customization::mojom::AcceleratorConfigurationProvider, public ui::InputDeviceEventObserver, @@ -33,7 +37,9 @@ mojom::AcceleratorSource, std::map<AcceleratorActionId, std::vector<ui::Accelerator>>>; - AcceleratorConfigurationProvider(); + explicit AcceleratorConfigurationProvider( + std::unique_ptr<ShortcutCustomizationDelegate> + shortcut_customization_delegate); AcceleratorConfigurationProvider(const AcceleratorConfigurationProvider&) = delete; AcceleratorConfigurationProvider& operator=( @@ -117,6 +123,11 @@ mojo::Remote<shortcut_customization::mojom::AcceleratorsUpdatedObserver> accelerators_updated_observers_; + // Provides browser functionality from //chrome to the Shortcut Customization + // UI. + std::unique_ptr<ShortcutCustomizationDelegate> + shortcut_customization_delegate_; + base::WeakPtrFactory<AcceleratorConfigurationProvider> weak_ptr_factory_{ this}; };
diff --git a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc index 7e203f0..4548fd36 100644 --- a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc +++ b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc
@@ -17,6 +17,7 @@ #include "ash/session/session_controller_impl.h" #include "ash/shell.h" #include "ash/test/ash_test_base.h" +#include "ash/webui/shortcut_customization_ui/backend/shortcut_customization_delegate.h" #include "ash/webui/shortcut_customization_ui/mojom/shortcut_customization.mojom.h" #include "base/bind.h" #include "base/callback_forward.h" @@ -164,6 +165,14 @@ namespace shortcut_ui { +class FakeShortcutCustomizationDelegate : public ShortcutCustomizationDelegate { + public: + FakeShortcutCustomizationDelegate() = default; + ~FakeShortcutCustomizationDelegate() override = default; + + PrefService* GetPrefService() override { return nullptr; } +}; + class AcceleratorConfigurationProviderTest : public AshTestBase { public: AcceleratorConfigurationProviderTest() = default; @@ -203,7 +212,8 @@ AshTestSuite::LoadTestResources(); AshTestBase::SetUp(); - provider_ = std::make_unique<AcceleratorConfigurationProvider>(); + provider_ = std::make_unique<AcceleratorConfigurationProvider>( + std::make_unique<FakeShortcutCustomizationDelegate>()); base::RunLoop().RunUntilIdle(); }
diff --git a/ash/webui/shortcut_customization_ui/backend/shortcut_customization_delegate.h b/ash/webui/shortcut_customization_ui/backend/shortcut_customization_delegate.h new file mode 100644 index 0000000..cb0597d --- /dev/null +++ b/ash/webui/shortcut_customization_ui/backend/shortcut_customization_delegate.h
@@ -0,0 +1,24 @@ +// Copyright 2022 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_WEBUI_SHORTCUT_CUSTOMIZATION_UI_BACKEND_SHORTCUT_CUSTOMIZATION_DELEGATE_H_ +#define ASH_WEBUI_SHORTCUT_CUSTOMIZATION_UI_BACKEND_SHORTCUT_CUSTOMIZATION_DELEGATE_H_ + +#include "components/prefs/pref_service.h" + +namespace ash::shortcut_ui { + +// A delegate which exposes browser functionality from //chrome to the Shortcut +// Customization UI. +class ShortcutCustomizationDelegate { + public: + virtual ~ShortcutCustomizationDelegate() = default; + + // Get the pref service. + virtual PrefService* GetPrefService() = 0; +}; + +} // namespace ash::shortcut_ui + +#endif // ASH_WEBUI_SHORTCUT_CUSTOMIZATION_UI_BACKEND_SHORTCUT_CUSTOMIZATION_DELEGATE_H_ \ No newline at end of file
diff --git a/ash/webui/shortcut_customization_ui/resources/js/accelerator_subsection.html b/ash/webui/shortcut_customization_ui/resources/js/accelerator_subsection.html index f9ff33a..04b2804 100644 --- a/ash/webui/shortcut_customization_ui/resources/js/accelerator_subsection.html +++ b/ash/webui/shortcut_customization_ui/resources/js/accelerator_subsection.html
@@ -13,11 +13,12 @@ <div id="container" part="container"> <div id="title">[[title]]</div> <div id="rowList"> - <template id="list" is="dom-repeat" items="[[acceleratorContainer]]"> - <accelerator-row class="acceleratorRow" description="[[item.description]]" - action="[[item.action]]" - source="[[item.source]]" - accelerator-infos="[[item.acceleratorInfos]]"> + <template id="list" is="dom-repeat" items="[[accelRowDataArray]]"> + <accelerator-row + accelerator-infos="[[item.acceleratorInfos]]" + action="[[item.layoutInfo.action]]" + description="[[item.layoutInfo.description]]" + source="[[item.layoutInfo.source]]"> </accelerator-row> </template> </div>
diff --git a/ash/webui/shortcut_customization_ui/resources/js/accelerator_subsection.ts b/ash/webui/shortcut_customization_ui/resources/js/accelerator_subsection.ts index fac1f00e..f694a56d 100644 --- a/ash/webui/shortcut_customization_ui/resources/js/accelerator_subsection.ts +++ b/ash/webui/shortcut_customization_ui/resources/js/accelerator_subsection.ts
@@ -1,4 +1,4 @@ -// Copyright 2021 The Chromium Authors +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -9,13 +9,15 @@ import {AcceleratorLookupManager} from './accelerator_lookup_manager.js'; import {getTemplate} from './accelerator_subsection.html.js'; import {fakeSubCategories} from './fake_data.js'; -import {AcceleratorCategory, AcceleratorInfo, AcceleratorState, AcceleratorSubcategory, AcceleratorType} from './shortcut_types.js'; +import {AcceleratorCategory, AcceleratorInfo, AcceleratorState, AcceleratorSubcategory, AcceleratorType, LayoutInfo} from './shortcut_types.js'; -export interface Accelerator { - description: string; - action: number; - source: number; +/** + * This interface is used to hold all the data needed by an + * AcceleratorRowElement. + */ +interface AcceleratorRowData { acceleratorInfos: AcceleratorInfo[]; + layoutInfo: LayoutInfo; } export interface AcceleratorSubsectionElement { @@ -67,7 +69,7 @@ override title: string; category: AcceleratorCategory; subcategory: AcceleratorSubcategory; - acceleratorContainer: Accelerator[]; + accelRowDataArray: AcceleratorRowData[]; private lookupManager_: AcceleratorLookupManager = AcceleratorLookupManager.getInstance(); @@ -98,26 +100,23 @@ // updates as one which results in strange behaviors with updating // individual subsections. An atomic replacement makes ensures each // subsection's accelerators are kept distinct from each other. - const tempAccelContainer: Accelerator[] = []; - layoutInfos!.forEach((value) => { - const acceleratorInfos = - this.lookupManager_.getAcceleratorInfos(value.source, value.action); - acceleratorInfos!.filter((accel) => { + const tempAccelRowData: AcceleratorRowData[] = []; + layoutInfos!.forEach((layoutInfo) => { + const acceleratorInfos = this.lookupManager_.getAcceleratorInfos( + layoutInfo.source, layoutInfo.action); + acceleratorInfos.filter((accel) => { // Hide accelerators that are default and disabled. return !( accel.type === AcceleratorType.kDefault && accel.state === AcceleratorState.kDisabledByUser); }); - const accel: Accelerator = { - description: - this.lookupManager_.getAcceleratorName(value.source, value.action), - action: value.action, - source: value.source, - acceleratorInfos: acceleratorInfos!, + const accelRowData: AcceleratorRowData = { + layoutInfo, + acceleratorInfos, }; - tempAccelContainer.push(accel); + tempAccelRowData.push(accelRowData); }); - this.acceleratorContainer = tempAccelContainer; + this.accelRowDataArray = tempAccelRowData; } static get template() {
diff --git a/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.cc b/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.cc index 22168dab..8ef7fdc0 100644 --- a/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.cc +++ b/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.cc
@@ -10,6 +10,7 @@ #include "ash/webui/grit/ash_shortcut_customization_app_resources.h" #include "ash/webui/grit/ash_shortcut_customization_app_resources_map.h" #include "ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h" +#include "ash/webui/shortcut_customization_ui/backend/shortcut_customization_delegate.h" #include "ash/webui/shortcut_customization_ui/mojom/shortcut_customization.mojom.h" #include "ash/webui/shortcut_customization_ui/url_constants.h" #include "chromeos/strings/grit/chromeos_strings.h" @@ -54,7 +55,10 @@ } // namespace -ShortcutCustomizationAppUI::ShortcutCustomizationAppUI(content::WebUI* web_ui) +ShortcutCustomizationAppUI::ShortcutCustomizationAppUI( + content::WebUI* web_ui, + std::unique_ptr<shortcut_ui::ShortcutCustomizationDelegate> + shortcut_customization_delegate) : ui::MojoWebUIController(web_ui) { content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd( web_ui->GetWebContents()->GetBrowserContext(), @@ -75,7 +79,10 @@ AddFeatureFlags(source); - provider_ = std::make_unique<shortcut_ui::AcceleratorConfigurationProvider>(); + // TODO(longbowei): Use GetPrefService() to get pref_service and + // add it as a param for AcceleratorConfigurationProvider. + provider_ = std::make_unique<shortcut_ui::AcceleratorConfigurationProvider>( + std::move(shortcut_customization_delegate)); } ShortcutCustomizationAppUI::~ShortcutCustomizationAppUI() = default;
diff --git a/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.h b/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.h index b3753ef..45ee5ffa 100644 --- a/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.h +++ b/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.h
@@ -20,20 +20,29 @@ namespace ash { +namespace shortcut_ui { +class ShortcutCustomizationDelegate; +} // namespace shortcut_ui + class ShortcutCustomizationAppUI; // The WebUIConfig for chrome://shortcut-customization. class ShortcutCustomizationAppUIConfig : public SystemWebAppUIConfig<ShortcutCustomizationAppUI> { public: - ShortcutCustomizationAppUIConfig() + explicit ShortcutCustomizationAppUIConfig( + SystemWebAppUIConfig::CreateWebUIControllerFunc create_controller_func) : SystemWebAppUIConfig(kChromeUIShortcutCustomizationAppHost, - SystemWebAppType::SHORTCUT_CUSTOMIZATION) {} + SystemWebAppType::SHORTCUT_CUSTOMIZATION, + create_controller_func) {} }; class ShortcutCustomizationAppUI : public ui::MojoWebUIController { public: - explicit ShortcutCustomizationAppUI(content::WebUI* web_ui); + ShortcutCustomizationAppUI( + content::WebUI* web_ui, + std::unique_ptr<shortcut_ui::ShortcutCustomizationDelegate> + shortcut_customization_delegate); ShortcutCustomizationAppUI(const ShortcutCustomizationAppUI&) = delete; ShortcutCustomizationAppUI& operator=(const ShortcutCustomizationAppUI&) = delete;
diff --git a/ash/wm/desks/desk.cc b/ash/wm/desks/desk.cc index f451090..51442fe 100644 --- a/ash/wm/desks/desk.cc +++ b/ash/wm/desks/desk.cc
@@ -140,32 +140,6 @@ } } -// Returns true for windows that are interesting from an all-desk z-order -// tracking perspective. -bool IsZOrderTracked(aura::Window* window) { - return window->GetType() == aura::client::WindowType::WINDOW_TYPE_NORMAL && - window->GetProperty(aura::client::kZOrderingKey) == - ui::ZOrderLevel::kNormal; -} - -// Get the position of `window` in `windows` (as filtered by `IsZOrderTracked`) -// in reverse order. If `window` is not in the list (or isn't z-order tracked), -// then nullopt is returned. -absl::optional<size_t> GetWindowZOrder( - const std::vector<aura::Window*>& windows, - aura::Window* window) { - size_t position = 0; - for (aura::Window* w : base::Reversed(windows)) { - if (IsZOrderTracked(w)) { - if (w == window) - return position; - ++position; - } - } - - return absl::nullopt; -} - // Used to temporarily turn off the automatic window positioning while windows // are being moved between desks. class ScopedWindowPositionerDisabler { @@ -367,7 +341,8 @@ // Find z-order of the added window. auto* container = GetDeskContainerForRoot(root); - if (auto order = GetWindowZOrder(container->children(), window)) { + if (auto order = + desks_util::GetWindowZOrder(container->children(), window)) { for (auto& adw : adw_data) { // All desk windows that are below the added window will have their // order updated (since they are now farther from the top). @@ -427,7 +402,7 @@ return; aura::Window* container = GetDeskContainerForRoot(root); - if (auto order = GetWindowZOrder(container->children(), window)) { + if (auto order = desks_util::GetWindowZOrder(container->children(), window)) { for (auto& info : adw_data) { // All-desk windows that are below the removed window will have their // order updated (since they are now closer to the top). @@ -831,7 +806,7 @@ adw_data.clear(); size_t order = 0; for (aura::Window* window : base::Reversed(desk_windows)) { - if (IsZOrderTracked(window)) { + if (desks_util::IsZOrderTracked(window)) { if (desks_util::IsWindowVisibleOnAllWorkspaces(window)) adw_data.push_back({.window = window, .order = order}); ++order;
diff --git a/ash/wm/desks/desk.h b/ash/wm/desks/desk.h index 5810f70..7e64d975 100644 --- a/ash/wm/desks/desk.h +++ b/ash/wm/desks/desk.h
@@ -50,6 +50,18 @@ virtual void OnDeskNameChanged(const std::u16string& new_name) = 0; }; + // Tracks stacking order for a window that is visible on all desks. This is + // used to support per-desk z-orders for all-desk windows. Entries are stored + // in ascending `order`. + struct AllDeskWindowStackingData { + aura::Window* window = nullptr; + // The z-order of the window. + // Note: this is reversed from how child windows are ordered in + // `aura::Window`, so an entry with `order == 0` means topmost. + // Note: this order ignores non-normal windows. + size_t order = 0; + }; + explicit Desk(int associated_container_id, bool desk_being_restored = false); Desk(const Desk&) = delete; @@ -101,6 +113,11 @@ interacted_with_this_week_ = interacted_with_this_week; } + const base::flat_map<aura::Window*, std::vector<AllDeskWindowStackingData>>& + all_desk_window_stacking() const { + return all_desk_window_stacking_; + } + void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); @@ -290,18 +307,6 @@ int first_day_visited_ = -1; int last_day_visited_ = -1; - // Tracks stacking order for a window that is visible on all desks. This is - // used to support per-desk z-orders for all-desk windows. Entries are stored - // in ascending `order`. - struct AllDeskWindowStackingData { - aura::Window* window = nullptr; - // The z-order of the window. - // Note: this is reversed from how child windows are ordered in - // `aura::Window`, so an entry with `order == 0` means topmost. - // Note: this order ignores non-normal windows. - size_t order = 0; - }; - // Stacking data for all all-desk windows. Ordered from topmost and // down. Keyed by root window. base::flat_map<aura::Window*, std::vector<AllDeskWindowStackingData>>
diff --git a/ash/wm/desks/desk_preview_view.cc b/ash/wm/desks/desk_preview_view.cc index f4617f2..fa781d5 100644 --- a/ash/wm/desks/desk_preview_view.cc +++ b/ash/wm/desks/desk_preview_view.cc
@@ -117,16 +117,37 @@ return iter == layers_data.end() ? LayerData{} : iter->second; } +// Get the z-order of all-desk `window` in `desk` for `root`. If it does not +// exist, then nullopt is returned. Please note, the z-order information is +// retrieved from the stored stacking data of `desk` for all-desk windows. +absl::optional<size_t> GetWindowZOrderForDeskAndRoot(const aura::Window* window, + const Desk* desk, + const aura::Window* root) { + const auto& adw_by_root = desk->all_desk_window_stacking(); + + if (auto it = adw_by_root.find(root); it != adw_by_root.end()) { + for (auto& adw : it->second) { + if (adw.window == window) + return adw.order; + } + } + + return absl::nullopt; +} + // Appends clones of all the visible on all desks windows' layers to -// |out_desk_container_children|. Should only be called if -// |visible_on_all_desks_windows| is not empty. +// `out_desk_container_children`. Should only be called if +// `visible_on_all_desks_windows` is not empty. void AppendVisibleOnAllDesksWindowsToDeskLayer( const base::flat_set<aura::Window*>& visible_on_all_desks_windows, const base::flat_map<ui::Layer*, LayerData>& layers_data, - std::vector<ui::Layer*>* out_desk_container_children) { + std::vector<ui::Layer*>* out_desk_container_children, + aura::Window* desk_container) { DCHECK(!visible_on_all_desks_windows.empty()); auto mru_windows = Shell::Get()->mru_window_tracker()->BuildMruWindowList(kAllDesks); + const Desk* desk = desks_util::GetDeskForContext(desk_container); + aura::Window* root = desk_container->GetRootWindow(); for (auto* window : visible_on_all_desks_windows) { const LayerData layer_data = @@ -138,36 +159,53 @@ if (window_iter == mru_windows.end()) continue; - auto closest_window_below_iter = std::next(window_iter); - while (closest_window_below_iter != mru_windows.end() && - !base::Contains(*out_desk_container_children, - (*closest_window_below_iter)->layer())) { - // Find the closest window to |window| in the MRU tracker whose layer also - // is in |out_desk_container_children|. This window will be used to - // determine the stacking order of the visible on all desks window in the - // preview view. - closest_window_below_iter = std::next(closest_window_below_iter); + auto insertion_point_iter = out_desk_container_children->end(); + auto desk_windows = desk_container->children(); + + // Find z order of `window`. If `features::IsPerDeskZOrderEnabled()` is not + // on, default value of zero will be used so `window` would be put on top. + size_t window_order = + GetWindowZOrderForDeskAndRoot(window, desk, root).value_or(0); + + // If `desk` has no child window, or `window` has lowest z order, use + // default `insertion_point_iter` to put it on top. + if (!desk_windows.empty() && window_order) { + // Find the nearest window that should be on top of `window`. + size_t order = 0, target_idx = desk_windows.size(); + for (int i = desk_windows.size() - 1; i >= 0 && order < window_order; + i--) { + if (desks_util::IsZOrderTracked(window)) { + target_idx = static_cast<size_t>(i); + ++order; + } + } + + // Move to the next nearest window until its layer is in the + // `out_desk_container_children`. + for (size_t i = target_idx; i < desk_windows.size(); i++) { + if (base::Contains(*out_desk_container_children, + desk_windows[i]->layer())) { + insertion_point_iter = base::ranges::find( + *out_desk_container_children, desk_windows[i]->layer()); + break; + } + } } - auto insertion_point_iter = - closest_window_below_iter == mru_windows.end() - ? out_desk_container_children->begin() - : std::next( - base::ranges::find(*out_desk_container_children, - (*closest_window_below_iter)->layer())); out_desk_container_children->insert(insertion_point_iter, window->layer()); } } -// Recursively mirrors |source_layer| and its children and adds them as children -// of |parent|, taking into account the given |layers_data|. If the layer data -// of |source_layer| has |should_clear_transform| set to true, the transforms of +// Recursively mirrors `source_layer` and its children and adds them as children +// of `parent`, taking into account the given |layers_data|. If the layer data +// of `source_layer` has `should_clear_transform` set to true, the transforms of // its mirror layers will be reset to identity. -void MirrorLayerTree(ui::Layer* source_layer, - ui::Layer* parent, - const base::flat_map<ui::Layer*, LayerData>& layers_data, - const base::flat_set<aura::Window*>& - visible_on_all_desks_windows_to_mirror) { +void MirrorLayerTree( + ui::Layer* source_layer, + ui::Layer* parent, + const base::flat_map<ui::Layer*, LayerData>& layers_data, + const base::flat_set<aura::Window*>& visible_on_all_desks_windows_to_mirror, + aura::Window* desk_container) { const LayerData layer_data = GetLayerDataEntry(layers_data, source_layer); if (layer_data.should_skip_layer) return; @@ -181,13 +219,14 @@ // preview so for inactive desks, we need to append the layers of visible on // all desks windows. AppendVisibleOnAllDesksWindowsToDeskLayer( - visible_on_all_desks_windows_to_mirror, layers_data, &children); + visible_on_all_desks_windows_to_mirror, layers_data, &children, + desk_container); } for (auto* child : children) { // Visible on all desks windows only needed to be added to the subtree once // so use an empty set for subsequent calls. - MirrorLayerTree(child, mirror, layers_data, - base::flat_set<aura::Window*>()); + MirrorLayerTree(child, mirror, layers_data, base::flat_set<aura::Window*>(), + desk_container); } mirror->set_sync_bounds_with_source(true); @@ -378,14 +417,16 @@ auto* desk_container_layer = desk_container->layer(); MirrorLayerTree(desk_container_layer, mirrored_content_root_layer.get(), - layers_data, visible_on_all_desks_windows_to_mirror); + layers_data, visible_on_all_desks_windows_to_mirror, + desk_container); // Since floated window is not stored in desk container, we need to mirror it // separately. if (floated_window) { auto* floated_window_layer = floated_window->layer(); MirrorLayerTree(floated_window_layer, mirrored_content_root_layer.get(), - layers_data, /*visible_on_all_desks_windows_to_mirror=*/{}); + layers_data, /*visible_on_all_desks_windows_to_mirror=*/{}, + desk_container); } // Add the root of the mirrored layer tree as a child of the
diff --git a/ash/wm/desks/desks_util.cc b/ash/wm/desks/desks_util.cc index b6c3e991..deb7c71 100644 --- a/ash/wm/desks/desks_util.cc +++ b/ash/wm/desks/desks_util.cc
@@ -18,6 +18,7 @@ #include "ash/wm/overview/overview_session.h" #include "ash/wm/window_state.h" #include "ash/wm/window_util.h" +#include "base/containers/adapters.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/window.h" #include "ui/compositor/layer.h" @@ -194,6 +195,27 @@ aura::client::kWindowWorkspaceVisibleOnAllWorkspaces; } +bool IsZOrderTracked(aura::Window* window) { + return window->GetType() == aura::client::WindowType::WINDOW_TYPE_NORMAL && + window->GetProperty(aura::client::kZOrderingKey) == + ui::ZOrderLevel::kNormal; +} + +absl::optional<size_t> GetWindowZOrder( + const std::vector<aura::Window*>& windows, + aura::Window* window) { + size_t position = 0; + for (aura::Window* w : base::Reversed(windows)) { + if (IsZOrderTracked(w)) { + if (w == window) + return position; + ++position; + } + } + + return absl::nullopt; +} + } // namespace desks_util } // namespace ash
diff --git a/ash/wm/desks/desks_util.h b/ash/wm/desks/desks_util.h index 03aafa5..49157722 100644 --- a/ash/wm/desks/desks_util.h +++ b/ash/wm/desks/desks_util.h
@@ -80,6 +80,17 @@ // Returns whether a |window| is visible on all workspaces. ASH_EXPORT bool IsWindowVisibleOnAllWorkspaces(const aura::Window* window); +// Returns true for windows that are interesting from an all-desk z-order +// tracking perspective. +ASH_EXPORT bool IsZOrderTracked(aura::Window* window); + +// Get the position of `window` in `windows` (as filtered by `IsZOrderTracked`) +// in reverse order. If `window` is not in the list (or isn't z-order tracked), +// then nullopt is returned. +ASH_EXPORT absl::optional<size_t> GetWindowZOrder( + const std::vector<aura::Window*>& windows, + aura::Window* window); + // Move an item at |old_index| to |new_index|. template <typename T> ASH_EXPORT void ReorderItem(std::vector<T>& items,
diff --git a/base/BUILD.gn b/base/BUILD.gn index c452179..1ad64d9 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -2381,8 +2381,6 @@ "trace_event/builtin_categories.h", "trace_event/category_registry.cc", "trace_event/category_registry.h", - "trace_event/event_name_filter.cc", - "trace_event/event_name_filter.h", "trace_event/heap_profiler.h", "trace_event/interned_args_helper.cc", "trace_event/interned_args_helper.h", @@ -2423,8 +2421,6 @@ "trace_event/trace_config_category_filter.h", "trace_event/trace_conversion_helper.h", "trace_event/trace_event.h", - "trace_event/trace_event_filter.cc", - "trace_event/trace_event_filter.h", "trace_event/trace_event_impl.cc", "trace_event/trace_event_impl.h", "trace_event/trace_event_memory_overhead.cc", @@ -3951,7 +3947,6 @@ if (enable_base_tracing) { sources += [ "test/trace_event_analyzer_unittest.cc", - "trace_event/event_name_filter_unittest.cc", "trace_event/heap_profiler_allocation_context_tracker_unittest.cc", "trace_event/memory_allocator_dump_unittest.cc", "trace_event/memory_dump_manager_unittest.cc", @@ -3963,8 +3958,6 @@ "trace_event/trace_category_unittest.cc", "trace_event/trace_config_unittest.cc", "trace_event/trace_conversion_helper_unittest.cc", - "trace_event/trace_event_filter_test_utils.cc", - "trace_event/trace_event_filter_test_utils.h", "trace_event/trace_event_unittest.cc", "trace_event/traced_value_support_unittest.cc", "trace_event/traced_value_unittest.cc",
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java index 3477c38b..312db52 100644 --- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java +++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -13,7 +13,6 @@ import android.system.Os; import androidx.annotation.IntDef; -import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import org.chromium.base.BaseSwitches; @@ -29,7 +28,6 @@ import org.chromium.base.TraceEvent; import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.NativeMethods; -import org.chromium.base.compat.ApiHelperForM; import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.UmaRecorderHolder; import org.chromium.build.BuildConfig; @@ -38,7 +36,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Locale; import javax.annotation.concurrent.GuardedBy; @@ -417,16 +414,6 @@ return mMediator; } - /** - * Call this method to determine if the chromium project must load the library - * directly from a zip file. - */ - private static boolean isInZipFile() { - // The auto-generated NativeLibraries.sUseLibraryInZipFile variable will be true - // iff the library remains embedded in the APK zip file on the target. - return NativeLibraries.sUseLibraryInZipFile; - } - public static LibraryLoader getInstance() { return sInstance; } @@ -507,26 +494,13 @@ useChromiumLinker(), mUseModernLinker); } - // LegacyLinker is buggy on Android 10, causing crashes (see crbug.com/980304). - // - // Rather than preventing people from running chrome_public_apk on Android 10, fallback to the - // system linker on this platform. We lose relocation sharing as a side-effect, but this - // configuration does not ship to users (since we only use LegacyLinker for APKs targeted at - // pre-N users). - // - // Note: This cannot be done in the build configuration, as otherwise chrome_public_apk cannot - // both be used as the basis to ship on L, and the default APK used by developers on 10+. - private boolean forceSystemLinker() { - return mUseChromiumLinker && !mUseModernLinker - && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q; - } - // Whether a Linker subclass is used for loading. Even if returns |true|, the Linker can // fall back to using the system dynamic linker on failure. Also it is common for App Zygote to // choose loading with the system linker when sharing RELRO with the browser process is not // supported. + // TODO(crbug.com/1383210): Remove the LegacyLinker and fold the ModernLinker into Linker. private boolean useChromiumLinker() { - return mUseChromiumLinker && !forceSystemLinker(); + return mUseChromiumLinker && mUseModernLinker; } /** @@ -803,50 +777,20 @@ private void loadWithChromiumLinker(ApplicationInfo appInfo, String library) { Linker linker = getLinker(); - - if (isInZipFile()) { - String sourceDir = appInfo.sourceDir; - linker.setApkFilePath(sourceDir); - Log.i(TAG, "Loading %s from within %s", library, sourceDir); - } else { - Log.i(TAG, "Loading %s", library); - } - + String sourceDir = appInfo.sourceDir; + linker.setApkFilePath(sourceDir); + Log.i(TAG, "Loading %s from within %s", library, sourceDir); linker.loadLibrary(library); // May throw UnsatisfiedLinkError. getMediator().recordLinkerHistogramsAfterLibraryLoad(); } @GuardedBy("mLock") - @SuppressLint({"UnsafeDynamicallyLoadedCode", "ObsoleteSdkInt"}) + @SuppressLint({"UnsafeDynamicallyLoadedCode"}) private void loadWithSystemLinkerAlreadyLocked(ApplicationInfo appInfo, boolean inZygote) { setEnvForNative(); preloadAlreadyLocked(appInfo.packageName, inZygote); - - // If the libraries are located in the zip file, assert that the device API level is M or - // higher. On devices <=M, the libraries should always be loaded by LegacyLinker. - assert !isInZipFile() || Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; - - // Load libraries using the system linker. for (String library : NativeLibraries.LIBRARIES) { - // TODO(crbug.com/1337134): Always use System.loadLibrary(). - boolean isTrichrome = !forceSystemLinker() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q; - if (!isInZipFile() || isTrichrome) { - System.loadLibrary(library); - } else { - // Load directly from the APK. - boolean is64Bit = ApiHelperForM.isProcess64Bit(); - String zipFilePath = appInfo.sourceDir; - boolean crazyPrefix = forceSystemLinker(); // See comment in this function. - String fullPath = zipFilePath + "!/" - + makeLibraryPathInZipFile(library, crazyPrefix, is64Bit); - Log.i(TAG, "libraryName: %s", fullPath); - if (crazyPrefix) { - Log.w(TAG, - "Forcing system linker, relocations will not be shared. " - + "This negatively impacts memory usage."); - } - System.load(fullPath); - } + System.loadLibrary(library); } } @@ -919,46 +863,6 @@ } } - /** - * @param library The library name that is looking for. - * @param crazyPrefix true iff adding crazy linker prefix to the file name. - * @param is64Bit true if the caller think it's run on a 64 bit device. - * @return the library path name in the zip file. - */ - @NonNull - public static String makeLibraryPathInZipFile( - String library, boolean crazyPrefix, boolean is64Bit) { - // Determine the ABI string that Android uses to find native libraries. Values are described - // in: https://developer.android.com/ndk/guides/abis.html - // The 'armeabi' is omitted here because it is not supported in Chrome/WebView, while Cronet - // and Cast load the native library via other paths. - String cpuAbi; - switch (NativeLibraries.sCpuFamily) { - case NativeLibraries.CPU_FAMILY_ARM: - cpuAbi = is64Bit ? "arm64-v8a" : "armeabi-v7a"; - break; - case NativeLibraries.CPU_FAMILY_X86: - cpuAbi = is64Bit ? "x86_64" : "x86"; - break; - case NativeLibraries.CPU_FAMILY_MIPS: - cpuAbi = is64Bit ? "mips64" : "mips"; - break; - default: - throw new RuntimeException("Unknown CPU ABI for native libraries"); - } - - // When both the Chromium linker and zip-uncompressed native libraries are used, - // the build system renames the native shared libraries with a 'crazy.' prefix - // (e.g. "/lib/armeabi-v7a/libfoo.so" -> "/lib/armeabi-v7a/crazy.libfoo.so"). - // - // This prevents the package manager from extracting them at installation/update time - // to the /data directory. The libraries can still be accessed directly by the Chromium - // linker from the APK. - String crazyPart = crazyPrefix ? "crazy." : ""; - return String.format( - Locale.US, "lib/%s/%s%s", cpuAbi, crazyPart, System.mapLibraryName(library)); - } - // The WebView requires the Command Line to be switched over before // initialization is done. This is okay in the WebView's case since the // JNI is already loaded by this point.
diff --git a/base/trace_event/event_name_filter.cc b/base/trace_event/event_name_filter.cc deleted file mode 100644 index 1078cc4a..0000000 --- a/base/trace_event/event_name_filter.cc +++ /dev/null
@@ -1,26 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/event_name_filter.h" - -#include "base/trace_event/trace_event_impl.h" - -namespace base { -namespace trace_event { - -// static -const char EventNameFilter::kName[] = "event_whitelist_predicate"; - -EventNameFilter::EventNameFilter( - std::unique_ptr<EventNamesAllowlist> event_names_allowlist) - : event_names_allowlist_(std::move(event_names_allowlist)) {} - -EventNameFilter::~EventNameFilter() = default; - -bool EventNameFilter::FilterTraceEvent(const TraceEvent& trace_event) const { - return event_names_allowlist_->count(trace_event.name()) != 0; -} - -} // namespace trace_event -} // namespace base
diff --git a/base/trace_event/event_name_filter.h b/base/trace_event/event_name_filter.h deleted file mode 100644 index fd9723c..0000000 --- a/base/trace_event/event_name_filter.h +++ /dev/null
@@ -1,47 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_EVENT_NAME_FILTER_H_ -#define BASE_TRACE_EVENT_EVENT_NAME_FILTER_H_ - -#include <memory> -#include <string> -#include <unordered_set> - -#include "base/base_export.h" -#include "base/trace_event/trace_event_filter.h" - -namespace base { -namespace trace_event { - -class TraceEvent; - -// Filters trace events by checking the full name against an allowlist. -// The current implementation is quite simple and dumb and just uses a -// hashtable which requires char* to std::string conversion. It could be smarter -// and use a bloom filter trie. However, today this is used too rarely to -// justify that cost. -class BASE_EXPORT EventNameFilter : public TraceEventFilter { - public: - using EventNamesAllowlist = std::unordered_set<std::string>; - static const char kName[]; - - EventNameFilter(std::unique_ptr<EventNamesAllowlist>); - - EventNameFilter(const EventNameFilter&) = delete; - EventNameFilter& operator=(const EventNameFilter&) = delete; - - ~EventNameFilter() override; - - // TraceEventFilter implementation. - bool FilterTraceEvent(const TraceEvent&) const override; - - private: - std::unique_ptr<const EventNamesAllowlist> event_names_allowlist_; -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_EVENT_NAME_FILTER_H_
diff --git a/base/trace_event/event_name_filter_unittest.cc b/base/trace_event/event_name_filter_unittest.cc deleted file mode 100644 index 3595a24..0000000 --- a/base/trace_event/event_name_filter_unittest.cc +++ /dev/null
@@ -1,41 +0,0 @@ -// Copyright 2015 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/event_name_filter.h" - -#include "base/memory/ptr_util.h" -#include "base/trace_event/trace_event_impl.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace trace_event { - -const TraceEvent& MakeTraceEvent(const char* name) { - static TraceEvent event; - event.Reset(0, TimeTicks(), ThreadTicks(), 'b', nullptr, name, "", 0, 0, - nullptr, 0); - return event; -} - -TEST(TraceEventNameFilterTest, Allowlist) { - auto empty_allowlist = - std::make_unique<EventNameFilter::EventNamesAllowlist>(); - auto filter = std::make_unique<EventNameFilter>(std::move(empty_allowlist)); - - // No events should be filtered if the allowlist is empty. - EXPECT_FALSE(filter->FilterTraceEvent(MakeTraceEvent("foo"))); - - auto allowlist = std::make_unique<EventNameFilter::EventNamesAllowlist>(); - allowlist->insert("foo"); - allowlist->insert("bar"); - filter = std::make_unique<EventNameFilter>(std::move(allowlist)); - EXPECT_TRUE(filter->FilterTraceEvent(MakeTraceEvent("foo"))); - EXPECT_FALSE(filter->FilterTraceEvent(MakeTraceEvent("fooz"))); - EXPECT_FALSE(filter->FilterTraceEvent(MakeTraceEvent("afoo"))); - EXPECT_TRUE(filter->FilterTraceEvent(MakeTraceEvent("bar"))); - EXPECT_FALSE(filter->FilterTraceEvent(MakeTraceEvent("foobar"))); -} - -} // namespace trace_event -} // namespace base
diff --git a/base/trace_event/trace_event_filter.cc b/base/trace_event/trace_event_filter.cc deleted file mode 100644 index c329b2e..0000000 --- a/base/trace_event/trace_event_filter.cc +++ /dev/null
@@ -1,17 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/trace_event_filter.h" - -namespace base { -namespace trace_event { - -TraceEventFilter::TraceEventFilter() = default; -TraceEventFilter::~TraceEventFilter() = default; - -void TraceEventFilter::EndEvent(const char* category_name, - const char* event_name) const {} - -} // namespace trace_event -} // namespace base
diff --git a/base/trace_event/trace_event_filter.h b/base/trace_event/trace_event_filter.h deleted file mode 100644 index 79e576f..0000000 --- a/base/trace_event/trace_event_filter.h +++ /dev/null
@@ -1,49 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_TRACE_EVENT_FILTER_H_ -#define BASE_TRACE_EVENT_TRACE_EVENT_FILTER_H_ - -#include "base/base_export.h" - -namespace base { -namespace trace_event { - -class TraceEvent; - -// TraceEventFilter is like iptables for TRACE_EVENT macros. Filters can be -// enabled on a per-category basis, hence a single filter instance can serve -// more than a TraceCategory. There are two use cases for filters: -// 1. Snooping TRACE_EVENT macros without adding them to the TraceLog. This is -// possible by setting the ENABLED_FOR_FILTERING flag on a category w/o -// ENABLED_FOR_RECORDING (see TraceConfig for user-facing configuration). -// 2. Filtering TRACE_EVENT macros before they are added to the TraceLog. This -// requires both the ENABLED_FOR_FILTERING and ENABLED_FOR_RECORDING flags -// on the category. -// More importantly, filters must be thread-safe. The FilterTraceEvent and -// EndEvent methods can be called concurrently as trace macros are hit on -// different threads. -class BASE_EXPORT TraceEventFilter { - public: - TraceEventFilter(); - - TraceEventFilter(const TraceEventFilter&) = delete; - TraceEventFilter& operator=(const TraceEventFilter&) = delete; - - virtual ~TraceEventFilter(); - - // If the category is ENABLED_FOR_RECORDING, the event is added iff all the - // filters enabled for the category return true. false causes the event to be - // discarded. - virtual bool FilterTraceEvent(const TraceEvent& trace_event) const = 0; - - // Notifies the end of a duration event when the RAII macro goes out of scope. - virtual void EndEvent(const char* category_name, - const char* event_name) const; -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_TRACE_EVENT_FILTER_H_
diff --git a/base/trace_event/trace_event_filter_test_utils.cc b/base/trace_event/trace_event_filter_test_utils.cc deleted file mode 100644 index e3a3da0..0000000 --- a/base/trace_event/trace_event_filter_test_utils.cc +++ /dev/null
@@ -1,63 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/trace_event_filter_test_utils.h" - -#include <memory> - -#include "base/check.h" - -namespace base { -namespace trace_event { - -namespace { -TestEventFilter::HitsCounter* g_hits_counter; -} // namespace; - -// static -const char TestEventFilter::kName[] = "testing_predicate"; -bool TestEventFilter::filter_return_value_; - -// static -std::unique_ptr<TraceEventFilter> TestEventFilter::Factory( - const std::string& predicate_name) { - std::unique_ptr<TraceEventFilter> res; - if (predicate_name == kName) - res = std::make_unique<TestEventFilter>(); - return res; -} - -TestEventFilter::TestEventFilter() = default; -TestEventFilter::~TestEventFilter() = default; - -bool TestEventFilter::FilterTraceEvent(const TraceEvent& trace_event) const { - if (g_hits_counter) - g_hits_counter->filter_trace_event_hit_count++; - return filter_return_value_; -} - -void TestEventFilter::EndEvent(const char* category_name, - const char* name) const { - if (g_hits_counter) - g_hits_counter->end_event_hit_count++; -} - -TestEventFilter::HitsCounter::HitsCounter() { - Reset(); - DCHECK(!g_hits_counter); - g_hits_counter = this; -} - -TestEventFilter::HitsCounter::~HitsCounter() { - DCHECK(g_hits_counter); - g_hits_counter = nullptr; -} - -void TestEventFilter::HitsCounter::Reset() { - filter_trace_event_hit_count = 0; - end_event_hit_count = 0; -} - -} // namespace trace_event -} // namespace base
diff --git a/base/trace_event/trace_event_filter_test_utils.h b/base/trace_event/trace_event_filter_test_utils.h deleted file mode 100644 index 26f9c2a..0000000 --- a/base/trace_event/trace_event_filter_test_utils.h +++ /dev/null
@@ -1,52 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_TRACE_EVENT_FILTER_TEST_UTILS_H_ -#define BASE_TRACE_EVENT_TRACE_EVENT_FILTER_TEST_UTILS_H_ - -#include <memory> -#include <string> - -#include "base/trace_event/trace_event_filter.h" - -namespace base { -namespace trace_event { - -class TestEventFilter : public TraceEventFilter { - public: - struct HitsCounter { - HitsCounter(); - ~HitsCounter(); - void Reset(); - size_t filter_trace_event_hit_count; - size_t end_event_hit_count; - }; - - static const char kName[]; - - // Factory method for TraceLog::SetFilterFactoryForTesting(). - static std::unique_ptr<TraceEventFilter> Factory( - const std::string& predicate_name); - - TestEventFilter(); - TestEventFilter(const TestEventFilter&) = delete; - TestEventFilter& operator=(const TestEventFilter&) = delete; - ~TestEventFilter() override; - - // TraceEventFilter implementation. - bool FilterTraceEvent(const TraceEvent& trace_event) const override; - void EndEvent(const char* category_name, const char* name) const override; - - static void set_filter_return_value(bool value) { - filter_return_value_ = value; - } - - private: - static bool filter_return_value_; -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_TRACE_EVENT_FILTER_TEST_UTILS_H_
diff --git a/base/trace_event/trace_event_unittest.cc b/base/trace_event/trace_event_unittest.cc index 35d3a49..e27ae69 100644 --- a/base/trace_event/trace_event_unittest.cc +++ b/base/trace_event/trace_event_unittest.cc
@@ -37,10 +37,7 @@ #include "base/threading/platform_thread.h" #include "base/threading/thread.h" #include "base/time/time.h" -#include "base/trace_event/event_name_filter.h" #include "base/trace_event/trace_buffer.h" -#include "base/trace_event/trace_event_filter.h" -#include "base/trace_event/trace_event_filter_test_utils.h" #include "base/values.h" #include "build/build_config.h" #include "testing/gmock/include/gmock/gmock.h" @@ -153,8 +150,7 @@ } void EndTraceAndFlushAsync(WaitableEvent* flush_complete_event) { - TraceLog::GetInstance()->SetDisabled(TraceLog::RECORDING_MODE | - TraceLog::FILTERING_MODE); + TraceLog::GetInstance()->SetDisabled(TraceLog::RECORDING_MODE); TraceLog::GetInstance()->Flush(base::BindRepeating( &TraceEventTestFixture::OnTraceDataCollected, base::Unretained(static_cast<TraceEventTestFixture*>(this)), @@ -2468,186 +2464,6 @@ } #endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) -// Runtime filtering isn't supported with Perfetto. -#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) -TEST_F(TraceEventTestFixture, TraceFilteringMode) { - const char config_json[] = - "{" - " \"event_filters\": [" - " {" - " \"filter_predicate\": \"testing_predicate\", " - " \"included_categories\": [\"*\"]" - " }" - " ]" - "}"; - - // Run RECORDING_MODE within FILTERING_MODE: - TestEventFilter::HitsCounter filter_hits_counter; - TestEventFilter::set_filter_return_value(true); - TraceLog::GetInstance()->SetFilterFactoryForTesting(TestEventFilter::Factory); - - // Only filtering mode is enabled with test filters. - TraceLog::GetInstance()->SetEnabled(TraceConfig(config_json), - TraceLog::FILTERING_MODE); - EXPECT_EQ(TraceLog::FILTERING_MODE, TraceLog::GetInstance()->enabled_modes()); - { - void* ptr = this; - TRACE_EVENT0("test_c0", "name0"); - TRACE_EVENT_ASYNC_BEGIN0("test_c1", "name1", ptr); - TRACE_EVENT_INSTANT0("test_c0", "name0", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_ASYNC_END0("test_c1", "name1", ptr); - } - - // Recording mode is enabled when filtering mode is turned on. - TraceLog::GetInstance()->SetEnabled(TraceConfig("", ""), - TraceLog::RECORDING_MODE); - EXPECT_EQ(TraceLog::RECORDING_MODE | TraceLog::FILTERING_MODE, - TraceLog::GetInstance()->enabled_modes()); - { TRACE_EVENT0("test_c2", "name2"); } - // Only recording mode is disabled and filtering mode will continue to run. - TraceLog::GetInstance()->SetDisabled(TraceLog::RECORDING_MODE); - EXPECT_EQ(TraceLog::FILTERING_MODE, TraceLog::GetInstance()->enabled_modes()); - - { TRACE_EVENT0("test_c0", "name0"); } - // Filtering mode is disabled and no tracing mode should be enabled. - TraceLog::GetInstance()->SetDisabled(TraceLog::FILTERING_MODE); - EXPECT_EQ(0, TraceLog::GetInstance()->enabled_modes()); - - EndTraceAndFlush(); - EXPECT_FALSE(FindMatchingValue("cat", "test_c0")); - EXPECT_FALSE(FindMatchingValue("cat", "test_c1")); - EXPECT_FALSE(FindMatchingValue("name", "name0")); - EXPECT_FALSE(FindMatchingValue("name", "name1")); - EXPECT_TRUE(FindMatchingValue("cat", "test_c2")); - EXPECT_TRUE(FindMatchingValue("name", "name2")); - EXPECT_EQ(6u, filter_hits_counter.filter_trace_event_hit_count); - EXPECT_EQ(3u, filter_hits_counter.end_event_hit_count); - Clear(); - filter_hits_counter.Reset(); - - // Run FILTERING_MODE within RECORDING_MODE: - // Only recording mode is enabled and all events must be recorded. - TraceLog::GetInstance()->SetEnabled(TraceConfig("", ""), - TraceLog::RECORDING_MODE); - EXPECT_EQ(TraceLog::RECORDING_MODE, TraceLog::GetInstance()->enabled_modes()); - { TRACE_EVENT0("test_c0", "name0"); } - - // Filtering mode is also enabled and all events must be filtered-out. - TestEventFilter::set_filter_return_value(false); - TraceLog::GetInstance()->SetEnabled(TraceConfig(config_json), - TraceLog::FILTERING_MODE); - EXPECT_EQ(TraceLog::RECORDING_MODE | TraceLog::FILTERING_MODE, - TraceLog::GetInstance()->enabled_modes()); - { TRACE_EVENT0("test_c1", "name1"); } - // Only filtering mode is disabled and recording mode should continue to run - // with all events being recorded. - TraceLog::GetInstance()->SetDisabled(TraceLog::FILTERING_MODE); - EXPECT_EQ(TraceLog::RECORDING_MODE, TraceLog::GetInstance()->enabled_modes()); - - { TRACE_EVENT0("test_c2", "name2"); } - // Recording mode is disabled and no tracing mode should be enabled. - TraceLog::GetInstance()->SetDisabled(TraceLog::RECORDING_MODE); - EXPECT_EQ(0, TraceLog::GetInstance()->enabled_modes()); - - EndTraceAndFlush(); - EXPECT_TRUE(FindMatchingValue("cat", "test_c0")); - EXPECT_TRUE(FindMatchingValue("cat", "test_c2")); - EXPECT_TRUE(FindMatchingValue("name", "name0")); - EXPECT_TRUE(FindMatchingValue("name", "name2")); - EXPECT_FALSE(FindMatchingValue("cat", "test_c1")); - EXPECT_FALSE(FindMatchingValue("name", "name1")); - EXPECT_EQ(1u, filter_hits_counter.filter_trace_event_hit_count); - EXPECT_EQ(1u, filter_hits_counter.end_event_hit_count); - Clear(); -} - -TEST_F(TraceEventTestFixture, EventFiltering) { - const char config_json[] = - "{" - " \"included_categories\": [" - " \"test_filtered_cat\"," - " \"test_unfiltered_cat\"," - " \"" TRACE_DISABLED_BY_DEFAULT("test_filtered_cat") "\"," - " \"" TRACE_DISABLED_BY_DEFAULT("test_unfiltered_cat") "\"]," - " \"event_filters\": [" - " {" - " \"filter_predicate\": \"testing_predicate\", " - " \"included_categories\": [" - " \"test_filtered_cat\"," - " \"" TRACE_DISABLED_BY_DEFAULT("test_filtered_cat") "\"]" - " }" - " " - " ]" - "}"; - - TestEventFilter::HitsCounter filter_hits_counter; - TestEventFilter::set_filter_return_value(true); - TraceLog::GetInstance()->SetFilterFactoryForTesting(TestEventFilter::Factory); - - TraceConfig trace_config(config_json); - TraceLog::GetInstance()->SetEnabled( - trace_config, TraceLog::RECORDING_MODE | TraceLog::FILTERING_MODE); - ASSERT_TRUE(TraceLog::GetInstance()->IsEnabled()); - - TRACE_EVENT0("test_filtered_cat", "a snake"); - TRACE_EVENT0("test_filtered_cat", "a mushroom"); - TRACE_EVENT0("test_unfiltered_cat", "a horse"); - - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("test_filtered_cat"), "a dog"); - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("test_unfiltered_cat"), "a pony"); - - // This is scoped so we can test the end event being filtered. - { TRACE_EVENT0("test_filtered_cat", "another cat whoa"); } - - EndTraceAndFlush(); - - EXPECT_EQ(4u, filter_hits_counter.filter_trace_event_hit_count); - EXPECT_EQ(1u, filter_hits_counter.end_event_hit_count); -} - -TEST_F(TraceEventTestFixture, EventAllowlistFiltering) { - std::string config_json = StringPrintf( - "{" - " \"included_categories\": [" - " \"test_filtered_cat\"," - " \"test_unfiltered_cat\"," - " \"" TRACE_DISABLED_BY_DEFAULT("test_filtered_cat") "\"]," - " \"event_filters\": [" - " {" - " \"filter_predicate\": \"%s\", " - " \"included_categories\": [" - " \"test_filtered_cat\"," - " \"" TRACE_DISABLED_BY_DEFAULT("*") "\"], " - " \"filter_args\": {" - " \"event_name_allowlist\": [\"a snake\", \"a dog\"]" - " }" - " }" - " " - " ]" - "}", - EventNameFilter::kName); - - TraceConfig trace_config(config_json); - TraceLog::GetInstance()->SetEnabled( - trace_config, TraceLog::RECORDING_MODE | TraceLog::FILTERING_MODE); - EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled()); - - TRACE_EVENT0("test_filtered_cat", "a snake"); - TRACE_EVENT0("test_filtered_cat", "a mushroom"); - TRACE_EVENT0("test_unfiltered_cat", "a cat"); - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("test_filtered_cat"), "a dog"); - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("test_filtered_cat"), "a pony"); - - EndTraceAndFlush(); - - EXPECT_TRUE(FindMatchingValue("name", "a snake")); - EXPECT_FALSE(FindMatchingValue("name", "a mushroom")); - EXPECT_TRUE(FindMatchingValue("name", "a cat")); - EXPECT_TRUE(FindMatchingValue("name", "a dog")); - EXPECT_FALSE(FindMatchingValue("name", "a pony")); -} -#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) - TEST_F(TraceEventTestFixture, ClockSyncEventsAreAlwaysAddedToTrace) { BeginSpecificTrace("-*"); TRACE_EVENT_CLOCK_SYNC_RECEIVER(1);
diff --git a/base/trace_event/trace_log.cc b/base/trace_event/trace_log.cc index 82b3c5dc..deb5197 100644 --- a/base/trace_event/trace_log.cc +++ b/base/trace_event/trace_log.cc
@@ -39,7 +39,6 @@ #include "base/threading/thread_id_name_manager.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" -#include "base/trace_event/event_name_filter.h" #include "base/trace_event/heap_profiler.h" #include "base/trace_event/heap_profiler_allocation_context_tracker.h" #include "base/trace_event/memory_dump_manager.h" @@ -108,14 +107,6 @@ TraceLog* g_trace_log_for_testing = nullptr; -#define MAX_TRACE_EVENT_FILTERS 32 - -// List of TraceEventFilter objects from the most recent tracing session. -std::vector<std::unique_ptr<TraceEventFilter>>& GetCategoryGroupFilters() { - static auto* filters = new std::vector<std::unique_ptr<TraceEventFilter>>(); - return *filters; -} - ThreadTicks ThreadNow() { return ThreadTicks::IsSupported() ? base::subtle::ThreadTicksNowIgnoringOverride() @@ -174,18 +165,6 @@ handle->event_index = static_cast<uint16_t>(event_index); } -template <typename Function> -void ForEachCategoryFilter(const unsigned char* category_group_enabled, - Function filter_fn) { - const TraceCategory* category = - CategoryRegistry::GetCategoryByStatePtr(category_group_enabled); - uint32_t filter_bitmap = category->enabled_filters(); - for (size_t index = 0; filter_bitmap != 0; filter_bitmap >>= 1, ++index) { - if (filter_bitmap & 1 && GetCategoryGroupFilters()[index]) - filter_fn(GetCategoryGroupFilters()[index].get()); - } -} - // The fallback arguments filtering function will filter away every argument. bool DefaultIsTraceEventArgsAllowlisted( const char* category_group_name, @@ -669,9 +648,7 @@ } TraceLog::TraceLog(int generation) - : enabled_modes_(0), - num_traces_recorded_(0), - process_sort_index_(0), + : process_sort_index_(0), process_id_hash_(0), process_id_(base::kNullProcessId), trace_options_(kInternalRecordUntilFull), @@ -793,16 +770,14 @@ lock_.AssertAcquired(); DCHECK(category->is_valid()); unsigned char state_flags = 0; - if (enabled_modes_ & RECORDING_MODE && - trace_config_.IsCategoryGroupEnabled(category->name())) { + if (enabled_ && trace_config_.IsCategoryGroupEnabled(category->name())) { state_flags |= TraceCategory::ENABLED_FOR_RECORDING; } // TODO(primiano): this is a temporary workaround for catapult:#2341, // to guarantee that metadata events are always added even if the category // filter is "-*". See crbug.com/618054 for more details and long-term fix. - if (enabled_modes_ & RECORDING_MODE && - category == CategoryRegistry::kCategoryMetadata) { + if (enabled_ && category == CategoryRegistry::kCategoryMetadata) { state_flags |= TraceCategory::ENABLED_FOR_RECORDING; } @@ -813,64 +788,20 @@ } #endif - uint32_t enabled_filters_bitmap = 0; - size_t index = 0; - for (const auto& event_filter : enabled_event_filters_) { - if (event_filter.IsCategoryGroupEnabled(category->name())) { - state_flags |= TraceCategory::ENABLED_FOR_FILTERING; - DCHECK(GetCategoryGroupFilters()[index]); - enabled_filters_bitmap |= 1 << index; - } - if (index++ >= MAX_TRACE_EVENT_FILTERS) { - NOTREACHED(); - break; - } - } - category->set_enabled_filters(enabled_filters_bitmap); category->set_state(state_flags); } void TraceLog::UpdateCategoryRegistry() { lock_.AssertAcquired(); - CreateFiltersForTraceConfig(); for (TraceCategory& category : CategoryRegistry::GetAllCategories()) { UpdateCategoryState(&category); } } -void TraceLog::CreateFiltersForTraceConfig() { - if (!(enabled_modes_ & FILTERING_MODE)) - return; - - // Filters were already added and tracing could be enabled. Filters list - // cannot be changed when trace events are using them. - if (GetCategoryGroupFilters().size()) - return; - - for (auto& filter_config : enabled_event_filters_) { - if (GetCategoryGroupFilters().size() >= MAX_TRACE_EVENT_FILTERS) { - NOTREACHED() - << "Too many trace event filters installed in the current session"; - break; - } - - std::unique_ptr<TraceEventFilter> new_filter; - const std::string& predicate_name = filter_config.predicate_name(); - if (predicate_name == EventNameFilter::kName) { - auto allowlist = std::make_unique<std::unordered_set<std::string>>(); - CHECK(filter_config.GetArgAsSet("event_name_allowlist", &*allowlist)); - new_filter = std::make_unique<EventNameFilter>(std::move(allowlist)); - } else { - if (filter_factory_for_testing_) - new_filter = filter_factory_for_testing_(predicate_name); - CHECK(new_filter) << "Unknown trace filter " << predicate_name; - } - GetCategoryGroupFilters().push_back(std::move(new_filter)); - } -} - void TraceLog::SetEnabled(const TraceConfig& trace_config, uint8_t modes_to_enable) { + // FILTERING_MODE is no longer supported. + DCHECK(modes_to_enable == RECORDING_MODE); DCHECK(trace_config.process_filter_config().IsEnabled(process_id_)); AutoLock lock(lock_); @@ -897,7 +828,6 @@ } #if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) - DCHECK(modes_to_enable == RECORDING_MODE); DCHECK(!trace_config.IsArgumentFilterEnabled()); perfetto::TraceConfig perfetto_config; @@ -959,38 +889,20 @@ return; } - // Clear all filters from previous tracing session. These filters are not - // cleared at the end of tracing because some threads which hit trace event - // when disabling, could try to use the filters. - if (!enabled_modes_) - GetCategoryGroupFilters().clear(); - // Update trace config for recording. - const bool already_recording = enabled_modes_ & RECORDING_MODE; - if (modes_to_enable & RECORDING_MODE) { - if (already_recording) { - trace_config_.Merge(trace_config); - } else { - trace_config_ = trace_config; - } + const bool already_recording = enabled_; + if (already_recording) { + trace_config_.Merge(trace_config); + } else { + trace_config_ = trace_config; } - // Update event filters only if filtering was not enabled. - if (modes_to_enable & FILTERING_MODE && enabled_event_filters_.empty()) { - DCHECK(!trace_config.event_filters().empty()); - enabled_event_filters_ = trace_config.event_filters(); - } - // Keep the |trace_config_| updated with only enabled filters in case anyone - // tries to read it using |GetCurrentTraceConfig| (even if filters are - // empty). - trace_config_.SetEventFilters(enabled_event_filters_); - - enabled_modes_ |= modes_to_enable; + enabled_ = true; UpdateCategoryRegistry(); // Do not notify observers or create trace buffer if only enabled for // filtering or if recording was already enabled. - if (!(modes_to_enable & RECORDING_MODE) || already_recording) + if (already_recording) return; // Discard events if new trace options are different. Reducing trace buffer @@ -1146,6 +1058,7 @@ } void TraceLog::SetDisabledWhileLocked(uint8_t modes_to_disable) { + DCHECK(modes_to_disable == RECORDING_MODE); #if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) if (!tracing_session_) return; @@ -1170,8 +1083,6 @@ tracing_session_->StopBlocking(); } #else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) - if (!(enabled_modes_ & modes_to_disable)) - return; if (dispatching_to_observers_) { // TODO(ssid): Change to NOTREACHED after fixing crbug.com/625170. @@ -1180,23 +1091,10 @@ return; } - bool is_recording_mode_disabled = - (enabled_modes_ & RECORDING_MODE) && (modes_to_disable & RECORDING_MODE); - enabled_modes_ &= ~modes_to_disable; - - if (modes_to_disable & FILTERING_MODE) - enabled_event_filters_.clear(); - - if (modes_to_disable & RECORDING_MODE) - trace_config_.Clear(); - + enabled_ = false; + trace_config_.Clear(); UpdateCategoryRegistry(); - // Add metadata events and notify observers only if recording mode was - // disabled now. - if (!is_recording_mode_disabled) - return; - AddMetadataEventsWhileLocked(); // Remove metadata events so they will not get added to a subsequent trace. @@ -1221,7 +1119,7 @@ int TraceLog::GetNumTracesRecorded() { AutoLock lock(lock_); - return (enabled_modes_ & RECORDING_MODE) ? num_traces_recorded_ : -1; + return enabled_ ? num_traces_recorded_ : -1; } void TraceLog::AddEnabledStateObserver(EnabledStateObserver* listener) { @@ -1846,28 +1744,10 @@ } std::string console_message; - std::unique_ptr<TraceEvent> filtered_trace_event; - bool disabled_by_filters = false; - if (*category_group_enabled & TraceCategory::ENABLED_FOR_FILTERING) { - auto new_trace_event = std::make_unique<TraceEvent>( - thread_id, offset_event_timestamp, thread_timestamp, phase, - category_group_enabled, name, scope, id, bind_id, args, flags); - - disabled_by_filters = true; - ForEachCategoryFilter( - category_group_enabled, [&new_trace_event, &disabled_by_filters]( - TraceEventFilter* trace_event_filter) { - if (trace_event_filter->FilterTraceEvent(*new_trace_event)) - disabled_by_filters = false; - }); - if (!disabled_by_filters) - filtered_trace_event = std::move(new_trace_event); - } // If enabled for recording, the event should be added only if one of the // filters indicates or category is not enabled for filtering. - if ((*category_group_enabled & TraceCategory::ENABLED_FOR_RECORDING) && - !disabled_by_filters) { + if ((*category_group_enabled & TraceCategory::ENABLED_FOR_RECORDING)) { OptionalAutoLock lock(&lock_); TraceEvent* trace_event = nullptr; @@ -1880,24 +1760,21 @@ // NO_THREAD_SAFETY_ANALYSIS: Conditional locking above. if (trace_event) { - if (filtered_trace_event) { - *trace_event = std::move(*filtered_trace_event); - } else { - trace_event->Reset(thread_id, offset_event_timestamp, thread_timestamp, - phase, category_group_enabled, name, scope, id, - bind_id, args, flags); - } + trace_event->Reset(thread_id, offset_event_timestamp, thread_timestamp, + phase, category_group_enabled, name, scope, id, + bind_id, args, flags); + } #if BUILDFLAG(IS_ANDROID) trace_event->SendToATrace(); #endif - } - if (trace_options() & kInternalEchoToConsole) { - console_message = EventToConsoleMessage( - phase == TRACE_EVENT_PHASE_COMPLETE ? TRACE_EVENT_PHASE_BEGIN : phase, - timestamp, trace_event); - } + if (trace_options() & kInternalEchoToConsole) { + console_message = EventToConsoleMessage( + phase == TRACE_EVENT_PHASE_COMPLETE ? TRACE_EVENT_PHASE_BEGIN + : phase, + timestamp, trace_event); + } } if (!console_message.empty()) @@ -1976,17 +1853,6 @@ return log.str(); } -void TraceLog::EndFilteredEvent(const unsigned char* category_group_enabled, - const char* name, - TraceEventHandle handle) { - const char* category_name = GetCategoryGroupName(category_group_enabled); - ForEachCategoryFilter( - category_group_enabled, - [name, category_name](TraceEventFilter* trace_event_filter) { - trace_event_filter->EndEvent(category_name, name); - }); -} - void TraceLog::UpdateTraceEventDuration( const unsigned char* category_group_enabled, const char* name, @@ -2055,9 +1921,6 @@ if (!console_message.empty()) LOG(ERROR) << console_message; - - if (*category_group_enabled & TraceCategory::ENABLED_FOR_FILTERING) - EndFilteredEvent(category_group_enabled, name, handle); } uint64_t TraceLog::MangleEventId(uint64_t id) {
diff --git a/base/trace_event/trace_log.h b/base/trace_event/trace_log.h index 4822a494..ea5b6ea7 100644 --- a/base/trace_event/trace_log.h +++ b/base/trace_event/trace_log.h
@@ -50,7 +50,6 @@ class TraceBuffer; class TraceBufferChunk; class TraceEvent; -class TraceEventFilter; class TraceEventMemoryOverhead; class JsonStringOutputWriter; @@ -70,11 +69,10 @@ // Argument passed to TraceLog::SetEnabled. enum Mode : uint8_t { // Enables normal tracing (recording trace events in the trace buffer). + // This is the only tracing mode supported now. + // TODO(khokhlov): Clean up all uses of tracing mode and remove this enum + // completely. RECORDING_MODE = 1 << 0, - - // Trace events are enabled just for filtering but not for recording. Only - // event filters config of |trace_config| argument is used. - FILTERING_MODE = 1 << 1 }; static TraceLog* GetInstance(); @@ -90,13 +88,7 @@ void InitializeThreadLocalEventBufferIfSupported(); // See TraceConfig comments for details on how to control which categories - // will be traced. SetDisabled must be called distinctly for each mode that is - // enabled. If tracing has already been enabled for recording, category filter - // (enabled and disabled categories) will be merged into the current category - // filter. Enabling RECORDING_MODE does not enable filters. Trace event - // filters will be used only if FILTERING_MODE is set on |modes_to_enable|. - // Conversely to RECORDING_MODE, FILTERING_MODE doesn't support upgrading, - // i.e. filters can only be enabled if not previously enabled. + // will be traced. Only RECORDING_MODE is supported. void SetEnabled(const TraceConfig& trace_config, uint8_t modes_to_enable); #if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) @@ -107,11 +99,7 @@ const perfetto::TraceConfig& perfetto_config); #endif - // TODO(ssid): Remove the default SetEnabled and IsEnabled. They should take - // Mode as argument. - - // Disables tracing for all categories for the specified |modes_to_disable| - // only. Only RECORDING_MODE is taken as default |modes_to_disable|. + // Disables tracing for all categories. Only RECORDING_MODE is supported. void SetDisabled(); void SetDisabled(uint8_t modes_to_disable); @@ -129,13 +117,10 @@ return track_event_enabled_; #else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) AutoLock lock(lock_); - return enabled_modes_ & RECORDING_MODE; + return enabled_; #endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) } - // Returns a bitmap of enabled modes from TraceLog::Mode. - uint8_t enabled_modes() { return enabled_modes_; } - // The number of times we have begun recording traces. If tracing is off, // returns -1. If tracing is on, then it returns the number of times we have // recorded a trace. By watching for this number to increment, you can @@ -379,10 +364,6 @@ const TimeTicks& now, const ThreadTicks& thread_now); - void EndFilteredEvent(const unsigned char* category_group_enabled, - const char* name, - TraceEventHandle handle); - ProcessId process_id() const { return process_id_; } std::unordered_map<int, std::string> process_labels() const { @@ -393,14 +374,6 @@ uint64_t MangleEventId(uint64_t id); // Exposed for unittesting: - - // Testing factory for TraceEventFilter. - typedef std::unique_ptr<TraceEventFilter> (*FilterFactoryForTesting)( - const std::string& /* predicate_name */); - void SetFilterFactoryForTesting(FilterFactoryForTesting factory) { - filter_factory_for_testing_ = factory; - } - // Allows clearing up our singleton instance. static void ResetForTesting(); @@ -490,13 +463,10 @@ // Enable/disable each category group based on the current mode_, // category_filter_ and event_filters_enabled_. // Enable the category group in the recording mode if category_filter_ matches - // the category group, is not null. Enable category for filtering if any - // filter in event_filters_enabled_ enables it. + // the category group, is not null. void UpdateCategoryRegistry(); void UpdateCategoryState(TraceCategory* category); - void CreateFiltersForTraceConfig(); - InternalTraceOptions GetInternalOptionsFromTraceConfig( const TraceConfig& config); @@ -583,8 +553,8 @@ // by thread_info_lock_) from arbitrary threads. mutable Lock lock_; Lock thread_info_lock_; - uint8_t enabled_modes_; // See TraceLog::Mode. - int num_traces_recorded_; + bool enabled_{false}; + int num_traces_recorded_{0}; std::unique_ptr<TraceBuffer> logged_events_; std::vector<std::unique_ptr<TraceEvent>> metadata_events_; @@ -626,7 +596,6 @@ std::atomic<InternalTraceOptions> trace_options_; TraceConfig trace_config_; - TraceConfig::EventFilters enabled_event_filters_; ThreadLocalPointer<ThreadLocalEventBuffer> thread_local_event_buffer_; ThreadLocalBoolean thread_blocks_message_loop_; @@ -669,8 +638,6 @@ #endif // !BUILDFLAG(IS_NACL) #endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) - FilterFactoryForTesting filter_factory_for_testing_ = nullptr; - #if BUILDFLAG(IS_ANDROID) absl::optional<TraceConfig> atrace_startup_config_; #endif
diff --git a/build/android/gyp/apkbuilder.py b/build/android/gyp/apkbuilder.py index cc1f1ac7..57c7c4eb 100755 --- a/build/android/gyp/apkbuilder.py +++ b/build/android/gyp/apkbuilder.py
@@ -112,6 +112,8 @@ '--library-always-compress', action='append', help='The list of library files that we always compress.') + # TODO(crbug.com/1337134): Remove all references to --library-renames. + # Setting it should be a no-op. parser.add_argument( '--library-renames', action='append', @@ -250,7 +252,7 @@ def _GetNativeLibrariesToAdd(native_libs, android_abi, uncompress, fast_align, - lib_always_compress, lib_renames): + lib_always_compress): """Returns the list of file_detail tuples for native libraries in the apk. Returns: A list of (src_path, apk_path, compress, alignment) tuple @@ -263,10 +265,6 @@ basename = os.path.basename(path) compress = not uncompress or any(lib_name in basename for lib_name in lib_always_compress) - rename = any(lib_name in basename for lib_name in lib_renames) - if rename: - basename = 'crazy.' + basename - lib_android_abi = android_abi if path.startswith('android_clang_arm64_hwasan/'): lib_android_abi = 'arm64-v8a-hwasan' @@ -371,15 +369,16 @@ allow_reads=allow_reads)) return ret - libs_to_add = _GetNativeLibrariesToAdd( - native_libs, options.android_abi, options.uncompress_shared_libraries, - fast_align, options.library_always_compress, options.library_renames) + libs_to_add = _GetNativeLibrariesToAdd(native_libs, options.android_abi, + options.uncompress_shared_libraries, + fast_align, + options.library_always_compress) if options.secondary_android_abi: libs_to_add.extend( - _GetNativeLibrariesToAdd( - secondary_native_libs, options.secondary_android_abi, - options.uncompress_shared_libraries, fast_align, - options.library_always_compress, options.library_renames)) + _GetNativeLibrariesToAdd(secondary_native_libs, + options.secondary_android_abi, + options.uncompress_shared_libraries, + fast_align, options.library_always_compress)) if options.expected_file: # We compute expectations without reading the files. This allows us to check
diff --git a/chrome/android/expectations/chrome_modern_public_bundle.arm.libs_and_assets.expected b/chrome/android/expectations/chrome_modern_public_bundle.arm.libs_and_assets.expected index cfbca51..6658d67 100644 --- a/chrome/android/expectations/chrome_modern_public_bundle.arm.libs_and_assets.expected +++ b/chrome/android/expectations/chrome_modern_public_bundle.arm.libs_and_assets.expected
@@ -1,4 +1,4 @@ -apk_path=lib/armeabi-v7a/crazy.libchrome.so, compress=False, alignment=4096 +apk_path=lib/armeabi-v7a/libchrome.so, compress=False, alignment=4096 apk_path=lib/armeabi-v7a/libchrome_crashpad_handler.so, compress=True, alignment=0 apk_path=lib/armeabi-v7a/libchromium_android_linker.so, compress=True, alignment=0 apk_path=assets/chrome_100_percent.pak, compress=False, alignment=4
diff --git a/chrome/android/features/cablev2_authenticator/native/cablev2_authenticator_android.cc b/chrome/android/features/cablev2_authenticator/native/cablev2_authenticator_android.cc index 3ea319e8..249a58ab 100644 --- a/chrome/android/features/cablev2_authenticator/native/cablev2_authenticator_android.cc +++ b/chrome/android/features/cablev2_authenticator/native/cablev2_authenticator_android.cc
@@ -37,7 +37,6 @@ using base::android::ConvertJavaStringToUTF8; using base::android::ConvertUTF8ToJavaString; -using base::android::JavaByteArrayToByteVector; using base::android::JavaParamRef; using base::android::JavaRef; using base::android::ScopedJavaGlobalRef; @@ -214,41 +213,17 @@ kMaxValue = 12, }; -// JavaByteArrayToSpan returns a span that aliases |data|. Be aware that the -// reference for |data| must outlive the span. -base::span<const uint8_t> JavaByteArrayToSpan( +// JavaByteArrayToByteVector returns a copy of the contents of |data|. +std::vector<uint8_t> JavaByteArrayToByteVector( JNIEnv* env, const JavaParamRef<jbyteArray>& data) { if (data.is_null()) { - return base::span<const uint8_t>(); + return std::vector<uint8_t>(); } - const size_t data_len = env->GetArrayLength(data); - const jbyte* data_bytes = env->GetByteArrayElements(data, /*iscopy=*/nullptr); - return base::as_bytes(base::make_span(data_bytes, data_len)); -} - -// JavaByteArrayToFixedSpan returns a span that aliases |data|, or |nullopt| if -// the span is not of the correct length. Be aware that the reference for |data| -// must outlive the span. -template <size_t N> -absl::optional<base::span<const uint8_t, N>> JavaByteArrayToFixedSpan( - JNIEnv* env, - const JavaParamRef<jbyteArray>& data) { - static_assert(N != 0, - "Zero case is different from JavaByteArrayToSpan as null " - "inputs will always be rejected here."); - - if (data.is_null()) { - return absl::nullopt; - } - - const size_t data_len = env->GetArrayLength(data); - if (data_len != N) { - return absl::nullopt; - } - const jbyte* data_bytes = env->GetByteArrayElements(data, /*iscopy=*/nullptr); - return base::as_bytes(base::make_span<N>(data_bytes, data_len)); + std::vector<uint8_t> ret; + base::android::JavaByteArrayToByteVector(env, data, &ret); + return ret; } // GlobalData holds all the state for ongoing security key operations. Since @@ -629,7 +604,7 @@ // The root_secret may not be provided when triggered for server-link. It // won't be used in that case either, but we need to be able to grab it if // setup() is called called for a different type of exchange. - base::span<const uint8_t> root_secret = JavaByteArrayToSpan(env, secret); + std::vector<uint8_t> root_secret = JavaByteArrayToByteVector(env, secret); if (!root_secret.empty() && !global_data.root_secret) { global_data.root_secret.emplace(); CHECK_EQ(global_data.root_secret->size(), root_secret.size()); @@ -735,16 +710,18 @@ const JavaParamRef<jbyteArray>& server_link_data_java) { constexpr size_t kDataSize = device::kP256X962Length + device::cablev2::kQRSecretSize; - const absl::optional<base::span<const uint8_t, kDataSize>> server_link_data = - JavaByteArrayToFixedSpan<kDataSize>(env, server_link_data_java); + const std::vector<uint8_t> server_link_data_vec = + JavaByteArrayToByteVector(env, server_link_data_java); // validateServerLinkData should have been called to check this already. - CHECK(server_link_data); + CHECK_EQ(server_link_data_vec.size(), kDataSize); + base::span<const uint8_t> server_link_data = + base::make_span(server_link_data_vec); const base::span<const uint8_t, device::kP256X962Length> peer_identity = - server_link_data->subspan<0, device::kP256X962Length>(); + server_link_data.subspan<0, device::kP256X962Length>(); const base::span<const uint8_t, device::cablev2::kQRSecretSize> qr_secret = server_link_data - ->subspan<device::kP256X962Length, device::cablev2::kQRSecretSize>(); + .subspan<device::kP256X962Length, device::cablev2::kQRSecretSize>(); const std::array<uint8_t, device::cablev2::kTunnelIdSize> tunnel_id = device::cablev2::Derive<device::cablev2::kTunnelIdSize>( qr_secret, base::span<uint8_t>(), @@ -793,7 +770,7 @@ auto event = device::cablev2::authenticator::Registration::Event::FromSerialized( - JavaByteArrayToSpan(env, serialized_event)); + JavaByteArrayToByteVector(env, serialized_event)); if (!event) { LOG(ERROR) << "Failed to parse event"; return 0; @@ -830,14 +807,15 @@ static int JNI_CableAuthenticator_ValidateServerLinkData( JNIEnv* env, const JavaParamRef<jbyteArray>& jdata) { - base::span<const uint8_t> data = JavaByteArrayToSpan(env, jdata); + std::vector<uint8_t> data = JavaByteArrayToByteVector(env, jdata); if (data.size() != device::kP256X962Length + device::cablev2::kQRSecretSize) { RecordResult(nullptr, CableV2MobileResult::kInvalidServerLink); return static_cast<int>(device::cablev2::authenticator::Platform::Error:: SERVER_LINK_WRONG_LENGTH); } - base::span<const uint8_t> x962 = data.subspan(0, device::kP256X962Length); + base::span<const uint8_t> x962 = + base::make_span(data).subspan(0, device::kP256X962Length); bssl::UniquePtr<EC_GROUP> p256( EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); bssl::UniquePtr<EC_POINT> point(EC_POINT_new(p256.get())); @@ -887,14 +865,14 @@ auto callback = std::move(*global_data.pending_make_credential_callback); global_data.pending_make_credential_callback.reset(); - absl::optional<base::span<const uint8_t>> device_public_key_signature; + absl::optional<std::vector<uint8_t>> device_public_key_signature; if (jdevice_public_key_signature) { device_public_key_signature = - JavaByteArrayToSpan(env, jdevice_public_key_signature); + JavaByteArrayToByteVector(env, jdevice_public_key_signature); } std::move(callback).Run(ctap_status, - JavaByteArrayToSpan(env, jattestation_object), + JavaByteArrayToByteVector(env, jattestation_object), device_public_key_signature); } @@ -914,8 +892,8 @@ if (ctap_status == static_cast<jint>(device::CtapDeviceResponseCode::kSuccess)) { - base::span<const uint8_t> response_bytes = - JavaByteArrayToSpan(env, jresponse_bytes); + std::vector<uint8_t> response_bytes = + JavaByteArrayToByteVector(env, jresponse_bytes); auto response = blink::mojom::GetAssertionAuthenticatorResponse::New(); if (blink::mojom::GetAssertionAuthenticatorResponse::Deserialize( response_bytes.data(), response_bytes.size(), &response)) { @@ -954,6 +932,6 @@ if (!usb_data) { global_data.usb_callback->Run(absl::nullopt); } else { - global_data.usb_callback->Run(JavaByteArrayToSpan(env, usb_data)); + global_data.usb_callback->Run(JavaByteArrayToByteVector(env, usb_data)); } }
diff --git a/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java b/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java index e0ea6afa..4446cbd 100644 --- a/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java +++ b/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java
@@ -67,8 +67,6 @@ import org.chromium.chrome.browser.flags.ChromeSwitches; import org.chromium.chrome.browser.homepage.HomepageManager; import org.chromium.chrome.browser.layouts.LayoutType; -import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; -import org.chromium.chrome.browser.preferences.SharedPreferencesManager; import org.chromium.chrome.browser.suggestions.SiteSuggestion; import org.chromium.chrome.browser.suggestions.mostvisited.MostVisitedSitesMetadataUtils; import org.chromium.chrome.browser.suggestions.tile.Tile; @@ -499,29 +497,6 @@ testShowLastTabAtStartUp(); } - @Test - @MediumTest - @Feature({"RenderTest"}) - // clang-format off - @EnableFeatures({ChromeFeatureList.FEED_ABLATION, - ChromeFeatureList.START_SURFACE_DISABLED_FEED_IMPROVEMENT, - ChromeFeatureList.TAB_SWITCHER_ON_RETURN, - ChromeFeatureList.START_SURFACE_ANDROID}) - public void renderImprovingStartSurfaceWhenFeedDisabled() throws IOException { - // clang-format on - StartSurfaceTestUtils.setMVTiles(mSuggestionsDeps); - SharedPreferencesManager.getInstance().writeBoolean( - ChromePreferenceKeys.FEED_ARTICLES_LIST_VISIBLE, false); - mActivityTestRule.startMainActivityFromLauncher(); - ChromeTabbedActivity cta = mActivityTestRule.getActivity(); - Assert.assertTrue(ReturnToChromeUtil.shouldImproveStartWhenFeedIsDisabled(cta)); - StartSurfaceTestUtils.waitForOverviewVisible(cta); - - View surface = cta.findViewById(R.id.primary_tasks_surface_view); - ChromeRenderTestRule.sanitize(surface); - mRenderTestRule.render(surface, "start_surface_no_feed_improvement"); - } - private void testShowLastTabAtStartUp() throws IOException { StartSurfaceTestUtils.createTabStateFile(new int[] {0}); StartSurfaceTestUtils.createThumbnailBitmapAndWriteToFile(0);
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn index 838c565..d860a8d 100644 --- a/chrome/android/features/tab_ui/BUILD.gn +++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -33,13 +33,11 @@ "java/res/drawable/fake_search_box_text_box_bg_incognito.xml", "java/res/drawable/ic_check_googblue_20dp_animated.xml", "java/res/drawable/ic_close_tabs_24dp.xml", - "java/res/drawable/ic_deselect_all_24dp.xml", "java/res/drawable/ic_group_icon_16dp.xml", "java/res/drawable/ic_price_alert_blue.xml", "java/res/drawable/ic_rating_star_full.xml", "java/res/drawable/ic_rating_star_half.xml", "java/res/drawable/ic_rating_star_outline.xml", - "java/res/drawable/ic_select_all_24dp.xml", "java/res/drawable/iph_drag_and_drop_animated_drawable.xml", "java/res/drawable/iph_drag_and_drop_drawable.xml", "java/res/drawable/price_card_background.xml",
diff --git a/chrome/android/features/tab_ui/java/res/drawable/ic_deselect_all_24dp.xml b/chrome/android/features/tab_ui/java/res/drawable/ic_deselect_all_24dp.xml deleted file mode 100644 index d183fc4..0000000 --- a/chrome/android/features/tab_ui/java/res/drawable/ic_deselect_all_24dp.xml +++ /dev/null
@@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2022 The Chromium Authors. All rights reserved. - Use of this source code is governed by a BSD-style license that can be - found in the LICENSE file. --> - -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24" - android:viewportHeight="24" - android:tint="@macro/default_icon_color"> - <path - android:fillColor="@android:color/white" - android:pathData="M7.94,5.12L6.49,3.66C8.07,2.61 9.96,2 12,2c5.52,0 10,4.48 10,10c0,2.04 -0.61,3.93 -1.66,5.51l-1.46,-1.46C19.59,14.86 20,13.48 20,12c0,-4.41 -3.59,-8 -8,-8C10.52,4 9.14,4.41 7.94,5.12zM17.66,9.53l-1.41,-1.41l-2.65,2.65l1.41,1.41L17.66,9.53zM19.78,22.61l-2.27,-2.27C15.93,21.39 14.04,22 12,22C6.48,22 2,17.52 2,12c0,-2.04 0.61,-3.93 1.66,-5.51L1.39,4.22l1.41,-1.41l18.38,18.38L19.78,22.61zM16.06,18.88l-3.88,-3.88l-1.59,1.59l-4.24,-4.24l1.41,-1.41l2.83,2.83l0.18,-0.18L5.12,7.94C4.41,9.14 4,10.52 4,12c0,4.41 3.59,8 8,8C13.48,20 14.86,19.59 16.06,18.88z"/> -</vector>
diff --git a/chrome/android/features/tab_ui/java/res/drawable/ic_select_all_24dp.xml b/chrome/android/features/tab_ui/java/res/drawable/ic_select_all_24dp.xml deleted file mode 100644 index 427d9b60..0000000 --- a/chrome/android/features/tab_ui/java/res/drawable/ic_select_all_24dp.xml +++ /dev/null
@@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2022 The Chromium Authors. All rights reserved. - Use of this source code is governed by a BSD-style license that can be -found in the LICENSE file. --> - -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24" - android:viewportHeight="24" - android:tint="@macro/default_icon_color"> - <path - android:fillColor="@android:color/white" - android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM10,14.2l-2.6,-2.6L6,13l4,4 8,-8 -1.4,-1.4z"/> -</vector>
diff --git a/chrome/android/features/tab_ui/java/res/values/dimens.xml b/chrome/android/features/tab_ui/java/res/values/dimens.xml index ba200e1..fe50f99 100644 --- a/chrome/android/features/tab_ui/java/res/values/dimens.xml +++ b/chrome/android/features/tab_ui/java/res/values/dimens.xml
@@ -81,5 +81,6 @@ <!-- Dimens for TabSelectionEditorV2 --> <dimen name="tab_selection_editor_action_view_padding">14dp</dimen> + <dimen name="tab_selection_editor_selection_action_inset">2dp</dimen> <dimen name="tab_selection_editor_share_sheet_preview_thumbnail_padding">10dp</dimen> </resources>
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java index c5f71ec..c77e9bb 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
@@ -556,8 +556,9 @@ if (TabUiFeatureUtilities.isTabSelectionEditorV2Enabled(mContext)) { List<TabSelectionEditorAction> actions = new ArrayList<>(); - actions.add(TabSelectionEditorSelectionAction.createAction( - mContext, ShowMode.MENU_ONLY, ButtonType.ICON_AND_TEXT, IconPosition.END)); + actions.add(TabSelectionEditorSelectionAction.createAction(mContext, ShowMode.MENU_ONLY, + ButtonType.ICON_AND_TEXT, IconPosition.END, + mTabModelSelector.getCurrentModel().isIncognito())); actions.add(TabSelectionEditorCloseAction.createAction( mContext, ShowMode.MENU_ONLY, ButtonType.ICON_AND_TEXT, IconPosition.START)); actions.add(TabSelectionEditorUngroupAction.createAction(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorAction.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorAction.java index 9189e22..9d20979f 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorAction.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorAction.java
@@ -159,10 +159,13 @@ ColorStateList.valueOf(Color.TRANSPARENT)) .with(TabSelectionEditorActionProperties.ICON_TINT, ColorStateList.valueOf(Color.TRANSPARENT)) + .with(TabSelectionEditorActionProperties.SKIP_ICON_TINT, false) .with(TabSelectionEditorActionProperties.ON_CLICK_LISTENER, this::perform) .with(TabSelectionEditorActionProperties.SHOULD_DISMISS_MENU, true) .with(TabSelectionEditorActionProperties.ON_SELECTION_STATE_CHANGE, this::onSelectionStateChange) + .with(TabSelectionEditorActionProperties.ON_SHOWN_IN_MENU, + this::onShownInMenu) .build(); if (contentDescriptionResourceId == null) return; @@ -196,6 +199,8 @@ return true; } + public void onShownInMenu() {} + /** * @return Whether the TabSelectionEditor supports applying the actions to related tabs. */
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionProperties.java index cd7d144..b4484258 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionProperties.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionProperties.java
@@ -41,29 +41,38 @@ new WritableObjectPropertyKey<>(); /** - * Tint for the icon. + * Tint for the icon. This should be null if {@code SKIP_ICON_TINT} is true. */ public static final WritableObjectPropertyKey<ColorStateList> ICON_TINT = new WritableObjectPropertyKey<>(); + /** + * If true skip usage of the {@code ICON_TINT} property. Used if the icon tint is handled + * directly by the {@link TabSelectionEditorAction}. + */ + public static final WritableBooleanPropertyKey SKIP_ICON_TINT = + new WritableBooleanPropertyKey(); + public static final WritableObjectPropertyKey<Runnable> ON_CLICK_LISTENER = new WritableObjectPropertyKey<>(); public static final WritableBooleanPropertyKey SHOULD_DISMISS_MENU = new WritableBooleanPropertyKey(); public static final WritableObjectPropertyKey<Callback<List<Integer>>> ON_SELECTION_STATE_CHANGE = new WritableObjectPropertyKey<>(); + public static final WritableObjectPropertyKey<Runnable> ON_SHOWN_IN_MENU = + new WritableObjectPropertyKey<>(); /** * Keys for the {@link TabSelectionEditorAction}. */ public static final PropertyKey[] ACTION_KEYS = {MENU_ITEM_ID, SHOW_MODE, BUTTON_TYPE, ICON_POSITION, TITLE_RESOURCE_ID, TITLE_IS_PLURAL, CONTENT_DESCRIPTION_RESOURCE_ID, - ICON, ENABLED, ITEM_COUNT, TEXT_TINT, ICON_TINT, ON_CLICK_LISTENER, SHOULD_DISMISS_MENU, - ON_SELECTION_STATE_CHANGE}; + ICON, ENABLED, ITEM_COUNT, TEXT_TINT, ICON_TINT, SKIP_ICON_TINT, ON_CLICK_LISTENER, + SHOULD_DISMISS_MENU, ON_SELECTION_STATE_CHANGE, ON_SHOWN_IN_MENU}; /** * Keys for the {@link TabSelectionEditorMenuItem}. */ - public static final PropertyKey[] MENU_ITEM_KEYS = { - MENU_ITEM_ID, TITLE, CONTENT_DESCRIPTION, ICON, ICON_TINT, ENABLED, ITEM_COUNT}; + public static final PropertyKey[] MENU_ITEM_KEYS = {MENU_ITEM_ID, TITLE, CONTENT_DESCRIPTION, + ICON, ICON_TINT, ENABLED, ITEM_COUNT, ON_SHOWN_IN_MENU}; }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionViewLayout.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionViewLayout.java index acfb435..e331abb 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionViewLayout.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionViewLayout.java
@@ -80,9 +80,12 @@ } /** + * @param popupListener for handling show events. * @param delegate for handling menu button presses. */ - public void setListMenuButtonDelegate(ListMenuButtonDelegate delegate) { + public void setListMenuButtonDelegate( + ListMenuButton.PopupMenuShownListener popupListener, ListMenuButtonDelegate delegate) { + mMenuButton.addPopupListener(popupListener); mMenuButton.setDelegate(delegate); }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMediator.java index 3a5815fb..17be223 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMediator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMediator.java
@@ -183,7 +183,10 @@ if (mActionListModel == null) return; for (PropertyModel model : mActionListModel) { + // TODO(ckitagawa): update these tints with input from UX. model.set(TabSelectionEditorActionProperties.TEXT_TINT, toolbarTintColorList); + if (model.get(TabSelectionEditorActionProperties.SKIP_ICON_TINT)) continue; + model.set(TabSelectionEditorActionProperties.ICON_TINT, toolbarTintColorList); } }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenu.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenu.java index b4915d03..7ae2503 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenu.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenu.java
@@ -16,6 +16,7 @@ import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorActionViewLayout.ActionViewLayoutDelegate; import org.chromium.chrome.tab_ui.R; import org.chromium.components.browser_ui.widget.listmenu.ListMenu; +import org.chromium.components.browser_ui.widget.listmenu.ListMenuButton; import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate; import org.chromium.ui.modelutil.LayoutViewBuilder; import org.chromium.ui.modelutil.MVCListAdapter.ListItem; @@ -35,9 +36,9 @@ * {@link TabSelectionEditorActionViewLayout} for Action views. The menu contains a list of * {@link TabSelectionEditorMenuItem}s which hold optional action views if room is available. */ -public class TabSelectionEditorMenu implements ListMenu, OnItemClickListener, - SelectionDelegate.SelectionObserver<Integer>, - ActionViewLayoutDelegate { +public class TabSelectionEditorMenu + implements ListMenu, OnItemClickListener, SelectionDelegate.SelectionObserver<Integer>, + ActionViewLayoutDelegate, ListMenuButton.PopupMenuShownListener { @Retention(RetentionPolicy.SOURCE) @IntDef({ListItemType.MENU_ITEM}) public static @interface ListItemType { @@ -84,7 +85,7 @@ mListView.setDivider(null); mListView.setOnItemClickListener(this); - mActionViewLayout.setListMenuButtonDelegate(() -> this); + mActionViewLayout.setListMenuButtonDelegate(this, () -> this); } private void registerItemTypes() { @@ -95,6 +96,13 @@ // clang-format on } + @Override + public void onPopupMenuShown() { + for (ListItem listItem : mModelList) { + listItem.model.get(TabSelectionEditorActionProperties.ON_SHOWN_IN_MENU).run(); + } + } + private ListItem buildListItem(int menuItemId) { // Model values are populated while configuring the TabSelectionEditorMenuItem. return new ListItem(ListItemType.MENU_ITEM,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuAdapter.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuAdapter.java index 57baf1402..a9c5ac19d 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuAdapter.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuAdapter.java
@@ -109,6 +109,9 @@ } else if (key == TabSelectionEditorActionProperties.ON_SELECTION_STATE_CHANGE) { menuItem.setOnSelectionStateChange( actionModel.get(TabSelectionEditorActionProperties.ON_SELECTION_STATE_CHANGE)); + } else if (key == TabSelectionEditorActionProperties.ON_SHOWN_IN_MENU) { + menuItem.setOnShownInMenu( + actionModel.get(TabSelectionEditorActionProperties.ON_SHOWN_IN_MENU)); } }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuItem.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuItem.java index 84eedd3..f8bfaa5 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuItem.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuItem.java
@@ -154,6 +154,15 @@ } public void setIconTint(@Nullable ColorStateList colorStateList) { + // A null colorStateList is used with TabSelectionEditorActionProperties.SKIP_ICON_TINT + // = true to signal that a custom tint is used. Ignore null so that this custom tint is + // not overridden. + if (colorStateList == null) { + mIconTint = null; + mListItem.model.set(TabSelectionEditorActionProperties.ICON_TINT, null); + return; + } + // mListItem uses the default icon tint whenever shown. Cache the tint to restore it when // the action view shown state is toggled. mListItem.model.set(TabSelectionEditorActionProperties.ICON_TINT, @@ -192,6 +201,10 @@ mOnSelectionStateChange = callback; } + public void setOnShownInMenu(Runnable runnable) { + mListItem.model.set(TabSelectionEditorActionProperties.ON_SHOWN_IN_MENU, runnable); + } + /** * Handler for click events on the menu item or action view. */
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorSelectionAction.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorSelectionAction.java index 5bf36a2..5a245a9 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorSelectionAction.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorSelectionAction.java
@@ -5,11 +5,15 @@ package org.chromium.chrome.browser.tasks.tab_management; import android.content.Context; +import android.graphics.Color; import android.graphics.drawable.Drawable; +import android.graphics.drawable.InsetDrawable; +import android.graphics.drawable.LayerDrawable; import androidx.annotation.IntDef; import androidx.annotation.VisibleForTesting; -import androidx.appcompat.content.res.AppCompatResources; +import androidx.core.content.res.ResourcesCompat; +import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat; import org.chromium.base.metrics.RecordUserAction; import org.chromium.chrome.browser.tab.Tab; @@ -23,10 +27,11 @@ * Select all and deselect all toggle action for the {@link TabSelectionEditorMenu}. */ public class TabSelectionEditorSelectionAction extends TabSelectionEditorAction { + private static final int BACKGROUND = 0; + private static final int CHECKMARK = 1; + private Context mContext; private @ActionState int mActionState; - private final Drawable mSelectAllIcon; - private final Drawable mDeselectAllIcon; @IntDef({ActionState.UNKNOWN, ActionState.SELECT_ALL, ActionState.DESELECT_ALL}) @Retention(RetentionPolicy.SOURCE) @@ -45,28 +50,42 @@ * @param isIncognito whether the current tab model is incognito this will update dynamically. */ public static TabSelectionEditorAction createAction(Context context, @ShowMode int showMode, - @ButtonType int buttonType, @IconPosition int iconPosition) { - Drawable selectAllIcon = - AppCompatResources.getDrawable(context, R.drawable.ic_select_all_24dp); - Drawable deselectAllIcon = - AppCompatResources.getDrawable(context, R.drawable.ic_deselect_all_24dp); + @ButtonType int buttonType, @IconPosition int iconPosition, boolean isIncognito) { return new TabSelectionEditorSelectionAction( - context, showMode, buttonType, iconPosition, selectAllIcon, deselectAllIcon); + context, showMode, buttonType, iconPosition, isIncognito, buildDrawable(context)); } @VisibleForTesting TabSelectionEditorSelectionAction(Context context, @ShowMode int showMode, - @ButtonType int buttonType, @IconPosition int iconPosition, Drawable selectAllIcon, - Drawable deselectAllIcon) { + @ButtonType int buttonType, @IconPosition int iconPosition, boolean isIncognito, + Drawable drawable) { super(R.id.tab_selection_editor_selection_menu_item, showMode, buttonType, iconPosition, - R.string.tab_selection_editor_select_all, null, selectAllIcon); + R.string.tab_selection_editor_select_all, null, drawable); mContext = context; mActionState = ActionState.UNKNOWN; - mSelectAllIcon = selectAllIcon; - mDeselectAllIcon = deselectAllIcon; + getPropertyModel().set(TabSelectionEditorActionProperties.ICON_TINT, null); + getPropertyModel().set(TabSelectionEditorActionProperties.SKIP_ICON_TINT, true); getPropertyModel().set(TabSelectionEditorActionProperties.SHOULD_DISMISS_MENU, false); - updateState(ActionState.SELECT_ALL); + updateState(ActionState.SELECT_ALL, isIncognito); + LayerDrawable layers = + (LayerDrawable) getPropertyModel().get(TabSelectionEditorActionProperties.ICON); + layers.setCallback(new Drawable.Callback() { + @Override + public void invalidateDrawable(Drawable who) { + // No-op. + } + + @Override + public void scheduleDrawable(Drawable who, Runnable what, long when) { + who.invalidateSelf(); + } + + @Override + public void unscheduleDrawable(Drawable who, Runnable what) { + who.unscheduleSelf(what); + } + }); } @Override @@ -75,10 +94,16 @@ } @Override + public void onShownInMenu() { + updateDrawable(); + } + + @Override public void onSelectionStateChange(List<Integer> tabIds) { setEnabledAndItemCount(true, tabIds.size()); updateState(getActionDelegate().areAllTabsSelected() ? ActionState.DESELECT_ALL - : ActionState.SELECT_ALL); + : ActionState.SELECT_ALL, + getTabModelSelector().getCurrentModel().isIncognito()); } @Override @@ -100,21 +125,64 @@ return false; } - private void updateState(@ActionState int selectionState) { + private void updateState(@ActionState int selectionState, boolean isIncognito) { if (mActionState == selectionState) return; mActionState = selectionState; + LayerDrawable layers = + (LayerDrawable) getPropertyModel().get(TabSelectionEditorActionProperties.ICON); if (mActionState == ActionState.SELECT_ALL) { getPropertyModel().set(TabSelectionEditorActionProperties.TITLE_RESOURCE_ID, R.string.tab_selection_editor_select_all); - getPropertyModel().set(TabSelectionEditorActionProperties.ICON, mSelectAllIcon); + updateDrawable(); } else if (mActionState == ActionState.DESELECT_ALL) { getPropertyModel().set(TabSelectionEditorActionProperties.TITLE_RESOURCE_ID, R.string.tab_selection_editor_deselect_all); - getPropertyModel().set(TabSelectionEditorActionProperties.ICON, mDeselectAllIcon); + updateDrawable(); } else { assert false : "Invalid selection state"; } } + + private void updateDrawable() { + LayerDrawable layers = + (LayerDrawable) getPropertyModel().get(TabSelectionEditorActionProperties.ICON); + if (mActionState == ActionState.SELECT_ALL) { + layers.getDrawable(BACKGROUND) + .setLevel( + mContext.getResources().getInteger(R.integer.list_item_level_default)); + + layers.getDrawable(CHECKMARK).setAlpha(0); + layers.getDrawable(CHECKMARK).setTint(Color.TRANSPARENT); + layers.invalidateSelf(); + } else if (mActionState == ActionState.DESELECT_ALL) { + layers.getDrawable(BACKGROUND) + .setLevel( + mContext.getResources().getInteger(R.integer.list_item_level_selected)); + + layers.getDrawable(CHECKMARK).setAlpha(255); + layers.getDrawable(CHECKMARK).setTint( + TabUiThemeProvider.getSelectionActionIconCheckedDrawableColor(mContext)); + layers.invalidateSelf(); + ((AnimatedVectorDrawableCompat) layers.getDrawable(CHECKMARK)).start(); + } else { + assert false : "Invalid selection state"; + } + } + + private static Drawable buildDrawable(Context context) { + Drawable[] drawables = new Drawable[2]; + + Drawable selectionListIcon = ResourcesCompat.getDrawable(context.getResources(), + R.drawable.tab_grid_selection_list_icon, context.getTheme()); + drawables[BACKGROUND] = new InsetDrawable(selectionListIcon, + (int) context.getResources().getDimension( + R.dimen.tab_selection_editor_selection_action_inset)); + drawables[BACKGROUND].setTint( + TabUiThemeProvider.getSelectionActionIconBackgroundColor(context)); + drawables[CHECKMARK] = AnimatedVectorDrawableCompat.create( + context, R.drawable.ic_check_googblue_20dp_animated); + return new LayerDrawable(drawables); + } }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java index 30261d2..1d03626 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
@@ -499,8 +499,9 @@ if (mTabSelectionEditorActions == null) { mTabSelectionEditorActions = new ArrayList<>(); - mTabSelectionEditorActions.add(TabSelectionEditorSelectionAction.createAction( - mActivity, ShowMode.MENU_ONLY, ButtonType.ICON_AND_TEXT, IconPosition.END)); + mTabSelectionEditorActions.add(TabSelectionEditorSelectionAction.createAction(mActivity, + ShowMode.MENU_ONLY, ButtonType.ICON_AND_TEXT, IconPosition.END, + mTabModelSelector.getCurrentModel().isIncognito())); mTabSelectionEditorActions.add(TabSelectionEditorCloseAction.createAction( mActivity, ShowMode.MENU_ONLY, ButtonType.ICON_AND_TEXT, IconPosition.START)); mTabSelectionEditorActions.add(TabSelectionEditorGroupAction.createAction(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java index 6b60a8e..e0ec536 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java
@@ -187,6 +187,28 @@ } /** + * Returns the {@link ColorInt} to use for the {@link TabSelectionEditorSelectionAction} + * icon background. + * + * @param context {@link Context} used to retrieve color. + * @return The {@link ColorInt} for select all icon background. + */ + public static @ColorInt int getSelectionActionIconBackgroundColor(Context context) { + return MaterialColors.getColor(context, R.attr.colorOnSurfaceVariant, TAG); + } + + /** + * Returns the {@link ColorInt} to use for the "check" drawable on the + * {@link TabSelectionEditorSelectionAction}. + * + * @param context {@link Context} used to retrieve color. + * @return The {@link ColorInt} for "check" drawable. + */ + public static @ColorInt int getSelectionActionIconCheckedDrawableColor(Context context) { + return MaterialColors.getColor(context, org.chromium.chrome.R.attr.colorOnPrimary, TAG); + } + + /** * Returns the thumbnail placeholder color resource id based on the incognito mode. * * @param isIncognito Whether the color is used for incognito mode.
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuTest.java index a83466e..14115fa4 100644 --- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuTest.java +++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuTest.java
@@ -102,8 +102,8 @@ public RenderTestRule mRenderTestRule = RenderTestRule.Builder.withPublicCorpus() .setBugComponent(Component.UI_BROWSER_MOBILE_TAB_SWITCHER_GRID) - .setRevision(4) - .setDescription("New selection icons") + .setRevision(3) + .setDescription("Pluralize strings") .build(); @Rule @@ -321,8 +321,8 @@ TestThreadUtils.runOnUiThreadBlocking(() -> { actions.add(new FakeTabSelectionEditorAction(getActivity(), R.id.tab_selection_editor_close_menu_item, ShowMode.IF_ROOM, ButtonType.TEXT, - IconPosition.END, R.string.tab_selection_editor_select_all, - R.drawable.ic_select_all_24dp)); + IconPosition.END, R.plurals.tab_selection_editor_close_tabs, + R.drawable.ic_close_tabs_24dp)); configureMenuWithActions(actions); }); @@ -359,8 +359,8 @@ TestThreadUtils.runOnUiThreadBlocking(() -> { actions.add(new FakeTabSelectionEditorAction(getActivity(), R.id.tab_selection_editor_close_menu_item, ShowMode.MENU_ONLY, ButtonType.TEXT, - IconPosition.START, R.string.tab_selection_editor_deselect_all, - R.drawable.ic_deselect_all_24dp)); + IconPosition.START, R.plurals.tab_selection_editor_close_tabs, + R.drawable.ic_close_tabs_24dp)); configureMenuWithActions(actions); }); @@ -370,7 +370,7 @@ PopupListener listener = new PopupListener(); openMenu(listener); - assertMenuItem("Deselect all", false); + assertMenuItem("Close tabs", false); forceFinishRollAnimation(); mRenderTestRule.render(
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java index e3629a2..8173d39 100644 --- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java +++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java
@@ -136,8 +136,8 @@ public ChromeRenderTestRule mRenderTestRule = ChromeRenderTestRule.Builder.withPublicCorpus() .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_MOBILE_TAB_SWITCHER) - .setRevision(5) - .setDescription("TabSelectionEditorV2 New selection icons") + .setRevision(4) + .setDescription("TabSelectionEditorV2 UI Polish") .build(); @Mock @@ -178,6 +178,7 @@ @After public void tearDown() { + TabSelectionEditorShareAction.setIntentCallbackForTesting(null); if (mTabSelectionEditorCoordinator != null) { if (sActivityTestRule.getActivity().findViewById(R.id.app_menu_list) != null) { Espresso.pressBack(); @@ -873,9 +874,9 @@ TestThreadUtils.runOnUiThreadBlocking(() -> { List<TabSelectionEditorAction> actions = new ArrayList<>(); - actions.add( - TabSelectionEditorSelectionAction.createAction(sActivityTestRule.getActivity(), - ShowMode.IF_ROOM, ButtonType.ICON_AND_TEXT, IconPosition.END)); + actions.add(TabSelectionEditorSelectionAction.createAction( + sActivityTestRule.getActivity(), ShowMode.IF_ROOM, ButtonType.ICON_AND_TEXT, + IconPosition.END, /*isIncognito=*/false)); mTabSelectionEditorController.configureToolbarWithMenuItems(actions, null); }); @@ -1080,9 +1081,9 @@ TestThreadUtils.runOnUiThreadBlocking(() -> { List<TabSelectionEditorAction> actions = new ArrayList<>(); - actions.add( - TabSelectionEditorSelectionAction.createAction(sActivityTestRule.getActivity(), - ShowMode.IF_ROOM, ButtonType.ICON_AND_TEXT, IconPosition.END)); + actions.add(TabSelectionEditorSelectionAction.createAction( + sActivityTestRule.getActivity(), ShowMode.IF_ROOM, ButtonType.ICON_AND_TEXT, + IconPosition.END, /*isIncognito=*/false)); mTabSelectionEditorController.configureToolbarWithMenuItems(actions, null); }); @@ -1241,9 +1242,9 @@ TestThreadUtils.runOnUiThreadBlocking(() -> { List<TabSelectionEditorAction> actions = new ArrayList<>(); - actions.add( - TabSelectionEditorSelectionAction.createAction(sActivityTestRule.getActivity(), - ShowMode.MENU_ONLY, ButtonType.TEXT, IconPosition.START)); + actions.add(TabSelectionEditorSelectionAction.createAction( + sActivityTestRule.getActivity(), ShowMode.MENU_ONLY, ButtonType.TEXT, + IconPosition.START, /*isIncognito=*/false)); actions.add(TabSelectionEditorCloseAction.createAction(sActivityTestRule.getActivity(), ShowMode.MENU_ONLY, ButtonType.TEXT, IconPosition.START));
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorSelectionActionUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorSelectionActionUnitTest.java index 22cfa19d..9686777 100644 --- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorSelectionActionUnitTest.java +++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorSelectionActionUnitTest.java
@@ -10,7 +10,6 @@ import android.app.Activity; import android.content.Context; -import android.graphics.drawable.Drawable; import androidx.test.filters.SmallTest; @@ -57,9 +56,10 @@ MockitoAnnotations.initMocks(this); mContext = Robolectric.buildActivity(Activity.class).get(); mContext.setTheme(org.chromium.chrome.tab_ui.R.style.Theme_BrowserUI_DayNight); - mAction = TabSelectionEditorSelectionAction.createAction( - mContext, ShowMode.IF_ROOM, ButtonType.ICON_AND_TEXT, IconPosition.END); - mTabModel = spy(new MockTabModel(false, null)); + boolean isIncognito = false; + mAction = TabSelectionEditorSelectionAction.createAction(mContext, ShowMode.IF_ROOM, + ButtonType.ICON_AND_TEXT, IconPosition.END, isIncognito); + mTabModel = spy(new MockTabModel(isIncognito, null)); when(mTabModelSelector.getCurrentModel()).thenReturn(mTabModel); // TODO(ckitagawa): Add tests for when this is true. mAction.configure(mTabModelSelector, mSelectionDelegate, mDelegate, false); @@ -80,8 +80,10 @@ TabSelectionEditorActionProperties.CONTENT_DESCRIPTION_RESOURCE_ID)); Assert.assertNotNull( mAction.getPropertyModel().get(TabSelectionEditorActionProperties.ICON)); - Assert.assertNotNull( + Assert.assertNull( mAction.getPropertyModel().get(TabSelectionEditorActionProperties.ICON_TINT)); + Assert.assertTrue( + mAction.getPropertyModel().get(TabSelectionEditorActionProperties.SKIP_ICON_TINT)); } @Test @@ -100,10 +102,6 @@ true, mAction.getPropertyModel().get(TabSelectionEditorActionProperties.ENABLED)); Assert.assertEquals( 0, mAction.getPropertyModel().get(TabSelectionEditorActionProperties.ITEM_COUNT)); - Assert.assertNotNull( - mAction.getPropertyModel().get(TabSelectionEditorActionProperties.ICON)); - Drawable selectAllIcon = - mAction.getPropertyModel().get(TabSelectionEditorActionProperties.ICON); // 1 tab of 2 selected. selectedTabIds.add(0); @@ -115,8 +113,6 @@ true, mAction.getPropertyModel().get(TabSelectionEditorActionProperties.ENABLED)); Assert.assertEquals( 1, mAction.getPropertyModel().get(TabSelectionEditorActionProperties.ITEM_COUNT)); - Assert.assertEquals(selectAllIcon, - mAction.getPropertyModel().get(TabSelectionEditorActionProperties.ICON)); // 2 tabs of 2 selected. selectedTabIds.add(1); @@ -129,8 +125,6 @@ true, mAction.getPropertyModel().get(TabSelectionEditorActionProperties.ENABLED)); Assert.assertEquals( 2, mAction.getPropertyModel().get(TabSelectionEditorActionProperties.ITEM_COUNT)); - Assert.assertNotEquals(selectAllIcon, - mAction.getPropertyModel().get(TabSelectionEditorActionProperties.ICON)); } @Test
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorShareActionUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorShareActionUnitTest.java index ef5b242..a35312b 100644 --- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorShareActionUnitTest.java +++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorShareActionUnitTest.java
@@ -15,6 +15,7 @@ import androidx.appcompat.content.res.AppCompatResources; import androidx.test.filters.SmallTest; +import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -47,7 +48,6 @@ import org.chromium.url.JUnitTestGURLs; import java.util.ArrayList; -import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -74,15 +74,11 @@ private MockTabModel mTabModel; private TabSelectionEditorShareAction mAction; - Map<Integer, GURL> mIdUrlMap = new HashMap<Integer, GURL>() { - { - put(1, JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_1)); - put(2, JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_2)); - put(3, JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_3)); - put(4, JUnitTestGURLs.getGURL(JUnitTestGURLs.NTP_URL)); - put(5, JUnitTestGURLs.getGURL(JUnitTestGURLs.ABOUT_BLANK)); - } - }; + Map<Integer, GURL> mIdUrlMap = Map.of(1, JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_1), 2, + JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_2), 3, + JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_3), 4, + JUnitTestGURLs.getGURL(JUnitTestGURLs.NTP_URL), 5, + JUnitTestGURLs.getGURL(JUnitTestGURLs.ABOUT_BLANK)); @Before public void setUp() { @@ -105,6 +101,11 @@ mAction.configure(mTabModelSelector, mSelectionDelegate, mDelegate, false); } + @After + public void tearDown() { + TabSelectionEditorShareAction.setIntentCallbackForTesting(null); + } + @Test @SmallTest public void testInherentActionProperties() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java index d54374ff5..ea12d419 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -433,7 +433,7 @@ // LifecycleRegistry normally enforces it is called on the main thread, but this // class will be preloaded in a background thread. The only method that gets called // in the activity constructor is addObserver(), so just override that. - mLifecycleRegistry = new LifecycleRegistry(null) { + mLifecycleRegistry = new LifecycleRegistry(this) { @Override public void addObserver(LifecycleObserver observer) {} };
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java index a27c047..3b3f27b1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
@@ -134,8 +134,7 @@ static final float BACKGROUND_TAB_BRIGHTNESS_DEFAULT = 1.f; static final float BACKGROUND_TAB_BRIGHTNESS_DIMMED = 0.65f; static final float DIVIDER_HIDDEN_OPACITY = 0.f; - static final float DIVIDER_DEFAULT_OPACITY = 0.2f; - static final float DIVIDER_BOLD_OPACITY = 0.6f; + static final float DIVIDER_DEFAULT_OPACITY = 1.f; static final float FADE_FULL_OPACITY_THRESHOLD_DP = 24.f; private static final int MESSAGE_RESIZE = 1; @@ -765,25 +764,25 @@ * a tab group when in edit mode. */ private void updateDividers() { - if (!ChromeFeatureList.sTabStripRedesign.isEnabled() || mMultiStepTabCloseAnimRunning) { - return; - } + if (!ChromeFeatureList.sTabStripRedesign.isEnabled()) return; + + // Validate the index. For example, the index can be {@link TabList.INVALID_TAB_INDEX} when + // all tabs are closed. + int index = mModel.index(); + if (index < 0 || index >= mStripTabs.length) return; // Divider is never shown for the first tab. mStripTabs[0].setDividerOpacity(DIVIDER_HIDDEN_OPACITY); - int selectedTabId = mStripTabs[mModel.index()].getId(); + int selectedTabId = mStripTabs[index].getId(); for (int i = 1; i < mStripTabs.length; i++) { final StripLayoutTab prevTab = mStripTabs[i - 1]; final StripLayoutTab currTab = mStripTabs[i]; - if (prevTab.getTrailingMargin() > 0) { - // The first divider after a tab group margin is bolded to help indicate separation. - currTab.setDividerOpacity(DIVIDER_BOLD_OPACITY); - } else if (prevTab.getId() == selectedTabId || currTab.getId() == selectedTabId) { + if (prevTab.getId() == selectedTabId || currTab.getId() == selectedTabId) { // Dividers adjacent to the selected tab are hidden. currTab.setDividerOpacity(DIVIDER_HIDDEN_OPACITY); } else { - // Otherwise return the divider to the default opacity. + // All other dividers are visible. currTab.setDividerOpacity(DIVIDER_DEFAULT_OPACITY); } } @@ -1094,9 +1093,9 @@ // 2. Set the dying state of the tab. tab.setIsDying(true); - Tab nextTab = mModel.getNextTabIfClosed(tab.getId(), /*uponExit=*/false); // 3. Setup animation end listener to resize and move tabs after tab closes. + Tab nextTab = mModel.getNextTabIfClosed(tab.getId(), /*uponExit=*/false); tabClosingAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -1255,6 +1254,9 @@ AnimatorSet set = new AnimatorSet(); set.playTogether(animationList); if (listener != null) set.addListener(listener); + if (mRunningAnimator != null && mRunningAnimator.isRunning()) { + mRunningAnimator.end(); + } mRunningAnimator = set; mRunningAnimator.start(); } @@ -2086,15 +2088,13 @@ // 4. We have hovered for the required time, so trigger a reorder. int direction = towardEnd ? 1 : -1; - int destinationTabId = mTabGroupModelFilter.getRootId( - getTabById(mStripTabs[curIndex + direction].getId())); + StripLayoutTab destTab = mStripTabs[curIndex + direction]; float effectiveWidth = mCachedTabWidth - mTabOverlapWidth; float flipThreshold = effectiveWidth * REORDER_OVERLAP_SWITCH_PERCENTAGE; float minFlipOffset = mTabMarginWidth + flipThreshold; int numTabsToSkip = 1 + (int) Math.floor((Math.abs(offset) - minFlipOffset) / effectiveWidth); - - mTabGroupModelFilter.mergeTabsToGroup(mInteractingTab.getId(), destinationTabId, true); + mTabGroupModelFilter.mergeTabsToGroup(mInteractingTab.getId(), destTab.getId(), true); RecordUserAction.record("MobileToolbarReorderTab.TabAddedToGroup"); return towardEnd ? curIndex + 1 + numTabsToSkip : curIndex - numTabsToSkip;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java index 7b3c2f2..3ee0251 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java
@@ -175,6 +175,8 @@ // Divider Constants // TODO(crbug.com/1373632): Temp value until the 9-patches are updated. private static final int DIVIDER_OFFSET_X = 9; + @VisibleForTesting + static final float DIVIDER_FOLIO_LIGHT_OPACITY = 0.2f; private int mId = Tab.INVALID_TAB_ID; @@ -419,7 +421,28 @@ * @return The tint color resource for the tab divider. */ public @ColorInt int getDividerTint() { - return SemanticColorUtils.getDefaultIconColorAccent1(mContext); + if (!ChromeFeatureList.sTabStripRedesign.isEnabled()) { + // Dividers are only present in TSR. Return arbitrary color to avoid calculation. + return Color.TRANSPARENT; + } + + if (mIncognito) { + return mContext.getColor(R.color.divider_line_bg_color_light); + } + + if (TabUiFeatureUtilities.isTabStripFolioEnabled() && !ColorUtils.inNightMode(mContext) + && !mIncognito) { + // This color will not be used at full opacity. We can't set this using the alpha + // component of the {@code @ColorInt}, since it is ignored when loading resources + // with a specified tint in the CC layer (instead retaining the alpha of the original + // image). Instead, this is reflected by setting the opacity of the divider itself. + // See https://crbug.com/1373634. + return androidx.core.graphics.ColorUtils.setAlphaComponent( + SemanticColorUtils.getDefaultIconColorAccent1(mContext), + (int) (DIVIDER_FOLIO_LIGHT_OPACITY * 255)); + } + + return SemanticColorUtils.getDividerLineBgColor(mContext); } /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java index bbbe82b..339fe479 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
@@ -632,16 +632,6 @@ } @Test - @MediumTest - @Feature({"NewTabPage", "FeedNewTabPage", "RenderTest"}) - @Features.EnableFeatures(ChromeFeatureList.FEED_ABLATION) - public void testRender_LoadNewTabPageWithDisabledFeed() throws IOException { - mRenderTestRule.render( - mActivityTestRule.getActivity().getActivityTab().getNativePage().getView(), - "feed_is_ablated"); - } - - @Test @SmallTest @Feature({"NewTabPage", "FeedNewTabPage"}) public void testFeedReliabilityLoggingHideWithBack() throws IOException {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java index a7a998f..66a4eb0 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
@@ -135,6 +135,8 @@ } TabUiFeatureUtilities.setTabMinWidthForTesting(null); + TabUiFeatureUtilities.setTabStripRedesignEnableFolioForTesting(false); + TabUiFeatureUtilities.setTabStripRedesignEnableDetachedForTesting(false); } /** @@ -477,6 +479,8 @@ @Test @Feature("Tab Strip Redesign") public void testUpdateDividers_WithTabSelected() { + TabUiFeatureUtilities.setTabStripRedesignEnableDetachedForTesting(true); + // Setup with 5 tabs. Select tab 2. initializeTest(false, false, 2); StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabs(); @@ -489,44 +493,15 @@ float visibleOpacity = StripLayoutHelper.DIVIDER_DEFAULT_OPACITY; // clang-format off assertEquals("First divider should always be hidden.", - hiddenOpacity, tabs[0].getDividerOpacity(), EPSILON); + hiddenOpacity, tabs[0].getDividerOpacity(), EPSILON); assertEquals("Divider should be at default opacity.", - visibleOpacity, tabs[1].getDividerOpacity(), EPSILON); + visibleOpacity, tabs[1].getDividerOpacity(), EPSILON); assertEquals("Divider is adjacent to selected tab and should be hidden.", - hiddenOpacity, tabs[2].getDividerOpacity(), EPSILON); + hiddenOpacity, tabs[2].getDividerOpacity(), EPSILON); assertEquals("Divider is adjacent to selected tab and should be hidden.", - hiddenOpacity, tabs[3].getDividerOpacity(), EPSILON); + hiddenOpacity, tabs[3].getDividerOpacity(), EPSILON); assertEquals("Divider should be at default opacity.", - visibleOpacity, tabs[4].getDividerOpacity(), EPSILON); - // clang-format on - } - - @Test - @Feature("Tab Strip Redesign") - public void testUpdateDividers_WithTabGroups() { - // Setup with 5 tabs. Select tab 4. - initializeTest(false, false, 4); - StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabs(); - - // Trigger update to set divider values. Mock a tab group margin after tab 1. - tabs[1].setTrailingMargin(100.f); - mStripLayoutHelper.updateLayout(TIMESTAMP); - - // Verify tab 2's divider is bolded. - float hiddenOpacity = StripLayoutHelper.DIVIDER_HIDDEN_OPACITY; - float visibleOpacity = StripLayoutHelper.DIVIDER_DEFAULT_OPACITY; - float boldedOpacity = StripLayoutHelper.DIVIDER_BOLD_OPACITY; - // clang-format off - assertEquals("First divider should always be hidden.", - hiddenOpacity, tabs[0].getDividerOpacity(), EPSILON); - assertEquals("Divider should be at default opacity.", - visibleOpacity, tabs[1].getDividerOpacity(), EPSILON); - assertEquals("Divider is after tab group margin and should be bolded.", - boldedOpacity, tabs[2].getDividerOpacity(), EPSILON); - assertEquals("Divider should be at default opacity.", - visibleOpacity, tabs[3].getDividerOpacity(), EPSILON); - assertEquals("Divider is adjacent to selected tab and should be hidden.", - hiddenOpacity, tabs[4].getDividerOpacity(), EPSILON); + visibleOpacity, tabs[4].getDividerOpacity(), EPSILON); // clang-format on } @@ -1427,6 +1402,7 @@ mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabs(); StripLayoutTab thirdTab = tabs[2]; + int oldSecondTabId = tabs[1].getId(); groupTabs(0, 2); // Start reorder mode on third tab. Drag between tabs in group. @@ -1452,9 +1428,9 @@ // Verify interacting tab was merged into group at the second index. tabs = mStripLayoutHelper.getStripLayoutTabs(); - // assertEquals("Third tab should now be second tab.", thirdTab, tabs[1]); + assertEquals("Third tab should now be second tab.", thirdTab, tabs[1]); verify(mTabGroupModelFilter) - .mergeTabsToGroup(eq(thirdTab.getId()), eq(tabs[0].getId()), eq(true)); + .mergeTabsToGroup(eq(thirdTab.getId()), eq(oldSecondTabId), eq(true)); } @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTabTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTabTest.java index 2498c5a5..870f82f 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTabTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTabTest.java
@@ -30,6 +30,7 @@ import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities; import org.chromium.chrome.test.util.browser.Features; import org.chromium.components.browser_ui.styles.ChromeColors; +import org.chromium.components.browser_ui.styles.SemanticColorUtils; import org.chromium.ui.util.ColorUtils; /** Tests for {@link StripLayoutTab}. */ @@ -215,6 +216,58 @@ mIncognitoTab.getOutlineTint(false)); } + @Test + @Feature("Tab Strip Redesign") + public void testGetDividerTint() { + int expectedColor = Color.TRANSPARENT; + + // Normal. + assertEquals("Non-TSR tabs should not have a divider.", expectedColor, + mNormalTab.getDividerTint()); + + // Incognito. + assertEquals("Non-TSR tabs should not have a divider.", expectedColor, + mNormalTab.getDividerTint()); + } + + @Test + @Feature("Tab Strip Redesign") + @Features.EnableFeatures({ChromeFeatureList.TAB_STRIP_REDESIGN}) + public void testGetDividerTint_TabStripRedesignFolio() { + TabUiFeatureUtilities.setTabStripRedesignEnableFolioForTesting(true); + int expectedColor; + + // Normal. + expectedColor = androidx.core.graphics.ColorUtils.setAlphaComponent( + SemanticColorUtils.getDefaultIconColorAccent1(mContext), + (int) (StripLayoutTab.DIVIDER_FOLIO_LIGHT_OPACITY * 255)); + assertEquals("TSR Folio light mode divider uses 20% icon color", expectedColor, + mNormalTab.getDividerTint()); + + // Incognito. + expectedColor = mContext.getColor(R.color.divider_line_bg_color_light); + assertEquals("TSR incognito dividers use the baseline color.", expectedColor, + mIncognitoTab.getDividerTint()); + } + + @Test + @Feature("Tab Strip Redesign") + @Features.EnableFeatures({ChromeFeatureList.TAB_STRIP_REDESIGN}) + public void testGetDividerTint_TabStripRedesignDetached() { + TabUiFeatureUtilities.setTabStripRedesignEnableDetachedForTesting(true); + int expectedColor; + + // Normal. + expectedColor = MaterialColors.getColor(mContext, R.attr.colorSurfaceVariant, TAG); + assertEquals("TSR detached divider uses surface variant.", expectedColor, + mNormalTab.getDividerTint()); + + // Incognito. + expectedColor = mContext.getColor(R.color.divider_line_bg_color_light); + assertEquals("TSR incognito dividers use the baseline color.", expectedColor, + mIncognitoTab.getDividerTint()); + } + private StripLayoutTab createStripLayoutTab(boolean incognito) { return new StripLayoutTab(mContext, 0, null, null, null, null, incognito); }
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index f460c47..1208f04 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -5028,18 +5028,6 @@ <message name="IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON_TOOLTIP_MULTIPLE_EXTENSIONS" desc="The tooltip text of the request access button that appears on the toolbar when an extension requests access to the site"> Click to allow on <ph name="ORIGIN">$1<ex>google.com</ex></ph>: </message> - <message name="IDS_EXTENSIONS_REQUEST_ACCESS_BUBBLE_SINGLE_EXTENSION_TITLE" desc="The title of the request access bubble to tell the user to accept the bubble to always run the extension on the site."> - Always allow "<ph name="EXTENSION_NAME">$1<ex>Gmail Checker</ex></ph>" to run on <ph name="ORIGIN">$2<ex>google.com</ex></ph>? - </message> - <message name="IDS_EXTENSIONS_REQUEST_ACCESS_BUBBLE_MULTIPLE_EXTENSIONS_TITLE" desc="The title of the request access bubble to tell the user to accept the bubble to always run the extensions on the site."> - Always allow these extensions to run on <ph name="ORIGIN">$1<ex>google.com</ex></ph>? - </message> - <message name="IDS_EXTENSIONS_REQUEST_ACCESS_BUBBLE_OK_BUTTON_LABEL" desc="The label of the button to allow the extension(s) to run on the site."> - Always allow - </message> - <message name="IDS_EXTENSIONS_REQUEST_ACCESS_BUBBLE_CANCEL_BUTTON_LABEL" desc="The label of the button to don't allow the extension(s) to run on the site."> - No thanks - </message> <message name="IDS_EXTENSIONS_TOOLBAR_ACTION_HOVER_CARD_TITLE_HAS_ACCESS" desc="The title in the card that shows up on mouse hover of a toolbar action when extension has access to the current site."> Allowed to read & change </message>
diff --git a/chrome/app/generated_resources_grd/IDS_EXTENSIONS_REQUEST_ACCESS_BUBBLE_CANCEL_BUTTON_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_REQUEST_ACCESS_BUBBLE_CANCEL_BUTTON_LABEL.png.sha1 deleted file mode 100644 index 0ee9c50f..0000000 --- a/chrome/app/generated_resources_grd/IDS_EXTENSIONS_REQUEST_ACCESS_BUBBLE_CANCEL_BUTTON_LABEL.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -736c2b36025d9d1c6b092c89c96c5d642286c7c8 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_EXTENSIONS_REQUEST_ACCESS_BUBBLE_MULTIPLE_EXTENSIONS_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_REQUEST_ACCESS_BUBBLE_MULTIPLE_EXTENSIONS_TITLE.png.sha1 deleted file mode 100644 index 1e6bb886..0000000 --- a/chrome/app/generated_resources_grd/IDS_EXTENSIONS_REQUEST_ACCESS_BUBBLE_MULTIPLE_EXTENSIONS_TITLE.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -5e2995b256c98bd2bbfd964668465c864db058bb \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_EXTENSIONS_REQUEST_ACCESS_BUBBLE_OK_BUTTON_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_REQUEST_ACCESS_BUBBLE_OK_BUTTON_LABEL.png.sha1 deleted file mode 100644 index 0ee9c50f..0000000 --- a/chrome/app/generated_resources_grd/IDS_EXTENSIONS_REQUEST_ACCESS_BUBBLE_OK_BUTTON_LABEL.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -736c2b36025d9d1c6b092c89c96c5d642286c7c8 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_EXTENSIONS_REQUEST_ACCESS_BUBBLE_SINGLE_EXTENSION_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_REQUEST_ACCESS_BUBBLE_SINGLE_EXTENSION_TITLE.png.sha1 deleted file mode 100644 index 0ee9c50f..0000000 --- a/chrome/app/generated_resources_grd/IDS_EXTENSIONS_REQUEST_ACCESS_BUBBLE_SINGLE_EXTENSION_TITLE.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -736c2b36025d9d1c6b092c89c96c5d642286c7c8 \ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 2c5015e..d47b606fc1 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -7989,9 +7989,6 @@ grit("resources") { source = "browser_resources.grd" - # Required due to flattenhtml = "true" on a generated file. - enable_input_discovery_for_gn_analyze = false - use_brotli = true defines = chrome_grit_defines @@ -8013,11 +8010,11 @@ deps = [ ":chrome_internal_resources_gen", - "//chrome/browser/ui/webui/usb_internals:mojo_bindings_webui_js", + "//chrome/browser/ui/webui/usb_internals:mojo_bindings_js__generator", "//device/bluetooth/public/mojom:deprecated_experimental_interfaces_js", "//device/bluetooth/public/mojom:mojom_js", - "//services/device/public/mojom:usb_test_webui_js", - "//services/device/public/mojom:usb_webui_js", + "//services/device/public/mojom:usb_js__generator", + "//services/device/public/mojom:usb_test_js__generator", "//url/mojom:url_mojom_gurl_js", "//url/mojom:url_mojom_origin_js", ] @@ -8046,6 +8043,7 @@ } if (is_chromeos_ash) { deps += [ + "//chrome/browser/ash/guest_os:guest_os_diagnostics_mojom_js__generator", "//chrome/browser/resources/chromeos/account_manager:css_wrapper_files", "//chrome/browser/resources/chromeos/account_manager:html_wrapper_files", "//chrome/browser/resources/chromeos/account_manager/components:html_wrapper_files", @@ -8068,7 +8066,7 @@ "//chrome/browser/ui/webui/ash/crostini_installer:mojo_bindings_js", "//chrome/browser/ui/webui/ash/crostini_upgrader:mojo_bindings_js", "//chrome/browser/ui/webui/ash/parent_access:mojo_bindings_js", - "//chrome/browser/ui/webui/ash/vm:mojo_bindings_webui_js", + "//chrome/browser/ui/webui/ash/vm:mojo_bindings_js__generator", "//chrome/browser/ui/webui/settings/ash:mojom_js", "//chrome/browser/ui/webui/settings/ash/os_apps_page/mojom:mojom_js", "//ui/webui/resources/cr_components/app_management:mojo_bindings_js", @@ -8117,7 +8115,7 @@ "//chrome/browser/resources/memory_internals:build_ts", "//chrome/browser/resources/predictors:build_ts", "//chrome/browser/ui/webui/internals/user_education:mojo_bindings_js", - "//components/site_engagement/core/mojom:mojo_bindings_webui_js", + "//components/site_engagement/core/mojom:mojo_bindings_js__generator", ] if (is_android || is_linux || is_chromeos || is_win) {
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index dc18063..11f7bf6 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -4677,9 +4677,6 @@ {"feed-stamp", flag_descriptions::kFeedStampName, flag_descriptions::kFeedStampDescription, kOsAndroid, FEATURE_VALUE_TYPE(feed::kFeedStamp)}, - {"feed-ablation", flag_descriptions::kFeedIsAblatedName, - flag_descriptions::kFeedIsAblatedDescription, kOsAndroid, - FEATURE_VALUE_TYPE(feed::kIsAblated)}, {"feed-v2-hearts", flag_descriptions::kInterestFeedV2HeartsName, flag_descriptions::kInterestFeedV2HeartsDescription, kOsAndroid, FEATURE_VALUE_TYPE(feed::kInterestFeedV2Hearts)}, @@ -8971,6 +8968,9 @@ kOsCrOS, FEATURE_VALUE_TYPE( chromeos::features::kEnableExternalKeyboardsInDiagnostics)}, + {"enable-power-sounds", flag_descriptions::kSystemSoundsName, + flag_descriptions::kSystemSoundsDescription, kOsCrOS, + FEATURE_VALUE_TYPE(ash::features::kSystemSounds)}, #endif // BUILDFLAG(IS_CHROMEOS_ASH) {"autofill-enforce-delays-in-strike-database",
diff --git a/chrome/browser/android/compositor/layer/tab_handle_layer.cc b/chrome/browser/android/compositor/layer/tab_handle_layer.cc index da66404..df14cf6 100644 --- a/chrome/browser/android/compositor/layer/tab_handle_layer.cc +++ b/chrome/browser/android/compositor/layer/tab_handle_layer.cc
@@ -52,10 +52,22 @@ brightness_ = brightness; foreground_ = foreground; opacity_ = opacity; - cc::FilterOperations filters; - if (brightness_ != 1.0f && !foreground_) - filters.Append(cc::FilterOperation::CreateBrightnessFilter(brightness_)); - layer_->SetFilters(filters); + + // With the Tab Strip Redesign (TSR), inactive tabs no longer have a visible + // container. To achieve the same dimming effect, we need to set the opacity + // rather than adding a brightness filter. We can't swap to simply setting + // the opacity when TSR is disabled, because then, the tab containers can + // be seen overlapping. (See https://crbug.com/1373632). + if (base::FeatureList::IsEnabled(chrome::android::kTabStripRedesign)) { + tab_->SetOpacity(brightness_); + } else { + cc::FilterOperations filters; + if (brightness_ != 1.0f) { + filters.Append( + cc::FilterOperation::CreateBrightnessFilter(brightness_)); + } + layer_->SetFilters(filters); + } } float original_x = x; @@ -95,14 +107,14 @@ if (title_layer) { title_layer->setOpacity(1.0f); - unsigned expected_children = 5; + unsigned expected_children = 4; title_layer_ = title_layer->layer(); - if (layer_->children().size() < expected_children) { - layer_->AddChild(title_layer_); - } else if (layer_->children()[expected_children - 1]->id() != + if (tab_->children().size() < expected_children) { + tab_->AddChild(title_layer_); + } else if (tab_->children()[expected_children - 1]->id() != title_layer_->id()) { - layer_->ReplaceChild((layer_->children()[expected_children - 1]).get(), - title_layer_); + tab_->ReplaceChild((tab_->children()[expected_children - 1]).get(), + title_layer_); } title_layer->SetUIResourceIds(); } else if (title_layer_.get()) { @@ -209,6 +221,7 @@ TabHandleLayer::TabHandleLayer(LayerTitleCache* layer_title_cache) : layer_title_cache_(layer_title_cache), layer_(cc::Layer::Create()), + tab_(cc::Layer::Create()), close_button_(cc::UIResourceLayer::Create()), divider_(cc::UIResourceLayer::Create()), decoration_tab_(cc::NinePatchLayer::Create()), @@ -220,9 +233,14 @@ if (!base::FeatureList::IsEnabled(chrome::android::kTabStripRedesign)) { tab_outline_->SetIsDrawable(true); } - layer_->AddChild(decoration_tab_); - layer_->AddChild(tab_outline_); - layer_->AddChild(close_button_); + + tab_->AddChild(decoration_tab_); + tab_->AddChild(tab_outline_); + tab_->AddChild(close_button_); + + // The divider is added as a separate child so its opacity can be controlled + // separately from the other tab items. + layer_->AddChild(tab_); layer_->AddChild(divider_); }
diff --git a/chrome/browser/android/compositor/layer/tab_handle_layer.h b/chrome/browser/android/compositor/layer/tab_handle_layer.h index bd6e9e8d..6fde0e8 100644 --- a/chrome/browser/android/compositor/layer/tab_handle_layer.h +++ b/chrome/browser/android/compositor/layer/tab_handle_layer.h
@@ -64,6 +64,7 @@ raw_ptr<LayerTitleCache> layer_title_cache_; scoped_refptr<cc::Layer> layer_; + scoped_refptr<cc::Layer> tab_; scoped_refptr<cc::UIResourceLayer> close_button_; scoped_refptr<cc::UIResourceLayer> divider_; scoped_refptr<cc::NinePatchLayer> decoration_tab_;
diff --git a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc index 3581aa6..af57558 100644 --- a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc +++ b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc
@@ -343,7 +343,7 @@ resource_manager->GetStaticResourceWithTint(close_resource_id, close_tint); ui::Resource* divider_resource = resource_manager->GetStaticResourceWithTint( - divider_resource_id, divider_tint); + divider_resource_id, divider_tint, true); layer->SetProperties(id, close_button_resource, divider_resource, tab_handle_resource, tab_handle_outline_resource, foreground, close_pressed, toolbar_width, x, y, width,
diff --git a/chrome/browser/apps/platform_apps/shortcut_manager_factory.cc b/chrome/browser/apps/platform_apps/shortcut_manager_factory.cc index 3c1aeac..2c044fb 100644 --- a/chrome/browser/apps/platform_apps/shortcut_manager_factory.cc +++ b/chrome/browser/apps/platform_apps/shortcut_manager_factory.cc
@@ -9,6 +9,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/web_applications/extensions/web_app_extension_shortcut.h" #include "chrome/browser/web_applications/os_integration/web_app_shortcut_manager.h" +#include "chrome/browser/web_applications/web_app_utils.h" // static AppShortcutManager* AppShortcutManagerFactory::GetForProfile(Profile* profile) { @@ -30,8 +31,16 @@ AppShortcutManagerFactory::~AppShortcutManagerFactory() {} KeyedService* AppShortcutManagerFactory::BuildServiceInstanceFor( - content::BrowserContext* profile) const { - return new AppShortcutManager(static_cast<Profile*>(profile)); + content::BrowserContext* context) const { + Profile* profile = Profile::FromBrowserContext(context); + if (!profile) + return nullptr; + + // Do not instantiate the AppShortcutManager if web_apps are not supported. + if (!web_app::AreWebAppsEnabled(profile)) + return nullptr; + + return new AppShortcutManager(profile); } bool AppShortcutManagerFactory::ServiceIsCreatedWithBrowserContext() const {
diff --git a/chrome/browser/apps/platform_apps/shortcut_manager_factory.h b/chrome/browser/apps/platform_apps/shortcut_manager_factory.h index 1e3fdb72..7732ab0 100644 --- a/chrome/browser/apps/platform_apps/shortcut_manager_factory.h +++ b/chrome/browser/apps/platform_apps/shortcut_manager_factory.h
@@ -19,7 +19,7 @@ // Singleton that owns all AppShortcutManagers and associates them with // Profiles. Listens for the Profile's destruction notification and cleans up // the associated AppShortcutManager. -// AppShortcutManagers should not exist in incognito profiles. +// AppShortcutManager should only exist for profiles where web apps are enabled. class AppShortcutManagerFactory : public ProfileKeyedServiceFactory { public: static AppShortcutManager* GetForProfile(Profile* profile); @@ -34,7 +34,7 @@ // BrowserContextKeyedServiceFactory: KeyedService* BuildServiceInstanceFor( - content::BrowserContext* profile) const override; + content::BrowserContext* context) const override; bool ServiceIsCreatedWithBrowserContext() const override; bool ServiceIsNULLWhileTesting() const override; };
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn index cb072996..314e989ab 100644 --- a/chrome/browser/ash/BUILD.gn +++ b/chrome/browser/ash/BUILD.gn
@@ -2376,6 +2376,7 @@ "policy/reporting/metrics_reporting/audio/audio_events_observer.h", "policy/reporting/metrics_reporting/cros_healthd_metric_sampler.cc", "policy/reporting/metrics_reporting/cros_healthd_metric_sampler.h", + "policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_sampler_handler.h", "policy/reporting/metrics_reporting/cros_reporting_settings.cc", "policy/reporting/metrics_reporting/cros_reporting_settings.h", "policy/reporting/metrics_reporting/metric_reporting_manager.cc", @@ -3049,6 +3050,8 @@ "web_applications/shimless_rma_system_web_app_info.h", "web_applications/shortcut_customization_system_web_app_info.cc", "web_applications/shortcut_customization_system_web_app_info.h", + "web_applications/shortcut_customization_ui/chrome_shortcut_customization_delegate.cc", + "web_applications/shortcut_customization_ui/chrome_shortcut_customization_delegate.h", "web_applications/system_web_app_install_utils.cc", "web_applications/system_web_app_install_utils.h", "web_applications/terminal_source.cc", @@ -3135,6 +3138,7 @@ "//ash/webui/shimless_rma", "//ash/webui/shimless_rma/backend", "//ash/webui/shortcut_customization_ui", + "//ash/webui/shortcut_customization_ui/backend", "//base", "//base:i18n", "//build:chromeos_buildflags",
diff --git a/chrome/browser/ash/arc/input_overlay/arc_input_overlay_manager.cc b/chrome/browser/ash/arc/input_overlay/arc_input_overlay_manager.cc index f4850ff..ccdecfdf 100644 --- a/chrome/browser/ash/arc/input_overlay/arc_input_overlay_manager.cc +++ b/chrome/browser/ash/arc/input_overlay/arc_input_overlay_manager.cc
@@ -133,27 +133,11 @@ ArcInputOverlayManager::~ArcInputOverlayManager() = default; -void ArcInputOverlayManager::ReadData(const std::string& package_name, - aura::Window* top_level_window) { - auto touch_injector = std::make_unique<TouchInjector>( - top_level_window, - base::BindRepeating(&ArcInputOverlayManager::OnSaveProtoFile, - weak_ptr_factory_.GetWeakPtr())); - loading_data_windows_.insert(top_level_window); - - task_runner_->PostTaskAndReplyWithResult( - FROM_HERE, - base::BindOnce(&ArcInputOverlayManager::ReadDefaultData, Unretained(this), - package_name, std::move(touch_injector)), - base::BindOnce(&ArcInputOverlayManager::OnFinishReadDefaultData, - Unretained(this), package_name)); -} - std::unique_ptr<TouchInjector> ArcInputOverlayManager::ReadDefaultData( - const std::string& package_name, std::unique_ptr<TouchInjector> touch_injector) { DCHECK(touch_injector); + const std::string& package_name = touch_injector->package_name(); auto resource_id = GetInputOverlayResourceId(package_name); if (!resource_id) return touch_injector; @@ -175,10 +159,13 @@ } void ArcInputOverlayManager::OnFinishReadDefaultData( - const std::string& package_name, std::unique_ptr<TouchInjector> touch_injector) { DCHECK(touch_injector); + // Save |touch_injector->package_name()| first because + // |std::move(touch_injector)| is also called in the task runner. + std::string package_name = touch_injector->package_name(); + if (touch_injector->actions().empty()) { if (!beta_) { ResetForPendingTouchInjector(std::move(touch_injector)); @@ -200,6 +187,7 @@ LOG(ERROR) << "GetTaskInfo method for ARC is not available"; return; } + VLOG(2) << "Fetch app category of package: " << package_name; app_instance->GetAppCategory( package_name, @@ -236,7 +224,7 @@ } std::unique_ptr<AppDataProto> ArcInputOverlayManager::GetProto( - const std::string& package_name) { + std::string package_name) { // |data_controller_| is null for test. return data_controller_ ? data_controller_->ReadProtoFromFile(package_name) : nullptr; @@ -271,7 +259,7 @@ void ArcInputOverlayManager::OnSaveProtoFile( std::unique_ptr<AppDataProto> proto, - const std::string& package_name) { + std::string package_name) { task_runner_->PostTask( FROM_HERE, base::BindOnce(&ArcInputOverlayManager::SaveFile, base::Unretained(this), @@ -279,7 +267,7 @@ } void ArcInputOverlayManager::SaveFile(std::unique_ptr<AppDataProto> proto, - const std::string& package_name) { + std::string package_name) { if (data_controller_) data_controller_->WriteProtoToFile(std::move(proto), package_name); } @@ -412,7 +400,20 @@ top_level_window->GetProperty(ash::kArcPackageNameKey); if (!package_name || package_name->empty()) return; - ReadData(*package_name, top_level_window); + + // Start to read data. + auto touch_injector = std::make_unique<TouchInjector>( + top_level_window, *package_name, + base::BindRepeating(&ArcInputOverlayManager::OnSaveProtoFile, + weak_ptr_factory_.GetWeakPtr())); + loading_data_windows_.insert(top_level_window); + + task_runner_->PostTaskAndReplyWithResult( + FROM_HERE, + base::BindOnce(&ArcInputOverlayManager::ReadDefaultData, Unretained(this), + std::move(touch_injector)), + base::BindOnce(&ArcInputOverlayManager::OnFinishReadDefaultData, + Unretained(this))); } void ArcInputOverlayManager::OnWindowDestroying(aura::Window* window) {
diff --git a/chrome/browser/ash/arc/input_overlay/arc_input_overlay_manager.h b/chrome/browser/ash/arc/input_overlay/arc_input_overlay_manager.h index 6cef463..6cb20ce3 100644 --- a/chrome/browser/ash/arc/input_overlay/arc_input_overlay_manager.h +++ b/chrome/browser/ash/arc/input_overlay/arc_input_overlay_manager.h
@@ -92,16 +92,11 @@ class InputMethodObserver; - // Read all the data including both default data and customized data. - void ReadData(const std::string& package_name, - aura::Window* top_level_window); // Read default data. std::unique_ptr<TouchInjector> ReadDefaultData( - const std::string& package_name, std::unique_ptr<TouchInjector> touch_injector); // Called when finishing reading default data. - void OnFinishReadDefaultData(const std::string& package_name, - std::unique_ptr<TouchInjector> touch_injector); + void OnFinishReadDefaultData(std::unique_ptr<TouchInjector> touch_injector); // Called when receiving app category from ARC. void OnReceiveAppCategory(std::unique_ptr<TouchInjector> touch_injector, arc::mojom::AppCategory category); @@ -110,15 +105,15 @@ void ReadCustomizedData(const std::string& package_name, std::unique_ptr<TouchInjector> touch_injector); // Get the Proto object from customized data. - std::unique_ptr<AppDataProto> GetProto(const std::string& package_name); + std::unique_ptr<AppDataProto> GetProto(std::string package_name); // Apply the customized proto data. void OnProtoDataAvailable(std::unique_ptr<TouchInjector> touch_injector, std::unique_ptr<AppDataProto> proto); // Callback function triggered by Save button. void OnSaveProtoFile(std::unique_ptr<AppDataProto> proto, - const std::string& package_name); - void SaveFile(std::unique_ptr<AppDataProto> proto, - const std::string& package_name); + std::string package_name); + // Pass |package_name| by value because it runs on task runner. + void SaveFile(std::unique_ptr<AppDataProto> proto, std::string package_name); void NotifyTextInputState(); void AddObserverToInputMethod(); void RemoveObserverFromInputMethod();
diff --git a/chrome/browser/ash/arc/input_overlay/arc_input_overlay_manager_unittest.cc b/chrome/browser/ash/arc/input_overlay/arc_input_overlay_manager_unittest.cc index b304830..7be3503 100644 --- a/chrome/browser/ash/arc/input_overlay/arc_input_overlay_manager_unittest.cc +++ b/chrome/browser/ash/arc/input_overlay/arc_input_overlay_manager_unittest.cc
@@ -36,8 +36,7 @@ "org.chromium.arc.testapp.inputoverlay_game"; constexpr const float kTolerance = 0.999f; -class MockDisplayOverlayController - : public input_overlay::DisplayOverlayController { +class MockDisplayOverlayController : public DisplayOverlayController { public: explicit MockDisplayOverlayController(TouchInjector* touch_injector) : DisplayOverlayController(touch_injector, false) {}
diff --git a/chrome/browser/ash/arc/input_overlay/arc_input_overlay_ukm.cc b/chrome/browser/ash/arc/input_overlay/arc_input_overlay_ukm.cc index 242a897..f23ec44a 100644 --- a/chrome/browser/ash/arc/input_overlay/arc_input_overlay_ukm.cc +++ b/chrome/browser/ash/arc/input_overlay/arc_input_overlay_ukm.cc
@@ -13,7 +13,7 @@ constexpr bool kCustomizationUsed = true; void InputOverlayUkm::RecordInputOverlayFeatureStateUkm( - const std::string& package_name, + std::string package_name, bool enable) { ukm::builders::GamingInputOverlay_Feature( ukm::AppSourceUrlRecorder::GetSourceIdForArcPackageName(package_name)) @@ -22,7 +22,7 @@ } void InputOverlayUkm::RecordInputOverlayMappingHintStateUkm( - const std::string& package_name, + std::string package_name, bool enable) { ukm::builders::GamingInputOverlay_MappingHint( ukm::AppSourceUrlRecorder::GetSourceIdForArcPackageName(package_name)) @@ -31,7 +31,7 @@ } void InputOverlayUkm::RecordInputOverlayCustomizedUsageUkm( - const std::string& package_name) { + std::string package_name) { ukm::builders::GamingInputOverlay_Customization( ukm::AppSourceUrlRecorder::GetSourceIdForArcPackageName(package_name)) .SetCustomizationUsed(kCustomizationUsed)
diff --git a/chrome/browser/ash/arc/input_overlay/arc_input_overlay_ukm.h b/chrome/browser/ash/arc/input_overlay/arc_input_overlay_ukm.h index aef7ac91..67a3a51 100644 --- a/chrome/browser/ash/arc/input_overlay/arc_input_overlay_ukm.h +++ b/chrome/browser/ash/arc/input_overlay/arc_input_overlay_ukm.h
@@ -13,15 +13,13 @@ // AppSourceUrlRecorder. class InputOverlayUkm { public: - static void RecordInputOverlayFeatureStateUkm(const std::string& package_name, + static void RecordInputOverlayFeatureStateUkm(std::string package_name, bool enable); - static void RecordInputOverlayMappingHintStateUkm( - const std::string& package_name, - bool enable); + static void RecordInputOverlayMappingHintStateUkm(std::string package_name, + bool enable); - static void RecordInputOverlayCustomizedUsageUkm( - const std::string& package_name); + static void RecordInputOverlayCustomizedUsageUkm(std::string package_name); }; } // namespace arc::input_overlay
diff --git a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc index fd8eec5..abd8f8d 100644 --- a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc +++ b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
@@ -616,8 +616,8 @@ touch_injector_->OnBindingRestore(); } -const std::string* DisplayOverlayController::GetPackageName() const { - return touch_injector_->GetPackageName(); +const std::string& DisplayOverlayController::GetPackageName() const { + return touch_injector_->package_name(); } void DisplayOverlayController::OnApplyMenuState() {
diff --git a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h index c8c5605..dbdb54b8 100644 --- a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h +++ b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h
@@ -70,7 +70,7 @@ // Restore back to original default binding when users press the restore // button after editing. void OnCustomizeRestore(); - const std::string* GetPackageName() const; + const std::string& GetPackageName() const; // Once the menu state is loaded from protobuf data, it should be applied on // the view. For example, |InputMappingView| may not be visible if it is // hidden or input overlay is disabled.
diff --git a/chrome/browser/ash/arc/input_overlay/display_overlay_controller_unittest.cc b/chrome/browser/ash/arc/input_overlay/display_overlay_controller_unittest.cc index 007abae..80cfb4a 100644 --- a/chrome/browser/ash/arc/input_overlay/display_overlay_controller_unittest.cc +++ b/chrome/browser/ash/arc/input_overlay/display_overlay_controller_unittest.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ash/arc/input_overlay/display_overlay_controller.h" +#include "ash/public/cpp/window_properties.h" #include "ash/shell.h" #include "base/json/json_reader.h" #include "base/test/bind.h" @@ -39,8 +40,9 @@ "org.chromium.arc.testapp.inputoverlay"); injector_ = std::make_unique<TouchInjector>( arc_test_window_->GetWindow(), + *arc_test_window_->GetWindow()->GetProperty(ash::kArcPackageNameKey), base::BindLambdaForTesting( - [&](std::unique_ptr<AppDataProto>, const std::string&) {})); + [&](std::unique_ptr<AppDataProto>, std::string) {})); controller_ = std::make_unique<DisplayOverlayController>(injector_.get(), false); }
diff --git a/chrome/browser/ash/arc/input_overlay/touch_injector.cc b/chrome/browser/ash/arc/input_overlay/touch_injector.cc index 2589931..fd7b990 100644 --- a/chrome/browser/ash/arc/input_overlay/touch_injector.cc +++ b/chrome/browser/ash/arc/input_overlay/touch_injector.cc
@@ -157,8 +157,10 @@ }; TouchInjector::TouchInjector(aura::Window* top_level_window, + const std::string& package_name, OnSaveProtoFileCallback save_file_callback) : window_(top_level_window), + package_name_(package_name), content_bounds_(CalculateWindowContentBounds(window_)), save_file_callback_(save_file_callback) {} @@ -316,10 +318,6 @@ action->RestoreToDefault(); } -const std::string* TouchInjector::GetPackageName() const { - return window_->GetProperty(ash::kArcPackageNameKey); -} - void TouchInjector::OnProtoDataAvailable(AppDataProto& proto) { LoadMenuEntryFromProto(proto); LoadMenuStateFromProto(proto); @@ -347,21 +345,20 @@ void TouchInjector::OnInputMenuViewRemoved() { OnSaveProtoFile(); - const auto* package_name = GetPackageName(); // Record UMA stats upon |InputMenuView| close because it needs to ignore the // unfinalized menu state change. if (touch_injector_enable_ != touch_injector_enable_uma_) { touch_injector_enable_uma_ = touch_injector_enable_; RecordInputOverlayFeatureState(touch_injector_enable_uma_); InputOverlayUkm::RecordInputOverlayFeatureStateUkm( - *package_name, touch_injector_enable_uma_); + package_name_, touch_injector_enable_uma_); } if (input_mapping_visible_ != input_mapping_visible_uma_) { input_mapping_visible_uma_ = input_mapping_visible_; RecordInputOverlayMappingHintState(input_mapping_visible_uma_); InputOverlayUkm::RecordInputOverlayMappingHintStateUkm( - *package_name, input_mapping_visible_uma_); + package_name_, input_mapping_visible_uma_); } } @@ -771,8 +768,7 @@ void TouchInjector::OnSaveProtoFile() { auto app_data_proto = ConvertToProto(); - std::string package_name(*GetPackageName()); - save_file_callback_.Run(std::move(app_data_proto), package_name); + save_file_callback_.Run(std::move(app_data_proto), package_name_); } void TouchInjector::AddMenuStateToProto(AppDataProto& proto) { @@ -941,13 +937,12 @@ void TouchInjector::RecordMenuStateOnLaunch() { touch_injector_enable_uma_ = touch_injector_enable_; input_mapping_visible_uma_ = input_mapping_visible_; - const auto* package_name = GetPackageName(); RecordInputOverlayFeatureState(touch_injector_enable_uma_); InputOverlayUkm::RecordInputOverlayFeatureStateUkm( - *package_name, touch_injector_enable_uma_); + package_name_, touch_injector_enable_uma_); RecordInputOverlayMappingHintState(input_mapping_visible_uma_); InputOverlayUkm::RecordInputOverlayMappingHintStateUkm( - *package_name, input_mapping_visible_uma_); + package_name_, input_mapping_visible_uma_); } int TouchInjector::GetRewrittenTouchIdForTesting(ui::PointerId original_id) {
diff --git a/chrome/browser/ash/arc/input_overlay/touch_injector.h b/chrome/browser/ash/arc/input_overlay/touch_injector.h index 00690f8e..98d6764c 100644 --- a/chrome/browser/ash/arc/input_overlay/touch_injector.h +++ b/chrome/browser/ash/arc/input_overlay/touch_injector.h
@@ -48,51 +48,14 @@ class TouchInjector : public ui::EventRewriter { public: using OnSaveProtoFileCallback = - base::RepeatingCallback<void(std::unique_ptr<AppDataProto>, - const std::string&)>; + base::RepeatingCallback<void(std::unique_ptr<AppDataProto>, std::string)>; TouchInjector(aura::Window* top_level_window, + const std::string& package_name, OnSaveProtoFileCallback save_file_callback); TouchInjector(const TouchInjector&) = delete; TouchInjector& operator=(const TouchInjector&) = delete; ~TouchInjector() override; - aura::Window* window() { return window_; } - const gfx::RectF& content_bounds() const { return content_bounds_; } - const gfx::Transform* rotation_transform() { - return rotation_transform_.get(); - } - const std::vector<std::unique_ptr<Action>>& actions() const { - return actions_; - } - bool is_mouse_locked() const { return is_mouse_locked_; } - - bool touch_injector_enable() const { return touch_injector_enable_; } - void store_touch_injector_enable(bool enable) { - touch_injector_enable_ = enable; - } - - bool input_mapping_visible() const { return input_mapping_visible_; } - void store_input_mapping_visible(bool enable) { - input_mapping_visible_ = enable; - } - - bool first_launch() const { return first_launch_; } - void set_first_launch(bool first_launch) { first_launch_ = first_launch; } - - bool show_nudge() const { return show_nudge_; } - void set_show_nudge(bool show_nudge) { show_nudge_ = show_nudge; } - - void set_display_mode(DisplayMode mode) { display_mode_ = mode; } - void set_display_overlay_controller(DisplayOverlayController* controller) { - display_overlay_controller_ = controller; - } - - bool enable_mouse_lock() { return enable_mouse_lock_; } - void set_enable_mouse_lock(bool enable) { enable_mouse_lock_ = true; } - - bool beta() const { return beta_; } - void set_beta(bool beta) { beta_ = beta; } - // Parse Json to actions. // Json value format: // { @@ -130,7 +93,6 @@ void OnBindingCancel(); // Set input binding back to original binding. void OnBindingRestore(); - const std::string* GetPackageName() const; void OnProtoDataAvailable(AppDataProto& proto); // Save the input menu state when the menu is closed. void OnInputMenuViewRemoved(); @@ -166,6 +128,44 @@ const ui::Event& event, const Continuation continuation) override; + aura::Window* window() { return window_; } + const std::string& package_name() const { return package_name_; } + const gfx::RectF& content_bounds() const { return content_bounds_; } + const gfx::Transform* rotation_transform() { + return rotation_transform_.get(); + } + const std::vector<std::unique_ptr<Action>>& actions() const { + return actions_; + } + bool is_mouse_locked() const { return is_mouse_locked_; } + + bool touch_injector_enable() const { return touch_injector_enable_; } + void store_touch_injector_enable(bool enable) { + touch_injector_enable_ = enable; + } + + bool input_mapping_visible() const { return input_mapping_visible_; } + void store_input_mapping_visible(bool enable) { + input_mapping_visible_ = enable; + } + + bool first_launch() const { return first_launch_; } + void set_first_launch(bool first_launch) { first_launch_ = first_launch; } + + bool show_nudge() const { return show_nudge_; } + void set_show_nudge(bool show_nudge) { show_nudge_ = show_nudge; } + + void set_display_mode(DisplayMode mode) { display_mode_ = mode; } + void set_display_overlay_controller(DisplayOverlayController* controller) { + display_overlay_controller_ = controller; + } + + bool enable_mouse_lock() { return enable_mouse_lock_; } + void set_enable_mouse_lock(bool enable) { enable_mouse_lock_ = true; } + + bool beta() const { return beta_; } + void set_beta(bool beta) { beta_ = beta; } + private: friend class ArcInputOverlayManagerTest; friend class TouchInjectorTest; @@ -260,6 +260,7 @@ // registered only when |window_| is focused. And TouchInjector doesn't own // |window_| and it is destroyed when |window_| is destroyed. raw_ptr<aura::Window> window_; + std::string package_name_; gfx::RectF content_bounds_; base::WeakPtr<ui::EventRewriterContinuation> continuation_; std::vector<std::unique_ptr<Action>> actions_;
diff --git a/chrome/browser/ash/arc/input_overlay/touch_injector_unittest.cc b/chrome/browser/ash/arc/input_overlay/touch_injector_unittest.cc index be08802..4659396c 100644 --- a/chrome/browser/ash/arc/input_overlay/touch_injector_unittest.cc +++ b/chrome/browser/ash/arc/input_overlay/touch_injector_unittest.cc
@@ -326,8 +326,9 @@ .y(); injector_ = std::make_unique<TouchInjector>( widget_->GetNativeWindow(), + *widget_->GetNativeWindow()->GetProperty(ash::kArcPackageNameKey), base::BindLambdaForTesting( - [&](std::unique_ptr<AppDataProto>, const std::string&) {})); + [&](std::unique_ptr<AppDataProto>, std::string) {})); injector_->set_beta(true); } @@ -907,8 +908,9 @@ // Check whether AppDataProto is deserialized correctly. auto injector = std::make_unique<TouchInjector>( widget_->GetNativeWindow(), + *widget_->GetNativeWindow()->GetProperty(ash::kArcPackageNameKey), base::BindLambdaForTesting( - [&](std::unique_ptr<AppDataProto>, const std::string&) {})); + [&](std::unique_ptr<AppDataProto>, std::string) {})); injector->set_beta(true); injector->ParseActions(*json_value); injector->OnProtoDataAvailable(*proto);
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_view_unittest.cc b/chrome/browser/ash/arc/input_overlay/ui/action_view_unittest.cc index 3bcb072..a35bd52 100644 --- a/chrome/browser/ash/arc/input_overlay/ui/action_view_unittest.cc +++ b/chrome/browser/ash/arc/input_overlay/ui/action_view_unittest.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ash/arc/input_overlay/ui/action_view.h" +#include "ash/public/cpp/window_properties.h" #include "base/json/json_reader.h" #include "base/test/bind.h" #include "chrome/browser/ash/arc/input_overlay/constants.h" @@ -129,8 +130,9 @@ widget_ = CreateArcWindow(root_window(), gfx::Rect(200, 100, 400, 600)); touch_injector_ = std::make_unique<TouchInjector>( widget_->GetNativeWindow(), + *widget_->GetNativeWindow()->GetProperty(ash::kArcPackageNameKey), base::BindLambdaForTesting( - [&](std::unique_ptr<AppDataProto>, const std::string&) {})); + [&](std::unique_ptr<AppDataProto>, std::string) {})); touch_injector_->set_beta(true); touch_injector_->ParseActions( *base::JSONReader::ReadAndReturnValueWithError(
diff --git a/chrome/browser/ash/arc/input_overlay/ui/input_menu_view.cc b/chrome/browser/ash/arc/input_overlay/ui/input_menu_view.cc index 12fd07a..1d76603 100644 --- a/chrome/browser/ash/arc/input_overlay/ui/input_menu_view.cc +++ b/chrome/browser/ash/arc/input_overlay/ui/input_menu_view.cc
@@ -89,10 +89,11 @@ constexpr char kBoardName[] = "entry.1492517074"; constexpr char kOsVersion[] = "entry.1961594320"; -GURL GetAssembleUrl(DisplayOverlayController& controller) { +// Pass |package_name| by value because the focus will be changed to the +// browser. +GURL GetAssembleUrl(std::string package_name) { GURL url(kFeedbackUrl); - const auto* package_name = controller.GetPackageName(); - url = net::AppendQueryParameter(url, kGamePackageName, *package_name); + url = net::AppendQueryParameter(url, kGamePackageName, package_name); url = net::AppendQueryParameter(url, kBoardName, base::SysInfo::HardwareModelName()); url = net::AppendQueryParameter(url, kOsVersion, @@ -405,7 +406,7 @@ } RecordInputOverlayCustomizedUsage(); InputOverlayUkm::RecordInputOverlayCustomizedUsageUkm( - *(display_overlay_controller_->GetPackageName())); + display_overlay_controller_->GetPackageName()); // Change display mode, load edit UI per action and overall edit buttons; make // sure the following line is at the bottom because edit mode will kill this // view. @@ -417,7 +418,7 @@ if (!display_overlay_controller_) return; - GURL url = GetAssembleUrl(*display_overlay_controller_); + GURL url = GetAssembleUrl(display_overlay_controller_->GetPackageName()); ash::NewWindowDelegate::GetPrimary()->OpenUrl( url, ash::NewWindowDelegate::OpenUrlFrom::kUserInteraction, ash::NewWindowDelegate::Disposition::kNewForegroundTab);
diff --git a/chrome/browser/ash/arc/input_overlay/ui/menu_entry_view_unittest.cc b/chrome/browser/ash/arc/input_overlay/ui/menu_entry_view_unittest.cc index bd8a62c0..ad635a7 100644 --- a/chrome/browser/ash/arc/input_overlay/ui/menu_entry_view_unittest.cc +++ b/chrome/browser/ash/arc/input_overlay/ui/menu_entry_view_unittest.cc
@@ -6,6 +6,7 @@ #include <string> +#include "ash/public/cpp/window_properties.h" #include "ash/shell.h" #include "base/test/bind.h" #include "chrome/browser/ash/arc/input_overlay/constants.h" @@ -132,8 +133,9 @@ "org.chromium.arc.testapp.inputoverlay"); touch_injector_ = std::make_unique<TouchInjector>( arc_test_window_->GetWindow(), + *arc_test_window_->GetWindow()->GetProperty(ash::kArcPackageNameKey), base::BindLambdaForTesting( - [&](std::unique_ptr<AppDataProto>, const std::string&) {})); + [&](std::unique_ptr<AppDataProto>, std::string) {})); touch_injector_->set_beta(true); display_overlay_controller_ = std::make_unique<DisplayOverlayController>( touch_injector_.get(), /*first_launch=*/false);
diff --git a/chrome/browser/ash/login/screens/recommend_apps/recommend_apps_fetcher_impl.cc b/chrome/browser/ash/login/screens/recommend_apps/recommend_apps_fetcher_impl.cc index 9155a65..5471e50 100644 --- a/chrome/browser/ash/login/screens/recommend_apps/recommend_apps_fetcher_impl.cc +++ b/chrome/browser/ash/login/screens/recommend_apps/recommend_apps_fetcher_impl.cc
@@ -531,25 +531,23 @@ absl::optional<base::Value> RecommendAppsFetcherImpl::ParseResponse( const base::Value& parsed_json) { - base::Value output(base::Value::Type::LIST); + base::Value::List output; // If the response is a dictionary, it is an error message in the // following format: // {"Error code":"error code","Error message":"Error message"} if (parsed_json.is_dict()) { - const base::Value* response_error_code_value = - parsed_json.FindKeyOfType("Error code", base::Value::Type::STRING); - if (!response_error_code_value) { + const std::string* response_error_code_str = + parsed_json.FindStringKey("Error code"); + if (!response_error_code_str) { LOG(ERROR) << "Unable to find error code"; RecordUmaResponseParseResult( RECOMMEND_APPS_RESPONSE_PARSE_RESULT_INVALID_JSON); return absl::nullopt; } - base::StringPiece response_error_code_str = - response_error_code_value->GetString(); int response_error_code = 0; - if (!base::StringToInt(response_error_code_str, &response_error_code)) { + if (!base::StringToInt(*response_error_code_str, &response_error_code)) { LOG(WARNING) << "Unable to parse error code: " << response_error_code_str; RecordUmaResponseParseResult( RECOMMEND_APPS_RESPONSE_PARSE_RESULT_INVALID_ERROR_CODE); @@ -579,24 +577,23 @@ } for (auto& item : app_list) { - base::Value output_map(base::Value::Type::DICTIONARY); - if (!item.is_dict()) { DVLOG(1) << "Cannot parse item."; continue; } + base::Value::Dict output_map; // Retrieve the app title. const base::Value* title = item.FindPathOfType({"title_", "name_"}, base::Value::Type::STRING); if (title) - output_map.SetKey("name", base::Value(title->GetString())); + output_map.Set("name", title->GetString()); // Retrieve the package name. const base::Value* package_name = item.FindPathOfType({"id_", "id_"}, base::Value::Type::STRING); if (package_name) - output_map.SetKey("package_name", base::Value(package_name->GetString())); + output_map.Set("package_name", package_name->GetString()); // Retrieve the icon URL for the app. // @@ -610,9 +607,9 @@ {"icon_", "url_", "privateDoNotAccessOrElseSafeUrlWrappedValue_"}, base::Value::Type::STRING); if (icon_url) - output_map.SetKey("icon", base::Value(icon_url->GetString())); + output_map.Set("icon", icon_url->GetString()); - if (output_map.DictEmpty()) { + if (output_map.empty()) { DVLOG(1) << "Invalid app item."; continue; } @@ -621,9 +618,9 @@ } RecordUmaResponseParseResult(RECOMMEND_APPS_RESPONSE_PARSE_RESULT_NO_ERROR); - RecordUmaResponseAppCount(static_cast<int>(output.GetList().size())); + RecordUmaResponseAppCount(static_cast<int>(output.size())); - return output; + return base::Value(std::move(output)); } void RecommendAppsFetcherImpl::OnJsonParsed(
diff --git a/chrome/browser/ash/login/screens/recommend_apps_screen_browsertest.cc b/chrome/browser/ash/login/screens/recommend_apps_screen_browsertest.cc index 5203856..2962806 100644 --- a/chrome/browser/ash/login/screens/recommend_apps_screen_browsertest.cc +++ b/chrome/browser/ash/login/screens/recommend_apps_screen_browsertest.cc
@@ -86,23 +86,6 @@ } ]})json"; -struct FakeAppInfo { - public: - FakeAppInfo(const std::string& package_name, const std::string& name) - : package_name(package_name), name(name) {} - ~FakeAppInfo() = default; - - base::Value ToValue() const { - base::Value result(base::Value::Type::DICTIONARY); - result.SetKey("package_name", base::Value(package_name)); - result.SetKey("name", base::Value(name)); - return result; - } - - const std::string package_name; - const std::string name; -}; - class StubRecommendAppsFetcher : public RecommendAppsFetcher { public: explicit StubRecommendAppsFetcher(RecommendAppsFetcherDelegate* delegate) @@ -263,7 +246,7 @@ ProfileManager::GetActiveUserProfile()->GetPrefs()->GetList( arc::prefs::kArcFastAppReinstallPackages); - base::Value expected_pref_value(base::Value::Type::LIST); + base::Value::List expected_pref_value; expected_pref_value.Append("test.app.foo.app1"); expected_pref_value.Append("test.app.foo.app2"); EXPECT_EQ(expected_pref_value, fast_reinstall_packages); @@ -297,7 +280,7 @@ ProfileManager::GetActiveUserProfile()->GetPrefs()->GetList( arc::prefs::kArcFastAppReinstallPackages); - base::Value expected_pref_value(base::Value::Type::LIST); + base::Value::List expected_pref_value; expected_pref_value.Append("test.app.foo.app2"); EXPECT_EQ(expected_pref_value, fast_reinstall_packages); } @@ -328,7 +311,7 @@ const base::Value::List& fast_reinstall_packages = ProfileManager::GetActiveUserProfile()->GetPrefs()->GetList( arc::prefs::kArcFastAppReinstallPackages); - EXPECT_EQ(base::Value(base::Value::Type::LIST), fast_reinstall_packages); + EXPECT_EQ(base::Value::List(), fast_reinstall_packages); } IN_PROC_BROWSER_TEST_F(RecommendAppsScreenTest, SkipWithNoAppsSelected) { @@ -360,15 +343,13 @@ const base::Value::List& fast_reinstall_packages = ProfileManager::GetActiveUserProfile()->GetPrefs()->GetList( arc::prefs::kArcFastAppReinstallPackages); - EXPECT_EQ(base::Value(base::Value::Type::LIST), fast_reinstall_packages); + EXPECT_EQ(base::Value::List(), fast_reinstall_packages); } IN_PROC_BROWSER_TEST_F(RecommendAppsScreenTest, InstallWithNoAppsSelectedDisabled) { ShowScreenAndExpectLoadingStep(); - std::vector<FakeAppInfo> test_apps = { - FakeAppInfo("test.app.foo.app1", "Test app 1")}; recommend_apps_fetcher_->SimulateSuccess(); ExpectAppSelectionStep(); @@ -390,7 +371,7 @@ const base::Value::List& fast_reinstall_packages = ProfileManager::GetActiveUserProfile()->GetPrefs()->GetList( arc::prefs::kArcFastAppReinstallPackages); - EXPECT_EQ(base::Value(base::Value::Type::LIST), fast_reinstall_packages); + EXPECT_EQ(base::Value::List(), fast_reinstall_packages); } IN_PROC_BROWSER_TEST_F(RecommendAppsScreenTest, ParseError) {
diff --git a/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate_browsertest.cc b/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate_browsertest.cc index ffefd636..4855968 100644 --- a/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate_browsertest.cc +++ b/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate_browsertest.cc
@@ -117,6 +117,9 @@ void NotifyFeedbackDelayed() const override; feedback::FeedbackUploader* GetFeedbackUploaderForContext( content::BrowserContext* context) const override; + void OpenFeedback( + content::BrowserContext* context, + extensions::api::feedback_private::FeedbackSource source) const override; private: base::RepeatingCallback<void(bool)> on_fetch_completed_; @@ -174,6 +177,10 @@ void FakeFeedbackPrivateDelegate::NotifyFeedbackDelayed() const {} +void FakeFeedbackPrivateDelegate::OpenFeedback( + content::BrowserContext* context, + extensions::api::feedback_private::FeedbackSource source) const {} + feedback::FeedbackUploader* FakeFeedbackPrivateDelegate::GetFeedbackUploaderForContext( content::BrowserContext* context) const {
diff --git a/chrome/browser/ash/policy/reporting/app_install_event_log_manager_wrapper_unittest.cc b/chrome/browser/ash/policy/reporting/app_install_event_log_manager_wrapper_unittest.cc index e6419a86..d37ddff6 100644 --- a/chrome/browser/ash/policy/reporting/app_install_event_log_manager_wrapper_unittest.cc +++ b/chrome/browser/ash/policy/reporting/app_install_event_log_manager_wrapper_unittest.cc
@@ -80,9 +80,10 @@ event.set_event_type(em::AppInstallReportLogEvent::SUCCESS); log.Add(kPackageName, event); log.Store(); - profile_.GetPrefs()->Set(arc::prefs::kArcPushInstallAppsRequested, - app_list_); - profile_.GetPrefs()->Set(arc::prefs::kArcPushInstallAppsPending, app_list_); + profile_.GetPrefs()->SetList(arc::prefs::kArcPushInstallAppsRequested, + app_list_.Clone()); + profile_.GetPrefs()->SetList(arc::prefs::kArcPushInstallAppsPending, + app_list_.Clone()); } void FlushPendingTasks() { @@ -133,7 +134,7 @@ TestingProfile profile_; const base::FilePath log_file_path_; - base::ListValue app_list_; + base::Value::List app_list_; std::unique_ptr<AppInstallEventLogManagerWrapperTestable> wrapper_;
diff --git a/chrome/browser/ash/policy/reporting/arc_app_install_event_log_manager_unittest.cc b/chrome/browser/ash/policy/reporting/arc_app_install_event_log_manager_unittest.cc index 60326b1..7ff8a2e11 100644 --- a/chrome/browser/ash/policy/reporting/arc_app_install_event_log_manager_unittest.cc +++ b/chrome/browser/ash/policy/reporting/arc_app_install_event_log_manager_unittest.cc
@@ -730,10 +730,12 @@ log.Add(kPackageNames[0], event_); log.Store(); - base::ListValue list; + base::Value::List list; list.Append("test"); - profile_.GetPrefs()->Set(arc::prefs::kArcPushInstallAppsRequested, list); - profile_.GetPrefs()->Set(arc::prefs::kArcPushInstallAppsPending, list); + profile_.GetPrefs()->SetList(arc::prefs::kArcPushInstallAppsRequested, + list.Clone()); + profile_.GetPrefs()->SetList(arc::prefs::kArcPushInstallAppsPending, + list.Clone()); ArcAppInstallEventLogManager::Clear(&log_task_runner_wrapper_, &profile_); EXPECT_TRUE(profile_.GetPrefs() @@ -764,10 +766,12 @@ FlushNonDelayedTasks(); VerifyLogFile(); - base::ListValue list; + base::Value::List list; list.Append("test"); - profile_.GetPrefs()->Set(arc::prefs::kArcPushInstallAppsRequested, list); - profile_.GetPrefs()->Set(arc::prefs::kArcPushInstallAppsPending, list); + profile_.GetPrefs()->SetList(arc::prefs::kArcPushInstallAppsRequested, + list.Clone()); + profile_.GetPrefs()->SetList(arc::prefs::kArcPushInstallAppsPending, + list.Clone()); ArcAppInstallEventLogManager::Clear(&log_task_runner_wrapper_, &profile_); EXPECT_TRUE(profile_.GetPrefs()
diff --git a/chrome/browser/ash/policy/reporting/arc_app_install_event_logger_unittest.cc b/chrome/browser/ash/policy/reporting/arc_app_install_event_logger_unittest.cc index 98a7e6c..6599fb4 100644 --- a/chrome/browser/ash/policy/reporting/arc_app_install_event_logger_unittest.cc +++ b/chrome/browser/ash/policy/reporting/arc_app_install_event_logger_unittest.cc
@@ -247,16 +247,16 @@ PolicyMap policy_map; base::DictionaryValue arc_policy; - auto list = std::make_unique<base::ListValue>(); + base::Value::List list; for (std::string package_name : package_names) { base::Value::Dict package; package.Set("installType", "FORCE_INSTALLED"); package.Set("packageName", package_name); - list->Append(base::Value(std::move(package))); + list.Append(std::move(package)); } - arc_policy.SetList("applications", std::move(list)); + arc_policy.GetDict().Set("applications", std::move(list)); std::string arc_policy_string; base::JSONWriter::Write(arc_policy, &arc_policy_string); SetPolicy(&policy_map, key::kArcEnabled, base::Value(true)); @@ -267,17 +267,17 @@ base::DictionaryValue CreateComplianceReport( std::set<std::string> noncompliant_packages) { - auto details = std::make_unique<base::ListValue>(); + base::Value::List details; for (std::string package_name : noncompliant_packages) { base::Value::Dict package; package.Set("nonComplianceReason", 5); package.Set("packageName", package_name); - details->Append(base::Value(std::move(package))); + details.Append(std::move(package)); } base::DictionaryValue compliance_report; - compliance_report.SetList("nonComplianceDetails", std::move(details)); + compliance_report.GetDict().Set("nonComplianceDetails", std::move(details)); return compliance_report; } @@ -299,10 +299,12 @@ // pending. Clear all data related to app-install event log collection. Verify // that the lists are cleared. TEST_F(AppInstallEventLoggerTest, Clear) { - base::ListValue list; + base::Value::List list; list.Append("test"); - profile_.GetPrefs()->Set(arc::prefs::kArcPushInstallAppsRequested, list); - profile_.GetPrefs()->Set(arc::prefs::kArcPushInstallAppsPending, list); + profile_.GetPrefs()->SetList(arc::prefs::kArcPushInstallAppsRequested, + list.Clone()); + profile_.GetPrefs()->SetList(arc::prefs::kArcPushInstallAppsPending, + list.Clone()); ArcAppInstallEventLogger::Clear(&profile_); EXPECT_TRUE(profile_.GetPrefs() ->FindPreference(arc::prefs::kArcPushInstallAppsRequested) @@ -463,31 +465,31 @@ PolicyMap new_policy_map; base::DictionaryValue arc_policy; - auto list = std::make_unique<base::ListValue>(); + base::Value::List list; // Test that REQUIRED, PREINSTALLED and FORCE_INSTALLED are markers to include // app to the tracking. BLOCKED and AVAILABLE are excluded. base::Value::Dict package1; package1.Set("installType", "REQUIRED"); package1.Set("packageName", kPackageName); - list->Append(base::Value(std::move(package1))); + list.Append(std::move(package1)); base::Value::Dict package2; package2.Set("installType", "PREINSTALLED"); package2.Set("packageName", kPackageName2); - list->Append(base::Value(std::move(package2))); + list.Append(std::move(package2)); base::Value::Dict package3; package3.Set("installType", "FORCE_INSTALLED"); package3.Set("packageName", kPackageName3); - list->Append(base::Value(std::move(package3))); + list.Append(std::move(package3)); base::Value::Dict package4; package4.Set("installType", "BLOCKED"); package4.Set("packageName", kPackageName4); - list->Append(base::Value(std::move(package4))); + list.Append(std::move(package4)); base::Value::Dict package5; package5.Set("installType", "AVAILABLE"); package5.Set("packageName", kPackageName5); - list->Append(base::Value(std::move(package5))); - arc_policy.SetList("applications", std::move(list)); + list.Append(std::move(package5)); + arc_policy.GetDict().Set("applications", std::move(list)); std::string arc_policy_string; base::JSONWriter::Write(arc_policy, &arc_policy_string);
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_sampler_handler.h b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_sampler_handler.h new file mode 100644 index 0000000..aae51f25 --- /dev/null +++ b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_sampler_handler.h
@@ -0,0 +1,29 @@ +// 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. + +#ifndef CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_CROS_HEALTHD_SAMPLER_HANDLERS_CROS_HEALTHD_SAMPLER_HANDLER_H_ +#define CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_CROS_HEALTHD_SAMPLER_HANDLERS_CROS_HEALTHD_SAMPLER_HANDLER_H_ + +#include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd_probe.mojom.h" +#include "components/reporting/metrics/sampler.h" + +namespace reporting { + +namespace cros_healthd = ::ash::cros_healthd::mojom; + +// CrosHealthdSamplerHandler is an interface that can be used to process the +// returned result after probing the croshealthd for a particular category. +class CrosHealthdSamplerHandler { + public: + virtual ~CrosHealthdSamplerHandler() = default; + + // Converts |result| to MetricData and passes it to |callback|. This method is + // used when there's only one possible MetricType for the metric category. + virtual void HandleResult(cros_healthd::TelemetryInfoPtr result, + OptionalMetricCallback callback) const = 0; +}; + +} // namespace reporting + +#endif // CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_CROS_HEALTHD_SAMPLER_HANDLERS_CROS_HEALTHD_SAMPLER_HANDLER_H_
diff --git a/chrome/browser/ash/web_applications/shortcut_customization_ui/OWNERS b/chrome/browser/ash/web_applications/shortcut_customization_ui/OWNERS new file mode 100644 index 0000000..d8674605 --- /dev/null +++ b/chrome/browser/ash/web_applications/shortcut_customization_ui/OWNERS
@@ -0,0 +1 @@ +file://ash/webui/shortcut_customization_ui/OWNERS \ No newline at end of file
diff --git a/chrome/browser/ash/web_applications/shortcut_customization_ui/chrome_shortcut_customization_delegate.cc b/chrome/browser/ash/web_applications/shortcut_customization_ui/chrome_shortcut_customization_delegate.cc new file mode 100644 index 0000000..e5a7b02 --- /dev/null +++ b/chrome/browser/ash/web_applications/shortcut_customization_ui/chrome_shortcut_customization_delegate.cc
@@ -0,0 +1,17 @@ +// Copyright 2022 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/web_applications/shortcut_customization_ui/chrome_shortcut_customization_delegate.h" +#include "chrome/browser/profiles/profile.h" + +ChromeShortcutCustomizationDelegate::ChromeShortcutCustomizationDelegate( + content::WebUI* web_ui) + : web_ui_(web_ui) {} + +ChromeShortcutCustomizationDelegate::~ChromeShortcutCustomizationDelegate() = + default; + +PrefService* ChromeShortcutCustomizationDelegate::GetPrefService() { + return Profile::FromWebUI(web_ui_)->GetPrefs(); +}
diff --git a/chrome/browser/ash/web_applications/shortcut_customization_ui/chrome_shortcut_customization_delegate.h b/chrome/browser/ash/web_applications/shortcut_customization_ui/chrome_shortcut_customization_delegate.h new file mode 100644 index 0000000..3e36a8c --- /dev/null +++ b/chrome/browser/ash/web_applications/shortcut_customization_ui/chrome_shortcut_customization_delegate.h
@@ -0,0 +1,35 @@ +// Copyright 2022 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_WEB_APPLICATIONS_SHORTCUT_CUSTOMIZATION_UI_CHROME_SHORTCUT_CUSTOMIZATION_DELEGATE_H_ +#define CHROME_BROWSER_ASH_WEB_APPLICATIONS_SHORTCUT_CUSTOMIZATION_UI_CHROME_SHORTCUT_CUSTOMIZATION_DELEGATE_H_ + +#include "ash/webui/shortcut_customization_ui/backend/shortcut_customization_delegate.h" +#include "components/prefs/pref_service.h" +#include "content/public/browser/web_ui.h" + +/** + * Implementation of the ShortcutCustomizationDelegate interface. Provides the + * shortcut customization app code in ash/ with functions that only exist in + * chrome/. + */ +class ChromeShortcutCustomizationDelegate + : public ash::shortcut_ui::ShortcutCustomizationDelegate { + public: + explicit ChromeShortcutCustomizationDelegate(content::WebUI* web_ui); + + ChromeShortcutCustomizationDelegate( + const ChromeShortcutCustomizationDelegate&) = delete; + ChromeShortcutCustomizationDelegate& operator=( + const ChromeShortcutCustomizationDelegate&) = delete; + ~ChromeShortcutCustomizationDelegate() override; + + // Get the pref service. + PrefService* GetPrefService() override; + + private: + content::WebUI* web_ui_; +}; + +#endif // CHROME_BROWSER_ASH_WEB_APPLICATIONS_SHORTCUT_CUSTOMIZATION_UI_CHROME_SHORTCUT_CUSTOMIZATION_DELEGATE_H_
diff --git a/chrome/browser/browser_features.cc b/chrome/browser/browser_features.cc index 0e52a00..0d66294 100644 --- a/chrome/browser/browser_features.cc +++ b/chrome/browser/browser_features.cc
@@ -153,7 +153,7 @@ // Motivation: // The blue border behavior used to cause problems on ChromeOS - see // crbug.com/1320262 for Ash (fixed) and crbug.com/1030925 for Lacros -// (relatively old bug -- we would like to observe whether it's still +// (relatively old bug - we would like to observe whether it's still // there). This flag is introduced as means of disabling this feature in case // of possible future regressions. //
diff --git a/chrome/browser/creator/android/java/res/values/styles.xml b/chrome/browser/creator/android/java/res/values/styles.xml index 8287a157..d20cad6d 100644 --- a/chrome/browser/creator/android/java/res/values/styles.xml +++ b/chrome/browser/creator/android/java/res/values/styles.xml
@@ -29,7 +29,7 @@ <item name="android:textAppearance">@style/TextAppearance.Button.Text.Blue</item> <item name="buttonTextColor">@macro/default_text_color_secondary</item> <item name="buttonColor">@android:color/white</item> - <item name="rippleColor">@color/text_button_ripple_color</item> + <item name="rippleColor">@color/text_button_ripple_color_list</item> <item name="buttonRaised">true</item> </style> </resources>
diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc b/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc index b4bbb10..d5b87a0 100644 --- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc +++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc
@@ -536,20 +536,18 @@ " \"priority\": 300 \n" "} "; - std::unique_ptr<base::Value> value1 = - base::JSONReader::ReadDeprecated(kRule1); - ASSERT_TRUE(value1.get()); - std::unique_ptr<base::Value> value2 = - base::JSONReader::ReadDeprecated(kRule2); - ASSERT_TRUE(value2.get()); + absl::optional<base::Value> value1 = base::JSONReader::Read(kRule1); + ASSERT_TRUE(value1); + absl::optional<base::Value> value2 = base::JSONReader::Read(kRule2); + ASSERT_TRUE(value2); std::vector<const api::events::Rule*> rules; api::events::Rule rule1; api::events::Rule rule2; rules.push_back(&rule1); rules.push_back(&rule2); - ASSERT_TRUE(api::events::Rule::Populate(*value1, &rule1)); - ASSERT_TRUE(api::events::Rule::Populate(*value2, &rule2)); + ASSERT_TRUE(api::events::Rule::Populate(value1.value(), &rule1)); + ASSERT_TRUE(api::events::Rule::Populate(value2.value(), &rule2)); scoped_refptr<WebRequestRulesRegistry> registry( new TestWebRequestRulesRegistry(&profile_));
diff --git a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc index 0751b03..10082140 100644 --- a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc +++ b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
@@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Disable everything on windows only. http://crbug.com/306144 #include "chrome/browser/extensions/api/downloads/downloads_api.h" #include <stddef.h> @@ -4817,8 +4816,7 @@ base::test::ScopedFeatureList feature_list_; }; -IN_PROC_BROWSER_TEST_F(DownloadExtensionBubbleEnabledTest, - DownloadExtensionBubbleEnabledTest_SetUiOptions) { +IN_PROC_BROWSER_TEST_F(DownloadExtensionBubbleEnabledTest, SetUiOptions) { DownloadManager::DownloadVector items; CreateTwoDownloads(&items); ScopedItemVectorCanceller delete_items(&items); @@ -4837,9 +4835,8 @@ EXPECT_FALSE(GetDownloadToolbarButton()->IsShowing()); } -IN_PROC_BROWSER_TEST_F( - DownloadExtensionBubbleEnabledTest, - DownloadExtensionBubbleEnabledTest_SetUiOptionsBeforeDownloadStart) { +IN_PROC_BROWSER_TEST_F(DownloadExtensionBubbleEnabledTest, + SetUiOptionsBeforeDownloadStart) { LoadExtension("downloads_split"); EXPECT_TRUE(RunFunction(base::MakeRefCounted<DownloadsSetUiOptionsFunction>(), R"([{"enabled": false}])")); @@ -4850,9 +4847,8 @@ } // Flaky. crbug.com/1386043 -IN_PROC_BROWSER_TEST_F( - DownloadExtensionBubbleEnabledTest, - DISABLED_DownloadExtensionBubbleEnabledTest_SetUiOptionsShowDetails) { +IN_PROC_BROWSER_TEST_F(DownloadExtensionBubbleEnabledTest, + DISABLED_SetUiOptionsShowDetails) { LoadExtension("downloads_split"); DownloadManager::DownloadVector items; CreateFirstSlowTestDownload(); @@ -4873,9 +4869,8 @@ EXPECT_TRUE(GetDownloadToolbarButton()->IsShowingDetails()); } -IN_PROC_BROWSER_TEST_F( - DownloadExtensionBubbleEnabledTest, - DownloadExtensionBubbleEnabledTest_SetUiOptionsOffTheRecord) { +IN_PROC_BROWSER_TEST_F(DownloadExtensionBubbleEnabledTest, + SetUiOptionsOffTheRecord) { LoadExtension("downloads_split"); EXPECT_TRUE(RunFunction(base::MakeRefCounted<DownloadsSetUiOptionsFunction>(), R"([{"enabled": false}])")); @@ -4896,9 +4891,8 @@ EXPECT_TRUE(GetDownloadToolbarButton()->IsShowing()); } -IN_PROC_BROWSER_TEST_F( - DownloadExtensionBubbleEnabledTest, - DownloadExtensionBubbleEnabledTest_SetUiOptionsMultipleExtensions) { +IN_PROC_BROWSER_TEST_F(DownloadExtensionBubbleEnabledTest, + SetUiOptionsMultipleExtensions) { LoadExtension("downloads_split"); EXPECT_TRUE(RunFunction(base::MakeRefCounted<DownloadsSetUiOptionsFunction>(), R"([{"enabled": false}])"));
diff --git a/chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.cc b/chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.cc index 3ec1b84..73aa3c6 100644 --- a/chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.cc +++ b/chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.cc
@@ -16,6 +16,7 @@ #include "chrome/browser/feedback/system_logs/chrome_system_logs_fetcher.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/identity_manager_factory.h" +#include "chrome/browser/ui/chrome_pages.h" #include "chrome/browser/ui/simple_message_box.h" #include "chrome/grit/generated_resources.h" #include "components/feedback/system_logs/system_logs_fetcher.h" @@ -269,4 +270,21 @@ return feedback::FeedbackUploaderFactoryChrome::GetForBrowserContext(context); } +void ChromeFeedbackPrivateDelegate::OpenFeedback( + content::BrowserContext* context, + api::feedback_private::FeedbackSource source) const { + GURL url; + + DCHECK(source == + api::feedback_private::FeedbackSource::FEEDBACK_SOURCE_QUICKOFFICE); + + Profile* profile = Profile::FromBrowserContext(context); + chrome::ShowFeedbackPage(url, profile, + /*source=*/chrome::kFeedbackSourceQuickOffice, + /*description_template=*/std::string(), + /*description_placeholder_text=*/std::string(), + /*category_tag=*/std::string(), + /*extra_diagnostics=*/std::string()); +} + } // namespace extensions
diff --git a/chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.h b/chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.h index 33b7cbad..20d472da 100644 --- a/chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.h +++ b/chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.h
@@ -42,6 +42,9 @@ void NotifyFeedbackDelayed() const override; feedback::FeedbackUploader* GetFeedbackUploaderForContext( content::BrowserContext* context) const override; + void OpenFeedback( + content::BrowserContext* context, + api::feedback_private::FeedbackSource source) const override; }; } // namespace extensions
diff --git a/chrome/browser/extensions/api/permissions/permissions_api_helpers.cc b/chrome/browser/extensions/api/permissions/permissions_api_helpers.cc index 16c9cef..fc857cb 100644 --- a/chrome/browser/extensions/api/permissions/permissions_api_helpers.cc +++ b/chrome/browser/extensions/api/permissions/permissions_api_helpers.cc
@@ -46,9 +46,9 @@ base::StringPiece permission_arg, const std::string& permission_str, std::string* error) { - std::unique_ptr<base::Value> permission_json = - base::JSONReader::ReadDeprecated(permission_arg); - if (!permission_json.get()) { + absl::optional<base::Value> permission_json = + base::JSONReader::Read(permission_arg); + if (!permission_json) { *error = ErrorUtils::FormatErrorMessage(kInvalidParameter, permission_str); return nullptr; } @@ -69,7 +69,7 @@ } CHECK(permission); - if (!permission->FromValue(permission_json.get(), nullptr, nullptr)) { + if (!permission->FromValue(&permission_json.value(), nullptr, nullptr)) { *error = ErrorUtils::FormatErrorMessage(kInvalidParameter, permission_str); return nullptr; }
diff --git a/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.cc b/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.cc index c226a376..ed798bc 100644 --- a/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.cc +++ b/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.cc
@@ -204,7 +204,7 @@ base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, base::BindOnce(&extensions::ExtensionService::TerminateExtension, - service->AsWeakPtr(), extension_id)); + service->AsExtensionServiceWeakPtr(), extension_id)); extensions::WarningSet warnings; warnings.insert( extensions::Warning::CreateReloadTooFrequentWarning( @@ -220,7 +220,7 @@ base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, base::BindOnce(&extensions::ExtensionService::ReloadExtension, - service->AsWeakPtr(), extension_id)); + service->AsExtensionServiceWeakPtr(), extension_id)); } }
diff --git a/chrome/browser/extensions/chrome_app_api_browsertest.cc b/chrome/browser/extensions/chrome_app_api_browsertest.cc index 031cc85c..ca9eb938 100644 --- a/chrome/browser/extensions/chrome_app_api_browsertest.cc +++ b/chrome/browser/extensions/chrome_app_api_browsertest.cc
@@ -152,13 +152,13 @@ browser()->tab_strip_model()->GetActiveWebContents(), kGetAppDetails, &result)); - std::unique_ptr<base::DictionaryValue> app_details( - static_cast<base::DictionaryValue*>( - base::JSONReader::ReadDeprecated(result).release())); + absl::optional<base::Value> result_value = base::JSONReader::Read(result); + ASSERT_TRUE(result_value); + base::Value app_details(std::move(*result_value)); + // extension->manifest() does not contain the id. - app_details->RemoveKey("id"); - EXPECT_TRUE(app_details.get()); - EXPECT_EQ(*app_details, *extension->manifest()->value()); + app_details.RemoveKey("id"); + EXPECT_EQ(app_details, *extension->manifest()->value()); // Try to change app.isInstalled. Should silently fail, so // that isInstalled should have the initial value.
diff --git a/chrome/browser/extensions/chrome_zipfile_installer.cc b/chrome/browser/extensions/chrome_zipfile_installer.cc index aed1524..83eacb2e 100644 --- a/chrome/browser/extensions/chrome_zipfile_installer.cc +++ b/chrome/browser/extensions/chrome_zipfile_installer.cc
@@ -35,7 +35,7 @@ zip_file, error, extension_service_weak->profile(), /*noisy_on_failure=*/true); }, - service->AsWeakPtr()); + service->AsExtensionServiceWeakPtr()); } } // namespace extensions
diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc index efd1c64..fb1bb2b2 100644 --- a/chrome/browser/extensions/crx_installer.cc +++ b/chrome/browser/extensions/crx_installer.cc
@@ -84,7 +84,7 @@ // static scoped_refptr<CrxInstaller> CrxInstaller::CreateSilent( ExtensionService* frontend) { - return new CrxInstaller(frontend->AsWeakPtr(), + return new CrxInstaller(frontend->AsExtensionServiceWeakPtr(), std::unique_ptr<ExtensionInstallPrompt>(), nullptr); } @@ -92,7 +92,8 @@ scoped_refptr<CrxInstaller> CrxInstaller::Create( ExtensionService* frontend, std::unique_ptr<ExtensionInstallPrompt> client) { - return new CrxInstaller(frontend->AsWeakPtr(), std::move(client), nullptr); + return new CrxInstaller(frontend->AsExtensionServiceWeakPtr(), + std::move(client), nullptr); } // static @@ -100,7 +101,8 @@ ExtensionService* service, std::unique_ptr<ExtensionInstallPrompt> client, const WebstoreInstaller::Approval* approval) { - return new CrxInstaller(service->AsWeakPtr(), std::move(client), approval); + return new CrxInstaller(service->AsExtensionServiceWeakPtr(), + std::move(client), approval); } CrxInstaller::CrxInstaller(base::WeakPtr<ExtensionService> service_weak,
diff --git a/chrome/browser/extensions/data_deleter.cc b/chrome/browser/extensions/data_deleter.cc index ea2ff96..f362491 100644 --- a/chrome/browser/extensions/data_deleter.cc +++ b/chrome/browser/extensions/data_deleter.cc
@@ -139,9 +139,10 @@ if (has_isolated_storage) { profile->AsyncObliterateStoragePartition( util::GetPartitionDomainForExtension(extension), - base::BindOnce( - &OnNeedsToGarbageCollectIsolatedStorage, - ExtensionSystem::Get(profile)->extension_service()->AsWeakPtr()), + base::BindOnce(&OnNeedsToGarbageCollectIsolatedStorage, + ExtensionSystem::Get(profile) + ->extension_service() + ->AsExtensionServiceWeakPtr()), subtask_done_callback); } if (delete_extension_origin) {
diff --git a/chrome/browser/extensions/extension_disabled_ui.cc b/chrome/browser/extensions/extension_disabled_ui.cc index 4ead87e..790a9db 100644 --- a/chrome/browser/extensions/extension_disabled_ui.cc +++ b/chrome/browser/extensions/extension_disabled_ui.cc
@@ -235,7 +235,8 @@ base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, base::BindOnce(&ExtensionService::GrantPermissionsAndEnableExtension, - service_->AsWeakPtr(), base::RetainedRef(extension_))); + service_->AsExtensionServiceWeakPtr(), + base::RetainedRef(extension_))); } void ExtensionDisabledGlobalError::BubbleViewCancelButtonPressed(
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc index cd8fa1d3..8826dd1 100644 --- a/chrome/browser/extensions/extension_service.cc +++ b/chrome/browser/extensions/extension_service.cc
@@ -447,6 +447,10 @@ return &corrupted_extension_reinstaller_; } +base::WeakPtr<ExtensionServiceInterface> ExtensionService::AsWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + ExtensionService::~ExtensionService() { UpgradeDetector::GetInstance()->RemoveObserver(this); // No need to unload extensions here because they are profile-scoped, and the @@ -727,8 +731,9 @@ UnpackedInstaller::Create(this); unpacked_installer->set_be_noisy_on_failure(load_error_behavior == LoadErrorBehavior::kNoisy); - unpacked_installer->set_completion_callback(base::BindOnce( - &ExtensionService::OnUnpackedReloadFailure, AsWeakPtr())); + unpacked_installer->set_completion_callback( + base::BindOnce(&ExtensionService::OnUnpackedReloadFailure, + AsExtensionServiceWeakPtr())); unpacked_installer->Load(path); } } @@ -1927,7 +1932,7 @@ scoped_refptr<CrxInstaller> installer(CrxInstaller::CreateSilent(this)); installer->AddInstallerCallback( base::BindOnce(&ExtensionService::InstallationFromExternalFileFinished, - AsWeakPtr(), info.extension_id)); + AsExtensionServiceWeakPtr(), info.extension_id)); installer->set_install_source(info.crx_location); installer->set_expected_id(info.extension_id); installer->set_expected_version(info.version, @@ -1984,7 +1989,8 @@ // access to the Extension and ExtensionHost. base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, base::BindOnce(&ExtensionService::TerminateExtension, - AsWeakPtr(), extension_host->extension_id())); + AsExtensionServiceWeakPtr(), + extension_host->extension_id())); } void ExtensionService::OnAppTerminating() { @@ -2035,7 +2041,8 @@ base::BindOnce( base::IgnoreResult( &ExtensionService::FinishDelayedInstallationIfReady), - AsWeakPtr(), extension_id, false /*install_immediately*/), + AsExtensionServiceWeakPtr(), extension_id, + false /*install_immediately*/), kUpdateIdleDelay); } } @@ -2136,7 +2143,8 @@ void ExtensionService::OnBlocklistUpdated() { blocklist_->GetBlocklistedIDs( registry_->GenerateInstalledExtensionsSet()->GetIDs(), - base::BindOnce(&ExtensionService::ManageBlocklist, AsWeakPtr())); + base::BindOnce(&ExtensionService::ManageBlocklist, + AsExtensionServiceWeakPtr())); } void ExtensionService::OnUpgradeRecommended() { @@ -2281,8 +2289,8 @@ blocklist_->IsDatabaseReady(base::BindOnce( [](base::WeakPtr<ExtensionService> service, bool is_ready) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (!service || !is_ready) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!service || !is_ready) { // Either the service was torn down or the database isn't // ready yet (and is effectively empty). Either way, no need // to update the blocklisted extensions. @@ -2290,7 +2298,7 @@ } service->OnBlocklistUpdated(); }, - AsWeakPtr())); + AsExtensionServiceWeakPtr())); } void ExtensionService::UninstallMigratedExtensions() {
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h index b55a1fd..ccbf89e7 100644 --- a/chrome/browser/extensions/extension_service.h +++ b/chrome/browser/extensions/extension_service.h
@@ -86,8 +86,7 @@ // This is an interface class to encapsulate the dependencies that // various classes have on ExtensionService. This allows easy mocking. -class ExtensionServiceInterface - : public base::SupportsWeakPtr<ExtensionServiceInterface> { +class ExtensionServiceInterface { public: virtual ~ExtensionServiceInterface() {} @@ -162,6 +161,8 @@ // This will trigger an update/reinstall of the extensions saved in the // provider's prefs. virtual void ReinstallProviderExtensions() = 0; + + virtual base::WeakPtr<ExtensionServiceInterface> AsWeakPtr() = 0; }; // Manages installed and running Chromium extensions. An instance is shared @@ -212,6 +213,7 @@ void CheckManagementPolicy() override; void CheckForUpdatesSoon() override; void ReinstallProviderExtensions() override; + base::WeakPtr<ExtensionServiceInterface> AsWeakPtr() override; // ExternalProvider::VisitorInterface implementation. // Exposed for testing. @@ -411,7 +413,9 @@ // Simple Accessors // Returns a WeakPtr to the ExtensionService. - base::WeakPtr<ExtensionService> AsWeakPtr() { return base::AsWeakPtr(this); } + base::WeakPtr<ExtensionService> AsExtensionServiceWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); + } // Returns profile_ as a BrowserContext. content::BrowserContext* GetBrowserContext() const; @@ -760,6 +764,8 @@ AshExtensionKeeplistManager ash_keeplist_manager_; #endif + base::WeakPtrFactory<ExtensionService> weak_ptr_factory_{this}; + FRIEND_TEST_ALL_PREFIXES(ExtensionServiceTest, DestroyingProfileClearsExtensions); FRIEND_TEST_ALL_PREFIXES(ExtensionServiceTest, SetUnsetBlocklistInPrefs);
diff --git a/chrome/browser/extensions/fake_crx_installer.cc b/chrome/browser/extensions/fake_crx_installer.cc index e3afeda..1239161 100644 --- a/chrome/browser/extensions/fake_crx_installer.cc +++ b/chrome/browser/extensions/fake_crx_installer.cc
@@ -9,7 +9,7 @@ namespace extensions { FakeCrxInstaller::FakeCrxInstaller(ExtensionService* frontend) - : CrxInstaller(frontend->AsWeakPtr(), nullptr, nullptr) {} + : CrxInstaller(frontend->AsExtensionServiceWeakPtr(), nullptr, nullptr) {} FakeCrxInstaller::~FakeCrxInstaller() = default;
diff --git a/chrome/browser/extensions/mock_crx_installer.cc b/chrome/browser/extensions/mock_crx_installer.cc index 1c69577..81c2306f 100644 --- a/chrome/browser/extensions/mock_crx_installer.cc +++ b/chrome/browser/extensions/mock_crx_installer.cc
@@ -7,7 +7,7 @@ namespace extensions { MockCrxInstaller::MockCrxInstaller(ExtensionService* frontend) - : CrxInstaller(frontend->AsWeakPtr(), nullptr, nullptr) {} + : CrxInstaller(frontend->AsExtensionServiceWeakPtr(), nullptr, nullptr) {} MockCrxInstaller::~MockCrxInstaller() = default;
diff --git a/chrome/browser/extensions/test_extension_service.cc b/chrome/browser/extensions/test_extension_service.cc index c81c699..0207ade49 100644 --- a/chrome/browser/extensions/test_extension_service.cc +++ b/chrome/browser/extensions/test_extension_service.cc
@@ -9,7 +9,9 @@ using extensions::Extension; -TestExtensionService::~TestExtensionService() {} +TestExtensionService::TestExtensionService() = default; + +TestExtensionService::~TestExtensionService() = default; extensions::PendingExtensionManager* TestExtensionService::pending_extension_manager() { @@ -85,3 +87,8 @@ void TestExtensionService::ReinstallProviderExtensions() { ADD_FAILURE(); } + +base::WeakPtr<extensions::ExtensionServiceInterface> +TestExtensionService::AsWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +}
diff --git a/chrome/browser/extensions/test_extension_service.h b/chrome/browser/extensions/test_extension_service.h index 7b010f95b..6425aed 100644 --- a/chrome/browser/extensions/test_extension_service.h +++ b/chrome/browser/extensions/test_extension_service.h
@@ -8,6 +8,7 @@ #include <string> #include <vector> +#include "base/memory/weak_ptr.h" #include "chrome/browser/extensions/extension_service.h" namespace extensions { @@ -20,6 +21,7 @@ // this and override the methods you care about. class TestExtensionService : public extensions::ExtensionServiceInterface { public: + TestExtensionService(); ~TestExtensionService() override; // ExtensionServiceInterface implementation. @@ -50,6 +52,11 @@ const std::string& extension_id) override; void ReinstallProviderExtensions() override; + + base::WeakPtr<ExtensionServiceInterface> AsWeakPtr() override; + + private: + base::WeakPtrFactory<TestExtensionService> weak_ptr_factory_{this}; }; #endif // CHROME_BROWSER_EXTENSIONS_TEST_EXTENSION_SERVICE_H_
diff --git a/chrome/browser/extensions/unpacked_installer.cc b/chrome/browser/extensions/unpacked_installer.cc index 498c6f7..7444723d 100644 --- a/chrome/browser/extensions/unpacked_installer.cc +++ b/chrome/browser/extensions/unpacked_installer.cc
@@ -83,7 +83,7 @@ } UnpackedInstaller::UnpackedInstaller(ExtensionService* extension_service) - : service_weak_(extension_service->AsWeakPtr()), + : service_weak_(extension_service->AsExtensionServiceWeakPtr()), profile_(extension_service->profile()), require_modern_manifest_version_(true), be_noisy_on_failure_(true) {
diff --git a/chrome/browser/feedback/show_feedback_page.cc b/chrome/browser/feedback/show_feedback_page.cc index 3a78cc2f7..99d1861 100644 --- a/chrome/browser/feedback/show_feedback_page.cc +++ b/chrome/browser/feedback/show_feedback_page.cc
@@ -139,6 +139,7 @@ case kFeedbackSourceMdSettingsAboutPage: case kFeedbackSourceOldSettingsAboutPage: case kFeedbackSourceQuickAnswers: + case kFeedbackSourceQuickOffice: case kFeedbackSourceSettingsPerformancePage: return true; default:
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 91985af..4ba956d 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -2348,7 +2348,7 @@ "jeffcchen", "chromeos-gfx-video@google.com" ], - "expiry_milestone": 110 + "expiry_milestone": 130 }, { "name": "enable-gpu-rasterization", @@ -2824,6 +2824,11 @@ "expiry_milestone": 110 }, { + "name": "enable-power-sounds", + "owners": [ "afakhry", "bicioglu", "hongyulong", "nupurjain" ], + "expiry_milestone": 123 + }, + { "name": "enable-preinstalled-web-app-duplication-fixer", "owners": [ "alancutter" ], "expiry_milestone": 110 @@ -6396,12 +6401,12 @@ { "name": "sync-standalone-invalidations", "owners": [ "treib", "rushans" ], - "expiry_milestone": 110 + "expiry_milestone": 115 }, { "name": "sync-standalone-invalidations-wallet-and-offer", "owners": [ "treib", "rushans" ], - "expiry_milestone": 110 + "expiry_milestone": 115 }, { "name": "sync-trusted-vault-passphrase-promo",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 5fe5ac0..7c926c42 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -2837,6 +2837,10 @@ "keyboard shortcuts and have the events routed directly to the website " "when in fullscreen mode."; +const char kSystemSoundsName[] = "Power Sounds"; +const char kSystemSoundsDescription[] = + "Enable device charging and low battery warning sounds."; + const char kStylusBatteryStatusName[] = "Show stylus battery stylus in the stylus tools menu"; const char kStylusBatteryStatusDescription[] = @@ -3636,9 +3640,6 @@ const char kFeedStampName[] = "StAMP cards in the feed"; const char kFeedStampDescription[] = "Enables StAMP cards in the feed."; -const char kFeedIsAblatedName[] = "Feed ablation"; -const char kFeedIsAblatedDescription[] = "Enables feed ablation."; - const char kFeedCloseRefreshName[] = "Feed-close refresh"; const char kFeedCloseRefreshDescription[] = "Enables scheduling a background refresh of the feed following feed use.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index f51043b..d8ff874 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -1575,6 +1575,9 @@ extern const char kSystemKeyboardLockName[]; extern const char kSystemKeyboardLockDescription[]; +extern const char kSystemSoundsName[]; +extern const char kSystemSoundsDescription[]; + extern const char kSuggestionsWithSubStringMatchName[]; extern const char kSuggestionsWithSubStringMatchDescription[]; @@ -2075,9 +2078,6 @@ extern const char kFeedStampName[]; extern const char kFeedStampDescription[]; -extern const char kFeedIsAblatedName[]; -extern const char kFeedIsAblatedDescription[]; - extern const char kFeedCloseRefreshName[]; extern const char kFeedCloseRefreshDescription[];
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java index e411cae..4323015 100644 --- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java +++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -600,7 +600,6 @@ public static final String WEB_APK_UNIQUE_ID = "WebApkUniqueId"; public static final String XSURFACE_METRICS_REPORTING = "XsurfaceMetricsReporting"; public static final String WEB_OTP_CROSS_DEVICE_SIMPLE_STRING = "WebOtpCrossDeviceSimpleString"; - public static final String FEED_ABLATION = "FeedAblation"; public static final String FEED_NO_VIEW_CACHE = "FeedNoViewCache"; public static final String FEED_REPLACE_ALL = "FeedReplaceAll"; public static final String FEED_SHOW_SIGN_IN_COMMAND = "FeedShowSignInCommand";
diff --git a/chrome/browser/google/google_brand_chromeos.cc b/chrome/browser/google/google_brand_chromeos.cc index a870c3f4..234ace3 100644 --- a/chrome/browser/google/google_brand_chromeos.cc +++ b/chrome/browser/google/google_brand_chromeos.cc
@@ -6,6 +6,7 @@ #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/logging.h" +#include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "base/task/thread_pool.h" #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h" @@ -27,7 +28,7 @@ const base::FilePath::CharType kRLZBrandFilePath[] = FILE_PATH_LITERAL("/opt/oem/etc/BRAND_CODE"); -bool IsBrandValid(const std::string& brand) { +bool IsBrandValid(base::StringPiece brand) { return !brand.empty(); } @@ -88,11 +89,10 @@ void InitBrand(base::OnceClosure callback) { ::chromeos::system::StatisticsProvider* provider = ::chromeos::system::StatisticsProvider::GetInstance(); - std::string brand; - const bool found = provider->GetMachineStatistic( - ::chromeos::system::kRlzBrandCodeKey, &brand); - if (found && IsBrandValid(brand)) { - SetBrand(std::move(callback), brand); + const absl::optional<base::StringPiece> brand = + provider->GetMachineStatistic(::chromeos::system::kRlzBrandCodeKey); + if (brand && IsBrandValid(brand.value())) { + SetBrand(std::move(callback), std::string(brand.value())); return; }
diff --git a/chrome/browser/history_clusters/entity_image_service.cc b/chrome/browser/history_clusters/entity_image_service.cc index 06001aef..a5400545 100644 --- a/chrome/browser/history_clusters/entity_image_service.cc +++ b/chrome/browser/history_clusters/entity_image_service.cc
@@ -4,6 +4,8 @@ #include "chrome/browser/history_clusters/entity_image_service.h" +#include "base/barrier_closure.h" +#include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/i18n/case_conversion.h" #include "base/memory/singleton.h" @@ -56,6 +58,85 @@ } }; +// A one-time use object that encapsulates tagging a vector of clusters with +// entity images. Used to manage all the fetch jobs dispatched, and runs the +// main callback after it's done. +// TODO(tommycli): This is kind of janky and surely not what we want to do. +// Replace this with a dedicated server-side service. +class FetchJobManager { + public: + using ResultCallback = + base::OnceCallback<void(std::vector<history::Cluster>)>; + + struct Request { + std::u16string query; + std::string entity_id; + history::ClusterVisit* visit; + }; + + explicit FetchJobManager(std::vector<history::Cluster>&& clusters) + : clusters_(clusters) {} + + void Start(EntityImageService* service, ResultCallback callback) { + std::vector<Request> requests; + for (auto& cluster : clusters_) { + for (auto& visit : cluster.visits) { + // Only tag search visits for now. + const auto& search_terms = + visit.annotated_visit.content_annotations.search_terms; + if (!search_terms.empty()) { + // TODO(tommycli): Add entity_id once implemented. + requests.push_back({search_terms, "", &visit}); + } + } + } + + // If no requests needed, just early exit and give back the clusters. + if (requests.empty()) { + return FinishJob(std::move(callback)); + } + + // This encapsulates the final callback and is called after all the requests + // are completed. + auto finish_callback = base::BarrierClosure( + requests.size(), + base::BindOnce(&FetchJobManager::FinishJob, weak_factory_.GetWeakPtr(), + std::move(callback))); + + for (auto& request : requests) { + service->FetchImageFor( + request.query, request.entity_id, + base::BindOnce(&FetchJobManager::OnImageFetchedForVisit, + weak_factory_.GetWeakPtr(), request.visit) + .Then(finish_callback)); + } + } + + private: + // Populates the cluster visit's field. This is a member method and not a + // free function, because `visit` points to memory owned by this object. + void OnImageFetchedForVisit(history::ClusterVisit* visit, + const GURL& image_url) { + visit->image_url = image_url; + } + + void FinishJob(ResultCallback callback) { + std::move(callback).Run(std::move(clusters_)); + } + + std::vector<history::Cluster> clusters_; + base::WeakPtrFactory<FetchJobManager> weak_factory_{this}; +}; + +// An anonymous function whose only job is to scope the lifetime of +// ClusterVectorImageTaggingJob, then call `callback` with `clusters`. +void DeleteManagerAndRunCallback( + std::unique_ptr<FetchJobManager> job, + base::OnceCallback<void(std::vector<history::Cluster>)> callback, + std::vector<history::Cluster> clusters) { + std::move(callback).Run(clusters); +} + } // namespace // A one-time use object that uses Suggest to get an image URL corresponding @@ -178,14 +259,28 @@ return EntityImageServiceFactory::GetForProfile(profile); } +void EntityImageService::PopulateEntityImagesFor( + std::vector<history::Cluster> clusters, + base::OnceCallback<void(std::vector<history::Cluster>)> callback) { + if (!GetConfig().images || !url_consent_helper_ || + !url_consent_helper_->IsEnabled()) { + return std::move(callback).Run(std::move(clusters)); + } + + auto manager = std::make_unique<FetchJobManager>(std::move(clusters)); + // Use a raw pointer temporary so we can give ownership of the unique_ptr to + // the callback and have a well defined object lifetime. + auto* manager_ptr = manager.get(); + manager_ptr->Start( + this, base::BindOnce(&DeleteManagerAndRunCallback, std::move(manager), + std::move(callback))); +} + bool EntityImageService::FetchImageFor(const std::u16string& search_query, const std::string& entity_id, ResultCallback callback) { - if (!GetConfig().images) - return false; - - if (!url_consent_helper_ || !url_consent_helper_->IsEnabled()) - return false; + DCHECK(GetConfig().images); + DCHECK(url_consent_helper_ && url_consent_helper_->IsEnabled()); auto fetcher = std::make_unique<SuggestEntityImageURLFetcher>( profile_, &autocomplete_provider_client_, search_query, entity_id);
diff --git a/chrome/browser/history_clusters/entity_image_service.h b/chrome/browser/history_clusters/entity_image_service.h index dafd4a9..6e7268e0 100644 --- a/chrome/browser/history_clusters/entity_image_service.h +++ b/chrome/browser/history_clusters/entity_image_service.h
@@ -7,9 +7,11 @@ #include <string> +#include "base/functional/callback_forward.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/autocomplete/chrome_autocomplete_provider_client.h" #include "chrome/browser/profiles/profile.h" +#include "components/history/core/browser/history_types.h" #include "components/keyed_service/core/keyed_service.h" #include "components/unified_consent/url_keyed_data_collection_consent_helper.h" @@ -32,6 +34,14 @@ // Gets the fetcher associated with `profile`. Always succeeds. static EntityImageService* Get(Profile* profile); + // Populates entity images into the `image_url` of any eligible visits within + // every cluster in `clusters`. `clusters` should be moved into the parameter. + // `callback` is called when we're done, and it can be called synchronously + // if there's nothing to do. + void PopulateEntityImagesFor( + std::vector<history::Cluster> clusters, + base::OnceCallback<void(std::vector<history::Cluster>)> callback); + // Fetches an image appropriate for `search_query` and `entity_id`, returning // the result asynchronously to `callback`. Returns false if we can't do it // for configuration or privacy reasons.
diff --git a/chrome/browser/history_clusters/history_clusters_service_factory.cc b/chrome/browser/history_clusters/history_clusters_service_factory.cc index 0a9ffbc..4d35b899 100644 --- a/chrome/browser/history_clusters/history_clusters_service_factory.cc +++ b/chrome/browser/history_clusters/history_clusters_service_factory.cc
@@ -12,6 +12,7 @@ #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h" #include "chrome/browser/optimization_guide/page_content_annotations_service_factory.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/search_engines/template_url_service_factory.h" #include "components/history_clusters/core/history_clusters_service.h" #include "components/keyed_service/core/service_access_type.h" #include "components/optimization_guide/content/browser/page_content_annotations_service.h" @@ -41,6 +42,7 @@ DependsOn(HistoryServiceFactory::GetInstance()); DependsOn(PageContentAnnotationsServiceFactory::GetInstance()); DependsOn(site_engagement::SiteEngagementServiceFactory::GetInstance()); + DependsOn(TemplateURLServiceFactory::GetInstance()); DependsOn(OptimizationGuideKeyedServiceFactory::GetInstance()); } @@ -63,5 +65,6 @@ g_browser_process->GetApplicationLocale(), history_service, PageContentAnnotationsServiceFactory::GetForProfile(profile), url_loader_factory, site_engagement::SiteEngagementService::Get(profile), + TemplateURLServiceFactory::GetForProfile(profile), OptimizationGuideKeyedServiceFactory::GetForProfile(profile)); }
diff --git a/chrome/browser/media/media_engagement_preloaded_list.cc b/chrome/browser/media/media_engagement_preloaded_list.cc index 9c4b2a4..2cbb677 100644 --- a/chrome/browser/media/media_engagement_preloaded_list.cc +++ b/chrome/browser/media/media_engagement_preloaded_list.cc
@@ -5,7 +5,6 @@ #include "chrome/browser/media/media_engagement_preloaded_list.h" #include "base/files/file_util.h" -#include "base/metrics/histogram_macros.h" #include "base/no_destructor.h" #include "base/path_service.h" #include "base/strings/string_number_conversions.h" @@ -15,12 +14,6 @@ #include "url/url_canon.h" #include "url/url_constants.h" -const char MediaEngagementPreloadedList::kHistogramCheckResultName[] = - "Media.Engagement.PreloadedList.CheckResult"; - -const char MediaEngagementPreloadedList::kHistogramLoadResultName[] = - "Media.Engagement.PreloadedList.LoadResult"; - // static MediaEngagementPreloadedList* MediaEngagementPreloadedList::GetInstance() { static base::NoDestructor<MediaEngagementPreloadedList> instance; @@ -51,21 +44,18 @@ // Check the file exists. if (!base::PathExists(path)) { - RecordLoadResult(LoadResult::kFileNotFound); return false; } // Read the file to a string. std::string file_data; if (!base::ReadFileToString(path, &file_data)) { - RecordLoadResult(LoadResult::kFileReadFailed); return false; } // Load the preloaded list into a proto message. chrome_browser_media::PreloadedData message; if (!message.ParseFromString(file_data)) { - RecordLoadResult(LoadResult::kParseProtoFailed); return false; } @@ -74,7 +64,6 @@ message.dafsa().c_str(), message.dafsa().c_str() + message.dafsa().length()); - RecordLoadResult(LoadResult::kLoaded); is_loaded_ = true; return true; } @@ -85,13 +74,11 @@ // Check if we have loaded the data. if (!loaded()) { - RecordCheckResult(CheckResult::kListNotLoaded); return false; } // Check if the data is empty. if (empty()) { - RecordCheckResult(CheckResult::kListEmpty); return false; } @@ -112,27 +99,21 @@ case DafsaResult::kFoundHttpsOnly: // Only HTTPS is allowed by default. if (origin.scheme() == url::kHttpsScheme) { - RecordCheckResult(CheckResult::kFoundHttpsOnly); return true; - } else { - RecordCheckResult(CheckResult::kFoundHttpsOnlyButWasHttp); } break; case DafsaResult::kFoundHttpOrHttps: // Allow either HTTP or HTTPS. if (origin.scheme() == url::kHttpScheme || origin.scheme() == url::kHttpsScheme) { - RecordCheckResult(CheckResult::kFoundHttpOrHttps); return true; } break; case DafsaResult::kNotFound: - RecordCheckResult(CheckResult::kNotFound); break; } - // If we do not match then we should record a not found result and return - // false. + // If we do not match then we should return false. return false; } @@ -143,13 +124,3 @@ net::LookupStringInFixedSet(dafsa_.data(), dafsa_.size(), input.c_str(), input.size())); } - -void MediaEngagementPreloadedList::RecordLoadResult(LoadResult result) { - UMA_HISTOGRAM_ENUMERATION(kHistogramLoadResultName, result, - LoadResult::kCount); -} - -void MediaEngagementPreloadedList::RecordCheckResult(CheckResult result) const { - UMA_HISTOGRAM_ENUMERATION(kHistogramCheckResultName, result, - CheckResult::kCount); -}
diff --git a/chrome/browser/media/media_engagement_preloaded_list.h b/chrome/browser/media/media_engagement_preloaded_list.h index 7b89548..df155f3 100644 --- a/chrome/browser/media/media_engagement_preloaded_list.h +++ b/chrome/browser/media/media_engagement_preloaded_list.h
@@ -46,55 +46,6 @@ protected: friend class MediaEngagementPreloadedListTest; - // The names of the CheckResult and LoadResult histograms. - static const char kHistogramCheckResultName[]; - static const char kHistogramLoadResultName[]; - - // The result of the CheckStringIsPresent operation. This enum is used to - // record a histogram and should not be renumbered. - enum class CheckResult { - // The check succeeded and the string was found in the data. - kFoundHttpsOnly = 0, - - // The check succeeded but the string was not found in the data. - kNotFound, - - // The check failed because the list is empty. - kListEmpty, - - // The check failed because the list has not been loaded. - kListNotLoaded, - - // The check succeeded, the string was found and it had metadata that it - // allows insecure origins. - kFoundHttpOrHttps, - - // The check succeeded, the string was found but it was https only and the - // origin was insecure. - kFoundHttpsOnlyButWasHttp, - - kCount - }; - - // The result of the LoadFromFile operation. This enum is used to record - // a histogram and should not be renumbered. - enum class LoadResult { - // The list was loaded successfully. - kLoaded = 0, - - // The list was not loaded because the file was not found. - kFileNotFound, - - // The list was not loaded because the file could not be read. - kFileReadFailed, - - // The list was not loaded because the proto stored in the file could not be - // parsed. - kParseProtoFailed, - - kCount - }; - enum class DafsaResult { // The string was not found. kNotFound = -1, @@ -109,12 +60,6 @@ // Checks if |input| is present in the preloaded data. DafsaResult CheckStringIsPresent(const std::string& input) const; - // Records |result| to the LoadResult histogram. - void RecordLoadResult(LoadResult result); - - // Records |result| to the CheckResult histogram. - void RecordCheckResult(CheckResult result) const; - private: // The preloaded data in dafsa format. std::vector<unsigned char> dafsa_;
diff --git a/chrome/browser/media/media_engagement_preloaded_list_unittest.cc b/chrome/browser/media/media_engagement_preloaded_list_unittest.cc index 7218437..2ae204e 100644 --- a/chrome/browser/media/media_engagement_preloaded_list_unittest.cc +++ b/chrome/browser/media/media_engagement_preloaded_list_unittest.cc
@@ -8,7 +8,6 @@ #include "base/files/file_path.h" #include "base/path_service.h" -#include "base/test/metrics/histogram_tester.h" #include "build/build_config.h" #include "chrome/browser/profiles/profile.h" #include "chrome/test/base/testing_profile.h" @@ -75,84 +74,7 @@ bool IsEmpty() { return preloaded_list_->empty(); } - void ExpectCheckResultFoundHttpsOnlyCount(int count) { - ExpectCheckResultCount( - MediaEngagementPreloadedList::CheckResult::kFoundHttpsOnly, count); - } - - void ExpectCheckResultFoundHttpsButWasHttpOnlyCount(int count) { - ExpectCheckResultCount( - MediaEngagementPreloadedList::CheckResult::kFoundHttpsOnlyButWasHttp, - count); - } - - void ExpectCheckResultFoundHttpOrHttpsCount(int count) { - ExpectCheckResultCount( - MediaEngagementPreloadedList::CheckResult::kFoundHttpOrHttps, count); - } - - void ExpectCheckResultNotFoundCount(int count) { - ExpectCheckResultCount(MediaEngagementPreloadedList::CheckResult::kNotFound, - count); - } - - void ExpectCheckResultNotLoadedCount(int count) { - ExpectCheckResultCount( - MediaEngagementPreloadedList::CheckResult::kListNotLoaded, count); - } - - void ExpectCheckResultListEmptyCount(int count) { - ExpectCheckResultCount( - MediaEngagementPreloadedList::CheckResult::kListEmpty, count); - } - - void ExpectCheckResultTotal(int total) { - histogram_tester_.ExpectTotalCount( - MediaEngagementPreloadedList::kHistogramCheckResultName, total); - } - - void ExpectLoadResultLoaded() { - ExpectLoadResult(MediaEngagementPreloadedList::LoadResult::kLoaded); - } - - void ExpectLoadResultFileNotFound() { - ExpectLoadResult(MediaEngagementPreloadedList::LoadResult::kFileNotFound); - } - - void ExpectLoadResultFileReadFailed() { - ExpectLoadResult(MediaEngagementPreloadedList::LoadResult::kFileReadFailed); - } - - void ExpectLoadResultParseProtoFailed() { - ExpectLoadResult( - MediaEngagementPreloadedList::LoadResult::kParseProtoFailed); - } - - const base::HistogramTester& histogram_tester() const { - return histogram_tester_; - } - - protected: - void ExpectLoadResult(MediaEngagementPreloadedList::LoadResult result) { - histogram_tester_.ExpectBucketCount( - MediaEngagementPreloadedList::kHistogramLoadResultName, - static_cast<int>(result), 1); - - // Ensure not other results were logged. - histogram_tester_.ExpectTotalCount( - MediaEngagementPreloadedList::kHistogramLoadResultName, 1); - } - - void ExpectCheckResultCount(MediaEngagementPreloadedList::CheckResult result, - int count) { - histogram_tester_.ExpectBucketCount( - MediaEngagementPreloadedList::kHistogramCheckResultName, - static_cast<int>(result), count); - } - std::unique_ptr<MediaEngagementPreloadedList> preloaded_list_; - - base::HistogramTester histogram_tester_; }; TEST_F(MediaEngagementPreloadedListTest, CheckOriginIsPresent) { @@ -161,31 +83,17 @@ EXPECT_TRUE(IsLoaded()); EXPECT_FALSE(IsEmpty()); - // Check the load result was recorded on the histogram. - ExpectLoadResultLoaded(); - // Check some origins that are not in the list. - ExpectCheckResultTotal(0); EXPECT_TRUE(CheckOriginIsPresent(GURL("https://example.com"))); EXPECT_TRUE(CheckOriginIsPresent(GURL("https://example.org:1234"))); EXPECT_TRUE(CheckOriginIsPresent(GURL("https://test--3ya.com"))); EXPECT_TRUE(CheckOriginIsPresent(GURL("http://123.123.123.123"))); - // Check they were recorded on the histogram. - ExpectCheckResultTotal(4); - ExpectCheckResultFoundHttpsOnlyCount(3); - ExpectCheckResultFoundHttpOrHttpsCount(1); - // Check some origins that are not in the list. EXPECT_FALSE(CheckOriginIsPresent(GURL("https://example.org"))); EXPECT_FALSE(CheckOriginIsPresent(GURL("http://example.com"))); EXPECT_FALSE(CheckOriginIsPresent(GURL("http://123.123.123.124"))); - // Check they were recorded on the histogram. - ExpectCheckResultTotal(7); - ExpectCheckResultNotFoundCount(2); - ExpectCheckResultFoundHttpsButWasHttpOnlyCount(1); - // Make sure only the full origin matches. EXPECT_FALSE(CheckStringIsPresent("123")); EXPECT_FALSE(CheckStringIsPresent("http")); @@ -197,15 +105,6 @@ LoadFromFile(GetAbsolutePathToGeneratedTestFile(kMissingFilePath))); EXPECT_FALSE(IsLoaded()); EXPECT_TRUE(IsEmpty()); - - // Check the load result was recorded on the histogram. - ExpectLoadResultFileNotFound(); - - // Test checking an origin and make sure the result is recorded to the - // histogram. - EXPECT_FALSE(CheckOriginIsPresent(GURL("https://example.com"))); - ExpectCheckResultTotal(1); - ExpectCheckResultNotLoadedCount(1); } #if BUILDFLAG(IS_FUCHSIA) @@ -219,14 +118,9 @@ EXPECT_FALSE(IsLoaded()); EXPECT_TRUE(IsEmpty()); - // Check the load result was recorded on the histogram. - ExpectLoadResultFileReadFailed(); - // Test checking an origin and make sure the result is recorded to the // histogram. EXPECT_FALSE(CheckOriginIsPresent(GURL("https://example.com"))); - ExpectCheckResultTotal(1); - ExpectCheckResultNotLoadedCount(1); } TEST_F(MediaEngagementPreloadedListTest, LoadBadFormatFile) { @@ -235,29 +129,15 @@ EXPECT_FALSE(IsLoaded()); EXPECT_TRUE(IsEmpty()); - // Check the load result was recorded on the histogram. - ExpectLoadResultParseProtoFailed(); - // Test checking an origin and make sure the result is recorded to the // histogram. EXPECT_FALSE(CheckOriginIsPresent(GURL("https://example.com"))); - ExpectCheckResultTotal(1); - ExpectCheckResultNotLoadedCount(1); } TEST_F(MediaEngagementPreloadedListTest, LoadEmptyFile) { ASSERT_TRUE(LoadFromFile(GetAbsolutePathToGeneratedTestFile(kEmptyFilePath))); EXPECT_TRUE(IsLoaded()); EXPECT_TRUE(IsEmpty()); - - // Check the load result was recorded on the histogram. - ExpectLoadResultLoaded(); - - // Test checking an origin and make sure the result is recorded to the - // histogram. - EXPECT_FALSE(CheckOriginIsPresent(GURL("https://example.com"))); - ExpectCheckResultTotal(1); - ExpectCheckResultListEmptyCount(1); } TEST_F(MediaEngagementPreloadedListTest, CheckOriginIsPresent_UnsecureSchemes) { @@ -266,25 +146,15 @@ EXPECT_TRUE(IsLoaded()); EXPECT_FALSE(IsEmpty()); - // Check the load result was recorded on the histogram. - ExpectLoadResultLoaded(); - // An origin that has both HTTP and HTTPS entries should allow either. EXPECT_TRUE(CheckOriginIsPresent(GURL("https://google.com"))); EXPECT_TRUE(CheckOriginIsPresent(GURL("http://google.com"))); - ExpectCheckResultTotal(2); - ExpectCheckResultFoundHttpOrHttpsCount(2); // An origin that only has a HTTP origin should allow either. EXPECT_TRUE(CheckOriginIsPresent(GURL("https://123.123.123.123"))); EXPECT_TRUE(CheckOriginIsPresent(GURL("http://123.123.123.123"))); - ExpectCheckResultTotal(4); - ExpectCheckResultFoundHttpOrHttpsCount(4); // An origin that has only HTTPS should only allow HTTPS. EXPECT_TRUE(CheckOriginIsPresent(GURL("https://example.com"))); EXPECT_FALSE(CheckOriginIsPresent(GURL("http://example.com"))); - ExpectCheckResultTotal(6); - ExpectCheckResultFoundHttpsOnlyCount(1); - ExpectCheckResultFoundHttpsButWasHttpOnlyCount(1); }
diff --git a/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc b/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc index 98de4754..48faac7 100644 --- a/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc +++ b/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc
@@ -964,7 +964,9 @@ void SetUpInProcessBrowserTestFixture() override { if (enable_hidpi()) { - feature_list_.InitWithFeatures({media::kWebContentsCaptureHiDpi}, {}); + feature_list_.InitAndEnableFeature(media::kWebContentsCaptureHiDpi); + } else { + feature_list_.InitAndDisableFeature(media::kWebContentsCaptureHiDpi); } WebRtcTestBase::SetUpInProcessBrowserTestFixture();
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h index db5c701..ef4cdf3f 100644 --- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h +++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
@@ -193,6 +193,10 @@ std::unique_ptr<optimization_guide::OptimizationGuideStore> prediction_model_and_features_store_; + // The logger that plumbs the debug logs to the optimization guide + // internals page. Must outlive `prediction_manager_`. + std::unique_ptr<OptimizationGuideLogger> optimization_guide_logger_; + // Manages the storing, loading, and evaluating of optimization target // prediction models. std::unique_ptr<optimization_guide::PredictionManager> prediction_manager_; @@ -205,10 +209,6 @@ // The tab URL provider to use for fetching information for the user's active // tabs. Will be null if the user is off the record. std::unique_ptr<optimization_guide::TabUrlProvider> tab_url_provider_; - - // The logger that plumbs the debug logs to the optimization guide - // internals page. - std::unique_ptr<OptimizationGuideLogger> optimization_guide_logger_; }; #endif // CHROME_BROWSER_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_KEYED_SERVICE_H_
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc index fae947b..a4aa237 100644 --- a/chrome/browser/password_manager/chrome_password_manager_client.cc +++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -1754,7 +1754,16 @@ driver->AsWeakPtr(), observer_, web_contents(), driver->render_frame_host()); popup_controller_->UpdateTypedPassword(ui_data.user_typed_password); - popup_controller_->Show(PasswordGenerationPopupController::kOfferGeneration); + + // TODO(crbug.com/1345766): Add separate flag for calculating strength and use + // this one only when UI needs to be displayed. + if (base::FeatureList::IsEnabled( + password_manager::features::kPasswordStrengthIndicator)) { + popup_controller_->UpdatePopupBasedOnTypedPasswordStrength(); + } else { + popup_controller_->Show( + PasswordGenerationPopupController::kOfferGeneration); + } } gfx::RectF ChromePasswordManagerClient::TransformToRootCoordinates(
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index 4d35e3380..a92551b9 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc
@@ -1066,6 +1066,7 @@ memory::EnterpriseMemoryLimitPrefObserver::RegisterPrefs(registry); metrics::RegisterDemographicsLocalStatePrefs(registry); network_time::NetworkTimeTracker::RegisterPrefs(registry); + optimization_guide::prefs::RegisterLocalStatePrefs(registry); password_manager::PasswordManager::RegisterLocalPrefs(registry); policy::BrowserPolicyConnector::RegisterPrefs(registry); policy::ManagementService::RegisterLocalStatePrefs(registry);
diff --git a/chrome/browser/printing/print_backend_service_manager.cc b/chrome/browser/printing/print_backend_service_manager.cc index aa556a0..2fa2bf9 100644 --- a/chrome/browser/printing/print_backend_service_manager.cc +++ b/chrome/browser/printing/print_backend_service_manager.cc
@@ -411,6 +411,26 @@ base::Unretained(this), context)); } +void PrintBackendServiceManager::Cancel( + const std::string& printer_name, + int document_cookie, + mojom::PrintBackendService::CancelCallback callback) { + CallbackContext context; + auto& service = GetServiceAndCallbackContext( + printer_name, ClientType::kPrintDocument, context); + + SaveCallback(GetRemoteSavedCancelCallbacks(context.is_sandboxed), + context.remote_id, context.saved_callback_id, + std::move(callback)); + + SetCrashKeys(printer_name); + + LogCallToRemote("Cancel", context); + service->Cancel(document_cookie, + base::BindOnce(&PrintBackendServiceManager::OnDidCancel, + base::Unretained(this), context)); +} + bool PrintBackendServiceManager::PrinterDriverFoundToRequireElevatedPrivilege( const std::string& printer_name) const { return drivers_requiring_elevated_privilege_.contains(printer_name); @@ -922,6 +942,7 @@ remote_id, mojom::ResultCode::kFailed); RunSavedCallbacks(GetRemoteSavedDocumentDoneCallbacks(sandboxed), remote_id, mojom::ResultCode::kFailed); + RunSavedCallbacks(GetRemoteSavedCancelCallbacks(sandboxed), remote_id); } PrintBackendServiceManager::RemoteSavedEnumeratePrintersCallbacks& @@ -1008,6 +1029,12 @@ : unsandboxed_saved_document_done_callbacks_; } +PrintBackendServiceManager::RemoteSavedCancelCallbacks& +PrintBackendServiceManager::GetRemoteSavedCancelCallbacks(bool sandboxed) { + return sandboxed ? sandboxed_saved_cancel_callbacks_ + : unsandboxed_saved_cancel_callbacks_; +} + const mojo::Remote<mojom::PrintBackendService>& PrintBackendServiceManager::GetServiceAndCallbackContext( const std::string& printer_name, @@ -1151,6 +1178,12 @@ context.remote_id, context.saved_callback_id, result); } +void PrintBackendServiceManager::OnDidCancel(const CallbackContext& context) { + LogCallbackFromRemote("Cancel", context); + ServiceCallbackDone(GetRemoteSavedCancelCallbacks(context.is_sandboxed), + context.remote_id, context.saved_callback_id); +} + template <class T> void PrintBackendServiceManager::RunSavedCallbacksStructResult( RemoteSavedStructCallbacks<T>& saved_callbacks,
diff --git a/chrome/browser/printing/print_backend_service_manager.h b/chrome/browser/printing/print_backend_service_manager.h index 08f05c90..9371c475 100644 --- a/chrome/browser/printing/print_backend_service_manager.h +++ b/chrome/browser/printing/print_backend_service_manager.h
@@ -132,6 +132,9 @@ void DocumentDone(const std::string& printer_name, int document_cookie, mojom::PrintBackendService::DocumentDoneCallback callback); + void Cancel(const std::string& printer_name, + int document_cookie, + mojom::PrintBackendService::CancelCallback callback); // Query if printer driver has been found to require elevated privilege in // order to have print queries/commands succeed. @@ -230,6 +233,7 @@ RemoteSavedCallbacks<mojom::ResultCode>; using RemoteSavedDocumentDoneCallbacks = RemoteSavedCallbacks<mojom::ResultCode>; + using RemoteSavedCancelCallbacks = RemoteSavedCallbacks<>; // Bundle of the `PrintBackendService` and its sandboxed/unsandboxed host // remotes. @@ -363,6 +367,7 @@ GetRemoteSavedRenderPrintedDocumentCallbacks(bool sandboxed); RemoteSavedDocumentDoneCallbacks& GetRemoteSavedDocumentDoneCallbacks( bool sandboxed); + RemoteSavedCancelCallbacks& GetRemoteSavedCancelCallbacks(bool sandboxed); // Helper function to get the service and initialize a `context` for a given // `printer_name`. @@ -415,6 +420,7 @@ mojom::ResultCode result); void OnDidDocumentDone(const CallbackContext& context, mojom::ResultCode result); + void OnDidCancel(const CallbackContext& context); // Helper functions to run outstanding callbacks when a remote has become // disconnected. @@ -509,6 +515,8 @@ unsandboxed_saved_render_printed_document_callbacks_; RemoteSavedDocumentDoneCallbacks sandboxed_saved_document_done_callbacks_; RemoteSavedDocumentDoneCallbacks unsandboxed_saved_document_done_callbacks_; + RemoteSavedCancelCallbacks sandboxed_saved_cancel_callbacks_; + RemoteSavedCancelCallbacks unsandboxed_saved_cancel_callbacks_; // Set of printer drivers which require elevated permissions to operate. // It is expected that most print drivers will succeed with the preconfigured
diff --git a/chrome/browser/printing/print_browsertest.cc b/chrome/browser/printing/print_browsertest.cc index 02f3002..465941d 100644 --- a/chrome/browser/printing/print_browsertest.cc +++ b/chrome/browser/printing/print_browsertest.cc
@@ -128,7 +128,8 @@ base::RepeatingCallback<void(mojom::ResultCode result)>; using OnDidDocumentDoneCallback = base::RepeatingCallback<void(mojom::ResultCode result)>; -using OnDidShowErrorDialog = base::RepeatingCallback<void()>; +using OnDidCancelCallback = base::RepeatingClosure; +using OnDidShowErrorDialog = base::RepeatingClosure; #endif // BUILDFLAG(ENABLE_OOP_PRINTING) @@ -2298,6 +2299,7 @@ #endif OnDidRenderPrintedDocumentCallback did_render_printed_document_callback; OnDidDocumentDoneCallback did_document_done_callback; + OnDidCancelCallback did_cancel_callback; }; TestPrintJobWorkerOop(content::GlobalRenderFrameHostId rfh_id, @@ -2369,6 +2371,15 @@ callbacks_->did_document_done_callback.Run(result); } + void OnDidCancel(scoped_refptr<PrintJob> job) override { + DVLOG(1) << "Observed: cancel"; + // Must not use `std::move(job)`, as that could potentially cause the `job` + // (and consequentially `this`) to be destroyed before + // `did_cancel_callback` is run. + PrintJobWorkerOop::OnDidCancel(job); + callbacks_->did_cancel_callback.Run(); + } + raw_ptr<PrintCallbacks> callbacks_; }; #endif // BUILDFLAG(ENABLE_OOP_PRINTING) @@ -2430,6 +2441,10 @@ base::BindRepeating( &SystemAccessProcessPrintBrowserTestBase::OnDidDocumentDone, base::Unretained(this)); + test_print_job_worker_oop_callbacks_.did_cancel_callback = + base::BindRepeating( + &SystemAccessProcessPrintBrowserTestBase::OnDidCancel, + base::Unretained(this)); } else { test_print_job_worker_callbacks_.did_use_default_settings_callback = base::BindRepeating( @@ -2620,6 +2635,8 @@ return document_done_result_; } + int cancel_count() const { return cancel_count_; } + int print_job_construction_count() const { return print_job_construction_count_; } @@ -2711,6 +2728,11 @@ CheckForQuit(); } + void OnDidCancel() { + ++cancel_count_; + CheckForQuit(); + } + void OnDidDestroyPrintJob() { ++print_job_destruction_count_; CheckForQuit(); @@ -2760,6 +2782,7 @@ mojom::ResultCode render_printed_document_result_ = mojom::ResultCode::kFailed; mojom::ResultCode document_done_result_ = mojom::ResultCode::kFailed; + int cancel_count_ = 0; int print_job_construction_count_ = 0; int print_job_destruction_count_ = 0; }; @@ -2985,14 +3008,17 @@ // sequence for this is: // 1. A print job is started. // 2. Spooling to send the render data will fail. An error dialog is shown. - // 3. Wait for the one print job to be destroyed, to ensure printing + // 3. The print job is canceled. The callback from the service could occur + // after the print job has been destroyed. + // 4. Wait for the one print job to be destroyed, to ensure printing // finished cleanly before completing the test. - SetNumExpectedMessages(/*num=*/3); + SetNumExpectedMessages(/*num=*/4); PrintAfterPreviewIsReadyAndLoaded(); EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess); EXPECT_EQ(error_dialog_shown_count(), 1u); + EXPECT_EQ(cancel_count(), 1); EXPECT_EQ(print_job_destruction_count(), 1); } @@ -3030,15 +3056,20 @@ // The expected events for this are: // 1. A print job is started, but that fails. // 2. An error dialog is shown. - // 3. Wait for the one print job to be destroyed, to ensure printing + // 3. The print job is canceled. The callback from the service could occur + // after the print job has been destroyed. + // 4. Wait for the one print job to be destroyed, to ensure printing // finished cleanly before completing the test. - SetNumExpectedMessages(/*num=*/3); + SetNumExpectedMessages(/*num=*/4); } PrintAfterPreviewIsReadyAndLoaded(); EXPECT_EQ(start_printing_result(), mojom::ResultCode::kFailed); EXPECT_EQ(error_dialog_shown_count(), 1u); + // No tracking of cancel for in-browser tests, only for OOP. + if (GetParam() != PrintBackendFeatureVariation::kInBrowserProcess) + EXPECT_EQ(cancel_count(), 1); EXPECT_EQ(print_job_destruction_count(), 1); } @@ -3103,14 +3134,17 @@ // 1. A print job is started, but has an access-denied error. // 2. A retry to start the print job with adjusted access will still fail. // 3. An error dialog is shown. - // 4. Wait for the one print job to be destroyed, to ensure printing + // 4. The print job is canceled. The callback from the service could occur + // after the print job has been destroyed. + // 5. Wait for the one print job to be destroyed, to ensure printing // finished cleanly before completing the test. - SetNumExpectedMessages(/*num=*/4); + SetNumExpectedMessages(/*num=*/5); PrintAfterPreviewIsReadyAndLoaded(); EXPECT_EQ(start_printing_result(), mojom::ResultCode::kAccessDenied); EXPECT_EQ(error_dialog_shown_count(), 1u); + EXPECT_EQ(cancel_count(), 1); EXPECT_EQ(print_job_destruction_count(), 1); } @@ -3135,9 +3169,11 @@ // 1. A print job is started. // 2. Rendering for 1 page of document of content fails with access denied. // 3. An error dialog is shown. - // 4. Wait for the one print job to be destroyed, to ensure printing + // 4. The print job is canceled. The callback from the service could occur + // after the print job has been destroyed. + // 5. Wait for the one print job to be destroyed, to ensure printing // finished cleanly before completing the test. - SetNumExpectedMessages(/*num=*/4); + SetNumExpectedMessages(/*num=*/5); PrintAfterPreviewIsReadyAndLoaded(); @@ -3145,13 +3181,12 @@ EXPECT_EQ(render_printed_page_result(), mojom::ResultCode::kAccessDenied); EXPECT_EQ(render_printed_page_count(), 0); EXPECT_EQ(error_dialog_shown_count(), 1u); + EXPECT_EQ(cancel_count(), 1); EXPECT_EQ(print_job_destruction_count(), 1); } -// TODO(crbug.com/1326580): Enable test once use-after-free after a failed -// call is avoided. IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest, - DISABLED_StartPrintingMultipageMidJobError) { + StartPrintingMultipageMidJobError) { AddPrinter("printer1"); SetPrinterNameForSubsequentContexts("printer1"); // Delay rendering until all pages have been sent, to avoid any race @@ -3170,17 +3205,31 @@ ASSERT_TRUE(web_contents); SetUpPrintViewManager(web_contents); - // TODO(crbug.com/1326580): Update behavior description after UAF during - // error processing in the PrintBackendService is resolved. In meantime - // replicate the expected message count from StartPrintingMultipage test. - SetNumExpectedMessages(/*num=*/6); + // The expected events for this are: + // 1. Start the print job. + // 2. First page render callback shows success. + // 3. Second page render callback shows failure. Will start failure + // processing to cancel the print job. + // 4. A printing error dialog is displayed. + // 5. Third page render callback will show it was canceled (due to prior + // failure). This is disregarded by the browser, since the job has + // already been canceled. + // 6. The print job is canceled. The callback from the service could occur + // after the print job has been destroyed. + // 7. Wait for the one print job to be destroyed, to ensure printing + // finished cleanly before completing the test. + SetNumExpectedMessages(/*num=*/7); PrintAfterPreviewIsReadyAndLoaded(); EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess); - EXPECT_EQ(render_printed_page_result(), mojom::ResultCode::kFailed); - // TODO(crbug.com/1326580): Update remaining behavior checks after UAF - // during error processing in the PrintBackendService is resolved. + // First failure page is `kFailed`, but is followed by another page with + // status `kCanceled`. + EXPECT_EQ(render_printed_page_result(), mojom::ResultCode::kCanceled); + EXPECT_EQ(render_printed_page_count(), 1); + EXPECT_EQ(error_dialog_shown_count(), 1u); + EXPECT_EQ(cancel_count(), 1); + EXPECT_EQ(print_job_destruction_count(), 1); } #endif // BUILDFLAG(IS_WIN) @@ -3206,15 +3255,18 @@ // 1. A print job is started. // 2. Rendering for 1 page of document of content fails with access denied. // 3. An error dialog is shown. - // 4. Wait for the one print job to be destroyed, to ensure printing + // 4. The print job is canceled. The callback from the service could occur + // after the print job has been destroyed. + // 5. Wait for the one print job to be destroyed, to ensure printing // finished cleanly before completing the test. - SetNumExpectedMessages(/*num=*/4); + SetNumExpectedMessages(/*num=*/5); PrintAfterPreviewIsReadyAndLoaded(); EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess); EXPECT_EQ(render_printed_document_result(), mojom::ResultCode::kAccessDenied); EXPECT_EQ(error_dialog_shown_count(), 1u); + EXPECT_EQ(cancel_count(), 1); EXPECT_EQ(print_job_destruction_count(), 1); } #endif // !BUILDFLAG(IS_WIN) @@ -3240,9 +3292,11 @@ // 2. Rendering for 1 page of document of content. // 3. Document done results in an access-denied error. // 4. An error dialog is shown. - // 5. Wait for the one print job to be destroyed, to ensure printing + // 5. The print job is canceled. The callback from the service could occur + // after the print job has been destroyed. + // 6. Wait for the one print job to be destroyed, to ensure printing // finished cleanly before completing the test. - SetNumExpectedMessages(/*num=*/5); + SetNumExpectedMessages(/*num=*/6); PrintAfterPreviewIsReadyAndLoaded(); @@ -3257,6 +3311,7 @@ #endif EXPECT_EQ(document_done_result(), mojom::ResultCode::kAccessDenied); EXPECT_EQ(error_dialog_shown_count(), 1u); + EXPECT_EQ(cancel_count(), 1); EXPECT_EQ(print_job_destruction_count(), 1); } @@ -3409,12 +3464,14 @@ // 2. Asks user for settings. // 3. A print job is started, which fails. // 4. An error dialog is shown. - // 5. Wait for the one print job to be destroyed, to ensure printing + // 5. The print job is canceled. The callback from the service could occur + // after the print job has been destroyed. + // 6. Wait for the one print job to be destroyed, to ensure printing // finished cleanly before completing the test. - // 6. The print compositor will have started to generate the document. + // 7. The print compositor will have started to generate the document. // Wait until that is known to have completed, to ensure printing // finished cleanly before completing the test. - SetNumExpectedMessages(/*num=*/6); + SetNumExpectedMessages(/*num=*/7); } StartBasicPrint(web_contents); @@ -3423,6 +3480,7 @@ EXPECT_EQ(start_printing_result(), mojom::ResultCode::kFailed); EXPECT_EQ(error_dialog_shown_count(), 1u); + EXPECT_EQ(cancel_count(), 1); EXPECT_EQ(did_composite_completion_count(), 1); EXPECT_EQ(print_job_destruction_count(), 1); }
diff --git a/chrome/browser/printing/print_job_manager.h b/chrome/browser/printing/print_job_manager.h index f200926..e1b9d1c 100644 --- a/chrome/browser/printing/print_job_manager.h +++ b/chrome/browser/printing/print_job_manager.h
@@ -35,8 +35,9 @@ // TODO(maruel): Have them vanish after a timeout (~5 minutes?) void QueuePrinterQuery(std::unique_ptr<PrinterQuery> query); - // Pops a queued PrinterQuery object that was previously queued or creates - // a new one. Can be called from any thread. + // Pops a queued PrinterQuery object that was previously queued. Returns + // nullptr if there is no query matching `document_cookie`. + // Can be called from any thread. std::unique_ptr<PrinterQuery> PopPrinterQuery(int document_cookie); // Creates new query. Virtual so that tests can override it.
diff --git a/chrome/browser/printing/print_job_worker_oop.cc b/chrome/browser/printing/print_job_worker_oop.cc index 795044e..aa2aa3b 100644 --- a/chrome/browser/printing/print_job_worker_oop.cc +++ b/chrome/browser/printing/print_job_worker_oop.cc
@@ -170,6 +170,8 @@ mojom::ResultCode result) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (result != mojom::ResultCode::kSuccess) { + // Once an error happens during rendering, there could be multiple calls + // to here as the queue of sent pages all return back with error. PRINTER_LOG(ERROR) << "Error rendering printed page via service for document " << document_oop_->cookie() << ": " << result; @@ -240,6 +242,17 @@ document_oop_ = nullptr; } +void PrintJobWorkerOop::OnDidCancel(scoped_refptr<PrintJob> job) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + DVLOG(1) << "Cancel completed for printing via service for document " + << document_oop_->cookie(); + + UnregisterServiceManagerClient(); + + // Done with private document reference. + document_oop_ = nullptr; +} + #if BUILDFLAG(IS_WIN) bool PrintJobWorkerOop::SpoolPage(PrintedPage* page) { DCHECK(task_runner()->RunsTasksInCurrentSequence()); @@ -359,10 +372,12 @@ } void PrintJobWorkerOop::OnFailure() { + // Retain a reference to the PrintJob to ensure it doesn't get deleted before + // the `OnDidCancel()` callback occurs. content::GetUIThreadTaskRunner({})->PostTask( - FROM_HERE, - base::BindOnce(&PrintJobWorkerOop::UnregisterServiceManagerClient, - ui_weak_factory_.GetWeakPtr())); + FROM_HERE, base::BindOnce(&PrintJobWorkerOop::SendCancel, + ui_weak_factory_.GetWeakPtr(), + base::WrapRefCounted(print_job()))); PrintJobWorker::OnFailure(); } @@ -581,4 +596,29 @@ printing_context()->job_id())); } +void PrintJobWorkerOop::SendCancel(scoped_refptr<PrintJob> job) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + // If an error has occurred during rendering in middle of a multi-page job, + // it could be possible for the `OnDidRenderPrintedPage()` callback of latter + // pages to still go through error processing. In such a case the document + // might already have been canceled, so we should ensure to only send a + // cancel request to the service if we haven't already done so. + if (print_cancel_requested_) + return; + + print_cancel_requested_ = true; + VLOG(1) << "Sending cancel for document " << document_oop_->cookie(); + + PrintBackendServiceManager& service_mgr = + PrintBackendServiceManager::GetInstance(); + + // Retain a reference to the PrintJob to ensure it doesn't get deleted before + // the `OnDidCancel()` callback occurs. + service_mgr.Cancel( + device_name_, document_oop_->cookie(), + base::BindOnce(&PrintJobWorkerOop::OnDidCancel, + ui_weak_factory_.GetWeakPtr(), std::move(job))); +} + } // namespace printing
diff --git a/chrome/browser/printing/print_job_worker_oop.h b/chrome/browser/printing/print_job_worker_oop.h index 36d51600..211f6dbb 100644 --- a/chrome/browser/printing/print_job_worker_oop.h +++ b/chrome/browser/printing/print_job_worker_oop.h
@@ -61,6 +61,7 @@ #endif virtual void OnDidRenderPrintedDocument(mojom::ResultCode result); virtual void OnDidDocumentDone(int job_id, mojom::ResultCode result); + virtual void OnDidCancel(scoped_refptr<PrintJob> job); // `PrintJobWorker` overrides. #if BUILDFLAG(IS_WIN) @@ -113,6 +114,7 @@ mojom::MetafileDataType data_type, base::ReadOnlySharedMemoryRegion serialized_data); void SendDocumentDone(); + void SendCancel(scoped_refptr<PrintJob> job); // Used to test spooling memory error handling. bool simulate_spooling_memory_errors_ = false; @@ -149,6 +151,10 @@ // Tracks if a restart for printing has already been attempted. bool print_retried_ = false; + // Tracks if the service has already been requested to cancel printing the + // document + bool print_cancel_requested_ = false; + // Weak pointers have flags that get bound to the thread where they are // checked, so it is necessary to use different factories when getting a // weak pointer to send to the worker task runner vs. to the UI thread.
diff --git a/chrome/browser/profiles/profile_keyed_service_browsertest.cc b/chrome/browser/profiles/profile_keyed_service_browsertest.cc index 3cee630..08cb184 100644 --- a/chrome/browser/profiles/profile_keyed_service_browsertest.cc +++ b/chrome/browser/profiles/profile_keyed_service_browsertest.cc
@@ -256,7 +256,6 @@ "AppLifetimeMonitor", "AppLoadService", "AppRestoreService", - "AppShortcutManager", "AppTerminationObserver", "AppWindowRegistry", "AudioAPI", @@ -548,7 +547,9 @@ "AppLoadService", "AppRestoreService", "AppServiceProxy", +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) "AppShortcutManager", +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) "AppTerminationObserver", "AppWindowRegistry", "AudioAPI",
diff --git a/chrome/browser/resources/access_code_cast/BUILD.gn b/chrome/browser/resources/access_code_cast/BUILD.gn index 5e5a6b5..acfdc79 100644 --- a/chrome/browser/resources/access_code_cast/BUILD.gn +++ b/chrome/browser/resources/access_code_cast/BUILD.gn
@@ -20,8 +20,8 @@ non_web_component_files = [ "browser_proxy.ts" ] mojo_files_deps = [ - "//chrome/browser/ui/webui/access_code_cast:mojo_bindings_webui_js", - "//components/media_router/common/mojom:route_request_result_code_webui_js", + "//chrome/browser/ui/webui/access_code_cast:mojo_bindings_js__generator", + "//components/media_router/common/mojom:route_request_result_code_js__generator", ] mojo_files = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/access_code_cast/access_code_cast.mojom-webui.js",
diff --git a/chrome/browser/resources/app_home/BUILD.gn b/chrome/browser/resources/app_home/BUILD.gn index 9b442a8..012beeda 100644 --- a/chrome/browser/resources/app_home/BUILD.gn +++ b/chrome/browser/resources/app_home/BUILD.gn
@@ -17,7 +17,7 @@ non_web_component_files = [ "browser_proxy.ts" ] mojo_files_deps = - [ "//chrome/browser/ui/webui/app_home:mojo_bindings_webui_js" ] + [ "//chrome/browser/ui/webui/app_home:mojo_bindings_js__generator" ] mojo_files = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/app_home/app_home.mojom-webui.js" ] ts_composite = true
diff --git a/chrome/browser/resources/app_service_internals/BUILD.gn b/chrome/browser/resources/app_service_internals/BUILD.gn index f18751dd..baeefcd 100644 --- a/chrome/browser/resources/app_service_internals/BUILD.gn +++ b/chrome/browser/resources/app_service_internals/BUILD.gn
@@ -11,9 +11,7 @@ web_component_files = [ "app_service_internals.ts" ] - mojo_files_deps = [ - "//chrome/browser/ui/webui/app_service_internals:mojo_bindings_webui_js", - ] + mojo_files_deps = [ "//chrome/browser/ui/webui/app_service_internals:mojo_bindings_js__generator" ] mojo_files = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/app_service_internals/app_service_internals.mojom-webui.js" ] ts_deps = [ "//third_party/polymer/v3_0:library" ]
diff --git a/chrome/browser/resources/browsing_topics/BUILD.gn b/chrome/browser/resources/browsing_topics/BUILD.gn index 6f313c6..dc5bd21a 100644 --- a/chrome/browser/resources/browsing_topics/BUILD.gn +++ b/chrome/browser/resources/browsing_topics/BUILD.gn
@@ -14,7 +14,7 @@ non_web_component_files = [ "browsing_topics_internals.ts" ] mojo_files = [ "$root_gen_dir/mojom-webui/components/browsing_topics/mojom/browsing_topics_internals.mojom-webui.js" ] mojo_files_deps = - [ "//components/browsing_topics/mojom:mojo_bindings_webui_js" ] + [ "//components/browsing_topics/mojom:mojo_bindings_js__generator" ] ts_deps = [ "//ui/webui/resources:library" ] }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js index 345722c..822e96c 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
@@ -25,7 +25,6 @@ import {ChromeVoxBackground} from './classic_background.js'; import {ClipboardHandler} from './clipboard_handler.js'; import {CommandHandler} from './command_handler.js'; -import {CommandHandlerInterface} from './command_handler_interface.js'; import {DesktopAutomationHandler} from './desktop_automation_handler.js'; import {DesktopAutomationInterface} from './desktop_automation_interface.js'; import {DownloadHandler} from './download_handler.js'; @@ -123,6 +122,7 @@ BackgroundKeyboardHandler.init(); BrailleCommandHandler.init(); ClipboardHandler.init(); + CommandHandler.init(); DesktopAutomationHandler.init(); DownloadHandler.init(); EventStreamLogger.init();
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js index 14c63229..19f6a0f3 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
@@ -14,7 +14,6 @@ import {KeyCode} from '../../common/key_code.js'; import {RectUtil} from '../../common/rect_util.js'; import {Earcon} from '../common/abstract_earcons.js'; -import {AbstractTts} from '../common/abstract_tts.js'; import {NavBraille} from '../common/braille/nav_braille.js'; import {BridgeConstants} from '../common/bridge_constants.js'; import {BridgeHelper} from '../common/bridge_helper.js'; @@ -27,7 +26,7 @@ import {Msgs} from '../common/msgs.js'; import {PanelCommand, PanelCommandType} from '../common/panel_command.js'; import {TreeDumper} from '../common/tree_dumper.js'; -import {QueueMode, TtsSpeechProperties} from '../common/tts_types.js'; +import {Personality, QueueMode, TtsSettings, TtsSpeechProperties} from '../common/tts_types.js'; import {AutoScrollHandler} from './auto_scroll_handler.js'; import {BrailleBackground} from './braille/braille_background.js'; @@ -83,7 +82,30 @@ this.languageLoggingEnabled_ = false; SmartStickyMode.init(); - this.init(); + this.init_(); + } + + /** @private */ + init_() { + chrome.commandLinePrivate.hasSwitch( + 'enable-experimental-accessibility-language-detection', enabled => { + if (enabled) { + this.languageLoggingEnabled_ = true; + } + }); + chrome.commandLinePrivate.hasSwitch( + 'enable-experimental-accessibility-language-detection-dynamic', + enabled => { + if (enabled) { + this.languageLoggingEnabled_ = true; + } + }); + + chrome.chromeosInfoPrivate.get(['sessionType'], result => { + /** @type {boolean} */ + this.isKioskSession_ = result['sessionType'] === + chrome.chromeosInfoPrivate.SessionType.KIOSK; + }); } /** @override */ @@ -130,22 +152,22 @@ root => LogStore.instance.writeTreeLog(new TreeDumper(root))); break; case Command.DECREASE_TTS_RATE: - this.increaseOrDecreaseSpeechProperty_(AbstractTts.RATE, false); + this.increaseOrDecreaseSpeechProperty_(TtsSettings.RATE, false); return false; case Command.INCREASE_TTS_RATE: - this.increaseOrDecreaseSpeechProperty_(AbstractTts.RATE, true); + this.increaseOrDecreaseSpeechProperty_(TtsSettings.RATE, true); return false; case Command.DECREASE_TTS_PITCH: - this.increaseOrDecreaseSpeechProperty_(AbstractTts.PITCH, false); + this.increaseOrDecreaseSpeechProperty_(TtsSettings.PITCH, false); return false; case Command.INCREASE_TTS_PITCH: - this.increaseOrDecreaseSpeechProperty_(AbstractTts.PITCH, true); + this.increaseOrDecreaseSpeechProperty_(TtsSettings.PITCH, true); return false; case Command.DECREASE_TTS_VOLUME: - this.increaseOrDecreaseSpeechProperty_(AbstractTts.VOLUME, false); + this.increaseOrDecreaseSpeechProperty_(TtsSettings.VOLUME, false); return false; case Command.INCREASE_TTS_VOLUME: - this.increaseOrDecreaseSpeechProperty_(AbstractTts.VOLUME, true); + this.increaseOrDecreaseSpeechProperty_(TtsSettings.VOLUME, true); return false; case Command.STOP_SPEECH: ChromeVox.tts.stop(); @@ -1110,8 +1132,7 @@ announce = Msgs.getMsg('none_echo'); break; } - ChromeVox.tts.speak( - announce, QueueMode.FLUSH, AbstractTts.PERSONALITY_ANNOTATION); + ChromeVox.tts.speak(announce, QueueMode.FLUSH, Personality.ANNOTATION); } /** @private */ @@ -1758,8 +1779,7 @@ ChromeVox.earcons.enabled = !ChromeVox.earcons.enabled; const announce = ChromeVox.earcons.enabled ? Msgs.getMsg('earcons_on') : Msgs.getMsg('earcons_off'); - ChromeVox.tts.speak( - announce, QueueMode.FLUSH, AbstractTts.PERSONALITY_ANNOTATION); + ChromeVox.tts.speak(announce, QueueMode.FLUSH, Personality.ANNOTATION); } /** @private */ @@ -1836,40 +1856,20 @@ /** * Performs global initialization. */ - init() { - ChromeVoxKbHandler.commandHandler = command => this.onCommand(command); + static init() { + CommandHandlerInterface.instance = new CommandHandler(); + ChromeVoxKbHandler.commandHandler = command => + CommandHandlerInterface.instance.onCommand(command); - chrome.commandLinePrivate.hasSwitch( - 'enable-experimental-accessibility-language-detection', enabled => { - if (enabled) { - this.languageLoggingEnabled_ = true; + BridgeHelper.registerHandler( + BridgeConstants.CommandHandler.TARGET, + BridgeConstants.CommandHandler.Action.ON_COMMAND, command => { + if (Object.values(Command).includes(command)) { + CommandHandlerInterface.instance.onCommand( + /** @type {Command} */ (command)); + } else { + console.warn('ChromeVox got an unrecognized command: ' + command); } }); - chrome.commandLinePrivate.hasSwitch( - 'enable-experimental-accessibility-language-detection-dynamic', - enabled => { - if (enabled) { - this.languageLoggingEnabled_ = true; - } - }); - - chrome.chromeosInfoPrivate.get(['sessionType'], result => { - /** @type {boolean} */ - this.isKioskSession_ = result['sessionType'] === - chrome.chromeosInfoPrivate.SessionType.KIOSK; - }); } } - -CommandHandlerInterface.instance = new CommandHandler(); - -BridgeHelper.registerHandler( - BridgeConstants.CommandHandler.TARGET, - BridgeConstants.CommandHandler.Action.ON_COMMAND, command => { - if (Object.values(Command).includes(command)) { - CommandHandlerInterface.instance.onCommand( - /** @type {Command} */ (command)); - } else { - console.warn('ChromeVox got an unrecognized command: ' + command); - } - });
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_text_base.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_text_base.js index 8d489e4..38c4f6c 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_text_base.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_text_base.js
@@ -13,10 +13,9 @@ * extended to override methods that extract lines for multiline fields * or to provide other customizations. */ -import {AbstractTts} from '../../common/abstract_tts.js'; import {Msgs} from '../../common/msgs.js'; import {TtsInterface} from '../../common/tts_interface.js'; -import {QueueMode, TtsCategory, TtsSpeechProperties} from '../../common/tts_types.js'; +import {Personality, QueueMode, TtsCategory, TtsSpeechProperties} from '../../common/tts_types.js'; import {ChromeVoxState} from '../chromevox_state.js'; /** @@ -335,19 +334,11 @@ this.speak(lineValue, evt.triggeredByUser); } else if (this.start === evt.start + 1 || this.start === evt.start - 1) { // Moved by one character; read it. - if (!ChromeVoxEditableTextBase.useIBeamCursor) { - if (evt.start === this.value.length) { - this.speak(Msgs.getMsg('end_of_text_verbose'), evt.triggeredByUser); - } else { - this.speak( - this.value.substr(evt.start, 1), evt.triggeredByUser, - new TtsSpeechProperties( - {'phoneticCharacters': evt.triggeredByUser})); - } + if (evt.start === this.value.length) { + this.speak(Msgs.getMsg('end_of_text_verbose'), evt.triggeredByUser); } else { this.speak( - this.value.substr(Math.min(this.start, evt.start), 1), - evt.triggeredByUser, + this.value.substr(evt.start, 1), evt.triggeredByUser, new TtsSpeechProperties( {'phoneticCharacters': evt.triggeredByUser})); } @@ -415,7 +406,7 @@ describeTextChanged(prev, evt) { let personality = new TtsSpeechProperties(); if (evt.value.length < (prev.value.length - 1)) { - personality = AbstractTts.PERSONALITY_DELETED; + personality = Personality.DELETED; } if (this.isPassword) { this.speak( @@ -469,8 +460,7 @@ // Forward deletions causes reading of the character immediately to the // right of the caret or the deleted text depending on the iBeam cursor // setting. - if (prev.start === evt.start && prev.end === evt.end && - !ChromeVoxEditableTextBase.useIBeamCursor) { + if (prev.start === evt.start && prev.end === evt.end) { this.speak(evt.value[evt.start], evt.triggeredByUser); } else { this.describeTextChangedHelper( @@ -613,8 +603,8 @@ utterance = deleted + ', deleted'; } else if (deletedLen === 1) { utterance = deleted; - // Single-deleted characters should also use PERSONALITY_DELETED. - opt_personality = AbstractTts.PERSONALITY_DELETED; + // Single-deleted characters should also use Personality.DELETED. + opt_personality = Personality.DELETED; } if (autocompleteSuffix && utterance) { @@ -693,27 +683,6 @@ } } - -/** - * Whether or not moving the cursor from one character to another considers - * the cursor to be a block (false) or an i-beam (true). - * - * If the cursor is a block, then the value of the character to the right - * of the cursor index is always read when the cursor moves, no matter what - * the previous cursor location was - this is how PC screenreaders work. - * - * If the cursor is an i-beam, moving the cursor by one character reads the - * character that was crossed over, which may be the character to the left or - * right of the new cursor index depending on the direction. - * - * If the current platform is a Mac, we will use an i-beam cursor. If not, - * then we will use the block cursor. - * - * @type {boolean} - */ -ChromeVoxEditableTextBase.useIBeamCursor = - localStorage['useIBeamCursor'] === String(true); - /** * @type {boolean} Whether insertions (i.e. changes of greater than one * character) should be spoken.
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_text_base_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_text_base_test.js index 743b7c3..a97d6ef6 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_text_base_test.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_text_base_test.js
@@ -95,11 +95,7 @@ await importModule( ['ChromeVoxEditableTextBase', 'TextChangedEvent', 'TypingEcho'], '/chromevox/background/editing/editable_text_base.js'); - await importModule('AbstractTts', '/chromevox/common/abstract_tts.js'); - // TODO: These tests are all assuming we used the IBeam cursor. - // We need to add coverage for block cursor. - ChromeVoxEditableTextBase.useIBeamCursor = true; ChromeVoxState.instance.typingEcho = TypingEcho.CHARACTER_AND_WORD; ChromeVoxEditableTextBase.eventTypingEcho = false; ChromeVoxEditableTextBase.shouldSpeakInsertions = true; @@ -184,7 +180,6 @@ assertEqualStringArrays(['.', 'd', 'l'], tts.get()); // Forward-delete - ChromeVoxEditableTextBase.useIBeamCursor = true; obj.changed(new TextChangeEvent('Hello, Wor', 9, 9)); obj.changed(new TextChangeEvent('Hello, Wor', 8, 8)); obj.changed(new TextChangeEvent('Hello, Wor', 7, 7)); @@ -194,7 +189,6 @@ assertEqualStringArrays(['r', 'o', 'W', 'W', 'o', 'r'], tts.get()); obj.changed(new TextChangeEvent('Hello, Wor', 10, 10)); - ChromeVoxEditableTextBase.useIBeamCursor = false; obj.changed(new TextChangeEvent('Hello, Wor', 9, 9)); obj.changed(new TextChangeEvent('Hello, Wor', 8, 8)); obj.changed(new TextChangeEvent('Hello, Wor', 7, 7));
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js index 7967c70..4cf776f9 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js
@@ -11,12 +11,11 @@ import {constants} from '../../../common/constants.js'; import {Cursor, CursorMovement, CursorUnit} from '../../../common/cursors/cursor.js'; import {CursorRange} from '../../../common/cursors/range.js'; -import {AbstractTts} from '../../common/abstract_tts.js'; import {NavBraille} from '../../common/braille/nav_braille.js'; import {ChromeVoxEvent} from '../../common/custom_automation_event.js'; import {Msgs} from '../../common/msgs.js'; import {MultiSpannable, Spannable} from '../../common/spannable.js'; -import {QueueMode} from '../../common/tts_types.js'; +import {Personality, QueueMode} from '../../common/tts_types.js'; import {BrailleBackground} from '../braille/braille_background.js'; import {LibLouis} from '../braille/liblouis.js'; import {BrailleTextStyleSpan, ValueSelectionSpan, ValueSpan} from '../braille/spans.js'; @@ -835,8 +834,7 @@ if (msgs.length) { msgs.forEach(msg => { ChromeVox.tts.speak( - Msgs.getMsg(msg), QueueMode.QUEUE, - AbstractTts.PERSONALITY_ANNOTATION); + Msgs.getMsg(msg), QueueMode.QUEUE, Personality.ANNOTATION); }); } } @@ -906,7 +904,7 @@ msgs.forEach(msgObject => { ChromeVox.tts.speak( Msgs.getMsg(msgObject.msg, msgObject.opt_subs), QueueMode.QUEUE, - AbstractTts.PERSONALITY_ANNOTATION); + Personality.ANNOTATION); }); } }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js index be62bc5..56babc2c 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js
@@ -6,10 +6,10 @@ * @fileoverview Common page for reading and writing preferences from * the background context (background page or options page). */ -import {AbstractTts} from '../common/abstract_tts.js'; import {BridgeConstants} from '../common/bridge_constants.js'; import {BridgeHelper} from '../common/bridge_helper.js'; import {Msgs} from '../common/msgs.js'; +import {Personality} from '../common/tts_types.js'; import {ChromeVox} from './chromevox.js'; import {ConsoleTts} from './console_tts.js'; @@ -129,7 +129,7 @@ setAndAnnounceStickyPref(value) { chrome.accessibilityPrivate.setKeyboardListener(true, value); new Output() - .withInitialSpeechProperties(AbstractTts.PERSONALITY_ANNOTATION) + .withInitialSpeechProperties(Personality.ANNOTATION) .withString( value ? Msgs.getMsg('sticky_mode_enabled') : Msgs.getMsg('sticky_mode_disabled')) @@ -182,7 +182,6 @@ 'speakTextUnderMouse': false, 'sticky': false, 'typingEcho': 0, - 'useIBeamCursor': false, 'useClassic': false, 'usePitchChanges': true, 'useVerboseMode': true,
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/settings_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/settings_test.js index 126ee7e..c1e11eb 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/settings_test.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/settings_test.js
@@ -34,7 +34,7 @@ await super.setUpDeferred(); // Alphabetical based on file path. - await importModule('AbstractTts', '/chromevox/common/abstract_tts.js'); + await importModule('TtsSettings', '/chromevox/common/tts_types.js'); } }; @@ -45,9 +45,9 @@ const mockFeedback = this.createMockFeedback(); await this.runWithLoadedTree(`unused`); const increaseRate = realTts.increaseOrDecreaseProperty.bind( - realTts, AbstractTts.RATE, true); + realTts, TtsSettings.RATE, true); const decreaseRate = realTts.increaseOrDecreaseProperty.bind( - realTts, AbstractTts.RATE, false); + realTts, TtsSettings.RATE, false); mockFeedback.call(doCmd('showTtsSettings')) .expectSpeech(
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/tts_background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/tts_background.js index bd02ef4c..eac102af 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/tts_background.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/tts_background.js
@@ -8,7 +8,6 @@ */ import {constants} from '../../common/constants.js'; -import {AbstractTts} from '../common/abstract_tts.js'; import {BridgeConstants} from '../common/bridge_constants.js'; import {BridgeHelper} from '../common/bridge_helper.js'; import {CompositeTts} from '../common/composite_tts.js'; @@ -16,7 +15,7 @@ import {PanelCommand, PanelCommandType} from '../common/panel_command.js'; import {ChromeTtsBase} from '../common/tts_base.js'; import {TtsCapturingEventListener, TtsInterface} from '../common/tts_interface.js'; -import {QueueMode, TtsSpeechProperties} from '../common/tts_types.js'; +import * as ttsTypes from '../common/tts_types.js'; import {ChromeVox} from './chromevox.js'; import {ConsoleTts} from './console_tts.js'; @@ -117,7 +116,7 @@ /** @private {number} */ this.currentPunctuationEcho_ = - parseInt(localStorage[AbstractTts.PUNCTUATION_ECHO] || 1, 10); + parseInt(localStorage[ttsTypes.TtsSettings.PUNCTUATION_ECHO] || 1, 10); /** * A list of punctuation characters that should always be spliced into @@ -218,9 +217,9 @@ /** * @param {string} textString The string of text to be spoken. - * @param {QueueMode} queueMode The queue mode to use for speaking. - * @param {TtsSpeechProperties=} properties Speech properties to use for - * this utterance. + * @param {ttsTypes.QueueMode} queueMode The queue mode to use for speaking. + * @param {ttsTypes.TtsSpeechProperties=} properties Speech properties to use + * for this utterance. * @return {TtsInterface} A tts object useful for chaining speak calls. * @override */ @@ -231,12 +230,12 @@ // original value for functions that may need it. const originalTextString = textString; - if (this.ttsProperties[AbstractTts.VOLUME] === 0) { + if (this.ttsProperties[ttsTypes.TtsSettings.VOLUME] === 0) { return this; } if (!properties) { - properties = new TtsSpeechProperties(); + properties = new ttsTypes.TtsSpeechProperties(); } if (textString.length > constants.OBJECT_MAX_CHARCOUNT) { @@ -270,7 +269,7 @@ } catch (e) { } } - if (queueMode === QueueMode.FLUSH) { + if (queueMode === ttsTypes.QueueMode.FLUSH) { this.stop(); } return this; @@ -299,16 +298,16 @@ * Split the given textString into smaller chunks and call this.speak() for * each chunks. * @param {string} textString The string of text to be spoken. - * @param {QueueMode} queueMode The queue mode to use for speaking. - * @param {TtsSpeechProperties=} properties Speech properties to use for - * this utterance. + * @param {ttsTypes.QueueMode} queueMode The queue mode to use for speaking. + * @param {ttsTypes.TtsSpeechProperties=} properties Speech properties to use + * for this utterance. * @private */ speakSplittingText_(textString, queueMode, properties) { const chunks = PrimaryTts.splitUntilSmall(textString, '\n\r '); for (const chunk of chunks) { this.speak(chunk, queueMode, properties); - queueMode = QueueMode.QUEUE; + queueMode = ttsTypes.QueueMode.QUEUE; } } @@ -362,9 +361,9 @@ // First, take care of removing the current utterance and flushing // anything from the queue we need to. If we remove the current utterance, // make a note that we're going to stop speech. - if (queueMode === QueueMode.FLUSH || - queueMode === QueueMode.CATEGORY_FLUSH || - queueMode === QueueMode.INTERJECT) { + if (queueMode === ttsTypes.QueueMode.FLUSH || + queueMode === ttsTypes.QueueMode.CATEGORY_FLUSH || + queueMode === ttsTypes.QueueMode.INTERJECT) { (new PanelCommand(PanelCommandType.CLEAR_SPEECH)).send(); if (this.shouldCancel_(this.currentUtterance_, utterance)) { @@ -385,7 +384,7 @@ } // Now, some special handling for interjections. - if (queueMode === QueueMode.INTERJECT) { + if (queueMode === ttsTypes.QueueMode.INTERJECT) { // Move all utterances to a secondary queue to be restored later. this.utteranceQueueInterruptedByInterjection_ = this.utteranceQueue_; @@ -405,7 +404,7 @@ setTimeout(() => { // Utterances on the current queue are now also interjections. for (let i = 0; i < this.utteranceQueue_.length; i++) { - this.utteranceQueue_[i].queueMode = QueueMode.INTERJECT; + this.utteranceQueue_[i].queueMode = ttsTypes.QueueMode.INTERJECT; } this.utteranceQueue_ = this.utteranceQueue_.concat( this.utteranceQueueInterruptedByInterjection_); @@ -567,13 +566,13 @@ return false; } switch (newUtterance.queueMode) { - case QueueMode.QUEUE: + case ttsTypes.QueueMode.QUEUE: return false; - case QueueMode.INTERJECT: - return utteranceToCancel.queueMode === QueueMode.INTERJECT; - case QueueMode.FLUSH: + case ttsTypes.QueueMode.INTERJECT: + return utteranceToCancel.queueMode === ttsTypes.QueueMode.INTERJECT; + case ttsTypes.QueueMode.FLUSH: return true; - case QueueMode.CATEGORY_FLUSH: + case ttsTypes.QueueMode.CATEGORY_FLUSH: return ( utteranceToCancel.properties['category'] === newUtterance.properties['category']); @@ -602,13 +601,13 @@ let pref; switch (propertyName) { - case AbstractTts.RATE: + case ttsTypes.TtsSettings.RATE: pref = 'settings.tts.speech_rate'; break; - case AbstractTts.PITCH: + case ttsTypes.TtsSettings.PITCH: pref = 'settings.tts.speech_pitch'; break; - case AbstractTts.VOLUME: + case ttsTypes.TtsSettings.VOLUME: pref = 'settings.tts.speech_volume'; break; default: @@ -685,14 +684,15 @@ // Perform any remaining processing such as punctuation expansion. let punctEcho = null; - if (properties[AbstractTts.PUNCTUATION_ECHO]) { - for (let i = 0; punctEcho = AbstractTts.PUNCTUATION_ECHOES[i]; i++) { - if (properties[AbstractTts.PUNCTUATION_ECHO] === punctEcho.name) { + if (properties[ttsTypes.TtsSettings.PUNCTUATION_ECHO]) { + for (let i = 0; punctEcho = ttsTypes.PunctuationEchoes[i]; i++) { + if (properties[ttsTypes.TtsSettings.PUNCTUATION_ECHO] === + punctEcho.name) { break; } } } else { - punctEcho = AbstractTts.PUNCTUATION_ECHOES[this.currentPunctuationEcho_]; + punctEcho = ttsTypes.PunctuationEchoes[this.currentPunctuationEcho_]; } text = text.replace( punctEcho.regexp, this.createPunctuationReplace_(punctEcho.clear)); @@ -718,12 +718,12 @@ /** @override */ toggleSpeechOnOrOff() { - const previousValue = this.ttsProperties[AbstractTts.VOLUME]; + const previousValue = this.ttsProperties[ttsTypes.TtsSettings.VOLUME]; const toggle = () => { if (previousValue === 0) { - this.ttsProperties[AbstractTts.VOLUME] = 1; + this.ttsProperties[ttsTypes.TtsSettings.VOLUME] = 1; } else { - this.ttsProperties[AbstractTts.VOLUME] = 0; + this.ttsProperties[ttsTypes.TtsSettings.VOLUME] = 0; this.stop(); } }; @@ -743,11 +743,11 @@ * Method that updates the punctuation echo level, and also persists setting * to local storage. * @param {number} punctuationEcho The index of the desired punctuation echo - * level in AbstractTts.PUNCTUATION_ECHOES. + * level in ttsTypes.PunctuationEchoes. */ updatePunctuationEcho(punctuationEcho) { this.currentPunctuationEcho_ = punctuationEcho; - localStorage[AbstractTts.PUNCTUATION_ECHO] = punctuationEcho; + localStorage[ttsTypes.TtsSettings.PUNCTUATION_ECHO] = punctuationEcho; } /** @@ -756,9 +756,8 @@ */ cyclePunctuationEcho() { this.updatePunctuationEcho( - (this.currentPunctuationEcho_ + 1) % - AbstractTts.PUNCTUATION_ECHOES.length); - return AbstractTts.PUNCTUATION_ECHOES[this.currentPunctuationEcho_].msg; + (this.currentPunctuationEcho_ + 1) % ttsTypes.PunctuationEchoes.length); + return ttsTypes.PunctuationEchoes[this.currentPunctuationEcho_].msg; } /** @@ -791,7 +790,7 @@ return clear ? retain : ' ' + (new goog.i18n.MessageFormat( - Msgs.getMsg(AbstractTts.CHARACTER_DICTIONARY[match]))) + Msgs.getMsg(ttsTypes.CharacterDictionary[match]))) .format({'COUNT': 1}) + retain + ' '; }; @@ -800,8 +799,8 @@ /** * Queues phonetic disambiguation for characters if disambiguation is found. * @param {string} text The text for which we want to get phonetic data. - * @param {!TtsSpeechProperties} properties Speech properties to use for - * this utterance. + * @param {!ttsTypes.TtsSpeechProperties} properties Speech properties to use + * for this utterance. * @private */ pronouncePhonetically_(text, properties) { @@ -827,7 +826,7 @@ const phoneticText = PhoneticData.forCharacter(text, properties.lang); if (phoneticText) { properties.delay = true; - this.speak(phoneticText, QueueMode.QUEUE, properties); + this.speak(phoneticText, ttsTypes.QueueMode.QUEUE, properties); } } @@ -880,16 +879,16 @@ let propertyName; switch (pref.key) { case 'settings.tts.speech_rate': - propertyName = AbstractTts.RATE; + propertyName = ttsTypes.TtsSettings.RATE; msg = 'announce_rate'; this.setHintDelayMS(/** @type {number} */ (pref.value)); break; case 'settings.tts.speech_pitch': - propertyName = AbstractTts.PITCH; + propertyName = ttsTypes.TtsSettings.PITCH; msg = 'announce_pitch'; break; case 'settings.tts.speech_volume': - propertyName = AbstractTts.VOLUME; + propertyName = ttsTypes.TtsSettings.VOLUME; msg = 'announce_volume'; break; default: @@ -906,7 +905,8 @@ Math.round(this.propertyToPercentage(propertyName) * 100); const announcement = Msgs.getMsg(msg, [valueAsPercent]); ChromeVox.tts.speak( - announcement, QueueMode.FLUSH, AbstractTts.PERSONALITY_ANNOTATION); + announcement, ttsTypes.QueueMode.FLUSH, + ttsTypes.Personality.ANNOTATION); }); } @@ -924,18 +924,19 @@ this.updateVoice_('', () => { // Ensure this announcement doesn't get cut off by speech triggered by // updateFromPrefs_(). - // Copy properties from AbstractTts.PERSONALITY_ANNOTATION and add the + // Copy properties from ttsTypes.Personality.ANNOTATION and add the // doNotInterrupt property. const speechProperties = {}; - const sourceProperties = AbstractTts.PERSONALITY_ANNOTATION || {}; + const sourceProperties = ttsTypes.Personality.ANNOTATION || {}; for (const [key, value] of Object.entries(sourceProperties)) { speechProperties[key] = value; } speechProperties['doNotInterrupt'] = true; ChromeVox.tts.speak( - Msgs.getMsg('announce_tts_default_settings'), QueueMode.FLUSH, - new TtsSpeechProperties(speechProperties)); + Msgs.getMsg('announce_tts_default_settings'), + ttsTypes.QueueMode.FLUSH, + new ttsTypes.TtsSpeechProperties(speechProperties)); }); }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/abstract_tts.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/abstract_tts.js index 0f534bc..4a1cb82 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/abstract_tts.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/abstract_tts.js
@@ -9,7 +9,7 @@ import {Msgs} from './msgs.js'; import {TtsInterface} from './tts_interface.js'; -import {QueueMode, TtsSpeechProperties} from './tts_types.js'; +import * as ttsTypes from './tts_types.js'; /** * Creates a new instance. @@ -61,7 +61,7 @@ // Create an expression that matches all words in the substitution // dictionary. const symbols = []; - for (const symbol in AbstractTts.SUBSTITUTION_DICTIONARY) { + for (const symbol in ttsTypes.SubstitutionDictionary) { symbols.push(symbol); } const expr = '(' + symbols.join('|') + ')'; @@ -71,8 +71,8 @@ /** * @param {string} textString - * @param {QueueMode} queueMode - * @param {TtsSpeechProperties=} properties + * @param {ttsTypes.QueueMode} queueMode + * @param {ttsTypes.TtsSpeechProperties=} properties * @override */ speak(textString, queueMode, properties) { @@ -130,7 +130,7 @@ } } if (properties) { - const tts = AbstractTts; + const tts = ttsTypes.TtsSettings; if (typeof (properties[tts.VOLUME]) === 'number') { mergedProperties[tts.VOLUME] = properties[tts.VOLUME]; } @@ -195,10 +195,10 @@ if (localStorage['capitalStrategy'] === 'increasePitch') { // Closure doesn't allow the use of for..in or [] with structs, so // convert to a pure JSON object. - const PERSONALITY_CAPITAL = AbstractTts.PERSONALITY_CAPITAL.toJSON(); - for (const prop in PERSONALITY_CAPITAL) { + const CAPITAL = ttsTypes.Personality.CAPITAL.toJSON(); + for (const prop in CAPITAL) { if (properties[prop] === undefined) { - properties[prop] = PERSONALITY_CAPITAL[prop]; + properties[prop] = CAPITAL[prop]; } } } else if (localStorage['capitalStrategy'] === 'announceCapitals') { @@ -226,14 +226,14 @@ // simultaneously. text = text.replace( AbstractTts.substitutionDictionaryRegexp_, function(symbol) { - return ' ' + AbstractTts.SUBSTITUTION_DICTIONARY[symbol] + ' '; + return ' ' + ttsTypes.SubstitutionDictionary[symbol] + ' '; }); // Handle single characters that we want to make sure we pronounce. if (text.length === 1) { - return AbstractTts.CHARACTER_DICTIONARY[text] ? + return ttsTypes.CharacterDictionary[text] ? (new goog.i18n.MessageFormat( - Msgs.getMsg(AbstractTts.CHARACTER_DICTIONARY[text]))) + Msgs.getMsg(ttsTypes.CharacterDictionary[text]))) .format({'COUNT': 1}) : text.toUpperCase(); } @@ -256,7 +256,7 @@ const count = match.length; return ' ' + (new goog.i18n.MessageFormat( - Msgs.getMsg(AbstractTts.CHARACTER_DICTIONARY[match[0]]))) + Msgs.getMsg(ttsTypes.CharacterDictionary[match[0]]))) .format({'COUNT': count}) + ' '; } @@ -281,7 +281,6 @@ } } - /** * Default TTS properties for this TTS engine. * @type {Object} @@ -289,210 +288,6 @@ */ AbstractTts.prototype.ttsProperties; - -/** TTS rate property. @type {string} */ -AbstractTts.RATE = 'rate'; -/** TTS pitch property. @type {string} */ -AbstractTts.PITCH = 'pitch'; -/** TTS volume property. @type {string} */ -AbstractTts.VOLUME = 'volume'; -/** TTS language property. @type {string} */ -AbstractTts.LANG = 'lang'; - -/** TTS relative rate property. @type {string} */ -AbstractTts.RELATIVE_RATE = 'relativeRate'; -/** TTS relative pitch property. @type {string} */ -AbstractTts.RELATIVE_PITCH = 'relativePitch'; -/** TTS relative volume property. @type {string} */ -AbstractTts.RELATIVE_VOLUME = 'relativeVolume'; - -/** TTS color property (for the lens display). @type {string} */ -AbstractTts.COLOR = 'color'; -/** TTS CSS font-weight property (for the lens display). @type {string} */ -AbstractTts.FONT_WEIGHT = 'fontWeight'; - -/** TTS punctuation-echo property. @type {string} */ -AbstractTts.PUNCTUATION_ECHO = 'punctuationEcho'; - -/** - * List of punctuation echoes that the user can cycle through. - * @type {!Array<{name:(string), - * msg:(string), - * regexp:(RegExp), - * clear:(boolean)}>} - */ -AbstractTts.PUNCTUATION_ECHOES = [ - // Punctuation echoed for the 'none' option. - { - name: 'none', - msg: 'no_punctuation', - regexp: /[-$#"()*;:<>\n\\\/+='~`@_]/g, - clear: true, - }, - - // Punctuation echoed for the 'some' option. - { - name: 'some', - msg: 'some_punctuation', - regexp: /[$#"*<>\\\/\{\}+=~`%\u2022\u25e6\u25a0]/g, - clear: false, - }, - - // Punctuation echoed for the 'all' option. - { - name: 'all', - msg: 'all_punctuation', - regexp: /[-$#"()*;:<>\n\\\/\{\}\[\]+='~`!@_.,?%\u2022\u25e6\u25a0]/g, - clear: false, - }, -]; - -/** TTS pause property. @type {string} */ -AbstractTts.PAUSE = 'pause'; - -/** - * TTS personality for annotations - text spoken by ChromeVox that - * elaborates on a user interface element but isn't displayed on-screen. - * @type {!TtsSpeechProperties} - */ -AbstractTts.PERSONALITY_ANNOTATION = new TtsSpeechProperties({ - 'relativePitch': -0.25, - // TODO:(rshearer) Added this color change for I/O presentation. - 'color': 'yellow', - 'punctuationEcho': 'none', -}); - - -/** - * TTS personality for announcements - text spoken by ChromeVox that - * isn't tied to any user interface elements. - * @type {!TtsSpeechProperties} - */ -AbstractTts.PERSONALITY_ANNOUNCEMENT = new TtsSpeechProperties({ - 'punctuationEcho': 'none', -}); - -/** - * TTS personality for alerts from the system, such as battery level - * warnings. - * @type {!TtsSpeechProperties} - */ -AbstractTts.PERSONALITY_SYSTEM_ALERT = new TtsSpeechProperties({ - 'punctuationEcho': 'none', - 'doNotInterrupt': true, -}); - -/** - * TTS personality for an aside - text in parentheses. - * @type {!TtsSpeechProperties} - */ -AbstractTts.PERSONALITY_ASIDE = new TtsSpeechProperties({ - 'relativePitch': -0.1, - 'color': '#669', -}); - -/** - * TTS personality for capital letters. - * @type {!TtsSpeechProperties} - */ -AbstractTts.PERSONALITY_CAPITAL = new TtsSpeechProperties({ - 'relativePitch': 0.2, -}); - -/** - * TTS personality for deleted text. - * @type {!TtsSpeechProperties} - */ -AbstractTts.PERSONALITY_DELETED = new TtsSpeechProperties({ - 'punctuationEcho': 'none', - 'relativePitch': -0.6, -}); - -/** - * TTS personality for quoted text. - * @type {!TtsSpeechProperties} - */ -AbstractTts.PERSONALITY_QUOTE = new TtsSpeechProperties({ - 'relativePitch': 0.1, - 'color': '#b6b', - 'fontWeight': 'bold', -}); - -/** - * TTS personality for strong or bold text. - * @type {!TtsSpeechProperties} - */ -AbstractTts.PERSONALITY_STRONG = new TtsSpeechProperties({ - 'relativePitch': 0.1, - 'color': '#b66', - 'fontWeight': 'bold', -}); - -/** - * TTS personality for emphasis or italicized text. - * @type {!TtsSpeechProperties} - */ -AbstractTts.PERSONALITY_EMPHASIS = new TtsSpeechProperties({ - 'relativeVolume': 0.1, - 'relativeRate': -0.1, - 'color': '#6bb', - 'fontWeight': 'bold', -}); - -/** - * Flag indicating if the TTS is being debugged. - * @type {boolean} - */ -AbstractTts.DEBUG = true; - -/** - * Character dictionary. These symbols are replaced with their human readable - * equivalents. This replacement only occurs for single character utterances. - * @type {Object<string>} - */ -AbstractTts.CHARACTER_DICTIONARY = { - ' ': 'space', - '\u00a0': 'space', - '`': 'backtick', - '~': 'tilde', - '!': 'exclamation', - '@': 'at', - '#': 'pound', - '$': 'dollar', - '%': 'percent', - '^': 'caret', - '&': 'ampersand', - '*': 'asterisk', - '(': 'open_paren', - ')': 'close_paren', - '-': 'dash', - '_': 'underscore', - '=': 'equals', - '+': 'plus', - '[': 'left_bracket', - ']': 'right_bracket', - '{': 'left_brace', - '}': 'right_brace', - '|': 'pipe', - ';': 'semicolon', - ':': 'colon', - ',': 'comma', - '.': 'dot', - '<': 'less_than', - '>': 'greater_than', - '/': 'slash', - '?': 'question_mark', - '"': 'quote', - '\'': 'apostrophe', - '\t': 'tab', - '\r': 'return', - '\n': 'new_line', - '\\': 'backslash', - '\u2022': 'bullet', - '\u25e6': 'white_bullet', - '\u25a0': 'square_bullet', -}; - /** * Pronunciation dictionary regexp. * @private {RegExp} @@ -500,58 +295,6 @@ AbstractTts.pronunciationDictionaryRegexp_; /** - * Substitution dictionary. These symbols or patterns are ALWAYS substituted - * whenever they occur, so this should be reserved only for unicode characters - * and characters that never have any different meaning in context. - * - * For example, do not include '$' here because $2 should be read as - * "two dollars". - * @type {Object<string>} - */ -AbstractTts.SUBSTITUTION_DICTIONARY = { - '://': 'colon slash slash', - '\u00bc': 'one fourth', - '\u00bd': 'one half', - '\u2190': 'left arrow', - '\u2191': 'up arrow', - '\u2192': 'right arrow', - '\u2193': 'down arrow', - '\u21d0': 'left double arrow', - '\u21d1': 'up double arrow', - '\u21d2': 'right double arrow', - '\u21d3': 'down double arrow', - '\u21e6': 'left arrow', - '\u21e7': 'up arrow', - '\u21e8': 'right arrow', - '\u21e9': 'down arrow', - '\u2303': 'control', - '\u2318': 'command', - '\u2325': 'option', - '\u25b2': 'up triangle', - '\u25b3': 'up triangle', - '\u25b4': 'up triangle', - '\u25b5': 'up triangle', - '\u25b6': 'right triangle', - '\u25b7': 'right triangle', - '\u25b8': 'right triangle', - '\u25b9': 'right triangle', - '\u25ba': 'right pointer', - '\u25bb': 'right pointer', - '\u25bc': 'down triangle', - '\u25bd': 'down triangle', - '\u25be': 'down triangle', - '\u25bf': 'down triangle', - '\u25c0': 'left triangle', - '\u25c1': 'left triangle', - '\u25c2': 'left triangle', - '\u25c3': 'left triangle', - '\u25c4': 'left pointer', - '\u25c5': 'left pointer', - '\uf8ff': 'apple', - '£': 'pound sterling', -}; - -/** * Substitution dictionary regexp. * @private {RegExp} */ @@ -564,9 +307,6 @@ AbstractTts.repetitionRegexp_ = /([-\/\\|!@#$%^&*\(\)=_+\[\]\{\}.?;'":<>\u2022\u25e6\u25a0])\1{2,}/g; -/** TTS phonetic-characters property. @type {string} */ -AbstractTts.PHONETIC_CHARACTERS = 'phoneticCharacters'; - /** * Regexp filter for negative dollar and pound amounts. * @private {RegExp}
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/background_bridge.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/background_bridge.js index 98ca8c813..a4be0333 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/background_bridge.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/background_bridge.js
@@ -351,7 +351,7 @@ * Method that updates the punctuation echo level, and also persists setting * to local storage. * @param {number} punctuationEcho The index of the desired punctuation echo - * level in AbstractTts.PUNCTUATION_ECHOES. + * level in PunctuationEchoes. * @return {!Promise} */ async updatePunctuationEcho(punctuationEcho) {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/tts_types.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/tts_types.js index 19fc719..9787976 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/tts_types.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/tts_types.js
@@ -128,3 +128,223 @@ Object.assign(this, opt_initialValues); } } + +/** + * A collection of TTS personalities to differentiate text. + * @type {!Object<!TtsSpeechProperties>} + */ +export const Personality = { + // TTS personality for annotations - text spoken by ChromeVox that + // elaborates on a user interface element but isn't displayed on-screen. + ANNOTATION: new TtsSpeechProperties({ + 'relativePitch': -0.25, + // TODO:(rshearer) Added this color change for I/O presentation. + 'color': 'yellow', + 'punctuationEcho': 'none', + }), + + // TTS personality for announcements - text spoken by ChromeVox that + // isn't tied to any user interface elements. + ANNOUNCEMENT: new TtsSpeechProperties({ + 'punctuationEcho': 'none', + }), + + // TTS personality for an aside - text in parentheses. + ASIDE: new TtsSpeechProperties({ + 'relativePitch': -0.1, + 'color': '#669', + }), + + // TTS personality for capital letters. + CAPITAL: new TtsSpeechProperties({ + 'relativePitch': 0.2, + }), + + // TTS personality for deleted text. + DELETED: new TtsSpeechProperties({ + 'punctuationEcho': 'none', + 'relativePitch': -0.6, + }), + + // TTS personality for emphasis or italicized text. + EMPHASIS: new TtsSpeechProperties({ + 'relativeVolume': 0.1, + 'relativeRate': -0.1, + 'color': '#6bb', + 'fontWeight': 'bold', + }), + + // TTS personality for quoted text. + QUOTE: new TtsSpeechProperties({ + 'relativePitch': 0.1, + 'color': '#b6b', + 'fontWeight': 'bold', + }), + + // TTS personality for strong or bold text. + STRONG: new TtsSpeechProperties({ + 'relativePitch': 0.1, + 'color': '#b66', + 'fontWeight': 'bold', + }), + + // TTS personality for alerts from the system, such as battery level + // warnings. + SYSTEM_ALERT: new TtsSpeechProperties({ + 'punctuationEcho': 'none', + 'doNotInterrupt': true, + }), +}; + +/** + * Various TTS-related settings keys. + * @enum {string} + */ +export const TtsSettings = { + // Color is for the lens display. + COLOR: 'color', + FONT_WEIGHT: 'fontWeight', + LANG: 'lang', + PAUSE: 'pause', + PHONETIC_CHARACTERS: 'phoneticCharacters', + PITCH: 'pitch', + PUNCTUATION_ECHO: 'punctuationEcho', + RATE: 'rate', + RELATIVE_PITCH: 'relativePitch', + RELATIVE_RATE: 'relativeRate', + RELATIVE_VOLUME: 'relativeVolume', + VOLUME: 'volume', +}; + +/** + * List of punctuation echoes that the user can cycle through. + * @type {!Array<{name:(string), + * msg:(string), + * regexp:(RegExp), + * clear:(boolean)}>} + */ +export const PunctuationEchoes = [ + // Punctuation echoed for the 'none' option. + { + name: 'none', + msg: 'no_punctuation', + regexp: /[-$#"()*;:<>\n\\\/+='~`@_]/g, + clear: true, + }, + + // Punctuation echoed for the 'some' option. + { + name: 'some', + msg: 'some_punctuation', + regexp: /[$#"*<>\\\/\{\}+=~`%\u2022\u25e6\u25a0]/g, + clear: false, + }, + + // Punctuation echoed for the 'all' option. + { + name: 'all', + msg: 'all_punctuation', + regexp: /[-$#"()*;:<>\n\\\/\{\}\[\]+='~`!@_.,?%\u2022\u25e6\u25a0]/g, + clear: false, + }, +]; + +/** + * Character dictionary. These symbols are replaced with their human readable + * equivalents. This replacement only occurs for single character utterances. + * @type {Object<string>} + */ +export const CharacterDictionary = { + ' ': 'space', + '\u00a0': 'space', + '`': 'backtick', + '~': 'tilde', + '!': 'exclamation', + '@': 'at', + '#': 'pound', + '$': 'dollar', + '%': 'percent', + '^': 'caret', + '&': 'ampersand', + '*': 'asterisk', + '(': 'open_paren', + ')': 'close_paren', + '-': 'dash', + '_': 'underscore', + '=': 'equals', + '+': 'plus', + '[': 'left_bracket', + ']': 'right_bracket', + '{': 'left_brace', + '}': 'right_brace', + '|': 'pipe', + ';': 'semicolon', + ':': 'colon', + ',': 'comma', + '.': 'dot', + '<': 'less_than', + '>': 'greater_than', + '/': 'slash', + '?': 'question_mark', + '"': 'quote', + '\'': 'apostrophe', + '\t': 'tab', + '\r': 'return', + '\n': 'new_line', + '\\': 'backslash', + '\u2022': 'bullet', + '\u25e6': 'white_bullet', + '\u25a0': 'square_bullet', +}; + +/** + * Substitution dictionary. These symbols or patterns are ALWAYS substituted + * whenever they occur, so this should be reserved only for unicode characters + * and characters that never have any different meaning in context. + * + * For example, do not include '$' here because $2 should be read as + * "two dollars". + * @type {Object<string>} + */ +export const SubstitutionDictionary = { + '://': 'colon slash slash', + '\u00bc': 'one fourth', + '\u00bd': 'one half', + '\u2190': 'left arrow', + '\u2191': 'up arrow', + '\u2192': 'right arrow', + '\u2193': 'down arrow', + '\u21d0': 'left double arrow', + '\u21d1': 'up double arrow', + '\u21d2': 'right double arrow', + '\u21d3': 'down double arrow', + '\u21e6': 'left arrow', + '\u21e7': 'up arrow', + '\u21e8': 'right arrow', + '\u21e9': 'down arrow', + '\u2303': 'control', + '\u2318': 'command', + '\u2325': 'option', + '\u25b2': 'up triangle', + '\u25b3': 'up triangle', + '\u25b4': 'up triangle', + '\u25b5': 'up triangle', + '\u25b6': 'right triangle', + '\u25b7': 'right triangle', + '\u25b8': 'right triangle', + '\u25b9': 'right triangle', + '\u25ba': 'right pointer', + '\u25bb': 'right pointer', + '\u25bc': 'down triangle', + '\u25bd': 'down triangle', + '\u25be': 'down triangle', + '\u25bf': 'down triangle', + '\u25c0': 'left triangle', + '\u25c1': 'left triangle', + '\u25c2': 'left triangle', + '\u25c3': 'left triangle', + '\u25c4': 'left pointer', + '\u25c5': 'left pointer', + '\uf8ff': 'apple', + '£': 'pound sterling', +};
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.js b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.js index 188f0a3..9126157 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.js
@@ -6,12 +6,12 @@ * @fileoverview ChromeVox options page. */ import {constants} from '../../common/constants.js'; -import {AbstractTts} from '../common/abstract_tts.js'; import {BackgroundBridge} from '../common/background_bridge.js'; import {BrailleTable} from '../common/braille/braille_table.js'; import {ExtensionBridge} from '../common/extension_bridge.js'; import {Msgs} from '../common/msgs.js'; import {PanelCommand, PanelCommandType} from '../common/panel_command.js'; +import {PunctuationEchoes, TtsSettings} from '../common/tts_types.js'; import {BluetoothBrailleDisplayUI} from './bluetooth_braille_display_ui.js'; @@ -96,10 +96,9 @@ } } - if (localStorage[AbstractTts.PUNCTUATION_ECHO]) { + if (localStorage[TtsSettings.PUNCTUATION_ECHO]) { const currentPunctuationEcho = - AbstractTts - .PUNCTUATION_ECHOES[localStorage[AbstractTts.PUNCTUATION_ECHO]]; + PunctuationEchoes[localStorage[TtsSettings.PUNCTUATION_ECHO]]; for (let i = 0, opt; opt = $('punctuationEcho').options[i]; ++i) { if (opt.id === currentPunctuationEcho.name) { opt.setAttribute('selected', ''); @@ -468,7 +467,7 @@ OptionsPage.setEventStreamFilter(target.name, target.checked); } else if (target.id === 'punctuationEcho') { const selectedPunctuationEcho = target.options[target.selectedIndex].id; - const punctuationEcho = AbstractTts.PUNCTUATION_ECHOES.findIndex( + const punctuationEcho = PunctuationEchoes.findIndex( echo => echo.name === selectedPunctuationEcho); BackgroundBridge.TtsBackground.updatePunctuationEcho(punctuationEcho); } else if (target.classList.contains('pref')) {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options_test.js index 3a9235a..f7ba5d9 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options_test.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options_test.js
@@ -24,7 +24,7 @@ await importModule( 'CommandHandlerInterface', '/chromevox/background/command_handler_interface.js'); - await importModule('AbstractTts', '/chromevox/common/abstract_tts.js'); + await importModule('TtsSettings', '/chromevox/common/tts_types.js'); await importModule('EventGenerator', '/common/event_generator.js'); await importModule('KeyCode', '/common/key_code.js'); } @@ -108,7 +108,7 @@ .call(() => { assertEquals( PUNCTUATION_ECHO_NONE, - localStorage[AbstractTts.PUNCTUATION_ECHO]); + localStorage[TtsSettings.PUNCTUATION_ECHO]); }) .call(press(KeyCode.DOWN)) @@ -118,7 +118,7 @@ .call(() => { assertEquals( PUNCTUATION_ECHO_SOME, - localStorage[AbstractTts.PUNCTUATION_ECHO]); + localStorage[TtsSettings.PUNCTUATION_ECHO]); }) .call(press(KeyCode.DOWN)) @@ -126,7 +126,7 @@ .call(() => { assertEquals( PUNCTUATION_ECHO_ALL, - localStorage[AbstractTts.PUNCTUATION_ECHO]); + localStorage[TtsSettings.PUNCTUATION_ECHO]); }); await mockFeedback.replay();
diff --git a/chrome/browser/resources/chromeos/login/BUILD.gn b/chrome/browser/resources/chromeos/login/BUILD.gn index cda0a5fb..b3f7991 100644 --- a/chrome/browser/resources/chromeos/login/BUILD.gn +++ b/chrome/browser/resources/chromeos/login/BUILD.gn
@@ -221,11 +221,9 @@ "screens/common/guest_tos.m.js", "screens/common/hw_data_collection.m.js", "screens/common/recommend_apps.m.js", - "screens/common/signin_fatal_error.m.js", "screens/common/smart_privacy_protection.m.js", "screens/common/sync_consent.m.js", "screens/oobe/enterprise_enrollment.m.js", - "screens/oobe/packaged_license.m.js", "screens/oobe/quick_start.m.js", # Special files. @@ -287,6 +285,7 @@ "screens/common/parental_handoff.js", "screens/common/pin_setup.js", "screens/common/saml_confirm_password.js", + "screens/common/signin_fatal_error.js", "screens/common/theme_selection.js", "screens/common/tpm_error.js", "screens/common/user_creation.js", @@ -307,6 +306,7 @@ "screens/oobe/enable_debugging.js", "screens/oobe/hid_detection.js", "screens/oobe/oobe_network.js", + "screens/oobe/packaged_license.js", "screens/oobe/update.js", "screens/oobe/welcome.js", "screens/oobe/welcome_dialog.js",
diff --git a/chrome/browser/resources/chromeos/login/screens.js b/chrome/browser/resources/chromeos/login/screens.js index 3217e13..ee765ce1 100644 --- a/chrome/browser/resources/chromeos/login/screens.js +++ b/chrome/browser/resources/chromeos/login/screens.js
@@ -35,7 +35,7 @@ import './screens/common/pin_setup.js'; import './screens/common/recommend_apps.m.js'; import './screens/common/saml_confirm_password.js'; -import './screens/common/signin_fatal_error.m.js'; +import './screens/common/signin_fatal_error.js'; import './screens/common/smart_privacy_protection.m.js'; import './screens/common/sync_consent.m.js'; import './screens/common/theme_selection.js'; @@ -60,7 +60,7 @@ import './screens/oobe/enterprise_enrollment.m.js'; import './screens/oobe/hid_detection.js'; import './screens/oobe/oobe_network.js'; -import './screens/oobe/packaged_license.m.js'; +import './screens/oobe/packaged_license.js'; import './screens/oobe/quick_start.m.js'; import './screens/oobe/update.js'; import './screens/oobe/welcome.js'; @@ -146,15 +146,15 @@ * List of screens that are used during the `oobe` flow only. */ export const oobeScreensList = [ - {tag: 'auto-enrollment-check-element', id: 'auto-enrollment-check'}, - {tag: 'demo-preferences-element', id: 'demo-preferences'}, - {tag: 'demo-setup-element', id: 'demo-setup'}, - {tag: 'enable-debugging-element', id: 'debugging'}, - {tag: 'enterprise-enrollment-element', id: 'enterprise-enrollment'}, - {tag: 'hid-detection-element', id: 'hid-detection'}, - {tag: 'oobe-network-element', id: 'network-selection'}, - {tag: 'packaged-license-element', id: 'packaged-license'}, - {tag: 'quick-start-element', id: 'quick-start'}, - {tag: 'update-element', id: 'oobe-update'}, - {tag: 'oobe-welcome-element', id: 'connect'}, -]; + {tag: 'auto-enrollment-check-element', id: 'auto-enrollment-check'}, + {tag: 'demo-preferences-element', id: 'demo-preferences'}, + {tag: 'demo-setup-element', id: 'demo-setup'}, + {tag: 'enable-debugging-element', id: 'debugging'}, + {tag: 'enterprise-enrollment-element', id: 'enterprise-enrollment'}, + {tag: 'hid-detection-element', id: 'hid-detection'}, + {tag: 'oobe-network-element', id: 'network-selection'}, + {tag: 'packaged-license-element', id: 'packaged-license'}, + {tag: 'quick-start-element', id: 'quick-start'}, + {tag: 'update-element', id: 'oobe-update'}, + {tag: 'oobe-welcome-element', id: 'connect'}, +]; \ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn b/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn index c1aef40..994bd2246 100644 --- a/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn +++ b/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
@@ -18,7 +18,6 @@ ":guest_tos_module", ":hw_data_collection_module", ":recommend_apps_module", - ":signin_fatal_error_module", ":smart_privacy_protection_module", ":sync_consent_module", ] @@ -70,7 +69,7 @@ ":pin_setup", ":recommend_apps.m", ":saml_confirm_password", - ":signin_fatal_error.m", + ":signin_fatal_error", ":smart_privacy_protection.m", ":sync_consent.m", ":theme_selection", @@ -551,8 +550,8 @@ extra_deps = [ ":web_components" ] } -js_library("signin_fatal_error.m") { - sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/common/signin_fatal_error.m.js" ] +js_library("signin_fatal_error") { + sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/common/signin_fatal_error.js" ] deps = [ "../../components:display_manager_types.m", "../../components:oobe_types.m", @@ -563,7 +562,7 @@ "../../components/dialogs:oobe_adaptive_dialog", "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", ] - extra_deps = [ ":signin_fatal_error_module" ] + extra_deps = [ ":web_components" ] } js_library("smart_privacy_protection.m") { @@ -711,15 +710,6 @@ migrated_imports = oobe_migrated_imports } -polymer_modulizer("signin_fatal_error") { - js_file = "signin_fatal_error.js" - html_file = "signin_fatal_error.html" - html_type = "dom-module" - auto_imports = oobe_auto_imports - migrated_imports = oobe_migrated_imports - namespace_rewrites = oobe_namespace_rewrites -} - polymer_modulizer("smart_privacy_protection") { js_file = "smart_privacy_protection.js" html_file = "smart_privacy_protection.html" @@ -764,6 +754,7 @@ "parental_handoff.js", "pin_setup.js", "saml_confirm_password.js", + "signin_fatal_error.js", "theme_selection.js", "tpm_error.js", "user_creation.js",
diff --git a/chrome/browser/resources/chromeos/login/screens/common/signin_fatal_error.html b/chrome/browser/resources/chromeos/login/screens/common/signin_fatal_error.html index 7a15bfcd..20e4f51 100644 --- a/chrome/browser/resources/chromeos/login/screens/common/signin_fatal_error.html +++ b/chrome/browser/resources/chromeos/login/screens/common/signin_fatal_error.html
@@ -4,46 +4,25 @@ found in the LICENSE file. --> -<link rel="import" href="chrome://resources/html/polymer.html"> - -<link rel="import" href="chrome://resources/html/action_link.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> - -<link rel="import" href="../../components/display_manager_types.html"> -<link rel="import" href="../../components/oobe_icons.html"> -<link rel="import" href="../../components/oobe_types.html"> -<link rel="import" href="../../components/behaviors/login_screen_behavior.html"> -<link rel="import" href="../../components/behaviors/oobe_dialog_host_behavior.html"> -<link rel="import" href="../../components/behaviors/oobe_i18n_behavior.html"> -<link rel="import" href="../../components/buttons/oobe_text_button.html"> -<link rel="import" href="../../components/common_styles/common_styles.html"> -<link rel="import" href="../../components/common_styles/oobe_dialog_host_styles.html"> -<link rel="import" href="../../components/dialogs/oobe_adaptive_dialog.html"> - -<dom-module id="signin-fatal-error-element"> - <template> - <style include="oobe-dialog-host-styles"> - </style> - <oobe-adaptive-dialog id="signinFatalErrorDialog" role="dialog"> - <iron-icon slot="icon" icon="oobe-32:alert"></iron-icon> - <h1 slot="title"> - [[i18nDynamic(locale, 'errorGenericFatalErrorTitle')]] - </h1> - <p id="subtitle" slot="subtitle"> - [[errorSubtitle_]] - </p> - <div slot="subtitle" hidden="[[!keyboardHint_]]">[[keyboardHint_]]</div> - <div slot="subtitle" hidden="[[!details_]]">[[details_]]</div> - <a slot="subtitle" on-click="onHelpLinkClicked_" - hidden="[[!helpLinkText_]]" class="oobe-local-link" is="action-link"> - [[helpLinkText_]] - </a> - <div slot="bottom-buttons"> - <oobe-text-button id="actionButton" inverse class="focus-on-show" - text-key="[[computeButtonKey_(errorState_)]]" on-tap="onClick_"> - </oobe-text-button> - </div> - </oobe-adaptive-dialog> - </template> - <script src="signin_fatal_error.js"></script> -</dom-module> +<style include="oobe-dialog-host-styles"> +</style> +<oobe-adaptive-dialog id="signinFatalErrorDialog" role="dialog"> + <iron-icon slot="icon" icon="oobe-32:alert"></iron-icon> + <h1 slot="title"> + [[i18nDynamic(locale, 'errorGenericFatalErrorTitle')]] + </h1> + <p id="subtitle" slot="subtitle"> + [[errorSubtitle_]] + </p> + <div slot="subtitle" hidden="[[!keyboardHint_]]">[[keyboardHint_]]</div> + <div slot="subtitle" hidden="[[!details_]]">[[details_]]</div> + <a slot="subtitle" on-click="onHelpLinkClicked_" + hidden="[[!helpLinkText_]]" class="oobe-local-link" is="action-link"> + [[helpLinkText_]] + </a> + <div slot="bottom-buttons"> + <oobe-text-button id="actionButton" inverse class="focus-on-show" + text-key="[[computeButtonKey_(errorState_)]]" on-tap="onClick_"> + </oobe-text-button> + </div> +</oobe-adaptive-dialog>
diff --git a/chrome/browser/resources/chromeos/login/screens/common/signin_fatal_error.js b/chrome/browser/resources/chromeos/login/screens/common/signin_fatal_error.js index 8b650fb..c8923f8 100644 --- a/chrome/browser/resources/chromeos/login/screens/common/signin_fatal_error.js +++ b/chrome/browser/resources/chromeos/login/screens/common/signin_fatal_error.js
@@ -6,7 +6,22 @@ * @fileoverview Polymer element for signin fatal error. */ -/* #js_imports_placeholder */ +import '//resources/js/action_link.js'; +import '//resources/polymer/v3_0/iron-icon/iron-icon.js'; +import '../../components/oobe_icons.m.js'; +import '../../components/common_styles/common_styles.m.js'; +import '../../components/common_styles/oobe_dialog_host_styles.m.js'; +import '../../components/dialogs/oobe_adaptive_dialog.js'; + +import {html, mixinBehaviors, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; + +import {LoginScreenBehavior, LoginScreenBehaviorInterface} from '../../components/behaviors/login_screen_behavior.m.js'; +import {OobeDialogHostBehavior} from '../../components/behaviors/oobe_dialog_host_behavior.m.js'; +import {OobeI18nBehavior, OobeI18nBehaviorInterface} from '../../components/behaviors/oobe_i18n_behavior.m.js'; +import {OobeTextButton} from '../../components/buttons/oobe_text_button.js'; +import {OOBE_UI_STATE, SCREEN_GAIA_SIGNIN} from '../../components/display_manager_types.m.js'; +import {OobeTypes} from '../../components/oobe_types.m.js'; + /** * @constructor @@ -14,9 +29,9 @@ * @implements {LoginScreenBehaviorInterface} * @implements {OobeI18nBehaviorInterface} */ -const SigninFatalErrorBase = Polymer.mixinBehaviors( +const SigninFatalErrorBase = mixinBehaviors( [OobeI18nBehavior, OobeDialogHostBehavior, LoginScreenBehavior], - Polymer.Element); + PolymerElement); /** * @typedef {{ @@ -33,7 +48,10 @@ return 'signin-fatal-error-element'; } - /* #html_template_placeholder */ + static get template() { + return html`{__html_template__}`; + } + static get properties() { return { @@ -52,6 +70,7 @@ */ errorState_: { type: Number, + value: 0, }, /** @@ -60,6 +79,7 @@ */ params_: { type: Object, + value: {}, }, keyboardHint_: { @@ -76,11 +96,6 @@ }; } - constructor() { - super(); - this.errorState_ = 0; - this.params_ = {}; - } ready() { super.ready();
diff --git a/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn b/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn index 2d8fca0..a244129 100644 --- a/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn +++ b/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn
@@ -11,7 +11,6 @@ group("polymer3_elements") { public_deps = [ ":enterprise_enrollment_module", - ":packaged_license_module", ":quick_start_module", ] } @@ -33,7 +32,7 @@ ":enterprise_enrollment.m", ":hid_detection", ":oobe_network", - ":packaged_license.m", + ":packaged_license", ":quick_start.m", ":update", ":welcome", @@ -158,8 +157,8 @@ extra_deps = [ ":web_components" ] } -js_library("packaged_license.m") { - sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/oobe/packaged_license.m.js" ] +js_library("packaged_license") { + sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/oobe/packaged_license.js" ] deps = [ "../../components/behaviors:login_screen_behavior.m", "../../components/behaviors:oobe_dialog_host_behavior.m", @@ -167,7 +166,7 @@ "../../components/dialogs:oobe_adaptive_dialog", "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", ] - extra_deps = [ ":packaged_license_module" ] + extra_deps = [ ":web_components" ] } js_library("update") { @@ -241,15 +240,6 @@ namespace_rewrites = oobe_namespace_rewrites } -polymer_modulizer("packaged_license") { - js_file = "packaged_license.js" - html_file = "packaged_license.html" - html_type = "dom-module" - auto_imports = oobe_auto_imports - migrated_imports = oobe_migrated_imports - namespace_rewrites = oobe_namespace_rewrites -} - html_to_js("web_components") { js_files = [ "auto_enrollment_check.js", @@ -258,6 +248,7 @@ "enable_debugging.js", "hid_detection.js", "oobe_network.js", + "packaged_license.js", "update.js", "welcome.js", "welcome_dialog.js",
diff --git a/chrome/browser/resources/chromeos/login/screens/oobe/packaged_license.html b/chrome/browser/resources/chromeos/login/screens/oobe/packaged_license.html index 3ae7692..d0d8e29 100644 --- a/chrome/browser/resources/chromeos/login/screens/oobe/packaged_license.html +++ b/chrome/browser/resources/chromeos/login/screens/oobe/packaged_license.html
@@ -4,50 +4,30 @@ found in the LICENSE file. --> -<link rel="import" href="chrome://resources/html/polymer.html"> - -<link rel="import" href="chrome://resources/cr_elements/cr_shared_vars.css.html"> -<link rel="import" href="chrome://resources/ash/common/i18n_behavior.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> - -<link rel="import" href="../../components/oobe_icons.html"> -<link rel="import" href="../../components/behaviors/login_screen_behavior.html"> -<link rel="import" href="../../components/behaviors/oobe_dialog_host_behavior.html"> -<link rel="import" href="../../components/behaviors/oobe_i18n_behavior.html"> -<link rel="import" href="../../components/buttons/oobe_text_button.html"> -<link rel="import" href="../../components/common_styles/common_styles.html"> -<link rel="import" href="../../components/common_styles/oobe_dialog_host_styles.html"> -<link rel="import" href="../../components/dialogs/oobe_adaptive_dialog.html"> - -<dom-module id="packaged-license-element"> - <template> - <style include="oobe-dialog-host-styles"></style> - <oobe-adaptive-dialog id="packagedLicenseDialog" - aria-label$="[[i18nDynamic(locale, 'oobePackagedLicenseTitle')]]" - role="dialog"> - <iron-icon slot="icon" icon="oobe-32:enterprise"></iron-icon> - <h1 slot="title"> - [[i18nDynamic(locale, 'oobePackagedLicenseTitle')]] - </h1> - <p slot="subtitle"> - [[i18nDynamic(locale, 'oobePackagedLicenseSubtitleP1')]] - </p> - <p slot="subtitle"> - [[i18nDynamic(locale, 'oobePackagedLicenseSubtitleP2')]] - </p> - <div slot="content" class="flex layout vertical center center-justified"> - <img src="../../images/enrollment_complete.svg" - class="oobe-illustration" aria-hidden="true"> - </div> - <div slot="bottom-buttons"> - <oobe-text-button id="dont-enroll-button" - text-key="oobePackagedLicenseDontEnroll" - on-click="onDontEnrollButtonPressed_"></oobe-text-button> - <oobe-text-button id="enroll-button" - text-key="oobePackagedLicenseEnroll" class="focus-on-show" - inverse on-click="onEnrollButtonPressed_"></oobe-text-button> - </div> - </oobe-adaptive-dialog> - </template> - <script src="packaged_license.js"></script> -</dom-module> +<style include="oobe-dialog-host-styles"></style> +<oobe-adaptive-dialog id="packagedLicenseDialog" + aria-label$="[[i18nDynamic(locale, 'oobePackagedLicenseTitle')]]" + role="dialog"> + <iron-icon slot="icon" icon="oobe-32:enterprise"></iron-icon> + <h1 slot="title"> + [[i18nDynamic(locale, 'oobePackagedLicenseTitle')]] + </h1> + <p slot="subtitle"> + [[i18nDynamic(locale, 'oobePackagedLicenseSubtitleP1')]] + </p> + <p slot="subtitle"> + [[i18nDynamic(locale, 'oobePackagedLicenseSubtitleP2')]] + </p> + <div slot="content" class="flex layout vertical center center-justified"> + <img src="../../images/enrollment_complete.svg" + class="oobe-illustration" aria-hidden="true"> + </div> + <div slot="bottom-buttons"> + <oobe-text-button id="dont-enroll-button" + text-key="oobePackagedLicenseDontEnroll" + on-click="onDontEnrollButtonPressed_"></oobe-text-button> + <oobe-text-button id="enroll-button" + text-key="oobePackagedLicenseEnroll" class="focus-on-show" + inverse on-click="onEnrollButtonPressed_"></oobe-text-button> + </div> +</oobe-adaptive-dialog>
diff --git a/chrome/browser/resources/chromeos/login/screens/oobe/packaged_license.js b/chrome/browser/resources/chromeos/login/screens/oobe/packaged_license.js index 45c73f4..20113e4e 100644 --- a/chrome/browser/resources/chromeos/login/screens/oobe/packaged_license.js +++ b/chrome/browser/resources/chromeos/login/screens/oobe/packaged_license.js
@@ -6,7 +6,20 @@ * @fileoverview Polymer element for Packaged License screen. */ -/* #js_imports_placeholder */ +import '//resources/cr_elements/cr_shared_vars.css.js'; +import '//resources/polymer/v3_0/iron-icon/iron-icon.js'; +import '../../components/oobe_icons.m.js'; +import '../../components/common_styles/common_styles.m.js'; +import '../../components/common_styles/oobe_dialog_host_styles.m.js'; + +import {afterNextRender, html, mixinBehaviors, Polymer, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; + +import {LoginScreenBehavior, LoginScreenBehaviorInterface} from '../../components/behaviors/login_screen_behavior.m.js'; +import {OobeDialogHostBehavior} from '../../components/behaviors/oobe_dialog_host_behavior.m.js'; +import {OobeI18nBehavior, OobeI18nBehaviorInterface} from '../../components/behaviors/oobe_i18n_behavior.m.js'; +import {OobeTextButton} from '../../components/buttons/oobe_text_button.js'; +import {OobeAdaptiveDialog} from '../../components/dialogs/oobe_adaptive_dialog.js'; + /** * @constructor @@ -14,9 +27,9 @@ * @implements {LoginScreenBehaviorInterface} * @implements {OobeI18nBehaviorInterface} */ -const PackagedLicenseScreenBase = Polymer.mixinBehaviors( +const PackagedLicenseScreenBase = mixinBehaviors( [OobeI18nBehavior, OobeDialogHostBehavior, LoginScreenBehavior], - Polymer.Element); + PolymerElement); /** * @typedef {{ @@ -25,21 +38,22 @@ */ PackagedLicenseScreenBase.$; +/** + * @polymer + */ class PackagedLicenseScreen extends PackagedLicenseScreenBase { static get is() { return 'packaged-license-element'; } - /* #html_template_placeholder */ + static get template() { + return html`{__html_template__}`; + } static get properties() { return {}; } - constructor() { - super(); - } - /** @override */ ready() { super.ready();
diff --git a/chrome/browser/resources/connectors_internals/BUILD.gn b/chrome/browser/resources/connectors_internals/BUILD.gn index cf9590e..db4820d 100644 --- a/chrome/browser/resources/connectors_internals/BUILD.gn +++ b/chrome/browser/resources/connectors_internals/BUILD.gn
@@ -14,9 +14,7 @@ "device_trust_connector.ts", ] mojo_files = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/connectors_internals/connectors_internals.mojom-webui.js" ] - mojo_files_deps = [ - "//chrome/browser/ui/webui/connectors_internals:mojo_bindings_webui_js", - ] + mojo_files_deps = [ "//chrome/browser/ui/webui/connectors_internals:mojo_bindings_js__generator" ] html_to_wrapper_template = "native"
diff --git a/chrome/browser/resources/discards/BUILD.gn b/chrome/browser/resources/discards/BUILD.gn index c1c57311..2ce4fba1 100644 --- a/chrome/browser/resources/discards/BUILD.gn +++ b/chrome/browser/resources/discards/BUILD.gn
@@ -80,8 +80,8 @@ copy("copy_mojo") { deps = [ - "//chrome/browser/resource_coordinator:mojo_bindings_webui_js", - "//chrome/browser/ui/webui/discards:mojo_bindings_webui_js", + "//chrome/browser/resource_coordinator:mojo_bindings_js__generator", + "//chrome/browser/ui/webui/discards:mojo_bindings_js__generator", ] sources = [ "$root_gen_dir/mojom-webui/chrome/browser/resource_coordinator/lifecycle_unit_state.mojom-webui.js",
diff --git a/chrome/browser/resources/downloads/BUILD.gn b/chrome/browser/resources/downloads/BUILD.gn index 6b102545..a806dad5 100644 --- a/chrome/browser/resources/downloads/BUILD.gn +++ b/chrome/browser/resources/downloads/BUILD.gn
@@ -34,7 +34,7 @@ icons_html_files = [ "icons.html" ] mojo_files_deps = - [ "//chrome/browser/ui/webui/downloads:mojo_bindings_webui_js" ] + [ "//chrome/browser/ui/webui/downloads:mojo_bindings_js__generator" ] mojo_files = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/downloads/downloads.mojom-webui.js" ] ts_composite = true
diff --git a/chrome/browser/resources/engagement/BUILD.gn b/chrome/browser/resources/engagement/BUILD.gn index d235373..520aabc 100644 --- a/chrome/browser/resources/engagement/BUILD.gn +++ b/chrome/browser/resources/engagement/BUILD.gn
@@ -5,7 +5,8 @@ import("//tools/typescript/ts_library.gni") copy("copy_src_and_mojo") { - deps = [ "//components/site_engagement/core/mojom:mojo_bindings_webui_js" ] + deps = + [ "//components/site_engagement/core/mojom:mojo_bindings_js__generator" ] sources = [ "$root_gen_dir/mojom-webui/components/site_engagement/core/mojom/site_engagement_details.mojom-webui.js", "site_engagement.ts",
diff --git a/chrome/browser/resources/feed_internals/BUILD.gn b/chrome/browser/resources/feed_internals/BUILD.gn index af0c468..23540308d 100644 --- a/chrome/browser/resources/feed_internals/BUILD.gn +++ b/chrome/browser/resources/feed_internals/BUILD.gn
@@ -17,7 +17,7 @@ non_web_component_files = [ "feed_internals.ts" ] mojo_files_deps = - [ "//chrome/browser/ui/webui/feed_internals:mojo_bindings_webui_js" ] + [ "//chrome/browser/ui/webui/feed_internals:mojo_bindings_js__generator" ] mojo_files = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/feed_internals/feed_internals.mojom-webui.js" ] ts_deps = [
diff --git a/chrome/browser/resources/image_editor/BUILD.gn b/chrome/browser/resources/image_editor/BUILD.gn index 208a4c8..f0d4664 100644 --- a/chrome/browser/resources/image_editor/BUILD.gn +++ b/chrome/browser/resources/image_editor/BUILD.gn
@@ -39,7 +39,8 @@ } copy("copy_mojo") { - deps = [ "//chrome/browser/ui/webui/image_editor:mojo_bindings_webui_js" ] + deps = + [ "//chrome/browser/ui/webui/image_editor:mojo_bindings_js__generator" ] sources = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/image_editor/image_editor.mojom-webui.js" ] outputs = [ "$preprocess_folder/{{source_file_part}}" ] }
diff --git a/chrome/browser/resources/internals/user_education/BUILD.gn b/chrome/browser/resources/internals/user_education/BUILD.gn index 01b3588..315c144 100644 --- a/chrome/browser/resources/internals/user_education/BUILD.gn +++ b/chrome/browser/resources/internals/user_education/BUILD.gn
@@ -28,9 +28,7 @@ } copy("copy_mojo") { - deps = [ - "//chrome/browser/ui/webui/internals/user_education:mojo_bindings_webui_js", - ] + deps = [ "//chrome/browser/ui/webui/internals/user_education:mojo_bindings_js__generator" ] sources = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/internals/user_education/user_education_internals.mojom-webui.js" ] outputs = [ "$preprocess_folder/{{source_file_part}}" ] }
diff --git a/chrome/browser/resources/omnibox/BUILD.gn b/chrome/browser/resources/omnibox/BUILD.gn index 285f7a6..2d545f21 100644 --- a/chrome/browser/resources/omnibox/BUILD.gn +++ b/chrome/browser/resources/omnibox/BUILD.gn
@@ -27,7 +27,7 @@ ] mojo_files_deps = - [ "//chrome/browser/ui/webui/omnibox:mojo_bindings_webui_js" ] + [ "//chrome/browser/ui/webui/omnibox:mojo_bindings_js__generator" ] mojo_files = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/omnibox/omnibox.mojom-webui.js" ] ts_deps = [
diff --git a/chrome/browser/resources/reset_password/BUILD.gn b/chrome/browser/resources/reset_password/BUILD.gn index c892db37..9e274c13 100644 --- a/chrome/browser/resources/reset_password/BUILD.gn +++ b/chrome/browser/resources/reset_password/BUILD.gn
@@ -10,7 +10,8 @@ # Copy mojo and src files to the same location so that they can be passed to # ts_library. copy("copy_src_and_mojo") { - deps = [ "//chrome/browser/ui/webui/reset_password:mojo_bindings_webui_js" ] + deps = + [ "//chrome/browser/ui/webui/reset_password:mojo_bindings_js__generator" ] sources = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/reset_password/reset_password.mojom-webui.js", "reset_password.ts",
diff --git a/chrome/browser/resources/segmentation_internals/BUILD.gn b/chrome/browser/resources/segmentation_internals/BUILD.gn index 2a382e0a..c4a86146 100644 --- a/chrome/browser/resources/segmentation_internals/BUILD.gn +++ b/chrome/browser/resources/segmentation_internals/BUILD.gn
@@ -11,9 +11,7 @@ "segmentation_internals.ts", "segmentation_internals_browser_proxy.ts", ] - mojo_files_deps = [ - "//chrome/browser/ui/webui/segmentation_internals:mojo_bindings_webui_js", - ] + mojo_files_deps = [ "//chrome/browser/ui/webui/segmentation_internals:mojo_bindings_js__generator" ] mojo_files = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/segmentation_internals/segmentation_internals.mojom-webui.js" ] ts_deps = [ "//ui/webui/resources:library" ] }
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn index f9898f6..b9aea33 100644 --- a/chrome/browser/resources/settings/chromeos/BUILD.gn +++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -332,7 +332,6 @@ ":global_scroll_target_behavior", ":icon", ":lazy_load", - ":main_page_behavior", ":metrics_recorder", ":os_page_visibility", ":os_route", @@ -377,15 +376,6 @@ ] } -js_library("main_page_behavior") { - deps = [ - "..:router", - "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", - "//ui/webui/resources/js:assert", - "//ui/webui/resources/js:util", - ] -} - js_library("metrics_recorder") { deps = [ "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings_webui_js",
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_page.ts b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_page.ts index efaaa6f7..088996f 100644 --- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_page.ts +++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_page.ts
@@ -63,11 +63,6 @@ static get properties() { return { - prefs: { - type: Object, - notify: true, - }, - focusConfig_: { type: Object, value() {
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.ts b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.ts index d26ec57..ac13eaa2 100644 --- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.ts +++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.ts
@@ -61,12 +61,6 @@ static get properties() { return { - /** Preferences state. */ - prefs: { - type: Object, - notify: true, - }, - showAddPortDialog_: { type: Boolean, value: false,
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_subpage.ts b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_subpage.ts index 4a421f4..0c2912e 100644 --- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_subpage.ts +++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_subpage.ts
@@ -68,12 +68,6 @@ static get properties() { return { - /** Preferences state. */ - prefs: { - type: Object, - notify: true, - }, - /** * Whether export / import UI should be displayed. */
diff --git a/chrome/browser/resources/settings/chromeos/device_page/pointers.ts b/chrome/browser/resources/settings/chromeos/device_page/pointers.ts index 858d499..d1291b7 100644 --- a/chrome/browser/resources/settings/chromeos/device_page/pointers.ts +++ b/chrome/browser/resources/settings/chromeos/device_page/pointers.ts
@@ -46,11 +46,6 @@ static get properties() { return { - prefs: { - type: Object, - notify: true, - }, - hasMouse: Boolean, hasPointingStick: Boolean,
diff --git a/chrome/browser/resources/settings/chromeos/main_page_behavior.js b/chrome/browser/resources/settings/chromeos/main_page_behavior.js deleted file mode 100644 index 2a3bfe3..0000000 --- a/chrome/browser/resources/settings/chromeos/main_page_behavior.js +++ /dev/null
@@ -1,379 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import {assert} from 'chrome://resources/js/assert.js'; -import {beforeNextRender} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; - -import {MinimumRoutes, Route, Router} from '../router.js'; - -import {ensureLazyLoaded} from './ensure_lazy_loaded.js'; - -/** - * @enum {string} - * A categorization of every possible Settings URL, necessary for implementing - * a finite state machine. - */ -export const RouteState = { - // Initial state before anything has loaded yet. - INITIAL: 'initial', - // A dialog that has a dedicated URL (e.g. /importData). - DIALOG: 'dialog', - // A section (basically a scroll position within the top level page, e.g, - // /appearance. - SECTION: 'section', - // A subpage, or sub-subpage e.g, /searchEngins. - SUBPAGE: 'subpage', - // The top level Settings page, '/'. - TOP_LEVEL: 'top-level', -}; - -/** - * @param {?Route=} route - * @return {!RouteState} - */ -function classifyRoute(route) { - if (!route) { - return RouteState.INITIAL; - } - const routes = - /** @type {!MinimumRoutes} */ (Router.getInstance().getRoutes()); - if (route === routes.BASIC || route === routes.ABOUT) { - return RouteState.TOP_LEVEL; - } - if (route.isSubpage()) { - return RouteState.SUBPAGE; - } - if (route.isNavigableDialog) { - return RouteState.DIALOG; - } - return RouteState.SECTION; -} - -/** - * Responds to route changes by expanding, collapsing, or scrolling to - * sections on the page. Expanded sections take up the full height of the - * container. At most one section should be expanded at any given time. - * @polymerBehavior - */ -export const MainPageBehavior = { - /** @type {?HTMLElement} */ - scroller: null, - - /** - * A map holding all valid state transitions. - * @private {!Map<!RouteState, !RouteState>} - */ - validTransitions_: (function() { - const allStates = new Set([ - RouteState.DIALOG, - RouteState.SECTION, - RouteState.SUBPAGE, - RouteState.TOP_LEVEL, - ]); - - return new Map([ - [RouteState.INITIAL, allStates], - [ - RouteState.DIALOG, - new Set([ - RouteState.SECTION, - RouteState.SUBPAGE, - RouteState.TOP_LEVEL, - ]), - ], - [RouteState.SECTION, allStates], - [RouteState.SUBPAGE, allStates], - [RouteState.TOP_LEVEL, allStates], - ]); - })(), - - /** @override */ - attached() { - this.scroller = this.domHost ? this.domHost.parentNode : document.body; - }, - - /** - * Method to be defined by users of MainPageBehavior. - * @param {!Route} route - * @return {boolean} Whether the given route is part of |this| page. - */ - containsRoute(route) { - return false; - }, - - /** - * @param {!Route} route - * @return {boolean} - * @private - */ - shouldExpandAdvanced_(route) { - const routes = - /** @type {!MinimumRoutes} */ (Router.getInstance().getRoutes()); - return (this.tagName === 'OS-SETTINGS-PAGE') && routes.ADVANCED && - routes.ADVANCED.contains(route); - }, - - /** - * Finds the settings section corresponding to the given route. If the - * section is lazily loaded it force-renders it. - * Note: If the section resides within "advanced" settings, a - * 'hide-container' event is fired (necessary to avoid flashing). Callers - * are responsible for firing a 'show-container' event. - * @param {!Route} route - * @return {!Promise<!HTMLElement>} - * @private - */ - ensureSectionForRoute_(route) { - const section = this.getSection(route.section); - if (section !== null) { - return Promise.resolve(section); - } - - // The function to use to wait for <dom-if>s to render. - const waitFn = beforeNextRender.bind(null, this); - - return new Promise(resolve => { - if (this.shouldExpandAdvanced_(route)) { - this.fire('hide-container'); - waitFn(() => { - this.$$('#advancedPageTemplate').get().then(() => { - resolve(this.getSection(route.section)); - }); - }); - } else { - waitFn(() => { - resolve(this.getSection(route.section)); - }); - } - }); - }, - - /** - * @param {!Route} route - * @private - */ - enterSubpage_(route) { - this.lastScrollTop_ = this.scroller.scrollTop; - this.scroller.scrollTop = 0; - this.classList.add('showing-subpage'); - this.fire('subpage-expand'); - - // Explicitly load the lazy_load module, since all subpages reside in - // the lazy loaded module. - ensureLazyLoaded(); - - this.ensureSectionForRoute_(route).then(section => { - section.classList.add('expanded'); - // Fire event used by a11y tests only. - this.fire('settings-section-expanded'); - - this.fire('show-container'); - }); - }, - - /** - * @param {!Route} oldRoute - * @return {!Promise<void>} - * @private - */ - enterMainPage_(oldRoute) { - const oldSection = this.getSection(oldRoute.section); - oldSection.classList.remove('expanded'); - this.classList.remove('showing-subpage'); - return new Promise((res, rej) => { - requestAnimationFrame(() => { - if (Router.getInstance().lastRouteChangeWasPopstate()) { - this.scroller.scrollTop = this.lastScrollTop_; - } - this.fire('showing-main-page'); - res(); - }); - }); - }, - - /** - * @param {!Route} route - * @private - */ - scrollToSection_(route) { - this.ensureSectionForRoute_(route).then(section => { - this.fire('showing-section', section); - this.fire('show-container'); - }); - }, - - /** - * Detects which state transition is appropriate for the given new/old - * routes. - * @param {!Route} newRoute - * @param {!Route=} oldRoute - * @private - */ - getStateTransition_(newRoute, oldRoute) { - const containsNew = this.containsRoute(newRoute); - const containsOld = this.containsRoute(oldRoute); - - if (!containsNew && !containsOld) { - // Nothing to do, since none of the old/new routes belong to this page. - return null; - } - - // Case where going from |this| page to an unrelated page. For example: - // |this| is os-settings-page AND - // oldRoute is /searchEngines AND - // newRoute is /help. - if (containsOld && !containsNew) { - return [classifyRoute(oldRoute), RouteState.TOP_LEVEL]; - } - - // Case where return from an unrelated page to |this| page. For example: - // |this| is os-settings-page AND - // oldRoute is /help AND - // newRoute is /searchEngines - if (!containsOld && containsNew) { - return [RouteState.TOP_LEVEL, classifyRoute(newRoute)]; - } - - // Case where transitioning between routes that both belong to |this| - // page. - return [classifyRoute(oldRoute), classifyRoute(newRoute)]; - }, - - /** - * @param {!Route} newRoute - * @param {!Route=} oldRoute - */ - currentRouteChanged(newRoute, oldRoute) { - const transition = this.getStateTransition_(newRoute, oldRoute); - if (transition === null) { - return; - } - - const oldState = transition[0]; - const newState = transition[1]; - assert(this.validTransitions_.get(oldState).has(newState)); - - if (oldState === RouteState.TOP_LEVEL) { - if (newState === RouteState.SECTION) { - this.scrollToSection_(newRoute); - } else if (newState === RouteState.SUBPAGE) { - this.enterSubpage_(newRoute); - } - // Nothing to do here for the case of RouteState.DIALOG or TOP_LEVEL. - // The latter happens when navigating from '/?search=foo' to '/' - // (clearing search results). - return; - } - - if (oldState === RouteState.SECTION) { - if (newState === RouteState.SECTION) { - this.scrollToSection_(newRoute); - } else if (newState === RouteState.SUBPAGE) { - this.enterSubpage_(newRoute); - } else if (newState === RouteState.TOP_LEVEL) { - this.scroller.scrollTop = 0; - } - // Nothing to do here for the case of RouteState.DIALOG. - return; - } - - if (oldState === RouteState.SUBPAGE) { - if (newState === RouteState.SECTION) { - this.enterMainPage_(oldRoute); - - // Scroll to the corresponding section, only if the user explicitly - // navigated to a section (via the menu). - if (!Router.getInstance().lastRouteChangeWasPopstate()) { - this.scrollToSection_(newRoute); - } - } else if (newState === RouteState.SUBPAGE) { - // Handle case where the two subpages belong to - // different sections, but are linked to each other. For example - // /storage and /accounts (in ChromeOS). - if (!oldRoute.contains(newRoute) && !newRoute.contains(oldRoute)) { - this.enterMainPage_(oldRoute).then(() => { - this.enterSubpage_(newRoute); - }); - return; - } - - // Handle case of subpage to sub-subpage navigation. - if (oldRoute.contains(newRoute)) { - this.scroller.scrollTop = 0; - return; - } - // When going from a sub-subpage to its parent subpage, scroll - // position is automatically restored, because we focus the - // sub-subpage entry point. - } else if (newState === RouteState.TOP_LEVEL) { - this.enterMainPage_(oldRoute); - } else if (newState === RouteState.DIALOG) { - // The only known case currently for such a transition is from - // /storage to /clearBrowserData. - this.enterMainPage_(oldRoute); - } - return; - } - - if (oldState === RouteState.INITIAL) { - if (newState === RouteState.SECTION) { - this.scrollToSection_(newRoute); - } else if (newState === RouteState.SUBPAGE) { - this.enterSubpage_(newRoute); - } - // Nothing to do here for the case of RouteState.DIALOG and TOP_LEVEL. - return; - } - - if (oldState === RouteState.DIALOG) { - if (newState === RouteState.SUBPAGE) { - // The only known case currently for such a transition is from - // /clearBrowserData back to /storage. - this.enterSubpage_(newRoute); - } - // Nothing to do for all other cases. - } - }, - - /** - * TODO(dpapad): Rename this to |querySection| to distinguish it from - * ensureSectionForRoute_() which force-renders the section as needed. - * Helper function to get a section from the local DOM. - * @param {string} section Section name of the element to get. - * @return {?HTMLElement} - */ - getSection(section) { - if (!section) { - return null; - } - return /** @type {?HTMLElement} */ ( - this.$$(`settings-section[section="${section}"]`)); - }, -}; - -/** @interface */ -export class MainPageBehaviorInterface { - constructor() { - /** @type {?HTMLElement} */ - this.scroller; - } - - /** - * @param {!Route} route - * @return {boolean} Whether the given route is part of |this| page. - */ - containsRoute(route) {} - - /** - * @param {!Route} newRoute - * @param {!Route=} oldRoute - */ - currentRouteChanged(newRoute, oldRoute) {} - - /** - * @param {string} section Section name of the element to get. - * @return {?HTMLElement} - */ - getSection(section) {} -}
diff --git a/chrome/browser/resources/settings/chromeos/main_page_mixin.ts b/chrome/browser/resources/settings/chromeos/main_page_mixin.ts new file mode 100644 index 0000000..823c949d --- /dev/null +++ b/chrome/browser/resources/settings/chromeos/main_page_mixin.ts
@@ -0,0 +1,344 @@ +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import {assert, assertNotReached} from 'chrome://resources/js/assert_ts.js'; +import {beforeNextRender, dedupingMixin, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; + +import {MinimumRoutes, Route, RouteObserverMixin, RouteObserverMixinInterface, Router} from '../router.js'; + +import {castExists} from './assert_extras.js'; +import {ensureLazyLoaded} from './ensure_lazy_loaded.js'; +import {SettingsIdleLoadElement} from './os_settings_page/settings_idle_load.js'; + +/** + * A categorization of every possible Settings URL, necessary for implementing + * a finite state machine. + */ +enum RouteState { + // Initial state before anything has loaded yet. + INITIAL = 'initial', + // A dialog that has a dedicated URL (e.g. /importData). + DIALOG = 'dialog', + // A section (basically a scroll position within the top level page, e.g, + // /appearance. + SECTION = 'section', + // A subpage, or sub-subpage e.g, /searchEngins. + SUBPAGE = 'subpage', + // The top level Settings page, '/'. + TOP_LEVEL = 'top-level', +} + +function classifyRoute(route: Route|undefined): RouteState { + if (!route) { + return RouteState.INITIAL; + } + const routes = Router.getInstance().getRoutes() as MinimumRoutes; + if (route === routes.BASIC || route === routes.ABOUT) { + return RouteState.TOP_LEVEL; + } + if (route.isSubpage()) { + return RouteState.SUBPAGE; + } + if (route.isNavigableDialog) { + return RouteState.DIALOG; + } + return RouteState.SECTION; +} + +const ALL_STATES = new Set([ + RouteState.DIALOG, + RouteState.SECTION, + RouteState.SUBPAGE, + RouteState.TOP_LEVEL, +]); + +/** + * A map holding all valid state transitions. + */ +const VALID_TRANSITIONS = new Map([ + [RouteState.INITIAL, ALL_STATES], + [ + RouteState.DIALOG, + new Set([ + RouteState.SECTION, + RouteState.SUBPAGE, + RouteState.TOP_LEVEL, + ]), + ], + [RouteState.SECTION, ALL_STATES], + [RouteState.SUBPAGE, ALL_STATES], + [RouteState.TOP_LEVEL, ALL_STATES], +]); + +type Constructor<T> = new (...args: any[]) => T; + +export interface MainPageMixinInterface extends RouteObserverMixinInterface { + containsRoute(route: Route|undefined): boolean; + currentRouteChanged(newRoute: Route, oldRoute?: Route): void; + querySection(section: string): HTMLElement|null; + loadAdvancedPage(): Promise<Element>; +} + +/** + * Responds to route changes by expanding, collapsing, or scrolling to + * sections on the page. Expanded sections take up the full height of the + * container. At most one section should be expanded at any given time. + */ +export const MainPageMixin = dedupingMixin( + <T extends Constructor<PolymerElement>>(superClass: T): T& + Constructor<MainPageMixinInterface> => { + const superclassBase = RouteObserverMixin(superClass) as T & + Constructor<RouteObserverMixinInterface>; + + class MainPageMixinInternal extends superclassBase implements + MainPageMixinInterface { + private lastScrollTop_: number = 0; + + private get scroller_(): HTMLElement { + const hostEl = (this.getRootNode() as ShadowRoot).host; + return castExists(hostEl ? hostEl.parentElement : document.body); + } + + /** + * Method to be overridden by users of MainPageMixin. + * @return Whether the given route is part of |this| page. + */ + containsRoute(_route: Route|undefined): boolean { + assertNotReached(); + } + + private shouldExpandAdvanced_(route: Route): boolean { + const routes = Router.getInstance().getRoutes() as MinimumRoutes; + return (this.tagName === 'OS-SETTINGS-PAGE') && routes.ADVANCED && + routes.ADVANCED.contains(route); + } + + loadAdvancedPage(): Promise<Element> { + return this.shadowRoot! + .querySelector<SettingsIdleLoadElement>( + '#advancedPageTemplate')!.get(); + } + + /** + * Finds the settings section corresponding to the given route. If the + * section is lazily loaded it force-renders it. + * Note: If the section resides within "advanced" settings, a + * 'hide-container' event is fired (necessary to avoid flashing). + * Callers are responsible for firing a 'show-container' event. + */ + private ensureSectionForRoute_(route: Route): Promise<HTMLElement> { + const section = this.querySection(route.section); + if (section) { + return Promise.resolve(section); + } + + // The function to use to wait for <dom-if>s to render. + const waitFn = beforeNextRender.bind(null, this); + + return new Promise(resolve => { + if (this.shouldExpandAdvanced_(route)) { + this.dispatchCustomEvent_('hide-container'); + waitFn(async () => { + await this.loadAdvancedPage(); + resolve(castExists(this.querySection(route.section))); + }); + } else { + waitFn(() => { + resolve(castExists(this.querySection(route.section))); + }); + } + }); + } + + private async enterSubpage_(route: Route) { + this.lastScrollTop_ = this.scroller_.scrollTop; + this.scroller_.scrollTop = 0; + this.classList.add('showing-subpage'); + this.dispatchCustomEvent_('subpage-expand'); + + // Explicitly load the lazy_load module, since all subpages reside in + // the lazy loaded module. + ensureLazyLoaded(); + + const section = await this.ensureSectionForRoute_(route); + section.classList.add('expanded'); + // Fire event used by a11y tests only. + this.dispatchCustomEvent_('settings-section-expanded'); + this.dispatchCustomEvent_('show-container'); + } + + private enterMainPage_(oldRoute: Route): Promise<void> { + const oldSection = castExists(this.querySection(oldRoute.section)); + oldSection.classList.remove('expanded'); + this.classList.remove('showing-subpage'); + return new Promise((resolve) => { + requestAnimationFrame(() => { + if (Router.getInstance().lastRouteChangeWasPopstate()) { + this.scroller_.scrollTop = this.lastScrollTop_; + } + this.dispatchCustomEvent_('showing-main-page'); + resolve(); + }); + }); + } + + private async scrollToSection_(route: Route) { + const section = await this.ensureSectionForRoute_(route); + this.dispatchCustomEvent_('showing-section', {detail: section}); + this.dispatchCustomEvent_('show-container'); + } + + /** + * Detects which state transition is appropriate for the given new/old + * routes. + */ + private getStateTransition_(newRoute: Route, oldRoute?: Route) { + const containsNew = this.containsRoute(newRoute); + const containsOld = this.containsRoute(oldRoute); + + if (!containsNew && !containsOld) { + // Nothing to do, since none of the old/new routes belong to this + // page. + return null; + } + + // Case where going from |this| page to an unrelated page. + // For example: + // |this| is os-settings-page AND + // oldRoute is /searchEngines AND + // newRoute is /help. + if (containsOld && !containsNew) { + return [classifyRoute(oldRoute), RouteState.TOP_LEVEL]; + } + + // Case where return from an unrelated page to |this| page. + // For example: + // |this| is os-settings-page AND + // oldRoute is /help AND + // newRoute is /searchEngines + if (!containsOld && containsNew) { + return [RouteState.TOP_LEVEL, classifyRoute(newRoute)]; + } + + // Case where transitioning between routes that both belong to |this| + // page. + return [classifyRoute(oldRoute), classifyRoute(newRoute)]; + } + + override currentRouteChanged(newRoute: Route, oldRoute?: Route) { + const transition = this.getStateTransition_(newRoute, oldRoute); + if (transition === null) { + return; + } + + const oldState = transition[0]; + const newState = transition[1]; + assert(VALID_TRANSITIONS.get(oldState)!.has(newState)); + + if (oldState === RouteState.TOP_LEVEL) { + if (newState === RouteState.SECTION) { + this.scrollToSection_(newRoute); + } else if (newState === RouteState.SUBPAGE) { + this.enterSubpage_(newRoute); + } + // Nothing to do here for the case of RouteState.DIALOG or + // TOP_LEVEL. The latter happens when navigating from '/?search=foo' + // to '/' (clearing search results). + return; + } + + if (oldState === RouteState.SECTION) { + if (newState === RouteState.SECTION) { + this.scrollToSection_(newRoute); + } else if (newState === RouteState.SUBPAGE) { + this.enterSubpage_(newRoute); + } else if (newState === RouteState.TOP_LEVEL) { + this.scroller_.scrollTop = 0; + } + // Nothing to do here for the case of RouteState.DIALOG. + return; + } + + if (oldState === RouteState.SUBPAGE) { + assert(oldRoute); + if (newState === RouteState.SECTION) { + this.enterMainPage_(oldRoute); + + // Scroll to the corresponding section, only if the user + // explicitly navigated to a section (via the menu). + if (!Router.getInstance().lastRouteChangeWasPopstate()) { + this.scrollToSection_(newRoute); + } + } else if (newState === RouteState.SUBPAGE) { + // Handle case where the two subpages belong to + // different sections, but are linked to each other. For example + // /storage and /accounts (in ChromeOS). + if (!oldRoute.contains(newRoute) && + !newRoute.contains(oldRoute)) { + this.enterMainPage_(oldRoute).then(() => { + this.enterSubpage_(newRoute); + }); + return; + } + + // Handle case of subpage to sub-subpage navigation. + if (oldRoute.contains(newRoute)) { + this.scroller_.scrollTop = 0; + return; + } + // When going from a sub-subpage to its parent subpage, scroll + // position is automatically restored, because we focus the + // sub-subpage entry point. + } else if (newState === RouteState.TOP_LEVEL) { + this.enterMainPage_(oldRoute); + } else if (newState === RouteState.DIALOG) { + // The only known case currently for such a transition is from + // /storage to /clearBrowserData. + this.enterMainPage_(oldRoute); + } + return; + } + + if (oldState === RouteState.INITIAL) { + if (newState === RouteState.SECTION) { + this.scrollToSection_(newRoute); + } else if (newState === RouteState.SUBPAGE) { + this.enterSubpage_(newRoute); + } + // Nothing to do here for the case of RouteState.DIALOG and + // TOP_LEVEL. + return; + } + + if (oldState === RouteState.DIALOG) { + if (newState === RouteState.SUBPAGE) { + // The only known case currently for such a transition is from + // /clearBrowserData back to /storage. + this.enterSubpage_(newRoute); + } + // Nothing to do for all other cases. + } + } + + /** + * Helper function to get a section from the local DOM. + */ + querySection(section: string): HTMLElement|null { + if (!section) { + return null; + } + return this.shadowRoot!.querySelector( + `settings-section[section="${section}"]`); + } + + private dispatchCustomEvent_( + name: string, options?: CustomEventInit<unknown>) { + const event = new CustomEvent( + name, {bubbles: true, composed: true, ...options}); + this.dispatchEvent(event); + } + } + + return MainPageMixinInternal; + });
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_setup_guide_dialog.ts b/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_setup_guide_dialog.ts index f763801..ba54df9a 100644 --- a/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_setup_guide_dialog.ts +++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_setup_guide_dialog.ts
@@ -162,11 +162,6 @@ static get properties() { return { - prefs: { - type: Object, - notify: true, - }, - autoScanSpeedRangeMs_: { type: Array, value: [],
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_subpage.ts b/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_subpage.ts index bc286bf4..d0681e9 100644 --- a/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_subpage.ts +++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_subpage.ts
@@ -83,11 +83,6 @@ static get properties() { return { - prefs: { - type: Object, - notify: true, - }, - selectAssignments_: { type: Array, value: [],
diff --git a/chrome/browser/resources/settings/chromeos/os_about_page/detailed_build_info.ts b/chrome/browser/resources/settings/chromeos/os_about_page/detailed_build_info.ts index 53094f3..1cf29ada 100644 --- a/chrome/browser/resources/settings/chromeos/os_about_page/detailed_build_info.ts +++ b/chrome/browser/resources/settings/chromeos/os_about_page/detailed_build_info.ts
@@ -66,12 +66,6 @@ static get properties() { return { - /** Preferences state. */ - prefs: { - type: Object, - notify: true, - }, - versionInfo_: Object, channelInfo_: Object,
diff --git a/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.ts b/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.ts index 07ffeca1..fa621ae 100644 --- a/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.ts +++ b/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.ts
@@ -37,10 +37,9 @@ import {Setting} from '../../mojom-webui/setting.mojom-webui.js'; import {Route, Router} from '../../router.js'; import {DeepLinkingBehavior, DeepLinkingBehaviorInterface} from '../deep_linking_behavior.js'; -import {MainPageBehavior, MainPageBehaviorInterface} from '../main_page_behavior.js'; +import {MainPageMixin, MainPageMixinInterface} from '../main_page_mixin.js'; import {recordSettingChange} from '../metrics_recorder.js'; import {routes} from '../os_route.js'; -import {RouteObserverBehavior, RouteObserverBehaviorInterface} from '../route_observer_behavior.js'; import {AboutPageBrowserProxy, AboutPageBrowserProxyImpl, AboutPageUpdateInfo, BrowserChannel, browserChannelToI18nId, RegulatoryInfo, TpmFirmwareUpdateStatusChangedEvent, UpdateStatus, UpdateStatusChangedEvent} from './about_page_browser_proxy.js'; import {getTemplate} from './os_about_page.html.js'; @@ -62,13 +61,11 @@ mixinBehaviors( [ DeepLinkingBehavior, - MainPageBehavior, - RouteObserverBehavior, ], - I18nMixin(WebUiListenerMixin(PolymerElement))) as { - new (): PolymerElement & DeepLinkingBehaviorInterface & - WebUiListenerMixinInterface & MainPageBehaviorInterface & - RouteObserverBehaviorInterface & I18nMixinInterface, + MainPageMixin(I18nMixin(WebUiListenerMixin(PolymerElement)))) as { + new (): PolymerElement & WebUiListenerMixinInterface & + I18nMixinInterface & MainPageMixinInterface & + DeepLinkingBehaviorInterface, }; class OsSettingsAboutPageElement extends OsSettingsAboutPageBaseElement { @@ -337,12 +334,9 @@ } override currentRouteChanged(newRoute: Route, oldRoute?: Route) { - // super.currentRouteChanged() does not produce desired results since - // RouteObserverBehavior has higher precedence than MainPageBehavior given - // this element's behavior list order. In order to trigger the - // MainPageBehavior method, we must directly call it. + // MainPageMixin#currentRouteChanged() should be the super class method // See https://crbug.com/1324103 for more details. - MainPageBehavior.currentRouteChanged.call(this, newRoute, oldRoute); + super.currentRouteChanged(newRoute, oldRoute); // Does not apply to this page. if (newRoute !== routes.ABOUT_ABOUT) {
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.gni b/chrome/browser/resources/settings/chromeos/os_settings.gni index 13173cd2..83f400e 100644 --- a/chrome/browser/resources/settings/chromeos/os_settings.gni +++ b/chrome/browser/resources/settings/chromeos/os_settings.gni
@@ -220,7 +220,7 @@ "chromeos/internet_page/internet_page_browser_proxy.js", "chromeos/kerberos_page/kerberos_accounts_browser_proxy.ts", "chromeos/lazy_load.js", - "chromeos/main_page_behavior.js", + "chromeos/main_page_mixin.ts", "chromeos/metrics_recorder.js", "chromeos/multidevice_page/multidevice_browser_proxy.js", "chromeos/multidevice_page/multidevice_constants.js",
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.ts b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.ts index 601becef..9fcceae6 100644 --- a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.ts +++ b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.ts
@@ -28,28 +28,22 @@ import '../os_bluetooth_page/os_bluetooth_page.js'; import '../os_settings_icons.html.js'; -import {WebUiListenerMixin, WebUiListenerMixinInterface} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js'; +import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js'; import {assert} from 'chrome://resources/js/assert_ts.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; -import {beforeNextRender, microTask, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {beforeNextRender, microTask, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {Route, Router} from '../../router.js'; import {castExists} from '../assert_extras.js'; -import {MainPageBehavior, MainPageBehaviorInterface} from '../main_page_behavior.js'; +import {MainPageMixin} from '../main_page_mixin.js'; import {AndroidAppsBrowserProxyImpl, AndroidAppsInfo} from '../os_apps_page/android_apps_browser_proxy.js'; import {OSPageVisibility} from '../os_page_visibility.js'; import {routes} from '../os_route.js'; -import {RouteObserverBehavior, RouteObserverBehaviorInterface} from '../route_observer_behavior.js'; import {getTemplate} from './os_settings_page.html.js'; -import {SettingsIdleLoadElement} from './settings_idle_load.js'; -const OsSettingsPageElementBase = mixinBehaviors( - [MainPageBehavior, RouteObserverBehavior], - WebUiListenerMixin(PolymerElement)) as { - new (): PolymerElement & WebUiListenerMixinInterface & - MainPageBehaviorInterface & RouteObserverBehaviorInterface, -}; +const OsSettingsPageElementBase = + MainPageMixin(WebUiListenerMixin(PolymerElement)); class OsSettingsPageElement extends OsSettingsPageElementBase { static get is() { @@ -212,7 +206,8 @@ assert(!this.hasExpandedSection_); } - MainPageBehavior.currentRouteChanged.call(this, newRoute, oldRoute); + // MainPageMixin#currentRouteChanged() should be the super class method + super.currentRouteChanged(newRoute, oldRoute); } override containsRoute(route: Route) { @@ -252,11 +247,6 @@ this.hasExpandedSection_ = true; } - private getAdvancedPageTemplate_(): SettingsIdleLoadElement { - return castExists(this.shadowRoot!.querySelector<SettingsIdleLoadElement>( - '#advancedPageTemplate')); - } - /** * Render the advanced page now (don't wait for idle). */ @@ -268,7 +258,7 @@ // In Polymer2, async() does not wait long enough for layout to complete. // beforeNextRender() must be used instead. beforeNextRender(this, () => { - this.getAdvancedPageTemplate_().get(); + this.loadAdvancedPage(); }); } @@ -284,7 +274,7 @@ if (!this.advancedToggleExpanded) { this.advancedToggleExpanded = true; microTask.run(() => { - this.getAdvancedPageTemplate_().get().then(() => { + this.loadAdvancedPage().then(() => { const event = new CustomEvent('scroll-to-top', { bubbles: true, composed: true,
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.ts b/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.ts index 22543a4..8ab15d404 100644 --- a/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.ts +++ b/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.ts
@@ -456,7 +456,7 @@ */ private onMenuClose_() { if (!this.getDrawer_().wasCanceled()) { - // If a navigation happened, MainPageBehavior#currentRouteChanged + // If a navigation happened, MainPageMixin#currentRouteChanged // handles focusing the corresponding section when we call // settings.NavigateTo(). this.navigateToActiveRoute_();
diff --git a/chrome/browser/resources/settings/router.js b/chrome/browser/resources/settings/router.js index 91631c4..c45a047 100644 --- a/chrome/browser/resources/settings/router.js +++ b/chrome/browser/resources/settings/router.js
@@ -231,6 +231,11 @@ document.title = loadTimeData.getStringF( 'settingsAltPageTitle', this.currentRoute.title); } else if ( + this.currentRoute.isNavigableDialog && this.currentRoute.parent && + this.currentRoute.parent.title) { + document.title = loadTimeData.getStringF( + 'settingsAltPageTitle', this.currentRoute.parent.title); + } else if ( !this.currentRoute.isSubpage() && !this.routes_.ABOUT.contains(this.currentRoute)) { document.title = loadTimeData.getString('settings');
diff --git a/chrome/browser/resources/side_panel/BUILD.gn b/chrome/browser/resources/side_panel/BUILD.gn index 28c4ba16..3809b5d 100644 --- a/chrome/browser/resources/side_panel/BUILD.gn +++ b/chrome/browser/resources/side_panel/BUILD.gn
@@ -49,33 +49,27 @@ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/side_panel" copy("copy_mojo_bookmarks") { - deps = [ - "//chrome/browser/ui/webui/side_panel/bookmarks:mojo_bindings_webui_js", - ] + deps = [ "//chrome/browser/ui/webui/side_panel/bookmarks:mojo_bindings_js__generator" ] sources = [ "$mojo_root_folder/bookmarks/bookmarks.mojom-webui.js" ] outputs = [ "$target_gen_dir/$preprocess_folder/bookmarks/{{source_file_part}}" ] } copy("copy_mojo_reading_list") { - deps = [ - "//chrome/browser/ui/webui/side_panel/reading_list:mojo_bindings_webui_js", - ] + deps = [ "//chrome/browser/ui/webui/side_panel/reading_list:mojo_bindings_js__generator" ] sources = [ "$mojo_root_folder/reading_list/reading_list.mojom-webui.js" ] outputs = [ "$target_gen_dir/$preprocess_folder/reading_list/{{source_file_part}}" ] } copy("copy_mojo_shopping_list") { - deps = [ "//components/commerce/core/mojom:mojo_bindings_webui_js" ] + deps = [ "//components/commerce/core/mojom:mojo_bindings_js__generator" ] sources = [ "$root_gen_dir/mojom-webui/components/commerce/core/mojom/shopping_list.mojom-webui.js" ] outputs = [ "$target_gen_dir/$preprocess_folder/bookmarks/commerce/{{source_file_part}}" ] } copy("copy_mojo_user_notes") { - deps = [ - "//chrome/browser/ui/webui/side_panel/user_notes:mojo_bindings_webui_js", - ] + deps = [ "//chrome/browser/ui/webui/side_panel/user_notes:mojo_bindings_js__generator" ] sources = [ "$mojo_root_folder/user_notes/user_notes.mojom-webui.js" ] outputs = [ "$target_gen_dir/$preprocess_folder/user_notes/{{source_file_part}}" ]
diff --git a/chrome/browser/resources/side_panel/bookmarks/bookmarks_api_proxy.ts b/chrome/browser/resources/side_panel/bookmarks/bookmarks_api_proxy.ts index 9c98b1f..ca106b9 100644 --- a/chrome/browser/resources/side_panel/bookmarks/bookmarks_api_proxy.ts +++ b/chrome/browser/resources/side_panel/bookmarks/bookmarks_api_proxy.ts
@@ -14,7 +14,6 @@ bookmarkCurrentTab(): void; cutBookmark(id: string): void; copyBookmark(id: string): Promise<void>; - getTopLevelBookmarks(): Promise<chrome.bookmarks.BookmarkTreeNode[]>; getFolders(): Promise<chrome.bookmarks.BookmarkTreeNode[]>; openBookmark( id: string, depth: number, clickModifiers: ClickModifiers, @@ -58,23 +57,6 @@ }); } - getTopLevelBookmarks() { - return new Promise<chrome.bookmarks.BookmarkTreeNode[]>( - resolve => chrome.bookmarks.getTree(results => { - if (results[0] && results[0].children) { - let allBookmarks: chrome.bookmarks.BookmarkTreeNode[] = []; - for (const child of results[0].children) { - if (child.children) { - allBookmarks = allBookmarks.concat(child.children); - } - } - resolve(allBookmarks); - return; - } - resolve([]); - })); - } - getFolders() { return new Promise<chrome.bookmarks.BookmarkTreeNode[]>( resolve => chrome.bookmarks.getTree(results => {
diff --git a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts index fbe7ddf..9338c51 100644 --- a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts +++ b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts
@@ -13,6 +13,7 @@ import '//resources/cr_elements/icons.html.js'; import {CrActionMenuElement} from '//resources/cr_elements/cr_action_menu/cr_action_menu.js'; +import {getInstance as getAnnouncerInstance} from 'chrome://resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; import {PluralStringProxyImpl} from 'chrome://resources/js/plural_string_proxy.js'; import {listenOnce} from 'chrome://resources/js/util.js'; @@ -24,6 +25,10 @@ import {ShoppingListApiProxy, ShoppingListApiProxyImpl} from './commerce/shopping_list_api_proxy.js'; import {getTemplate} from './power_bookmarks_list.html.js'; +function getBookmarkName(bookmark: chrome.bookmarks.BookmarkTreeNode): string { + return bookmark.title || bookmark.url || ''; +} + interface Label { label: string; icon: string; @@ -48,7 +53,7 @@ static get properties() { return { - topLevelBookmarks_: { + folders_: { type: Array, value: () => [], }, @@ -100,17 +105,18 @@ static get observers() { return [ - 'updateShownBookmarks_(topLevelBookmarks_, ' + + 'updateShownBookmarks_(folders_.*, ' + 'activeFolderPath_.*, labels_.*, activeSortIndex_)', ]; } - private topLevelBookmarks_: chrome.bookmarks.BookmarkTreeNode[]; + private folders_: chrome.bookmarks.BookmarkTreeNode[]; private shownBookmarks_: chrome.bookmarks.BookmarkTreeNode[]; private bookmarksApi_: BookmarksApiProxy = BookmarksApiProxyImpl.getInstance(); private shoppingListApi_: ShoppingListApiProxy = ShoppingListApiProxyImpl.getInstance(); + private listeners_ = new Map<string, Function>(); private productInfos_ = new Map<string, BookmarkProductInfo>(); private shoppingListenerIds_: number[] = []; private compact_: boolean; @@ -128,11 +134,24 @@ listenOnce(this.$.powerBookmarksContainer, 'dom-change', () => { setTimeout(() => this.bookmarksApi_.showUI(), 0); }); - this.bookmarksApi_.getTopLevelBookmarks().then(topLevelBookmarks => { - this.topLevelBookmarks_ = topLevelBookmarks; - this.topLevelBookmarks_.forEach(bookmark => { - this.findBookmarkDescriptions_(bookmark); + this.bookmarksApi_.getFolders().then(folders => { + this.folders_ = folders; + this.folders_.forEach(bookmark => { + this.findBookmarkDescriptions_(bookmark, true); }); + this.addListener_( + 'onChanged', + (id: string, changedInfo: chrome.bookmarks.ChangeInfo) => + this.onChanged_(id, changedInfo)); + this.addListener_( + 'onCreated', + (_id: string, node: chrome.bookmarks.BookmarkTreeNode) => + this.onCreated_(node)); + this.addListener_( + 'onMoved', + (_id: string, movedInfo: chrome.bookmarks.MoveInfo) => + this.onMoved_(movedInfo)); + this.addListener_('onRemoved', (id: string) => this.onRemoved_(id)); }); this.shoppingListApi_.getAllPriceTrackedBookmarkProductInfo().then(res => { res.productInfos.forEach( @@ -152,16 +171,190 @@ } override disconnectedCallback() { + for (const [eventName, callback] of this.listeners_.entries()) { + this.bookmarksApi_.callbackRouter[eventName]!.removeListener(callback); + } this.shoppingListenerIds_.forEach( id => this.shoppingListApi_.getCallbackRouter().removeListener(id)); } + private addListener_(eventName: string, callback: Function): void { + this.bookmarksApi_.callbackRouter[eventName]!.addListener(callback); + this.listeners_.set(eventName, callback); + } + /** - * Assigns a text description for the given bookmark and all descendants, to - * be displayed following the bookmark title. + * Returns the index of the given node id in the currently shown bookmarks, + * or -1 if not shown. */ - private findBookmarkDescriptions_(bookmark: - chrome.bookmarks.BookmarkTreeNode) { + private visibleIndex_(nodeId: string): number { + return this.shownBookmarks_.findIndex(b => b.id === nodeId); + } + + /** + * Returns true if the given node is either the current active folder or a + * root folder while the all bookmarks list is shown. + */ + private visibleParent_(parent: chrome.bookmarks.BookmarkTreeNode): boolean { + const activeFolder = this.getActiveFolder_(); + return (!activeFolder && parent.parentId === '0') || + parent === activeFolder; + } + + private onChanged_(id: string, changedInfo: chrome.bookmarks.ChangeInfo) { + const path = this.findPathToId_(id); + const bookmark = path[path.length - 1]!; + Object.assign(bookmark, changedInfo); + this.findBookmarkDescriptions_(bookmark, false); + Object.keys(changedInfo).forEach(key => { + const visibleIndex = this.visibleIndex_(id); + if (visibleIndex > -1) { + this.notifyPath(`shownBookmarks_.${visibleIndex}.${key}`); + } + }); + } + + private onCreated_(node: chrome.bookmarks.BookmarkTreeNode) { + const pathToParent = this.findPathToId_(node.parentId as string); + const parent = pathToParent[pathToParent.length - 1]!; + if (!parent.children) { + // Newly created folders in this session may not have an array of + // children yet, so create an empty one. + parent.children = []; + } + parent.children!.splice(node.index!, 0, node); + if (this.visibleParent_(parent)) { + this.shownBookmarks_.unshift(node); + this.sortBookmarks_(this.shownBookmarks_); + this.shownBookmarks_ = this.shownBookmarks_.slice(); + getAnnouncerInstance().announce( + loadTimeData.getStringF('bookmarkCreated', getBookmarkName(node))); + } + this.findBookmarkDescriptions_(parent, false); + this.findBookmarkDescriptions_(node, false); + } + + private onMoved_(movedInfo: chrome.bookmarks.MoveInfo) { + // Get old path and remove node from oldParent at oldIndex. + const oldParentPath = this.findPathToId_(movedInfo.oldParentId); + const oldParent = oldParentPath[oldParentPath.length - 1]!; + const movedNode = oldParent!.children![movedInfo.oldIndex]!; + Object.assign( + movedNode, {index: movedInfo.index, parentId: movedInfo.parentId}); + oldParent.children!.splice(movedInfo.oldIndex, 1); + + // Get new parent's path and add the node to the new parent at index. + const newParentPath = this.findPathToId_(movedInfo.parentId); + const newParent = newParentPath[newParentPath.length - 1]!; + if (!newParent.children) { + newParent.children = []; + } + newParent.children!.splice(movedInfo.index, 0, movedNode); + + const shouldUpdateUIAdded = this.visibleParent_(newParent); + const shouldUpdateUIRemoved = this.visibleParent_(oldParent); + const shouldUpdateUIReordered = + shouldUpdateUIAdded && shouldUpdateUIRemoved; + + if (shouldUpdateUIReordered) { + getAnnouncerInstance().announce(loadTimeData.getStringF( + 'bookmarkReordered', getBookmarkName(movedNode))); + } else if (shouldUpdateUIAdded) { + this.shownBookmarks_.unshift(movedNode); + this.sortBookmarks_(this.shownBookmarks_); + this.shownBookmarks_ = this.shownBookmarks_.slice(); + getAnnouncerInstance().announce(loadTimeData.getStringF( + 'bookmarkMoved', getBookmarkName(movedNode), + getBookmarkName(newParent))); + } else if (shouldUpdateUIRemoved) { + this.splice('shownBookmarks_', this.visibleIndex_(movedNode.id), 1); + getAnnouncerInstance().announce(loadTimeData.getStringF( + 'bookmarkMoved', getBookmarkName(movedNode), + getBookmarkName(newParent))); + } + + if (movedInfo.oldParentId !== movedInfo.parentId) { + this.findBookmarkDescriptions_(oldParent, false); + this.findBookmarkDescriptions_(newParent, false); + } + } + + private onRemoved_(id: string) { + const oldPath = this.findPathToId_(id); + const removedNode = oldPath.pop()!; + const oldParent = oldPath[oldPath.length - 1]!; + oldParent.children!.splice(oldParent.children!.indexOf(removedNode), 1); + this.productInfos_.delete(id); + const visibleIndex = this.visibleIndex_(id); + if (visibleIndex > -1) { + this.splice('shownBookmarks_', visibleIndex, 1); + getAnnouncerInstance().announce(loadTimeData.getStringF( + 'bookmarkDeleted', getBookmarkName(removedNode))); + } + this.findBookmarkDescriptions_(oldParent, false); + } + + /** + * Finds the node within all bookmarks and returns the path to the node in + * the tree. + */ + private findPathToId_(id: string): chrome.bookmarks.BookmarkTreeNode[] { + const path: chrome.bookmarks.BookmarkTreeNode[] = []; + + function findPathByIdInternal( + id: string, node: chrome.bookmarks.BookmarkTreeNode) { + if (node.id === id) { + path.push(node); + return true; + } + + if (!node.children) { + return false; + } + + path.push(node); + const foundInChildren = + node.children.some(child => findPathByIdInternal(id, child)); + if (!foundInChildren) { + path.pop(); + } + + return foundInChildren; + } + + this.folders_.some(bookmark => findPathByIdInternal(id, bookmark)); + return path; + } + + private getActiveFolder_(): chrome.bookmarks.BookmarkTreeNode|undefined { + if (this.activeFolderPath_.length) { + return this.activeFolderPath_[this.activeFolderPath_.length - 1]; + } + return undefined; + } + + /** + * Reduces an array of nodes to a string to notify Polymer of changes to the + * nested array. + */ + private getPathString_(path: chrome.bookmarks.BookmarkTreeNode[]): string { + return path.reduce((reducedString, pathItem, index) => { + if (index === 0) { + return `folders_.${this.folders_.indexOf(pathItem)}`; + } + + const parent = path[index - 1]; + return `${reducedString}.children.${parent!.children!.indexOf(pathItem)}`; + }, ''); + } + + /** + * Assigns a text description for the given bookmark, to be displayed + * following the bookmark title. Also assigns a description to all + * descendants if recurse is true. + */ + private findBookmarkDescriptions_( + bookmark: chrome.bookmarks.BookmarkTreeNode, recurse: boolean) { if (bookmark.children) { PluralStringProxyImpl.getInstance() .getPluralString('bookmarkFolderChildCount', bookmark.children.length) @@ -179,11 +372,17 @@ this.set(`expandedDescriptions_.${bookmark.id}`, url.hostname); } } - if (bookmark.children) { - bookmark.children.forEach(child => this.findBookmarkDescriptions_(child)); + if (recurse && bookmark.children) { + bookmark.children.forEach( + child => this.findBookmarkDescriptions_(child, recurse)); } } + private getBookmarkName_(bookmark: chrome.bookmarks.BookmarkTreeNode): + string { + return bookmark.title || bookmark.url || ''; + } + private getBookmarkDescription_(bookmark: chrome.bookmarks.BookmarkTreeNode): string|undefined { if (this.compact_) { @@ -194,9 +393,8 @@ } private getFolderLabel_(): string { - if (this.activeFolderPath_.length) { - const activeFolder = - this.activeFolderPath_[this.activeFolderPath_.length - 1]; + const activeFolder = this.getActiveFolder_(); + if (activeFolder) { return activeFolder!.title; } else { return loadTimeData.getString('allBookmarks'); @@ -221,12 +419,15 @@ */ private updateShownBookmarks_() { let shownBookmarks; - const activeFolder = - this.activeFolderPath_[this.activeFolderPath_.length - 1]; + const activeFolder = this.getActiveFolder_(); if (activeFolder) { - shownBookmarks = activeFolder.children!; + shownBookmarks = activeFolder.children!.slice(); } else { - shownBookmarks = this.topLevelBookmarks_; + let topLevelBookmarks: chrome.bookmarks.BookmarkTreeNode[] = []; + this.folders_.forEach( + folder => topLevelBookmarks = + topLevelBookmarks.concat(folder.children!)); + shownBookmarks = topLevelBookmarks; } // Price tracking label if (this.labels_[0]!.active) {
diff --git a/chrome/browser/resources/side_panel/customize_chrome/BUILD.gn b/chrome/browser/resources/side_panel/customize_chrome/BUILD.gn index e999a83..21d0eb46 100644 --- a/chrome/browser/resources/side_panel/customize_chrome/BUILD.gn +++ b/chrome/browser/resources/side_panel/customize_chrome/BUILD.gn
@@ -15,6 +15,9 @@ web_component_files = [ "app.ts", "shortcuts.ts", + "categories.ts", + "appearance.ts", + "themes.ts", ] non_web_component_files = [ "customize_chrome_api_proxy.ts" ] @@ -24,7 +27,7 @@ html_files += [ string_replace(f, ".ts", ".html") ] } - mojo_files_deps = [ "//chrome/browser/ui/webui/side_panel/customize_chrome:mojo_bindings_webui_js" ] + mojo_files_deps = [ "//chrome/browser/ui/webui/side_panel/customize_chrome:mojo_bindings_js__generator" ] mojo_files = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom-webui.js" ] ts_composite = true
diff --git a/chrome/browser/resources/side_panel/customize_chrome/app.html b/chrome/browser/resources/side_panel/customize_chrome/app.html index 320a4704..45dca72c 100644 --- a/chrome/browser/resources/side_panel/customize_chrome/app.html +++ b/chrome/browser/resources/side_panel/customize_chrome/app.html
@@ -1,3 +1,16 @@ -<div id="body"> +<iron-pages selected="[[page_]]" attr-for-selected="page-name"> + <div page-name="overview" id="overviewPage"> + <customize-chrome-appearance on-edit-theme-click="onEditThemeClick_" + id="appearanceElement"> + </customize-chrome-appearance> <customize-chrome-shortcuts></customize-chrome-shortcuts> -</div> + </div> + <customize-chrome-categories on-back-click="onBackClick_" + on-category-select="onCategorySelect_" page-name="categories" + id="categoriesPage"> + </customize-chrome-categories> + <customize-chrome-themes on-back-click="onBackClick_" + on-theme-select="onThemeSelect_" page-name="themes" + id="themesPage"> + </customize-chrome-themes> +</iron-pages>
diff --git a/chrome/browser/resources/side_panel/customize_chrome/app.ts b/chrome/browser/resources/side_panel/customize_chrome/app.ts index 441da4e7..4f680062 100644 --- a/chrome/browser/resources/side_panel/customize_chrome/app.ts +++ b/chrome/browser/resources/side_panel/customize_chrome/app.ts
@@ -1,13 +1,33 @@ // Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'chrome://resources/polymer/v3_0/iron-pages/iron-pages.js'; +import './appearance.js'; +import './categories.js'; import './shortcuts.js'; +import './themes.js'; import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {getTemplate} from './app.html.js'; +import {AppearanceElement} from './appearance.js'; +import {CategoriesElement} from './categories.js'; +import {ThemesElement} from './themes.js'; -export interface AppElement {} +export enum CustomizeChromePage { + OVERVIEW = 'overview', + CATEGORIES = 'categories', + THEMES = 'themes', +} + +export interface AppElement { + $: { + overviewPage: HTMLDivElement, + categoriesPage: CategoriesElement, + themesPage: ThemesElement, + appearanceElement: AppearanceElement, + }; +} export class AppElement extends PolymerElement { static get is() { @@ -18,13 +38,46 @@ return getTemplate(); } - static get properties() { - return {}; + return { + page_: { + type: String, + value: CustomizeChromePage.OVERVIEW, + }, + selectedCategory_: { + type: Object, + value: null, + }, + }; } - constructor() { - super(); + private page_: CustomizeChromePage; + // Will make this a more specific object when the + // handler is updated to support collections/categories. + private selectedCategory_: object|null; + + private onBackClick_() { + switch (this.page_) { + case CustomizeChromePage.CATEGORIES: + this.page_ = CustomizeChromePage.OVERVIEW; + break; + case CustomizeChromePage.THEMES: + this.page_ = CustomizeChromePage.CATEGORIES; + break; + } + } + + private onEditThemeClick_() { + this.page_ = CustomizeChromePage.CATEGORIES; + } + + private onCategorySelect_(event: CustomEvent<object>) { + this.selectedCategory_ = event.detail; + this.page_ = CustomizeChromePage.THEMES; + } + + private onThemeSelect_() { + this.page_ = CustomizeChromePage.OVERVIEW; } }
diff --git a/chrome/browser/resources/side_panel/customize_chrome/appearance.html b/chrome/browser/resources/side_panel/customize_chrome/appearance.html new file mode 100644 index 0000000..b643145 --- /dev/null +++ b/chrome/browser/resources/side_panel/customize_chrome/appearance.html
@@ -0,0 +1 @@ +<button id="editThemeButton" on-click="onEditThemeClicked_">Edit Theme</button> \ No newline at end of file
diff --git a/chrome/browser/resources/side_panel/customize_chrome/appearance.ts b/chrome/browser/resources/side_panel/customize_chrome/appearance.ts new file mode 100644 index 0000000..04805ac --- /dev/null +++ b/chrome/browser/resources/side_panel/customize_chrome/appearance.ts
@@ -0,0 +1,38 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; + +import {getTemplate} from './appearance.html.js'; + +export interface AppearanceElement { + $: { + editThemeButton: HTMLButtonElement, + }; +} + +export class AppearanceElement extends PolymerElement { + static get is() { + return 'customize-chrome-appearance'; + } + + static get template() { + return getTemplate(); + } + + static get properties() { + return {}; + } + + private onEditThemeClicked_() { + this.dispatchEvent(new Event('edit-theme-click')); + } +} + +declare global { + interface HTMLElementTagNameMap { + 'customize-chrome-appearance': AppearanceElement; + } +} + +customElements.define(AppearanceElement.is, AppearanceElement);
diff --git a/chrome/browser/resources/side_panel/customize_chrome/categories.html b/chrome/browser/resources/side_panel/customize_chrome/categories.html new file mode 100644 index 0000000..9c5b6e4 --- /dev/null +++ b/chrome/browser/resources/side_panel/customize_chrome/categories.html
@@ -0,0 +1,7 @@ +<button on-click="onBackClick_" id="backButton"> + Go Back +</button> +<p>Category List</p> +<button on-click="onCategoryClick_" class="category"> + Select Category +</button> \ No newline at end of file
diff --git a/chrome/browser/resources/side_panel/customize_chrome/categories.ts b/chrome/browser/resources/side_panel/customize_chrome/categories.ts new file mode 100644 index 0000000..ea70ee77 --- /dev/null +++ b/chrome/browser/resources/side_panel/customize_chrome/categories.ts
@@ -0,0 +1,42 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; + +import {getTemplate} from './categories.html.js'; + +export interface CategoriesElement { + $: { + backButton: HTMLButtonElement, + }; +} + +export class CategoriesElement extends PolymerElement { + static get is() { + return 'customize-chrome-categories'; + } + + static get template() { + return getTemplate(); + } + + static get properties() { + return {}; + } + + private onCategoryClick_() { + this.dispatchEvent(new CustomEvent<object>('category-select')); + } + + private onBackClick_() { + this.dispatchEvent(new Event('back-click')); + } +} + +declare global { + interface HTMLElementTagNameMap { + 'customize-chrome-categories': CategoriesElement; + } +} + +customElements.define(CategoriesElement.is, CategoriesElement);
diff --git a/chrome/browser/resources/side_panel/customize_chrome/themes.html b/chrome/browser/resources/side_panel/customize_chrome/themes.html new file mode 100644 index 0000000..0c0705c --- /dev/null +++ b/chrome/browser/resources/side_panel/customize_chrome/themes.html
@@ -0,0 +1,7 @@ +<button on-click="onBackClick_" id="backButton"> + Go Back +</button> +<p>Category Theme List</p> +<button on-click="onSelectTheme_" class="theme"> + Select Theme +</button> \ No newline at end of file
diff --git a/chrome/browser/resources/side_panel/customize_chrome/themes.ts b/chrome/browser/resources/side_panel/customize_chrome/themes.ts new file mode 100644 index 0000000..e8c046a --- /dev/null +++ b/chrome/browser/resources/side_panel/customize_chrome/themes.ts
@@ -0,0 +1,42 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; + +import {getTemplate} from './themes.html.js'; + +export interface ThemesElement { + $: { + backButton: HTMLButtonElement, + }; +} + +export class ThemesElement extends PolymerElement { + static get is() { + return 'customize-chrome-themes'; + } + + static get template() { + return getTemplate(); + } + + static get properties() { + return {}; + } + + private onBackClick_() { + this.dispatchEvent(new Event('back-click')); + } + + private onSelectTheme_() { + this.dispatchEvent(new Event('theme-select')); + } +} + +declare global { + interface HTMLElementTagNameMap { + 'customize-chrome-themes': ThemesElement; + } +} + +customElements.define(ThemesElement.is, ThemesElement);
diff --git a/chrome/browser/resources/tab_search/BUILD.gn b/chrome/browser/resources/tab_search/BUILD.gn index 14712820..5c57ccd1 100644 --- a/chrome/browser/resources/tab_search/BUILD.gn +++ b/chrome/browser/resources/tab_search/BUILD.gn
@@ -45,9 +45,9 @@ css_files = [ "tab_group_shared_vars.css" ] mojo_files_deps = [ - "//chrome/browser/ui/webui/tab_search:mojo_bindings_webui_js", - "//chrome/browser/ui/webui/tabs:mojo_bindings_webui_js", - "//components/tab_groups/public/mojom:mojo_bindings_webui_js", + "//chrome/browser/ui/webui/tab_search:mojo_bindings_js__generator", + "//chrome/browser/ui/webui/tabs:mojo_bindings_js__generator", + "//components/tab_groups/public/mojom:mojo_bindings_js__generator", ] mojo_files = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/tab_search/tab_search.mojom-webui.js",
diff --git a/chrome/browser/resources/tab_strip/BUILD.gn b/chrome/browser/resources/tab_strip/BUILD.gn index 472379b1e..2a0eb34 100644 --- a/chrome/browser/resources/tab_strip/BUILD.gn +++ b/chrome/browser/resources/tab_strip/BUILD.gn
@@ -42,8 +42,8 @@ ] mojo_files_deps = [ - "//chrome/browser/ui/webui/tab_strip:mojo_bindings_webui_js", - "//chrome/browser/ui/webui/tabs:mojo_bindings_webui_js", + "//chrome/browser/ui/webui/tab_strip:mojo_bindings_js__generator", + "//chrome/browser/ui/webui/tabs:mojo_bindings_js__generator", ] mojo_files = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/tab_strip/tab_strip.mojom-webui.js",
diff --git a/chrome/browser/resources/usb_internals/BUILD.gn b/chrome/browser/resources/usb_internals/BUILD.gn index f6d283a..8c0d498 100644 --- a/chrome/browser/resources/usb_internals/BUILD.gn +++ b/chrome/browser/resources/usb_internals/BUILD.gn
@@ -19,9 +19,9 @@ ] mojo_files_deps = [ - "//chrome/browser/ui/webui/usb_internals:mojo_bindings_webui_js", - "//services/device/public/mojom:usb_test_js", - "//services/device/public/mojom:usb_webui_js", + "//chrome/browser/ui/webui/usb_internals:mojo_bindings_js__generator", + "//services/device/public/mojom:usb_js__generator", + "//services/device/public/mojom:usb_test_js__generator", ] mojo_files = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/usb_internals/usb_internals.mojom-webui.js",
diff --git a/chrome/browser/sync/test/integration/single_client_custom_passphrase_sync_test.cc b/chrome/browser/sync/test/integration/single_client_custom_passphrase_sync_test.cc index 6e7301b3..26fd100 100644 --- a/chrome/browser/sync/test/integration/single_client_custom_passphrase_sync_test.cc +++ b/chrome/browser/sync/test/integration/single_client_custom_passphrase_sync_test.cc
@@ -175,8 +175,8 @@ const GURL page_url1("https://google.com/"); const GURL page_url2("https://example.com/"); - SetEncryptionPassphraseForClient(/*index=*/0, "hunter2"); - ASSERT_TRUE(SetupSync(WAIT_FOR_SYNC_SETUP_TO_COMPLETE)); + ASSERT_TRUE(SetupSync()); + GetSyncService()->GetUserSettings()->SetEncryptionPassphrase("hunter2"); ASSERT_TRUE(AddURL(/*profile=*/0, title1, page_url1)); ASSERT_TRUE(AddURL(/*profile=*/0, title2, page_url2)); @@ -301,23 +301,22 @@ DoesNotLeakUnencryptedData) { const std::string title = "Should be encrypted"; const GURL page_url("https://google.com/encrypted"); - SetEncryptionPassphraseForClient(/*index=*/0, "hunter2"); ASSERT_TRUE(SetupClients()); - // Create local bookmarks before sync is enabled. + // Create local bookmarks before setting up sync. + CommittedBookmarkEntityNameObserver observer(GetFakeServer()); ASSERT_TRUE(AddURL(/*profile=*/0, title, page_url)); - CommittedBookmarkEntityNameObserver observer(GetFakeServer()); - ASSERT_TRUE(SetupSync(WAIT_FOR_SYNC_SETUP_TO_COMPLETE)); + // Mimic custom passphrase being set during initial sync setup. + ASSERT_TRUE(GetClient(0)->SignInPrimaryAccount()); + ASSERT_TRUE(GetClient(0)->AwaitEngineInitialization()); + GetSyncService()->GetUserSettings()->SetSyncRequested(true); + GetSyncService()->GetUserSettings()->SetEncryptionPassphrase("hunter2"); + GetClient(0)->FinishSyncSetup(); ASSERT_TRUE(WaitForNigori(PassphraseType::kCustomPassphrase)); - // If WaitForEncryptedServerBookmarks() succeeds, that means that a - // cryptographer initialized with only the key params was able to decrypt the - // data, so the data must be encrypted using a passphrase-derived key (and not - // e.g. a keystore key), because that cryptographer has never seen the - // server-side Nigori. Furthermore, if a bookmark commit has happened only - // once, we are certain that no bookmarks other than those we've verified to - // be encrypted have been committed. + // Ensure that only encrypted bookmarks were committed and that they are + // encrypted using custom passprhase. EXPECT_TRUE(WaitForEncryptedServerBookmarks({{title, page_url}}, /*passphrase=*/"hunter2")); EXPECT_THAT(observer.GetCommittedEntityNames(), ElementsAre("encrypted"));
diff --git a/chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc b/chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc index acf839c..9cc05ce 100644 --- a/chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc +++ b/chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc
@@ -207,8 +207,8 @@ // data even further. IN_PROC_BROWSER_TEST_F(SingleClientPasswordsSyncTestWithVerifier, CommitWithCustomPassphrase) { - SetEncryptionPassphraseForClient(/*index=*/0, "hunter2"); ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; + GetSyncService(0)->GetUserSettings()->SetEncryptionPassphrase("hunter2"); PasswordForm form = CreateTestPasswordForm(0); GetVerifierProfilePasswordStoreInterface()->AddLogin(form);
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index a73b4694..2fa82233 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -4497,8 +4497,6 @@ "views/extensions/extensions_request_access_button.h", "views/extensions/extensions_request_access_button_hover_card.cc", "views/extensions/extensions_request_access_button_hover_card.h", - "views/extensions/extensions_request_access_dialog_view.cc", - "views/extensions/extensions_request_access_dialog_view.h", "views/extensions/extensions_tabbed_menu_coordinator.cc", "views/extensions/extensions_tabbed_menu_coordinator.h", "views/extensions/extensions_tabbed_menu_view.cc",
diff --git a/chrome/browser/ui/ash/clipboard_history_browsertest.cc b/chrome/browser/ui/ash/clipboard_history_browsertest.cc index c310efcc..1c65c80 100644 --- a/chrome/browser/ui/ash/clipboard_history_browsertest.cc +++ b/chrome/browser/ui/ash/clipboard_history_browsertest.cc
@@ -350,8 +350,9 @@ base::test::ScopedFeatureList feature_list_; }; -// Verifies the history menu's ui interaction with the menu item selection. -IN_PROC_BROWSER_TEST_F(ClipboardHistoryBrowserTest, VerifySelectionBehavior) { +// Verifies the clipboard history menu response to mouse and arrow key inputs. +IN_PROC_BROWSER_TEST_F(ClipboardHistoryBrowserTest, + VerifyMouseAndArrowKeyTraversal) { SetClipboardText("A"); SetClipboardText("B"); SetClipboardText("C"); @@ -367,7 +368,7 @@ // The history menu's first item should be selected as default after the menu // shows. Meanwhile, its delete button should not show. - const views::MenuItemView* first_menu_item_view = + const views::MenuItemView* const first_menu_item_view = GetMenuItemViewForIndex(/*index=*/0); EXPECT_TRUE(first_menu_item_view->IsSelected()); EXPECT_FALSE(GetHistoryItemViewForIndex(/*index=*/0) @@ -376,7 +377,7 @@ EXPECT_EQ(gfx::Size(256, 36), first_menu_item_view->size()); // Move the mouse to the second menu item. - const views::MenuItemView* second_menu_item_view = + const views::MenuItemView* const second_menu_item_view = GetMenuItemViewForIndex(/*index=*/1); EXPECT_FALSE(second_menu_item_view->IsSelected()); GetEventGenerator()->MoveMouseTo( @@ -391,13 +392,13 @@ ->GetViewByID(MenuViewID::kDeleteButtonViewID) ->GetVisible()); - const views::MenuItemView* third_menu_item_view = + // Move the selection to the third item by pressing the arrow key. + const views::MenuItemView* const third_menu_item_view = GetMenuItemViewForIndex(/*index=*/2); EXPECT_FALSE(third_menu_item_view->IsSelected()); PressAndRelease(ui::KeyboardCode::VKEY_DOWN, ui::EF_NONE); - // Move the selection to the third item by pressing the arrow key. The third - // item should be selected and its delete button should not show. + // The third item should be selected and its delete button should not show. EXPECT_FALSE(second_menu_item_view->IsSelected()); EXPECT_TRUE(third_menu_item_view->IsSelected()); EXPECT_FALSE(GetHistoryItemViewForIndex(/*index=*/2) @@ -405,9 +406,57 @@ ->GetVisible()); } -// Verifies the selection traversal via the tab key. +// Verifies tab traversal behavior when there is only one item in clipboard +// history. IN_PROC_BROWSER_TEST_F(ClipboardHistoryBrowserTest, - VerifyTabSelectionTraversal) { + VerifySingleItemTabTraversal) { + SetClipboardText("A"); + ShowContextMenuViaAccelerator(/*wait_for_selection=*/true); + + // Verify the default state right after the menu shows. + ASSERT_TRUE(GetClipboardHistoryController()->IsMenuShowing()); + ASSERT_EQ(1u, GetContextMenu()->GetMenuItemsCount()); + + const views::MenuItemView* const menu_item_view = + GetMenuItemViewForIndex(/*index=*/0); + const ash::ClipboardHistoryItemView* const history_item_view = + GetHistoryItemViewForIndex(/*index=*/0); + + EXPECT_TRUE(menu_item_view->IsSelected()); + EXPECT_TRUE(history_item_view->IsMainButtonPseudoFocused()); + EXPECT_FALSE(history_item_view->IsDeleteButtonPseudoFocused()); + + // Press the Tab key. Verify that the history item's pseudo focus moves from + // the main button to the delete button. + PressAndRelease(ui::VKEY_TAB); + EXPECT_TRUE(menu_item_view->IsSelected()); + EXPECT_FALSE(history_item_view->IsMainButtonPseudoFocused()); + EXPECT_TRUE(history_item_view->IsDeleteButtonPseudoFocused()); + + // Verify that the history item's delete button shows. In addition, the + // delete button's inkdrop highlight should fade in or be visible because the + // button is focused. + const views::View* const delete_button = + history_item_view->GetViewByID(MenuViewID::kDeleteButtonViewID); + EXPECT_TRUE(delete_button->GetVisible()); + EXPECT_TRUE(views::InkDrop::Get(const_cast<views::View*>(delete_button)) + ->GetInkDrop() + ->IsHighlightFadingInOrVisible()); + + // Press the Tab key. Verify that the history item's pseudo focus moves from + // the delete button back to the main button and the delete button stops being + // visible. + PressAndRelease(ui::VKEY_TAB); + EXPECT_TRUE(menu_item_view->IsSelected()); + EXPECT_TRUE(history_item_view->IsMainButtonPseudoFocused()); + EXPECT_FALSE(history_item_view->IsDeleteButtonPseudoFocused()); + EXPECT_FALSE(delete_button->GetVisible()); +} + +// Verifies tab traversal behavior when there are multiple items in clipboard +// history. +IN_PROC_BROWSER_TEST_F(ClipboardHistoryBrowserTest, + VerifyMultiItemTabTraversal) { SetClipboardText("A"); SetClipboardText("B"); ShowContextMenuViaAccelerator(/*wait_for_selection=*/true); @@ -415,110 +464,71 @@ // Verify the default state right after the menu shows. ASSERT_TRUE(GetClipboardHistoryController()->IsMenuShowing()); ASSERT_EQ(2u, GetContextMenu()->GetMenuItemsCount()); - const views::MenuItemView* first_menu_item_view = + + const views::MenuItemView* const first_menu_item_view = GetMenuItemViewForIndex(/*index=*/0); - ASSERT_TRUE(first_menu_item_view->IsSelected()); - const ash::ClipboardHistoryItemView* first_history_item_view = - GetHistoryItemViewForIndex(/*index=*/0); - ASSERT_FALSE( - first_history_item_view->GetViewByID(MenuViewID::kDeleteButtonViewID) - ->GetVisible()); - - // Press the tab key. - PressAndRelease(ui::VKEY_TAB); - EXPECT_TRUE(first_menu_item_view->IsSelected()); - - // Verify that the first menu item's delete button shows. In addition, the - // delete button's inkdrop highlight should fade in or be visible. - const views::View* const delete_button = - first_history_item_view->GetViewByID(MenuViewID::kDeleteButtonViewID); - ASSERT_TRUE(delete_button->GetVisible()); - EXPECT_TRUE(views::InkDrop::Get(const_cast<views::View*>(delete_button)) - ->GetInkDrop() - ->IsHighlightFadingInOrVisible()); - - const views::MenuItemView* second_menu_item_view = + const views::MenuItemView* const second_menu_item_view = GetMenuItemViewForIndex(/*index=*/1); - EXPECT_FALSE(second_menu_item_view->IsSelected()); - const ash::ClipboardHistoryItemView* second_history_item_view = + const ash::ClipboardHistoryItemView* const first_history_item_view = + GetHistoryItemViewForIndex(/*index=*/0); + const ash::ClipboardHistoryItemView* const second_history_item_view = GetHistoryItemViewForIndex(/*index=*/1); - EXPECT_FALSE( - second_history_item_view->GetViewByID(MenuViewID::kDeleteButtonViewID) - ->GetVisible()); - // Press the tab key. Verify that the second menu item is selected while its - // delete button is hidden. + EXPECT_TRUE(first_menu_item_view->IsSelected()); + EXPECT_TRUE(first_history_item_view->IsMainButtonPseudoFocused()); + EXPECT_FALSE(first_history_item_view->IsDeleteButtonPseudoFocused()); + + EXPECT_FALSE(second_menu_item_view->IsSelected()); + EXPECT_FALSE(second_history_item_view->IsMainButtonPseudoFocused()); + EXPECT_FALSE(second_history_item_view->IsDeleteButtonPseudoFocused()); + + // Press the Tab key. Verify that the first menu item is still selected while + // the history item's pseudo focus moves from the main button to the delete + // button. + PressAndRelease(ui::VKEY_TAB); + EXPECT_TRUE(first_menu_item_view->IsSelected()); + EXPECT_FALSE(first_history_item_view->IsMainButtonPseudoFocused()); + EXPECT_TRUE(first_history_item_view->IsDeleteButtonPseudoFocused()); + + // Press the Tab key. Verify that the second menu item is selected and its + // main button has pseudo focus. PressAndRelease(ui::VKEY_TAB); EXPECT_TRUE(second_menu_item_view->IsSelected()); - EXPECT_FALSE( - second_history_item_view->GetViewByID(MenuViewID::kDeleteButtonViewID) - ->GetVisible()); + EXPECT_TRUE(second_history_item_view->IsMainButtonPseudoFocused()); + EXPECT_FALSE(second_history_item_view->IsDeleteButtonPseudoFocused()); - // Press the tab key. Verify that the second item's delete button shows. + // Press the Tab key. Verify that the second history item's pseudo focus moves + // from its main button to its delete button. PressAndRelease(ui::VKEY_TAB); EXPECT_TRUE(second_menu_item_view->IsSelected()); - EXPECT_TRUE( - second_history_item_view->GetViewByID(MenuViewID::kDeleteButtonViewID) - ->GetVisible()); + EXPECT_FALSE(second_history_item_view->IsMainButtonPseudoFocused()); + EXPECT_TRUE(second_history_item_view->IsDeleteButtonPseudoFocused()); - // Press the tab key with the shift key pressed. Verify that the second item - // is selected while its delete button is hidden. + // Press the Tab key with the Shift key pressed. Verify that the second + // history item's pseudo focus goes back to its main button. PressAndRelease(ui::VKEY_TAB, ui::EF_SHIFT_DOWN); EXPECT_TRUE(second_menu_item_view->IsSelected()); - EXPECT_FALSE( - second_history_item_view->GetViewByID(MenuViewID::kDeleteButtonViewID) - ->GetVisible()); + EXPECT_TRUE(second_history_item_view->IsMainButtonPseudoFocused()); + EXPECT_FALSE(second_history_item_view->IsDeleteButtonPseudoFocused()); - // Press the tab key with the shift key pressed. Verify that the first item - // is selected while its delete button is visible. + // Press the Tab key with the Shift key pressed. Verify that the first menu + // item is selected and its delete button has pseudo focus. PressAndRelease(ui::VKEY_TAB, ui::EF_SHIFT_DOWN); EXPECT_TRUE(first_menu_item_view->IsSelected()); - EXPECT_TRUE( - first_history_item_view->GetViewByID(MenuViewID::kDeleteButtonViewID) - ->GetVisible()); - EXPECT_FALSE(second_menu_item_view->IsSelected()); + EXPECT_FALSE(first_history_item_view->IsMainButtonPseudoFocused()); + EXPECT_TRUE(first_history_item_view->IsDeleteButtonPseudoFocused()); - // Press the ENTER key. Verifies that the first item is deleted. The second - // item is selected and its delete button should not show. + EXPECT_FALSE(second_menu_item_view->IsSelected()); + EXPECT_FALSE(second_history_item_view->IsMainButtonPseudoFocused()); + EXPECT_FALSE(second_history_item_view->IsDeleteButtonPseudoFocused()); + + // Press the Enter key. Verify that the first item is deleted. The second item + // should now be selected and its main button should have pseudo focus. PressAndRelease(ui::VKEY_RETURN); EXPECT_EQ(1u, GetContextMenu()->GetMenuItemsCount()); EXPECT_TRUE(second_menu_item_view->IsSelected()); - EXPECT_FALSE( - second_history_item_view->GetViewByID(MenuViewID::kDeleteButtonViewID) - ->GetVisible()); -} - -// Verifies the tab traversal on the history menu with only one item. -IN_PROC_BROWSER_TEST_F(ClipboardHistoryBrowserTest, - VerifyTabTraversalOnOneItemMenu) { - SetClipboardText("A"); - ShowContextMenuViaAccelerator(/*wait_for_selection=*/true); - - // Verify the default state right after the menu shows. - ASSERT_TRUE(GetClipboardHistoryController()->IsMenuShowing()); - ASSERT_EQ(1u, GetContextMenu()->GetMenuItemsCount()); - const ash::ClipboardHistoryItemView* first_history_item_view = - GetHistoryItemViewForIndex(/*index=*/0); - ASSERT_FALSE( - first_history_item_view->GetViewByID(MenuViewID::kDeleteButtonViewID) - ->GetVisible()); - const views::MenuItemView* first_menu_item_view = - GetMenuItemViewForIndex(/*index=*/0); - ASSERT_TRUE(first_menu_item_view->IsSelected()); - - // Press the tab key. Verify that the delete button is visible. - PressAndRelease(ui::VKEY_TAB); - ASSERT_TRUE( - first_history_item_view->GetViewByID(MenuViewID::kDeleteButtonViewID) - ->GetVisible()); - - // Press the tab key. Verify that the delete button is hidden. The menu item - // is still under selection. - PressAndRelease(ui::VKEY_TAB); - ASSERT_FALSE( - first_history_item_view->GetViewByID(MenuViewID::kDeleteButtonViewID) - ->GetVisible()); - EXPECT_TRUE(first_menu_item_view->IsSelected()); + EXPECT_TRUE(second_history_item_view->IsMainButtonPseudoFocused()); + EXPECT_FALSE(second_history_item_view->IsDeleteButtonPseudoFocused()); } // Verifies that the history menu is anchored at the cursor's location when
diff --git a/chrome/browser/ui/chrome_pages.h b/chrome/browser/ui/chrome_pages.h index c2499d8..44830334 100644 --- a/chrome/browser/ui/chrome_pages.h +++ b/chrome/browser/ui/chrome_pages.h
@@ -94,6 +94,7 @@ kFeedbackSourceChannelIndicator, kFeedbackSourceLauncher, kFeedbackSourceSettingsPerformancePage, + kFeedbackSourceQuickOffice, // Must be last. kFeedbackSourceCount,
diff --git a/chrome/browser/ui/color/new_tab_page_color_mixer.cc b/chrome/browser/ui/color/new_tab_page_color_mixer.cc index ad64a28..50c2359 100644 --- a/chrome/browser/ui/color/new_tab_page_color_mixer.cc +++ b/chrome/browser/ui/color/new_tab_page_color_mixer.cc
@@ -312,7 +312,7 @@ /* 10% opacity */ 0.1 * SK_AlphaOPAQUE); mixer[kColorRealboxResultsDimSelected] = { kColorOmniboxResultsBackgroundSelected}; - mixer[kColorRealboxResultsForeground] = {ui::kColorTextfieldForeground}; + mixer[kColorRealboxResultsForeground] = {kColorOmniboxText}; mixer[kColorRealboxResultsForegroundDimmed] = { kColorOmniboxResultsTextDimmed}; mixer[kColorRealboxResultsIcon] = {kColorOmniboxResultsIcon};
diff --git a/chrome/browser/ui/passwords/password_generation_popup_controller.h b/chrome/browser/ui/passwords/password_generation_popup_controller.h index cb4ef1e..df42c3b4 100644 --- a/chrome/browser/ui/passwords/password_generation_popup_controller.h +++ b/chrome/browser/ui/passwords/password_generation_popup_controller.h
@@ -40,6 +40,7 @@ virtual GenerationUIState state() const = 0; virtual bool password_selected() const = 0; virtual const std::u16string& password() const = 0; + virtual bool IsUserTypedPasswordWeak() const = 0; // Translated strings virtual std::u16string SuggestedText() = 0;
diff --git a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc index 190d4e4..dbe10279 100644 --- a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc +++ b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc
@@ -226,6 +226,18 @@ weak_this->HideImpl(); } +// TODO(crbug.com/1345766): Add test checking that delayed call to this function +// does not hide generation popup triggered by an empty password field. +void PasswordGenerationPopupControllerImpl::OnWeakCheckComplete(bool is_weak) { + user_typed_password_is_weak_ = is_weak; + + if (is_weak) { + Show(kOfferGeneration); + } else if (!user_typed_password_.empty()) { + HideImpl(); + } +} + void PasswordGenerationPopupControllerImpl::Show(GenerationUIState state) { // When switching from editing to generation state, regenerate the password. if (state == kOfferGeneration && @@ -270,6 +282,28 @@ observer_->OnPopupShown(state_); } +void PasswordGenerationPopupControllerImpl:: + UpdatePopupBasedOnTypedPasswordStrength() { + if (user_typed_password_.empty()) { + user_typed_password_is_weak_ = false; + Show(kOfferGeneration); + return; + } + +#if !BUILDFLAG(IS_ANDROID) + if (!password_strength_calculation_) { + password_strength_calculation_ = + std::make_unique<password_manager::PasswordStrengthCalculation>(); + } + password_manager::PasswordStrengthCalculation::CompletionCallback completion = + base::BindOnce( + &PasswordGenerationPopupControllerImpl::OnWeakCheckComplete, + weak_ptr_factory_.GetWeakPtr()); + password_strength_calculation_->CheckPasswordWeakInSandbox( + base::UTF16ToUTF8(user_typed_password_), std::move(completion)); +#endif // !BUILDFLAG(IS_ANDROID) +} + void PasswordGenerationPopupControllerImpl::UpdateTypedPassword( const std::u16string& new_user_typed_password) { user_typed_password_ = new_user_typed_password; @@ -407,3 +441,7 @@ const std::u16string& PasswordGenerationPopupControllerImpl::HelpText() { return help_text_; } + +bool PasswordGenerationPopupControllerImpl::IsUserTypedPasswordWeak() const { + return user_typed_password_is_weak_; +}
diff --git a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.h b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.h index a382dcb..35efbf9 100644 --- a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.h +++ b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.h
@@ -24,6 +24,7 @@ #include "ui/gfx/native_widget_types.h" #if !BUILDFLAG(IS_ANDROID) +#include "components/password_manager/core/browser/password_strength_calculation.h" #include "components/zoom/zoom_observer.h" #endif // !BUILDFLAG(IS_ANDROID) @@ -90,6 +91,12 @@ // Create a PasswordGenerationPopupView if one doesn't already exist. void Show(GenerationUIState state); + // Updates popup based on the strength of the password typed by the user. + // If typed password is empty, creates a popup without the strength indicator. + // If typed password is weak, creates a popup with the strength indicator. + // If typed password is not weak, hides the popup (if one exists). + void UpdatePopupBasedOnTypedPasswordStrength(); + // Update the password typed by the user. void UpdateTypedPassword(const std::u16string& new_user_typed_password); @@ -163,6 +170,7 @@ const std::u16string& password() const override; std::u16string SuggestedText() override; const std::u16string& HelpText() override; + bool IsUserTypedPasswordWeak() const override; bool HandleKeyPressEvent(const content::NativeWebKeyboardEvent& event); @@ -172,6 +180,10 @@ // Accept password if it's selected. bool PossiblyAcceptPassword(); + // Displays password generation dropdown with strength indicator when + // `is_weak` is true, hides the dropdown otherwise. + void OnWeakCheckComplete(bool is_weak); + // Handle to the popup. May be NULL if popup isn't showing. raw_ptr<PasswordGenerationPopupView> view_; @@ -205,16 +217,25 @@ // strength. std::u16string user_typed_password_; + // Whether the password currently typed by the user is weak. + bool user_typed_password_is_weak_ = false; + // The current password that is considered generated. This is the password to // be displayed in the user generation dialog. std::u16string current_generated_password_; // Whether the row with the password is currently selected/highlighted. - bool password_selected_; + bool password_selected_ = false; // The state of the generation popup. GenerationUIState state_; +#if !BUILDFLAG(IS_ANDROID) + // Calculates password strength in a sandboxed utility process. + std::unique_ptr<password_manager::PasswordStrengthCalculation> + password_strength_calculation_; +#endif + std::unique_ptr<KeyPressRegistrator> key_press_handler_manager_; base::WeakPtrFactory<PasswordGenerationPopupControllerImpl> weak_ptr_factory_{
diff --git a/chrome/browser/ui/passwords/password_generation_popup_view_browsertest.cc b/chrome/browser/ui/passwords/password_generation_popup_view_browsertest.cc index 051cb79..fb4fd65e 100644 --- a/chrome/browser/ui/passwords/password_generation_popup_view_browsertest.cc +++ b/chrome/browser/ui/passwords/password_generation_popup_view_browsertest.cc
@@ -7,6 +7,8 @@ #include <string> #include "base/memory/weak_ptr.h" +#include "base/test/scoped_feature_list.h" +#include "chrome/browser/password_manager/password_manager_uitest_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/passwords/password_generation_popup_controller_impl.h" @@ -14,6 +16,7 @@ #include "chrome/test/base/in_process_browser_test.h" #include "components/password_manager/content/browser/content_password_manager_driver.h" #include "components/password_manager/content/browser/content_password_manager_driver_factory.h" +#include "components/password_manager/core/common/password_manager_features.h" #include "content/public/test/browser_test.h" #include "testing/gtest/include/gtest/gtest.h" @@ -21,6 +24,20 @@ class PasswordGenerationPopupViewTest : public InProcessBrowserTest {}; +class PasswordGenerationPopupViewWithStrengthIndicatorTest + : public InProcessBrowserTest { + public: + PasswordGenerationPopupViewWithStrengthIndicatorTest() { + feature_list_.InitWithFeatures( + /*enabled_features=*/{password_manager::features:: + kPasswordStrengthIndicator}, + /*disabled_features=*/{}); + } + + private: + base::test::ScopedFeatureList feature_list_; +}; + // Regression test for crbug.com/400543. Verifying that moving the mouse in the // editing dialog doesn't crash. IN_PROC_BROWSER_TEST_F(PasswordGenerationPopupViewTest, @@ -118,4 +135,101 @@ EXPECT_FALSE(controller); } +IN_PROC_BROWSER_TEST_F(PasswordGenerationPopupViewWithStrengthIndicatorTest, + ShowsPopupWithEmptyPasswordField) { + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + password_generation::PasswordGenerationUIData ui_data( + gfx::RectF(web_contents->GetContainerBounds().x(), + web_contents->GetContainerBounds().y(), 10, 10), + /*max_length=*/10, + /*generation_element=*/std::u16string(), + /*user_typed_password=*/std::u16string(), FieldRendererId(100), + /*is_generation_element_password_type=*/true, base::i18n::TextDirection(), + FormData()); + + base::WeakPtr<PasswordGenerationPopupControllerImpl> controller = + PasswordGenerationPopupControllerImpl::GetOrCreate( + /*previous=*/nullptr, ui_data.bounds, ui_data, + password_manager::ContentPasswordManagerDriverFactory:: + FromWebContents(web_contents) + ->GetDriverForFrame(web_contents->GetPrimaryMainFrame()) + ->AsWeakPtr(), + /*observer=*/nullptr, web_contents, + web_contents->GetPrimaryMainFrame()); + + EXPECT_FALSE(controller->IsVisible()); + controller->UpdatePopupBasedOnTypedPasswordStrength(); + EXPECT_TRUE(controller->IsVisible()); + + web_contents->Close(); +} + +IN_PROC_BROWSER_TEST_F(PasswordGenerationPopupViewWithStrengthIndicatorTest, + ShowsPopupWithWeakPasswordTyped) { + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + password_generation::PasswordGenerationUIData ui_data( + gfx::RectF(web_contents->GetContainerBounds().x(), + web_contents->GetContainerBounds().y(), 10, 10), + /*max_length=*/10, + /*generation_element=*/std::u16string(), + /*user_typed_password=*/std::u16string(), FieldRendererId(100), + /*is_generation_element_password_type=*/true, base::i18n::TextDirection(), + FormData()); + + TestGenerationPopupObserver observer; + base::WeakPtr<PasswordGenerationPopupControllerImpl> controller = + PasswordGenerationPopupControllerImpl::GetOrCreate( + /*previous=*/nullptr, ui_data.bounds, ui_data, + password_manager::ContentPasswordManagerDriverFactory:: + FromWebContents(web_contents) + ->GetDriverForFrame(web_contents->GetPrimaryMainFrame()) + ->AsWeakPtr(), + &observer, web_contents, web_contents->GetPrimaryMainFrame()); + + EXPECT_FALSE(controller->IsVisible()); + controller->UpdateTypedPassword(u"weak"); + controller->UpdatePopupBasedOnTypedPasswordStrength(); + observer.WaitForStatus(TestGenerationPopupObserver::GenerationPopup::kShown); + EXPECT_TRUE(controller->IsVisible()); + + web_contents->Close(); +} + +IN_PROC_BROWSER_TEST_F(PasswordGenerationPopupViewWithStrengthIndicatorTest, + HidesPopupWithStrongPasswordTyped) { + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + password_generation::PasswordGenerationUIData ui_data( + gfx::RectF(web_contents->GetContainerBounds().x(), + web_contents->GetContainerBounds().y(), 10, 10), + /*max_length=*/10, + /*generation_element=*/std::u16string(), + /*user_typed_password=*/std::u16string(), FieldRendererId(100), + /*is_generation_element_password_type=*/true, base::i18n::TextDirection(), + FormData()); + + TestGenerationPopupObserver observer; + base::WeakPtr<PasswordGenerationPopupControllerImpl> controller = + PasswordGenerationPopupControllerImpl::GetOrCreate( + /*previous=*/nullptr, ui_data.bounds, ui_data, + password_manager::ContentPasswordManagerDriverFactory:: + FromWebContents(web_contents) + ->GetDriverForFrame(web_contents->GetPrimaryMainFrame()) + ->AsWeakPtr(), + &observer, web_contents, web_contents->GetPrimaryMainFrame()); + + // Make the popup visible first. + EXPECT_FALSE(controller->IsVisible()); + controller->UpdatePopupBasedOnTypedPasswordStrength(); + EXPECT_TRUE(controller->IsVisible()); + + // Popup should be hidden and controller destroyed with strong password typed. + controller->UpdateTypedPassword(u"fnxsr4@cm^mdls#fkbhisg3d"); + controller->UpdatePopupBasedOnTypedPasswordStrength(); + observer.WaitForStatus(TestGenerationPopupObserver::GenerationPopup::kHidden); + EXPECT_FALSE(controller); +} + } // namespace autofill
diff --git a/chrome/browser/ui/side_search/side_search_tab_contents_helper_unittest.cc b/chrome/browser/ui/side_search/side_search_tab_contents_helper_unittest.cc index 092e7892..8bca817 100644 --- a/chrome/browser/ui/side_search/side_search_tab_contents_helper_unittest.cc +++ b/chrome/browser/ui/side_search/side_search_tab_contents_helper_unittest.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "base/feature_list.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "chrome/browser/ui/side_search/side_search_config.h" @@ -116,6 +117,11 @@ }; TEST_F(SideSearchTabContentsHelperTest, LastSearchURLUpdatesCorrectly) { + // TODO(crbug.com/1384174): Update this test to pass with the + // kUnifiedSidePanel flag enabled. + if (base::FeatureList::IsEnabled(features::kUnifiedSidePanel)) + GTEST_SKIP(); + // When a tab is first opened there should be no last encountered search URL. EXPECT_FALSE(helper()->last_search_url().has_value()); EXPECT_TRUE(GetLastCommittedSideContentsEntry()->IsInitialEntry()); @@ -162,6 +168,11 @@ } TEST_F(SideSearchTabContentsHelperTest, IndicatesWhenSidePanelShouldBeShown) { + // TODO(crbug.com/1384174): Update this test to pass with the + // kUnifiedSidePanel flag enabled. + if (base::FeatureList::IsEnabled(features::kUnifiedSidePanel)) + GTEST_SKIP(); + // With no initial navigation the side panel should not be showing. EXPECT_FALSE(helper()->CanShowSidePanelForCommittedNavigation()); @@ -217,6 +228,11 @@ } TEST_F(SideSearchTabContentsHelperTest, EmitsReturnedToSRPMetrics) { + // TODO(crbug.com/1384174): Update this test to pass with the + // kUnifiedSidePanel flag enabled. + if (base::FeatureList::IsEnabled(features::kUnifiedSidePanel)) + GTEST_SKIP(); + // Navigating to a matching search. Then navigate to a non-matching URL and // navigate back, doing so twice. LoadURL(kSearchMatchUrl1);
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc index 119e55ab..16f6a85a 100644 --- a/chrome/browser/ui/ui_features.cc +++ b/chrome/browser/ui/ui_features.cc
@@ -312,7 +312,7 @@ BASE_FEATURE(kUnifiedSidePanel, "UnifiedSidePanel", - base::FEATURE_DISABLED_BY_DEFAULT); + base::FEATURE_ENABLED_BY_DEFAULT); // This enables enables persistence of a WebContents in a 1-to-1 association // with the current Profile for WebUI bubbles. See https://crbug.com/1177048.
diff --git a/chrome/browser/ui/views/accelerator_table.cc b/chrome/browser/ui/views/accelerator_table.cc index f6d5a034..a3ecd7f 100644 --- a/chrome/browser/ui/views/accelerator_table.cc +++ b/chrome/browser/ui/views/accelerator_table.cc
@@ -6,6 +6,8 @@ #include <stddef.h> +#include <vector> + #include "base/containers/contains.h" #include "base/feature_list.h" #include "base/no_destructor.h" @@ -237,7 +239,8 @@ {ui::VKEY_SPACE, ui::EF_CONTROL_DOWN, IDC_TOGGLE_QUICK_COMMANDS}, #endif // !BUILDFLAG(IS_CHROMEOS) #endif // !BUILDFLAG(IS_MAC) -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) && \ + (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) {ui::VKEY_S, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN, IDC_RUN_SCREEN_AI_VISUAL_ANNOTATIONS}, #endif
diff --git a/chrome/browser/ui/views/extensions/extensions_request_access_button.cc b/chrome/browser/ui/views/extensions/extensions_request_access_button.cc index 0307c4a..d93b52d1 100644 --- a/chrome/browser/ui/views/extensions/extensions_request_access_button.cc +++ b/chrome/browser/ui/views/extensions/extensions_request_access_button.cc
@@ -19,7 +19,6 @@ #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h" #include "chrome/browser/ui/views/extensions/extensions_dialogs_utils.h" #include "chrome/browser/ui/views/extensions/extensions_request_access_button_hover_card.h" -#include "chrome/browser/ui/views/extensions/extensions_request_access_dialog_view.h" #include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h" #include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h" #include "chrome/grit/generated_resources.h" @@ -50,9 +49,6 @@ } void ExtensionsRequestAccessButton::MaybeShowHoverCard() { - // TODO(crbug.com/1319555): Don't show the hover card if the dialog opened by - // pressing the button is still open. This will be easier to add once we - // address the TODO below for blocked action dialog. if (ExtensionsRequestAccessButtonHoverCard::IsShowing() || !GetWidget()->IsMouseEventsEnabled()) return;
diff --git a/chrome/browser/ui/views/extensions/extensions_request_access_dialog_view.cc b/chrome/browser/ui/views/extensions/extensions_request_access_dialog_view.cc deleted file mode 100644 index 81071da..0000000 --- a/chrome/browser/ui/views/extensions/extensions_request_access_dialog_view.cc +++ /dev/null
@@ -1,68 +0,0 @@ -// Copyright 2022 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/ui/views/extensions/extensions_request_access_dialog_view.h" - -#include "base/callback_helpers.h" -#include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h" -#include "chrome/browser/ui/views/extensions/extensions_dialogs_utils.h" -#include "chrome/grit/generated_resources.h" -#include "content/public/browser/web_contents.h" -#include "ui/base/l10n/l10n_util.h" -#include "ui/base/models/dialog_model.h" -#include "ui/views/bubble/bubble_dialog_model_host.h" - -namespace { - -std::u16string GetTitle( - const std::vector<ToolbarActionViewController*>& actions, - std::u16string current_site) { - if (actions.size() == 1) { - return l10n_util::GetStringFUTF16( - IDS_EXTENSIONS_REQUEST_ACCESS_BUBBLE_SINGLE_EXTENSION_TITLE, - actions[0]->GetActionName(), current_site); - } - return l10n_util::GetStringFUTF16( - IDS_EXTENSIONS_REQUEST_ACCESS_BUBBLE_MULTIPLE_EXTENSIONS_TITLE, - current_site); -} - -} // namespace - -void ShowExtensionsRequestAccessDialogView( - content::WebContents* web_contents, - views::View* anchor_view, - std::vector<ToolbarActionViewController*> actions) { - DCHECK(!actions.empty()); - DCHECK(web_contents); - - ui::DialogModel::Builder dialog_builder = - ui::DialogModel::Builder(std::make_unique<ui::DialogModelDelegate>()); - dialog_builder - .SetTitle(GetTitle(actions, GetCurrentHost(web_contents))) - // TODO(crbug.com/1239772): Grant access to all the extensions when dialog - // is accepted - .AddOkButton(base::DoNothing(), - l10n_util::GetStringUTF16( - IDS_EXTENSIONS_REQUEST_ACCESS_BUBBLE_OK_BUTTON_LABEL)) - .AddCancelButton( - base::DoNothing(), - l10n_util::GetStringUTF16( - IDS_EXTENSIONS_REQUEST_ACCESS_BUBBLE_CANCEL_BUTTON_LABEL)); - - if (actions.size() == 1) { - dialog_builder.SetIcon(GetIcon(actions[0], web_contents)); - } else { - for (auto* action : actions) { - dialog_builder.AddMenuItem( - GetIcon(action, web_contents), action->GetActionName(), - base::DoNothing(), - ui::DialogModelMenuItem::Params().SetIsEnabled(false)); - } - } - - auto bubble = std::make_unique<views::BubbleDialogModelHost>( - dialog_builder.Build(), anchor_view, views::BubbleBorder::TOP_RIGHT); - views::BubbleDialogDelegate::CreateBubble(std::move(bubble))->Show(); -}
diff --git a/chrome/browser/ui/views/extensions/extensions_request_access_dialog_view.h b/chrome/browser/ui/views/extensions/extensions_request_access_dialog_view.h deleted file mode 100644 index bd34b03..0000000 --- a/chrome/browser/ui/views/extensions/extensions_request_access_dialog_view.h +++ /dev/null
@@ -1,25 +0,0 @@ -// Copyright 2022 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSIONS_REQUEST_ACCESS_DIALOG_VIEW_H_ -#define CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSIONS_REQUEST_ACCESS_DIALOG_VIEW_H_ - -#include <vector> - -namespace content { -class WebContents; -} // namespace content - -namespace views { -class View; -} // namespace views - -class ToolbarActionViewController; - -void ShowExtensionsRequestAccessDialogView( - content::WebContents* web_contents, - views::View* anchor_view, - std::vector<ToolbarActionViewController*> extensions); - -#endif // CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSIONS_REQUEST_ACCESS_DIALOG_VIEW_H_
diff --git a/chrome/browser/ui/views/extensions/extensions_request_access_dialog_view_browsertest.cc b/chrome/browser/ui/views/extensions/extensions_request_access_dialog_view_browsertest.cc deleted file mode 100644 index 201df3b6..0000000 --- a/chrome/browser/ui/views/extensions/extensions_request_access_dialog_view_browsertest.cc +++ /dev/null
@@ -1,46 +0,0 @@ -// Copyright 2022 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/ui/views/extensions/extensions_request_access_dialog_view.h" - -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/toolbar/test_toolbar_action_view_controller.h" -#include "chrome/browser/ui/views/extensions/extensions_dialogs_browsertest.h" -#include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h" -#include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h" -#include "content/public/test/browser_test.h" -#include "extensions/common/extension_features.h" - -class ExtensionsRequestAccessDialogViewBrowserTest - : public ExtensionsDialogBrowserTest { - public: - // DialogBrowserTest: - void ShowUi(const std::string& name) override { - // Install extension so the extensions menu button is visible and can serve - // as the dialog's anchor point. - InstallExtension("Extension"); - views::View* const anchor_view = - extensions_container()->GetExtensionsButton(); - EXPECT_TRUE(anchor_view->GetVisible()); - - auto controller_A = std::make_unique<TestToolbarActionViewController>("A"); - std::vector<ToolbarActionViewController*> extensions_requesting_access; - extensions_requesting_access.push_back(controller_A.get()); - - ShowExtensionsRequestAccessDialogView( - browser()->tab_strip_model()->GetActiveWebContents(), anchor_view, - extensions_requesting_access); - } - - private: - base::test::ScopedFeatureList scoped_feature_list_{ - extensions_features::kExtensionsMenuAccessControl}; -}; - -// TODO(crbug.com/1339738): Flaky on win-clang and win/win64 trunk builds. -// ExtensionsRequestAccessDialog may not longer be used, wait to see if the class is -// deleted before fixing this. -IN_PROC_BROWSER_TEST_F(ExtensionsRequestAccessDialogViewBrowserTest, DISABLED_InvokeUi) { - ShowAndVerifyUi(); -}
diff --git a/chrome/browser/ui/views/frame/DEPS b/chrome/browser/ui/views/frame/DEPS index 98d14392..a7be0a4e 100644 --- a/chrome/browser/ui/views/frame/DEPS +++ b/chrome/browser/ui/views/frame/DEPS
@@ -10,6 +10,10 @@ "dbus_appmenu\.*": [ "+dbus", ], + # Parts of this class use Ash, but only when run in classic Ash mode. + "picture_in_picture_browser_frame_view\.*": [ + "+ash", + ], "top_controls_slide_controller_chromeos_browsertest.cc": [ "+cc/base/math_util.h", ],
diff --git a/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc b/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc index c7ff2b5..29ca536 100644 --- a/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc +++ b/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc
@@ -39,6 +39,14 @@ #include "chrome/browser/ui/views/frame/desktop_browser_frame_aura_linux.h" #endif +#if BUILDFLAG(IS_CHROMEOS_ASH) +#include "ash/wm/window_util.h" +#endif + +#if BUILDFLAG(IS_CHROMEOS_LACROS) +#include "chromeos/ui/frame/interior_resize_handler_targeter.h" +#endif + namespace { // TODO(https://crbug.com/1346734): Check whether any of the below should be @@ -50,13 +58,12 @@ // The height of the controls bar at the top of the window. constexpr int kTopControlsHeight = 30; +#if BUILDFLAG(IS_LINUX) // Frame border when window shadow is not drawn. constexpr int kFrameBorderThickness = 4; - -#if BUILDFLAG(IS_LINUX) -constexpr int kResizeBorder = 10; #endif +constexpr int kResizeBorder = 10; constexpr int kResizeAreaCornerSize = 16; // The window has a smaller minimum size than normal Chrome windows. @@ -164,6 +171,16 @@ #if BUILDFLAG(IS_LINUX) frame_background_ = std::make_unique<views::FrameBackground>(); #endif + +#if BUILDFLAG(IS_CHROMEOS_ASH) + ash::window_util::InstallResizeHandleWindowTargeterForWindow( + frame->GetNativeWindow()); +#endif + +#if BUILDFLAG(IS_CHROMEOS_LACROS) + frame->GetNativeWindow()->SetEventTargeter( + std::make_unique<chromeos::InteriorResizeHandleTargeter>()); +#endif } PictureInPictureBrowserFrameView::~PictureInPictureBrowserFrameView() = default; @@ -222,7 +239,7 @@ // Allow dragging and resizing the window. int window_component = GetHTComponentForFrame( - point, FrameBorderInsets(), kResizeAreaCornerSize, kResizeAreaCornerSize, + point, ResizeBorderInsets(), kResizeAreaCornerSize, kResizeAreaCornerSize, GetWidget()->widget_delegate()->CanResize()); if (window_component != HTNOWHERE) return window_component; @@ -608,7 +625,15 @@ ShouldDrawFrameShadow(), gfx::Insets(kFrameBorderThickness), frame()->tiled_edges(), GetShadowValues(), kResizeBorder); #else - return gfx::Insets(kFrameBorderThickness); + return gfx::Insets(); +#endif +} + +gfx::Insets PictureInPictureBrowserFrameView::ResizeBorderInsets() const { +#if BUILDFLAG(IS_LINUX) + return FrameBorderInsets(); +#else + return gfx::Insets(kResizeBorder); #endif }
diff --git a/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.h b/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.h index 6dcdc9c..57408db 100644 --- a/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.h +++ b/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.h
@@ -147,6 +147,9 @@ // Returns the insets of the window frame borders. gfx::Insets FrameBorderInsets() const; + // Returns the insets of the window frame borders for resizing. + gfx::Insets ResizeBorderInsets() const; + // Returns the height of the top bar area, including the window top border. int GetTopAreaHeight() const;
diff --git a/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc b/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc index d7cce4d..2addf34 100644 --- a/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc +++ b/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc
@@ -276,10 +276,7 @@ const int kHorizontalMargin = provider->GetDistanceMetric(DISTANCE_UNRELATED_CONTROL_HORIZONTAL); - if (base::FeatureList::IsEnabled( - password_manager::features::kPasswordStrengthIndicator)) { - // TODO(crbug.com/1345766): Adjust according to the calculated password - // strength. + if (controller_->IsUserTypedPasswordWeak()) { auto* password_strength_view = AddChildView(CreatePasswordStrengthView( l10n_util::GetStringUTF16(IDS_PASSWORD_WEAKNESS_INDICATOR))); password_strength_view->SetBorder(views::CreateEmptyBorder(
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc index e720fb6c..201197c2 100644 --- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc +++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
@@ -589,14 +589,6 @@ helper_.CheckInstallIconNotShown(); } -IN_PROC_BROWSER_TEST_F(WebAppIntegration, WAI_29MinimalUiBrowser) { - // Test contents are generated by script. Please do not modify! - // See `docs/webapps/why-is-this-test-failing.md` or - // `docs/webapps/integration-testing-framework` for more info. - // Sheriffs: Disabling this test is supported. - helper_.CreateShortcut(Site::kMinimalUi, WindowOptions::kBrowser); -} - IN_PROC_BROWSER_TEST_F( WebAppIntegration, WAI_29StandaloneWindowed_24_12Standalone_7Standalone_112StandaloneNotShown_39StandaloneMinimalUi_16_27_14) { @@ -3917,26 +3909,6 @@ helper_.CheckLaunchIconNotShown(); } -IN_PROC_BROWSER_TEST_F(WebAppIntegration, - WAI_32MinimalUiWithShortcutBrowserWebApp) { - // Test contents are generated by script. Please do not modify! - // See `docs/webapps/why-is-this-test-failing.md` or - // `docs/webapps/integration-testing-framework` for more info. - // Sheriffs: Disabling this test is supported. - helper_.InstallPolicyApp(Site::kMinimalUi, ShortcutOptions::kWithShortcut, - WindowOptions::kBrowser, InstallMode::kWebApp); -} - -IN_PROC_BROWSER_TEST_F(WebAppIntegration, - WAI_32MinimalUiNoShortcutBrowserWebApp) { - // Test contents are generated by script. Please do not modify! - // See `docs/webapps/why-is-this-test-failing.md` or - // `docs/webapps/integration-testing-framework` for more info. - // Sheriffs: Disabling this test is supported. - helper_.InstallPolicyApp(Site::kMinimalUi, ShortcutOptions::kNoShortcut, - WindowOptions::kBrowser, InstallMode::kWebApp); -} - IN_PROC_BROWSER_TEST_F( WebAppIntegration, WAI_29StandaloneWindowed_24_12Standalone_7Standalone_112StandaloneNotShown_73_37Standalone_19) { @@ -4883,5 +4855,43 @@ helper_.CheckWindowControlsOverlay(Site::kWco, IsOn::kOn); } +IN_PROC_BROWSER_TEST_F( + WebAppIntegration, + WAI_31Standalone_24_12Standalone_7Standalone_112StandaloneNotShown_68StandaloneAcceptUpdate_117Standalone_110StandaloneRed) { + // Test contents are generated by script. Please do not modify! + // See `docs/webapps/why-is-this-test-failing.md` or + // `docs/webapps/integration-testing-framework` for more info. + // Sheriffs: Disabling this test is supported. + helper_.InstallOmniboxIcon(InstallableSite::kStandalone); + helper_.CheckWindowCreated(); + helper_.CheckAppInListWindowed(Site::kStandalone); + helper_.CheckPlatformShortcutAndIcon(Site::kStandalone); + helper_.CheckWindowControlsOverlayToggle(Site::kStandalone, + IsShown::kNotShown); + helper_.ManifestUpdateIcon(Site::kStandalone, + UpdateDialogResponse::kAcceptUpdate); + helper_.AwaitManifestUpdate(Site::kStandalone); + helper_.CheckAppIcon(Site::kStandalone, Color::kRed); +} + +IN_PROC_BROWSER_TEST_F( + WebAppIntegration, + WAI_47Standalone_24_12Standalone_7Standalone_112StandaloneNotShown_68StandaloneAcceptUpdate_117Standalone_110StandaloneRed) { + // Test contents are generated by script. Please do not modify! + // See `docs/webapps/why-is-this-test-failing.md` or + // `docs/webapps/integration-testing-framework` for more info. + // Sheriffs: Disabling this test is supported. + helper_.InstallMenuOption(InstallableSite::kStandalone); + helper_.CheckWindowCreated(); + helper_.CheckAppInListWindowed(Site::kStandalone); + helper_.CheckPlatformShortcutAndIcon(Site::kStandalone); + helper_.CheckWindowControlsOverlayToggle(Site::kStandalone, + IsShown::kNotShown); + helper_.ManifestUpdateIcon(Site::kStandalone, + UpdateDialogResponse::kAcceptUpdate); + helper_.AwaitManifestUpdate(Site::kStandalone); + helper_.CheckAppIcon(Site::kStandalone, Color::kRed); +} + } // namespace } // namespace web_app::integration_tests
diff --git a/chrome/browser/ui/webui/chromeos/chrome_web_ui_configs_chromeos.cc b/chrome/browser/ui/webui/chromeos/chrome_web_ui_configs_chromeos.cc index 2011d22..00ca936 100644 --- a/chrome/browser/ui/webui/chromeos/chrome_web_ui_configs_chromeos.cc +++ b/chrome/browser/ui/webui/chromeos/chrome_web_ui_configs_chromeos.cc
@@ -14,6 +14,7 @@ #include "ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.h" #include "ash/webui/system_extensions_internals_ui/system_extensions_internals_ui.h" #include "chrome/browser/ash/web_applications/camera_app/chrome_camera_app_ui_delegate.h" +#include "chrome/browser/ash/web_applications/shortcut_customization_ui/chrome_shortcut_customization_delegate.h" #include "chrome/browser/ui/webui/ash/account_manager/account_manager_error_ui.h" #include "chrome/browser/ui/webui/ash/account_manager/account_migration_welcome_ui.h" #include "chrome/browser/ui/webui/ash/add_supervision/add_supervision_ui.h" @@ -90,6 +91,11 @@ map.AddWebUIConfig( MakeComponentConfig<ash::CameraAppUIConfig, ash::CameraAppUI, ChromeCameraAppUIDelegate>()); + map.AddWebUIConfig( + MakeComponentConfig<ash::ShortcutCustomizationAppUIConfig, + ash::ShortcutCustomizationAppUI, + ChromeShortcutCustomizationDelegate>()); + map.AddWebUIConfig(std::make_unique<ash::AccountManagerErrorUIConfig>()); map.AddWebUIConfig(std::make_unique<ash::AccountMigrationWelcomeUIConfig>()); map.AddWebUIConfig(std::make_unique<ash::AddSupervisionUIConfig>()); @@ -133,7 +139,6 @@ map.AddWebUIConfig(std::make_unique<ash::PasswordChangeUIConfig>()); map.AddWebUIConfig(std::make_unique<ash::PowerUIConfig>()); map.AddWebUIConfig(std::make_unique<ash::SetTimeUIConfig>()); - map.AddWebUIConfig(std::make_unique<ash::ShortcutCustomizationAppUIConfig>()); map.AddWebUIConfig(std::make_unique<ash::SlowTraceControllerConfig>()); map.AddWebUIConfig(std::make_unique<ash::SlowUIConfig>()); map.AddWebUIConfig(
diff --git a/chrome/browser/ui/webui/history_clusters/history_clusters_handler.cc b/chrome/browser/ui/webui/history_clusters/history_clusters_handler.cc index 8fa1ef2..834bab1 100644 --- a/chrome/browser/ui/webui/history_clusters/history_clusters_handler.cc +++ b/chrome/browser/ui/webui/history_clusters/history_clusters_handler.cc
@@ -512,27 +512,23 @@ const std::vector<history::Cluster> clusters_batch, bool can_load_more, bool is_continuation) { + // TODO(tommycli): It's weird that there's one more post-processing step here + // that's not encapsulated within `QueryClustersState`. That's because + // `EntityImageService` can't live in the component yet, because it depends + // on code in the /chrome directory. Fix this using dependency injection. auto* entity_image_service = EntityImageService::Get(GetProfile()); - // Kick off a bunch of image fetch requests if this feature is enabled. - for (size_t i = 0; i < clusters_batch.size(); ++i) { - auto& cluster = clusters_batch[i]; - if (cluster.label_source != history::Cluster::LabelSource::kSearch) { - continue; - } + entity_image_service->PopulateEntityImagesFor( + std::move(clusters_batch), + base::BindOnce(&HistoryClustersHandler::SendClustersToPage, + weak_ptr_factory_.GetWeakPtr(), query, can_load_more, + is_continuation)); +} - if (!cluster.raw_label || cluster.raw_label->empty()) - continue; - - // TODO(tommycli): Populate this with the actual entity ID once available. - std::string entity_id; - size_t cluster_index = - query_clusters_state_->number_clusters_sent_to_page() + i; - entity_image_service->FetchImageFor( - *cluster.raw_label, entity_id, - base::BindOnce(&HistoryClustersHandler::OnImageFetchedForCluster, - weak_ptr_factory_.GetWeakPtr(), cluster_index)); - } - +void HistoryClustersHandler::SendClustersToPage( + const std::string& query, + bool can_load_more, + bool is_continuation, + const std::vector<history::Cluster> clusters_batch) { auto query_result = QueryClustersResultToMojom(profile_, query, std::move(clusters_batch), can_load_more, is_continuation); @@ -543,13 +539,6 @@ LaunchJourneysSurvey(); } -void HistoryClustersHandler::OnImageFetchedForCluster(size_t cluster_index, - const GURL& image_url) { - if (!image_url.is_valid()) - return; - page_->OnClusterImageUpdated(cluster_index, image_url); -} - void HistoryClustersHandler::LaunchJourneysSurvey() { // All the below is to attempt launch a survey, after loading the first set of // clusters.
diff --git a/chrome/browser/ui/webui/history_clusters/history_clusters_handler.h b/chrome/browser/ui/webui/history_clusters/history_clusters_handler.h index 3290a6c..ee4088b 100644 --- a/chrome/browser/ui/webui/history_clusters/history_clusters_handler.h +++ b/chrome/browser/ui/webui/history_clusters/history_clusters_handler.h
@@ -99,15 +99,18 @@ Profile* GetProfile() override; private: - // Called with the result of querying clusters. Subsequently, the result is - // is sent to the JS to update the UI. + // Called with the result of querying clusters. Does one more round of + // post-processing, then calls `SendClustersToPage()`. void OnGotClustersBatch(const std::string& query, const std::vector<history::Cluster> clusters_batch, bool can_load_more, bool is_continuation); - // Callback for images fetched for clusters. - void OnImageFetchedForCluster(size_t cluster_index, const GURL& image_url); + // Callback to `OnGotClustersBatch()`. + void SendClustersToPage(const std::string& query, + bool can_load_more, + bool is_continuation, + const std::vector<history::Cluster> clusters_batch); // Launches the Journeys survey, if user is eligible. void LaunchJourneysSurvey();
diff --git a/chrome/browser/ui/webui/invalidations/invalidations_ui.cc b/chrome/browser/ui/webui/invalidations/invalidations_ui.cc index 66750c2..9b61351 100644 --- a/chrome/browser/ui/webui/invalidations/invalidations_ui.cc +++ b/chrome/browser/ui/webui/invalidations/invalidations_ui.cc
@@ -29,7 +29,9 @@ "'unsafe-eval';"); source->AddResourcePath("test_loader_util.js", IDR_WEBUI_JS_TEST_LOADER_UTIL_JS); - source->DisableTrustedTypesCSP(); + source->OverrideContentSecurityPolicy( + network::mojom::CSPDirectiveName::TrustedTypes, + "trusted-types jstemplate webui-test-script;"); source->AddResourcePaths( base::make_span(kInvalidationsResources, kInvalidationsResourcesSize)); source->SetDefaultResource(IDR_INVALIDATIONS_ABOUT_INVALIDATIONS_HTML);
diff --git a/chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.cc b/chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.cc index 7488a35f..98d1c44 100644 --- a/chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.cc +++ b/chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.cc
@@ -100,9 +100,9 @@ return; } - sync_service->GetExtensionStatusMap( - base::BindOnce(&ConvertExtensionStatusToDictionary, - extension_service->AsWeakPtr(), std::move(callback))); + sync_service->GetExtensionStatusMap(base::BindOnce( + &ConvertExtensionStatusToDictionary, + extension_service->AsExtensionServiceWeakPtr(), std::move(callback))); } void ExtensionStatusesHandler::HandleGetExtensionStatuses(
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt index e7f72d1..2f8cb73 100644 --- a/chrome/build/linux.pgo.txt +++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@ -chrome-linux-main-1669031864-63b0b0032d6a090362866d49188e5ef44e9fe52c.profdata +chrome-linux-main-1669052379-349c9b09fc496ab00d547f4d7a2d7b10068fd2f3.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index 2e9ef9a..8f1379d7 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1669031864-d48a189ff6c71cf5e85969defaf6f9724ab2b856.profdata +chrome-mac-arm-main-1669052379-c0d13ae4b3813f67ea71a047d260d6eb215ac467.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt index de8ce4cbf..78559a8f8 100644 --- a/chrome/build/mac.pgo.txt +++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@ -chrome-mac-main-1669031864-c66f15651feb086287df0998ee34279131694d3b.profdata +chrome-mac-main-1669052379-0db4490b297ef571c169300f9ae958f9623dea28.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index a0864bd0..b273ed0 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1669031864-39e75e7ffe92a3412b931f296cb39e2d3f5451fa.profdata +chrome-win32-main-1669042776-901bce6dc7880edbf227ab7d1793b8aabb385ce3.profdata
diff --git a/chrome/services/printing/print_backend_service_impl.cc b/chrome/services/printing/print_backend_service_impl.cc index d8a0c00c..3201f736 100644 --- a/chrome/services/printing/print_backend_service_impl.cc +++ b/chrome/services/printing/print_backend_service_impl.cc
@@ -241,6 +241,7 @@ if (result != mojom::ResultCode::kSuccess) { DLOG(ERROR) << "Failure updating printer settings for document " << document_->cookie() << ", error: " << result; + context_->Cancel(); return result; } @@ -248,6 +249,7 @@ if (result != mojom::ResultCode::kSuccess) { DLOG(ERROR) << "Failure initializing new document " << document_->cookie() << ", error: " << result; + context_->Cancel(); return result; } @@ -269,8 +271,12 @@ absl::optional<RenderData> render_data = PrepareRenderData(document_->cookie(), page_data_type, serialized_page); - if (!render_data) + if (!render_data) { + DLOG(ERROR) << "Failure preparing render data for document " + << document_->cookie(); + context_->Cancel(); return mojom::ResultCode::kFailed; + } document_->SetPage(page_index, std::move(render_data->metafile), shrink_factor, page_size, page_content_rect); @@ -280,10 +286,9 @@ if (result != mojom::ResultCode::kSuccess) { DLOG(ERROR) << "Failure rendering page " << page_index << " of document " << document_->cookie() << ", error: " << result; - return result; + context_->Cancel(); } - - return mojom::ResultCode::kSuccess; + return result; } #endif // BUILDFLAG(IS_WIN) @@ -297,20 +302,36 @@ absl::optional<RenderData> render_data = PrepareRenderData(document_->cookie(), data_type, serialized_document); - if (!render_data) + if (!render_data) { + DLOG(ERROR) << "Failure preparing render data for document " + << document_->cookie(); + context_->Cancel(); return mojom::ResultCode::kFailed; + } document_->set_page_count(page_count); document_->SetDocument(std::move(render_data->metafile)); - return document_->RenderPrintedDocument(context_.get()); + mojom::ResultCode result = document_->RenderPrintedDocument(context_.get()); + if (result != mojom::ResultCode::kSuccess) { + DLOG(ERROR) << "Failure rendering document " << document_->cookie() + << ", error: " << result; + context_->Cancel(); + } + return result; } mojom::ResultCode DocumentContainer::DoDocumentDone() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DVLOG(1) << "Document done for document " << document_->cookie(); - return context_->DocumentDone(); + mojom::ResultCode result = context_->DocumentDone(); + if (result != mojom::ResultCode::kSuccess) { + DLOG(ERROR) << "Failure completing document " << document_->cookie() + << ", error: " << result; + context_->Cancel(); + } + return result; } void DocumentContainer::DoCancel() { @@ -738,16 +759,11 @@ DocumentHelper* document_helper = GetDocumentHelper(document_cookie); DCHECK(document_helper); - // Safe to use `base::Unretained(this)` because `this` outlives the async - // call and callback. The entire service process goes away when `this` - // lifetime expires. document_helper->document_container() .AsyncCall(&DocumentContainer::DoRenderPrintedPage) .WithArgs(page_index, page_data_type, std::move(serialized_page), page_size, page_content_rect, shrink_factor) - .Then(base::BindOnce(&PrintBackendServiceImpl::OnDidRenderPrintedPage, - base::Unretained(this), std::ref(*document_helper), - std::move(callback))); + .Then(std::move(callback)); } #endif // BUILDFLAG(IS_WIN) @@ -760,15 +776,10 @@ DocumentHelper* document_helper = GetDocumentHelper(document_cookie); DCHECK(document_helper); - // Safe to use `base::Unretained(this)` because `this` outlives the async - // call and callback. The entire service process goes away when `this` - // lifetime expires. document_helper->document_container() .AsyncCall(&DocumentContainer::DoRenderPrintedDocument) .WithArgs(page_count, data_type, std::move(serialized_document)) - .Then(base::BindOnce(&PrintBackendServiceImpl::OnDidRenderPrintedDocument, - base::Unretained(this), std::ref(*document_helper), - std::move(callback))); + .Then(std::move(callback)); } void PrintBackendServiceImpl::DocumentDone( @@ -809,37 +820,6 @@ mojom::ResultCode result) { DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_); document_helper.TakeStartPrintingCallback().Run(result); - if (result == mojom::ResultCode::kSuccess) - return; - - // Remove this document due to the failure to do setup. - RemoveDocumentHelper(document_helper); -} - -#if BUILDFLAG(IS_WIN) -void PrintBackendServiceImpl::OnDidRenderPrintedPage( - DocumentHelper& document_helper, - mojom::PrintBackendService::RenderPrintedPageCallback callback, - mojom::ResultCode result) { - std::move(callback).Run(result); - if (result == mojom::ResultCode::kSuccess) - return; - - // Remove this document due to the rendering failure. - RemoveDocumentHelper(document_helper); -} -#endif // BUILDFLAG(IS_WIN) - -void PrintBackendServiceImpl::OnDidRenderPrintedDocument( - DocumentHelper& document_helper, - mojom::PrintBackendService::RenderPrintedDocumentCallback callback, - mojom::ResultCode result) { - std::move(callback).Run(result); - if (result == mojom::ResultCode::kSuccess) - return; - - // Remove this document due to the rendering failure. - RemoveDocumentHelper(document_helper); } void PrintBackendServiceImpl::OnDidDocumentDone( @@ -848,8 +828,12 @@ mojom::ResultCode result) { std::move(callback).Run(result); - // All complete for this document. - RemoveDocumentHelper(document_helper); + // The service expects that the calling process will call `Cancel()` if there + // are any errors during printing. + if (result == mojom::ResultCode::kSuccess) { + // All complete for this document. + RemoveDocumentHelper(document_helper); + } } void PrintBackendServiceImpl::OnDidCancel(
diff --git a/chrome/services/printing/print_backend_service_impl.h b/chrome/services/printing/print_backend_service_impl.h index bdbc1dc4..6658512 100644 --- a/chrome/services/printing/print_backend_service_impl.h +++ b/chrome/services/printing/print_backend_service_impl.h
@@ -203,16 +203,6 @@ // Callbacks from worker functions. void OnDidStartPrintingReadyDocument(DocumentHelper& document_helper, mojom::ResultCode result); -#if BUILDFLAG(IS_WIN) - void OnDidRenderPrintedPage( - DocumentHelper& document_helper, - mojom::PrintBackendService::RenderPrintedPageCallback callback, - mojom::ResultCode result); -#endif - void OnDidRenderPrintedDocument( - DocumentHelper& document_helper, - mojom::PrintBackendService::RenderPrintedDocumentCallback callback, - mojom::ResultCode result); void OnDidDocumentDone( DocumentHelper& document_helper, mojom::PrintBackendService::DocumentDoneCallback callback,
diff --git a/chrome/services/sharing/webrtc/ipc_network_manager.cc b/chrome/services/sharing/webrtc/ipc_network_manager.cc index 59c497b..4775596 100644 --- a/chrome/services/sharing/webrtc/ipc_network_manager.cc +++ b/chrome/services/sharing/webrtc/ipc_network_manager.cc
@@ -106,8 +106,8 @@ if (adapter_type == rtc::ADAPTER_TYPE_UNKNOWN) { adapter_type = rtc::GetAdapterTypeFromName(it->name.c_str()); } - auto network = std::make_unique<rtc::Network>( - it->name, it->name, prefix, it->prefix_length, adapter_type); + auto network = CreateNetwork(it->name, it->name, prefix, it->prefix_length, + adapter_type); network->set_default_local_address_provider(this); network->set_mdns_responder_provider(this);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index dcb1ee91..15192f17 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -3396,7 +3396,6 @@ "../browser/ui/views/extensions/extensions_dialogs_browsertest.cc", "../browser/ui/views/extensions/extensions_dialogs_browsertest.h", "../browser/ui/views/extensions/extensions_request_access_button_hover_card_browsertest.cc", - "../browser/ui/views/extensions/extensions_request_access_dialog_view_browsertest.cc", "../browser/ui/views/extensions/media_galleries_dialog_views_browsertest.cc", "../browser/ui/views/extensions/reload_page_dialog_browsertest.cc", "../browser/ui/views/extensions/settings_overridden_dialog_browsertest.cc",
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py index 5abfba4..638afcd 100755 --- a/chrome/test/chromedriver/test/run_py_tests.py +++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -5036,7 +5036,8 @@ '--remote-debugging-port=%d' % port, '--user-data-dir=%s' % temp_dir, '--use-mock-keychain', - '--password-store=basic'] + '--password-store=basic', + 'about:blank'] process = subprocess.Popen(cmd) try: driver = self.CreateDriver(
diff --git a/chrome/test/data/webui/chromeos/shortcut_customization/accelerator_subsection_test.ts b/chrome/test/data/webui/chromeos/shortcut_customization/accelerator_subsection_test.ts index 2bd6bf1..191315f7 100644 --- a/chrome/test/data/webui/chromeos/shortcut_customization/accelerator_subsection_test.ts +++ b/chrome/test/data/webui/chromeos/shortcut_customization/accelerator_subsection_test.ts
@@ -9,7 +9,7 @@ import {AcceleratorLookupManager} from 'chrome://shortcut-customization/js/accelerator_lookup_manager.js'; import {AcceleratorSubsectionElement} from 'chrome://shortcut-customization/js/accelerator_subsection.js'; import {fakeAcceleratorConfig, fakeLayoutInfo} from 'chrome://shortcut-customization/js/fake_data.js'; -import {AcceleratorSource, Modifier} from 'chrome://shortcut-customization/js/shortcut_types.js'; +import {AcceleratorSource, LayoutInfo, LayoutStyle, Modifier} from 'chrome://shortcut-customization/js/shortcut_types.js'; import {assertEquals} from 'chrome://webui-test/chai_assert.js'; import {flushTasks} from 'chrome://webui-test/polymer_test_util.js'; @@ -50,16 +50,22 @@ /*key=*/ 67, /*keyDisplay=*/ 'c'); - const accelerators = [acceleratorInfo1, acceleratorInfo2]; + const expectedAccelInfos = [acceleratorInfo1, acceleratorInfo2]; const description = 'test shortcut'; const title = 'test title'; + const expectedLayoutInfo: LayoutInfo = { + action: 0, + category: 0, + description, + source: AcceleratorSource.kAsh, + style: LayoutStyle.kDefault, + subCategory: 0, + }; sectionElement!.title = title; - sectionElement!.acceleratorContainer = [{ - description: description, - acceleratorInfos: accelerators, - source: AcceleratorSource.kAsh, - action: 0, + sectionElement!.accelRowDataArray = [{ + acceleratorInfos: expectedAccelInfos, + layoutInfo: expectedLayoutInfo, }]; await flush();
diff --git a/chrome/test/data/webui/chromeos/shortcut_customization/shortcut_customization_test.ts b/chrome/test/data/webui/chromeos/shortcut_customization/shortcut_customization_test.ts index 002362712..40e0322 100644 --- a/chrome/test/data/webui/chromeos/shortcut_customization/shortcut_customization_test.ts +++ b/chrome/test/data/webui/chromeos/shortcut_customization/shortcut_customization_test.ts
@@ -112,7 +112,7 @@ // Asert 2 accelerators are loaded for Window Management. assertEquals( (expectedLayouts!.get(windowManagementValue) as LayoutInfo[]).length, - subSections[0]!.acceleratorContainer!.length); + subSections[0]!.accelRowDataArray!.length); // Assert subsection title (Virtual Desks) matches expected value from // fake lookup. @@ -122,7 +122,7 @@ // Asert 2 accelerators are loaded for Virtual Desks. assertEquals( (expectedLayouts!.get(virtualDesksValue) as LayoutInfo[]).length, - subSections[1]!.acceleratorContainer!.length); + subSections[1]!.accelRowDataArray!.length); }); test('LoadFakeBrowserPage', async () => { @@ -154,7 +154,7 @@ // Assert only 1 accelerator is within Tabs. assertEquals( (expectedLayouts!.get(keyIterator.value) as LayoutInfo[]).length, - subSections[0]!.acceleratorContainer.length); + subSections[0]!.accelRowDataArray.length); }); test('OpenDialogFromAccelerator', async () => {
diff --git a/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js b/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js index 705644e6..cf3ee697 100644 --- a/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js +++ b/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js
@@ -553,7 +553,6 @@ aboutPageEndOfLifeMessage: 'message', }); await initNewPage(); - page.scroller = page.offsetParent; assertTrue(!!page.$['detailed-build-info-trigger']); page.$['detailed-build-info-trigger'].click(); const buildInfoPage = @@ -581,7 +580,6 @@ aboutPageEndOfLifeMessage: '', }); await initNewPage(); - page.scroller = page.offsetParent; assertTrue(!!page.$['detailed-build-info-trigger']); page.$['detailed-build-info-trigger'].click(); const buildInfoPage = @@ -595,14 +593,12 @@ aboutPageEndOfLifeMessage: 'message', }); await initNewPage(); - page.scroller = page.offsetParent; assertTrue(!!page.$['detailed-build-info-trigger']); page.$['detailed-build-info-trigger'].click(); checkEndOfLifeSection(); }); function getBuildInfoPage() { - page.scroller = page.offsetParent; assertTrue(!!page.$['detailed-build-info-trigger']); page.$['detailed-build-info-trigger'].click(); const buildInfoPage =
diff --git a/chrome/test/data/webui/settings/settings_main_test.ts b/chrome/test/data/webui/settings/settings_main_test.ts index 4540858..9cfc3b9 100644 --- a/chrome/test/data/webui/settings/settings_main_test.ts +++ b/chrome/test/data/webui/settings/settings_main_test.ts
@@ -290,4 +290,13 @@ loadTimeData.getStringF( 'settingsAltPageTitle', loadTimeData.getString('aboutPageTitle'))); }); + + test('uses parent title for navigable dialog routes', function() { + Router.getInstance().navigateTo(routes.CLEAR_BROWSER_DATA); + assertEquals( + document.title, + loadTimeData.getStringF( + 'settingsAltPageTitle', + loadTimeData.getString('privacyPageTitle'))); + }); });
diff --git a/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_test.ts b/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_test.ts index 31c153e..397b22a 100644 --- a/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_test.ts +++ b/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_test.ts
@@ -7,9 +7,11 @@ import {BookmarksApiProxyImpl} from 'chrome://read-later.top-chrome/bookmarks/bookmarks_api_proxy.js'; import {ShoppingListApiProxyImpl} from 'chrome://read-later.top-chrome/bookmarks/commerce/shopping_list_api_proxy.js'; +import {PowerBookmarkRowElement} from 'chrome://read-later.top-chrome/bookmarks/power_bookmark_row.js'; import {PowerBookmarksListElement} from 'chrome://read-later.top-chrome/bookmarks/power_bookmarks_list.js'; import {PluralStringProxyImpl} from 'chrome://resources/js/plural_string_proxy.js'; -import {assertEquals} from 'chrome://webui-test/chai_assert.js'; +import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {assertEquals, assertNotEquals} from 'chrome://webui-test/chai_assert.js'; import {flushTasks} from 'chrome://webui-test/polymer_test_util.js'; import {TestPluralStringProxy} from 'chrome://webui-test/test_plural_string_proxy.js'; @@ -21,33 +23,40 @@ let bookmarksApi: TestBookmarksApiProxy; let shoppingListApi: TestShoppingListApiProxy; - const topLevelBookmarks: chrome.bookmarks.BookmarkTreeNode[] = [ + const folders: chrome.bookmarks.BookmarkTreeNode[] = [ { id: '1', parentId: '0', - title: 'First child bookmark', - url: 'http://child/bookmark/1/', - dateAdded: 1, - }, - { - id: '2', - parentId: '0', - title: 'Second child bookmark', - url: 'http://child/bookmark/2/', - dateAdded: 3, - }, - { - id: '3', - parentId: '0', - title: 'Child folder', - dateAdded: 2, + title: 'Bookmarks bar', children: [ { - id: '5', - parentId: '3', - title: 'Nested bookmark', - url: 'http://nested/bookmark/', - dateAdded: 4, + id: '2', + parentId: '1', + title: 'First child bookmark', + url: 'http://child/bookmark/1/', + dateAdded: 1, + }, + { + id: '3', + parentId: '1', + title: 'Second child bookmark', + url: 'http://child/bookmark/2/', + dateAdded: 3, + }, + { + id: '4', + parentId: '1', + title: 'Child folder', + dateAdded: 2, + children: [ + { + id: '5', + parentId: '4', + title: 'Nested bookmark', + url: 'http://nested/bookmark/', + dateAdded: 4, + }, + ], }, ], }, @@ -61,8 +70,7 @@ document.body.innerHTML = window.trustedTypes!.emptyHTML; bookmarksApi = new TestBookmarksApiProxy(); - bookmarksApi.setTopLevelBookmarks( - JSON.parse(JSON.stringify(topLevelBookmarks))); + bookmarksApi.setFolders(JSON.parse(JSON.stringify(folders))); BookmarksApiProxyImpl.setInstance(bookmarksApi); shoppingListApi = new TestShoppingListApiProxy(); @@ -78,20 +86,121 @@ }); test('GetsAndShowsTopLevelBookmarks', () => { - assertEquals(1, bookmarksApi.getCallCount('getTopLevelBookmarks')); + assertEquals(1, bookmarksApi.getCallCount('getFolders')); assertEquals( - topLevelBookmarks.length, + folders[0]!.children!.length, getBookmarkElements(powerBookmarksList).length); }); test('DefaultsToSortByNewest', () => { - assertEquals(1, bookmarksApi.getCallCount('getTopLevelBookmarks')); const bookmarkElements = getBookmarkElements(powerBookmarksList); // All folders should come first - assertEquals(bookmarkElements[0]!.id, 'bookmark-3'); + assertEquals(bookmarkElements[0]!.id, 'bookmark-4'); // Newest URL should come next - assertEquals(bookmarkElements[1]!.id, 'bookmark-2'); + assertEquals(bookmarkElements[1]!.id, 'bookmark-3'); // Older URL should be last - assertEquals(bookmarkElements[2]!.id, 'bookmark-1'); + assertEquals(bookmarkElements[2]!.id, 'bookmark-2'); + }); + + test('UpdatesChangedBookmarks', () => { + const changedBookmark = folders[0]!.children![0]!; + bookmarksApi.callbackRouter.onChanged.callListeners(changedBookmark.id, { + title: 'New title', + url: 'http://new/url', + }); + + const bookmarkElement = getBookmarkElements(powerBookmarksList)[2]!; + assertEquals( + 'New title', + (bookmarkElement as PowerBookmarkRowElement).bookmark.title); + assertEquals( + 'http://new/url', + (bookmarkElement as PowerBookmarkRowElement).bookmark.url); + assertNotEquals( + undefined, + Array.from(bookmarkElement.shadowRoot!.querySelectorAll('div')) + .find(el => el.textContent === 'New title')); + }); + + test('AddsCreatedBookmark', async () => { + bookmarksApi.callbackRouter.onCreated.callListeners('999', { + id: '999', + title: 'New bookmark', + index: 0, + parentId: folders[0]!.id, + url: 'http://new/bookmark', + }); + flush(); + + const bookmarkElements = getBookmarkElements(powerBookmarksList); + assertEquals(4, bookmarkElements.length); + }); + + test('AddsCreatedBookmarkForNewFolder', () => { + // Create a new folder without a children array. + bookmarksApi.callbackRouter.onCreated.callListeners('1000', { + id: '1000', + title: 'New folder', + index: 0, + parentId: folders[0]!.id, + }); + flush(); + + // Create a new bookmark within that folder. + bookmarksApi.callbackRouter.onCreated.callListeners('1001', { + id: '1001', + title: 'New bookmark in new folder', + index: 0, + parentId: '1000', + url: 'http://google.com', + }); + flush(); + + const newFolder = getBookmarkElements(powerBookmarksList)[0]!; + assertEquals( + 1, (newFolder as PowerBookmarkRowElement).bookmark.children!.length); + }); + + test('MovesBookmarks', () => { + const movedBookmark = folders[0]!.children![2]!.children![0]!; + bookmarksApi.callbackRouter.onMoved.callListeners(movedBookmark.id, { + index: 0, + parentId: folders[0]!.id, // Moving to bookmarks bar. + oldParentId: folders[0]!.children![2]!.id, // Moving from child folder. + oldIndex: 0, + }); + flush(); + + const bookmarkElements = getBookmarkElements(powerBookmarksList); + assertEquals(4, bookmarkElements.length); + const childFolder = bookmarkElements[0]!; + assertEquals('4', (childFolder as PowerBookmarkRowElement).bookmark.id); + assertEquals( + 0, (childFolder as PowerBookmarkRowElement).bookmark.children!.length); + }); + + test('MovesBookmarksIntoNewFolder', () => { + // Create a new folder without a children array. + bookmarksApi.callbackRouter.onCreated.callListeners('1000', { + id: '1000', + title: 'New folder', + index: 0, + parentId: folders[0]!.id, + }); + flush(); + + const movedBookmark = folders[0]!.children![2]!.children![0]!; + bookmarksApi.callbackRouter.onMoved.callListeners(movedBookmark.id, { + index: 0, + parentId: '1000', + oldParentId: folders[0]!.children![2]!.id, + oldIndex: 0, + }); + flush(); + + const newFolder = + powerBookmarksList.shadowRoot!.querySelector('#bookmark-1000'); + assertEquals( + 1, (newFolder as PowerBookmarkRowElement).bookmark.children!.length); }); });
diff --git a/chrome/test/data/webui/side_panel/bookmarks/test_bookmarks_api_proxy.ts b/chrome/test/data/webui/side_panel/bookmarks/test_bookmarks_api_proxy.ts index bc4087c..22460e9 100644 --- a/chrome/test/data/webui/side_panel/bookmarks/test_bookmarks_api_proxy.ts +++ b/chrome/test/data/webui/side_panel/bookmarks/test_bookmarks_api_proxy.ts
@@ -10,7 +10,6 @@ export class TestBookmarksApiProxy extends TestBrowserProxy implements BookmarksApiProxy { - private topLevelBookmarks_: chrome.bookmarks.BookmarkTreeNode[] = []; private folders_: chrome.bookmarks.BookmarkTreeNode[] = []; callbackRouter: { onChanged: FakeChromeEvent, @@ -22,7 +21,6 @@ constructor() { super([ - 'getTopLevelBookmarks', 'getFolders', 'bookmarkCurrentTab', 'openBookmark', @@ -42,11 +40,6 @@ }; } - getTopLevelBookmarks() { - this.methodCalled('getTopLevelBookmarks'); - return Promise.resolve(this.topLevelBookmarks_); - } - getFolders() { this.methodCalled('getFolders'); return Promise.resolve(this.folders_); @@ -62,10 +55,6 @@ this.methodCalled('openBookmark', id, depth, clickModifiers, source); } - setTopLevelBookmarks(topLevelBookmarks: chrome.bookmarks.BookmarkTreeNode[]) { - this.topLevelBookmarks_ = topLevelBookmarks; - } - setFolders(folders: chrome.bookmarks.BookmarkTreeNode[]) { this.folders_ = folders; }
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/BUILD.gn b/chrome/test/data/webui/side_panel/customize_chrome/BUILD.gn index 980129f..04f1be5d 100644 --- a/chrome/test/data/webui/side_panel/customize_chrome/BUILD.gn +++ b/chrome/test/data/webui/side_panel/customize_chrome/BUILD.gn
@@ -20,6 +20,10 @@ in_files = [ "shortcuts_test.ts", "test_support.ts", + "app_test.ts", + "appearance_test.ts", + "categories_test.ts", + "themes_test.ts", ] definitions = [ "//tools/typescript/definitions/metrics_private.d.ts" ] deps = [
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/app_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/app_test.ts new file mode 100644 index 0000000..d4061dd --- /dev/null +++ b/chrome/test/data/webui/side_panel/customize_chrome/app_test.ts
@@ -0,0 +1,58 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'chrome://webui-test/mojo_webui_test_support.js'; +import 'chrome://customize-chrome-side-panel.top-chrome/app.js'; + +import {AppElement} from 'chrome://customize-chrome-side-panel.top-chrome/app.js'; +import {assertTrue} from 'chrome://webui-test/chai_assert.js'; + +suite('AppTest', () => { + let customizeChromeApp: AppElement; + + setup(async () => { + document.body.innerHTML = window.trustedTypes!.emptyHTML; + customizeChromeApp = document.createElement('customize-chrome-app'); + document.body.appendChild(customizeChromeApp); + }); + + test('app changes pages', async () => { + // Test initial page state. + assertTrue( + customizeChromeApp.$.overviewPage.classList.contains('iron-selected')); + + // Send event for edit theme being clicked. + customizeChromeApp.$.appearanceElement.dispatchEvent( + new Event('edit-theme-click')); + // Current page should now be categories. + assertTrue(customizeChromeApp.$.categoriesPage.classList.contains( + 'iron-selected')); + + // Send event for category selected. + customizeChromeApp.$.categoriesPage.dispatchEvent( + new CustomEvent<object>('category-select', {detail: {}})); + // Current page should now be themes. + assertTrue( + customizeChromeApp.$.themesPage.classList.contains('iron-selected')); + + // Send event for theme selected. + customizeChromeApp.$.themesPage.dispatchEvent(new Event('theme-select')); + // Current page should now be overview. + assertTrue( + customizeChromeApp.$.overviewPage.classList.contains('iron-selected')); + + // Set page back to themes and then go back a page. + customizeChromeApp.$.categoriesPage.dispatchEvent( + new CustomEvent<object>('category-select', {detail: {}})); + customizeChromeApp.$.themesPage.dispatchEvent(new Event('back-click')); + // Current page should now be categories. + assertTrue(customizeChromeApp.$.categoriesPage.classList.contains( + 'iron-selected')); + // Go back again. + customizeChromeApp.$.categoriesPage.dispatchEvent(new Event('back-click')); + // Current page should now be overview. + assertTrue( + customizeChromeApp.$.overviewPage.classList.contains('iron-selected')); + }); +});
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/appearance_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/appearance_test.ts new file mode 100644 index 0000000..6eff157 --- /dev/null +++ b/chrome/test/data/webui/side_panel/customize_chrome/appearance_test.ts
@@ -0,0 +1,27 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'chrome://webui-test/mojo_webui_test_support.js'; +import 'chrome://customize-chrome-side-panel.top-chrome/appearance.js'; + +import {AppearanceElement} from 'chrome://customize-chrome-side-panel.top-chrome/appearance.js'; +import {assertTrue} from 'chrome://webui-test/chai_assert.js'; +import {eventToPromise} from 'chrome://webui-test/test_util.js'; + +suite('AppearanceTest', () => { + let appearanceElement: AppearanceElement; + + setup(async () => { + document.body.innerHTML = window.trustedTypes!.emptyHTML; + appearanceElement = document.createElement('customize-chrome-appearance'); + document.body.appendChild(appearanceElement); + }); + + test('appearance edit button creates event', async () => { + const eventPromise = eventToPromise('edit-theme-click', appearanceElement); + appearanceElement.$.editThemeButton.click(); + const event = await eventPromise; + assertTrue(!!event); + }); +});
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/categories_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/categories_test.ts new file mode 100644 index 0000000..9a27af5f2 --- /dev/null +++ b/chrome/test/data/webui/side_panel/customize_chrome/categories_test.ts
@@ -0,0 +1,36 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'chrome://webui-test/mojo_webui_test_support.js'; +import 'chrome://customize-chrome-side-panel.top-chrome/categories.js'; + +import {CategoriesElement} from 'chrome://customize-chrome-side-panel.top-chrome/categories.js'; +import {assertTrue} from 'chrome://webui-test/chai_assert.js'; +import {eventToPromise} from 'chrome://webui-test/test_util.js'; + +suite('CategoriesTest', () => { + let categoriesElement: CategoriesElement; + + setup(async () => { + document.body.innerHTML = window.trustedTypes!.emptyHTML; + categoriesElement = document.createElement('customize-chrome-categories'); + document.body.appendChild(categoriesElement); + }); + + test('categories buttons create events', async () => { + // Check that clicking the back button produces a back-click event. + let eventPromise = eventToPromise('back-click', categoriesElement); + categoriesElement.$.backButton.click(); + let event = await eventPromise; + assertTrue(!!event); + + // Check that clicking a category produces a category-select event. + eventPromise = eventToPromise('category-select', categoriesElement); + const category = categoriesElement.shadowRoot!.querySelector( + '.category')! as HTMLButtonElement; + category.click(); + event = await eventPromise; + assertTrue(!!event); + }); +});
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/side_panel_customize_chrome_browsertest.js b/chrome/test/data/webui/side_panel/customize_chrome/side_panel_customize_chrome_browsertest.js index 0426e34..ad0ab7e 100644 --- a/chrome/test/data/webui/side_panel/customize_chrome/side_panel_customize_chrome_browsertest.js +++ b/chrome/test/data/webui/side_panel/customize_chrome/side_panel_customize_chrome_browsertest.js
@@ -26,7 +26,8 @@ }; } -var ShortcutsTest = class extends SidePanelCustomizeChromeBrowserTest { +var SidePanelCustomizeChromeShortcutsTest = + class extends SidePanelCustomizeChromeBrowserTest { /** @override */ get browsePreload() { return 'chrome://customize-chrome-side-panel.top-chrome/test_loader.html' + @@ -34,6 +35,58 @@ } }; -TEST_F('ShortcutsTest', 'All', function() { +var SidePanelCustomizeChromeAppTest = + class extends SidePanelCustomizeChromeBrowserTest { + /** @override */ + get browsePreload() { + return 'chrome://customize-chrome-side-panel.top-chrome/test_loader.html' + + '?module=side_panel_customize_chrome/app_test.js'; + } +}; + +var SidePanelCustomizeChromeAppearanceTest = + class extends SidePanelCustomizeChromeBrowserTest { + /** @override */ + get browsePreload() { + return 'chrome://customize-chrome-side-panel.top-chrome/test_loader.html' + + '?module=side_panel_customize_chrome/appearance_test.js'; + } +}; + +var SidePanelCustomizeChromeCategoriesTest = + class extends SidePanelCustomizeChromeBrowserTest { + /** @override */ + get browsePreload() { + return 'chrome://customize-chrome-side-panel.top-chrome/test_loader.html' + + '?module=side_panel_customize_chrome/categories_test.js'; + } +}; + +var SidePanelCustomizeChromeThemesTest = + class extends SidePanelCustomizeChromeBrowserTest { + /** @override */ + get browsePreload() { + return 'chrome://customize-chrome-side-panel.top-chrome/test_loader.html' + + '?module=side_panel_customize_chrome/themes_test.js'; + } +}; + +TEST_F('SidePanelCustomizeChromeShortcutsTest', 'All', function() { mocha.run(); }); + +TEST_F('SidePanelCustomizeChromeAppTest', 'All', function() { + mocha.run(); +}); + +TEST_F('SidePanelCustomizeChromeAppearanceTest', 'All', function() { + mocha.run(); +}); + +TEST_F('SidePanelCustomizeChromeCategoriesTest', 'All', function() { + mocha.run(); +}); + +TEST_F('SidePanelCustomizeChromeThemesTest', 'All', function() { + mocha.run(); +}); \ No newline at end of file
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/themes_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/themes_test.ts new file mode 100644 index 0000000..d6b8a8ce --- /dev/null +++ b/chrome/test/data/webui/side_panel/customize_chrome/themes_test.ts
@@ -0,0 +1,36 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'chrome://webui-test/mojo_webui_test_support.js'; +import 'chrome://customize-chrome-side-panel.top-chrome/themes.js'; + +import {ThemesElement} from 'chrome://customize-chrome-side-panel.top-chrome/themes.js'; +import {assertTrue} from 'chrome://webui-test/chai_assert.js'; +import {eventToPromise} from 'chrome://webui-test/test_util.js'; + +suite('ThemesTest', () => { + let themesElement: ThemesElement; + + setup(async () => { + document.body.innerHTML = window.trustedTypes!.emptyHTML; + themesElement = document.createElement('customize-chrome-themes'); + document.body.appendChild(themesElement); + }); + + test('themes buttons create events', async () => { + // Check that clicking the back button produces a back-click event. + let eventPromise = eventToPromise('back-click', themesElement); + themesElement.$.backButton.click(); + let event = await eventPromise; + assertTrue(!!event); + + // Check that clicking a theme produces a theme-select event. + eventPromise = eventToPromise('theme-select', themesElement); + const theme = + themesElement.shadowRoot!.querySelector('.theme')! as HTMLButtonElement; + theme.click(); + event = await eventPromise; + assertTrue(!!event); + }); +});
diff --git a/chrome/test/webapps/coverage/coverage_cros.tsv b/chrome/test/webapps/coverage/coverage_cros.tsv index 4bd5da34..2cf2bc9 100644 --- a/chrome/test/webapps/coverage/coverage_cros.tsv +++ b/chrome/test/webapps/coverage/coverage_cros.tsv
@@ -1,5 +1,5 @@ # This is a generated file. -# Full coverage: 50%, with partial coverage: 70% +# Full coverage: 53%, with partial coverage: 75% create_shortcut_Standalone_Windowed🌕 launch_from_menu_option_Standalone🌕 check_app_title_Standalone_StandaloneOriginal🌑 create_shortcut_Standalone_Windowed🌕 launch_from_launch_icon_Standalone🌕 check_app_title_Standalone_StandaloneOriginal🌑 create_shortcut_Standalone_Windowed🌕 launch_from_chrome_apps_Standalone🌓 check_app_title_Standalone_StandaloneOriginal🌑 @@ -27,12 +27,8 @@ create_shortcut_Standalone_Windowed🌕 manifest_update_title_Standalone_StandaloneUpdated_CancelDialogAndUninstall🌑 check_app_not_in_list_Standalone🌑 check_platform_shortcut_not_exists_Standalone🌑 install_omnibox_icon_Standalone🌕 manifest_update_title_Standalone_StandaloneUpdated_CancelDialogAndUninstall🌑 check_app_not_in_list_Standalone🌑 check_platform_shortcut_not_exists_Standalone🌑 install_menu_option_Standalone🌕 manifest_update_title_Standalone_StandaloneUpdated_CancelDialogAndUninstall🌑 check_app_not_in_list_Standalone🌑 check_platform_shortcut_not_exists_Standalone🌑 -create_shortcut_Standalone_Windowed🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_app_in_list_icon_correct_Standalone🌑 -install_omnibox_icon_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_app_in_list_icon_correct_Standalone🌑 -install_menu_option_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_app_in_list_icon_correct_Standalone🌑 -create_shortcut_Standalone_Windowed🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_platform_shortcut_and_icon_Standalone🌑 -install_omnibox_icon_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_platform_shortcut_and_icon_Standalone🌑 -install_menu_option_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_platform_shortcut_and_icon_Standalone🌑 +install_omnibox_icon_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌕 await_manifest_update_Standalone🌕 check_app_icon_Standalone_Red🌕 +install_menu_option_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌕 await_manifest_update_Standalone🌕 check_app_icon_Standalone_Red🌕 install_policy_app_Standalone_NoShortcut_Windowed_WebApp🌓 manifest_update_title_Standalone_StandaloneUpdated_SkipUpdate🌑 check_update_dialog_not_shown🌑 await_manifest_update_Standalone🌑 launch_from_menu_option_Standalone🌑 check_app_title_Standalone_StandaloneUpdated🌑 install_policy_app_Standalone_NoShortcut_Windowed_WebApp🌓 manifest_update_title_Standalone_StandaloneUpdated_SkipUpdate🌑 check_update_dialog_not_shown🌑 await_manifest_update_Standalone🌑 launch_from_launch_icon_Standalone🌑 check_app_title_Standalone_StandaloneUpdated🌑 install_policy_app_Standalone_NoShortcut_Windowed_WebApp🌓 manifest_update_title_Standalone_StandaloneUpdated_SkipUpdate🌑 check_update_dialog_not_shown🌑 await_manifest_update_Standalone🌑 launch_from_chrome_apps_Standalone🌑 check_app_title_Standalone_StandaloneUpdated🌑 @@ -955,183 +951,153 @@ install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_menu_option_MinimalUi🌕 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_menu_option_MinimalUi🌕 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_menu_option_MinimalUi🌕 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -create_shortcut_MinimalUi_Windowed🌕 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -create_shortcut_MinimalUi_Browser🌕 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_omnibox_icon_MinimalUi🌕 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_menu_option_MinimalUi🌕 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -create_shortcut_MinimalUi_Windowed🌕 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +create_shortcut_FileHandler_Windowed🌕 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +create_shortcut_FileHandler_Browser🌕 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +create_shortcut_FileHandler_Windowed🌕 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
diff --git a/chrome/test/webapps/coverage/coverage_linux.tsv b/chrome/test/webapps/coverage/coverage_linux.tsv index 4bddd70..564e7282 100644 --- a/chrome/test/webapps/coverage/coverage_linux.tsv +++ b/chrome/test/webapps/coverage/coverage_linux.tsv
@@ -1,5 +1,5 @@ # This is a generated file. -# Full coverage: 59%, with partial coverage: 81% +# Full coverage: 62%, with partial coverage: 85% create_shortcut_Standalone_Windowed🌕 launch_from_menu_option_Standalone🌕 check_app_title_Standalone_StandaloneOriginal🌑 create_shortcut_Standalone_Windowed🌕 launch_from_launch_icon_Standalone🌕 check_app_title_Standalone_StandaloneOriginal🌑 create_shortcut_Standalone_Windowed🌕 launch_from_chrome_apps_Standalone🌓 check_app_title_Standalone_StandaloneOriginal🌑 @@ -27,12 +27,8 @@ create_shortcut_Standalone_Windowed🌕 manifest_update_title_Standalone_StandaloneUpdated_CancelDialogAndUninstall🌑 check_app_not_in_list_Standalone🌑 check_platform_shortcut_not_exists_Standalone🌑 install_omnibox_icon_Standalone🌕 manifest_update_title_Standalone_StandaloneUpdated_CancelDialogAndUninstall🌑 check_app_not_in_list_Standalone🌑 check_platform_shortcut_not_exists_Standalone🌑 install_menu_option_Standalone🌕 manifest_update_title_Standalone_StandaloneUpdated_CancelDialogAndUninstall🌑 check_app_not_in_list_Standalone🌑 check_platform_shortcut_not_exists_Standalone🌑 -create_shortcut_Standalone_Windowed🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_app_in_list_icon_correct_Standalone🌑 -install_omnibox_icon_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_app_in_list_icon_correct_Standalone🌑 -install_menu_option_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_app_in_list_icon_correct_Standalone🌑 -create_shortcut_Standalone_Windowed🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_platform_shortcut_and_icon_Standalone🌑 -install_omnibox_icon_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_platform_shortcut_and_icon_Standalone🌑 -install_menu_option_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_platform_shortcut_and_icon_Standalone🌑 +install_omnibox_icon_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌕 await_manifest_update_Standalone🌕 check_app_icon_Standalone_Red🌕 +install_menu_option_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌕 await_manifest_update_Standalone🌕 check_app_icon_Standalone_Red🌕 install_policy_app_Standalone_NoShortcut_Windowed_WebApp🌓 manifest_update_title_Standalone_StandaloneUpdated_SkipUpdate🌑 check_update_dialog_not_shown🌑 await_manifest_update_Standalone🌑 launch_from_menu_option_Standalone🌑 check_app_title_Standalone_StandaloneUpdated🌑 install_policy_app_Standalone_NoShortcut_Windowed_WebApp🌓 manifest_update_title_Standalone_StandaloneUpdated_SkipUpdate🌑 check_update_dialog_not_shown🌑 await_manifest_update_Standalone🌑 launch_from_launch_icon_Standalone🌑 check_app_title_Standalone_StandaloneUpdated🌑 install_policy_app_Standalone_NoShortcut_Windowed_WebApp🌓 manifest_update_title_Standalone_StandaloneUpdated_SkipUpdate🌑 check_update_dialog_not_shown🌑 await_manifest_update_Standalone🌑 launch_from_chrome_apps_Standalone🌑 check_app_title_Standalone_StandaloneUpdated🌑 @@ -1172,183 +1168,153 @@ install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 check_site_handles_file_FileHandler_Foo🌕 check_site_handles_file_FileHandler_Bar🌕 install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 check_site_handles_file_FileHandler_Foo🌕 check_site_handles_file_FileHandler_Bar🌕 install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 check_site_handles_file_FileHandler_Foo🌕 check_site_handles_file_FileHandler_Bar🌕 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_menu_option_MinimalUi🌕 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_menu_option_MinimalUi🌕 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_menu_option_MinimalUi🌕 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -create_shortcut_MinimalUi_Windowed🌕 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -create_shortcut_MinimalUi_Browser🌕 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_omnibox_icon_MinimalUi🌕 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_menu_option_MinimalUi🌕 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -create_shortcut_MinimalUi_Windowed🌕 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +create_shortcut_FileHandler_Windowed🌕 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +create_shortcut_FileHandler_Browser🌕 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +create_shortcut_FileHandler_Windowed🌕 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
diff --git a/chrome/test/webapps/coverage/coverage_mac.tsv b/chrome/test/webapps/coverage/coverage_mac.tsv index bc62982..b38e486 100644 --- a/chrome/test/webapps/coverage/coverage_mac.tsv +++ b/chrome/test/webapps/coverage/coverage_mac.tsv
@@ -1,5 +1,5 @@ # This is a generated file. -# Full coverage: 54%, with partial coverage: 73% +# Full coverage: 57%, with partial coverage: 77% create_shortcut_Standalone_Windowed🌕 launch_from_menu_option_Standalone🌕 check_app_title_Standalone_StandaloneOriginal🌑 create_shortcut_Standalone_Windowed🌕 launch_from_launch_icon_Standalone🌕 check_app_title_Standalone_StandaloneOriginal🌑 create_shortcut_Standalone_Windowed🌕 launch_from_chrome_apps_Standalone🌓 check_app_title_Standalone_StandaloneOriginal🌑 @@ -27,12 +27,8 @@ create_shortcut_Standalone_Windowed🌕 manifest_update_title_Standalone_StandaloneUpdated_CancelDialogAndUninstall🌑 check_app_not_in_list_Standalone🌑 check_platform_shortcut_not_exists_Standalone🌑 install_omnibox_icon_Standalone🌕 manifest_update_title_Standalone_StandaloneUpdated_CancelDialogAndUninstall🌑 check_app_not_in_list_Standalone🌑 check_platform_shortcut_not_exists_Standalone🌑 install_menu_option_Standalone🌕 manifest_update_title_Standalone_StandaloneUpdated_CancelDialogAndUninstall🌑 check_app_not_in_list_Standalone🌑 check_platform_shortcut_not_exists_Standalone🌑 -create_shortcut_Standalone_Windowed🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_app_in_list_icon_correct_Standalone🌑 -install_omnibox_icon_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_app_in_list_icon_correct_Standalone🌑 -install_menu_option_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_app_in_list_icon_correct_Standalone🌑 -create_shortcut_Standalone_Windowed🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_platform_shortcut_and_icon_Standalone🌑 -install_omnibox_icon_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_platform_shortcut_and_icon_Standalone🌑 -install_menu_option_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_platform_shortcut_and_icon_Standalone🌑 +install_omnibox_icon_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌕 await_manifest_update_Standalone🌕 check_app_icon_Standalone_Red🌕 +install_menu_option_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌕 await_manifest_update_Standalone🌕 check_app_icon_Standalone_Red🌕 install_policy_app_Standalone_NoShortcut_Windowed_WebApp🌓 manifest_update_title_Standalone_StandaloneUpdated_SkipUpdate🌑 check_update_dialog_not_shown🌑 await_manifest_update_Standalone🌑 launch_from_menu_option_Standalone🌑 check_app_title_Standalone_StandaloneUpdated🌑 install_policy_app_Standalone_NoShortcut_Windowed_WebApp🌓 manifest_update_title_Standalone_StandaloneUpdated_SkipUpdate🌑 check_update_dialog_not_shown🌑 await_manifest_update_Standalone🌑 launch_from_launch_icon_Standalone🌑 check_app_title_Standalone_StandaloneUpdated🌑 install_policy_app_Standalone_NoShortcut_Windowed_WebApp🌓 manifest_update_title_Standalone_StandaloneUpdated_SkipUpdate🌑 check_update_dialog_not_shown🌑 await_manifest_update_Standalone🌑 launch_from_chrome_apps_Standalone🌑 check_app_title_Standalone_StandaloneUpdated🌑 @@ -1175,183 +1171,153 @@ install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 check_site_handles_file_FileHandler_Foo🌕 check_site_handles_file_FileHandler_Bar🌕 install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 check_site_handles_file_FileHandler_Foo🌕 check_site_handles_file_FileHandler_Bar🌕 install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 check_site_handles_file_FileHandler_Foo🌕 check_site_handles_file_FileHandler_Bar🌕 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_menu_option_MinimalUi🌕 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_menu_option_MinimalUi🌕 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_menu_option_MinimalUi🌕 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -create_shortcut_MinimalUi_Windowed🌕 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -create_shortcut_MinimalUi_Browser🌕 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_omnibox_icon_MinimalUi🌕 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_menu_option_MinimalUi🌕 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -create_shortcut_MinimalUi_Windowed🌕 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +create_shortcut_FileHandler_Windowed🌕 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +create_shortcut_FileHandler_Browser🌕 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +create_shortcut_FileHandler_Windowed🌕 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
diff --git a/chrome/test/webapps/coverage/coverage_win.tsv b/chrome/test/webapps/coverage/coverage_win.tsv index 7e7ca2c..eb76fe2 100644 --- a/chrome/test/webapps/coverage/coverage_win.tsv +++ b/chrome/test/webapps/coverage/coverage_win.tsv
@@ -1,5 +1,5 @@ # This is a generated file. -# Full coverage: 60%, with partial coverage: 82% +# Full coverage: 63%, with partial coverage: 87% create_shortcut_Standalone_Windowed🌕 launch_from_menu_option_Standalone🌕 check_app_title_Standalone_StandaloneOriginal🌑 create_shortcut_Standalone_Windowed🌕 launch_from_launch_icon_Standalone🌕 check_app_title_Standalone_StandaloneOriginal🌑 create_shortcut_Standalone_Windowed🌕 launch_from_chrome_apps_Standalone🌓 check_app_title_Standalone_StandaloneOriginal🌑 @@ -27,12 +27,8 @@ create_shortcut_Standalone_Windowed🌕 manifest_update_title_Standalone_StandaloneUpdated_CancelDialogAndUninstall🌑 check_app_not_in_list_Standalone🌑 check_platform_shortcut_not_exists_Standalone🌑 install_omnibox_icon_Standalone🌕 manifest_update_title_Standalone_StandaloneUpdated_CancelDialogAndUninstall🌑 check_app_not_in_list_Standalone🌑 check_platform_shortcut_not_exists_Standalone🌑 install_menu_option_Standalone🌕 manifest_update_title_Standalone_StandaloneUpdated_CancelDialogAndUninstall🌑 check_app_not_in_list_Standalone🌑 check_platform_shortcut_not_exists_Standalone🌑 -create_shortcut_Standalone_Windowed🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_app_in_list_icon_correct_Standalone🌑 -install_omnibox_icon_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_app_in_list_icon_correct_Standalone🌑 -install_menu_option_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_app_in_list_icon_correct_Standalone🌑 -create_shortcut_Standalone_Windowed🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_platform_shortcut_and_icon_Standalone🌑 -install_omnibox_icon_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_platform_shortcut_and_icon_Standalone🌑 -install_menu_option_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌑 await_manifest_update_Standalone🌑 check_platform_shortcut_and_icon_Standalone🌑 +install_omnibox_icon_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌕 await_manifest_update_Standalone🌕 check_app_icon_Standalone_Red🌕 +install_menu_option_Standalone🌕 manifest_update_icon_Standalone_AcceptUpdate🌕 await_manifest_update_Standalone🌕 check_app_icon_Standalone_Red🌕 install_policy_app_Standalone_NoShortcut_Windowed_WebApp🌓 manifest_update_title_Standalone_StandaloneUpdated_SkipUpdate🌑 check_update_dialog_not_shown🌑 await_manifest_update_Standalone🌑 launch_from_menu_option_Standalone🌑 check_app_title_Standalone_StandaloneUpdated🌑 install_policy_app_Standalone_NoShortcut_Windowed_WebApp🌓 manifest_update_title_Standalone_StandaloneUpdated_SkipUpdate🌑 check_update_dialog_not_shown🌑 await_manifest_update_Standalone🌑 launch_from_launch_icon_Standalone🌑 check_app_title_Standalone_StandaloneUpdated🌑 install_policy_app_Standalone_NoShortcut_Windowed_WebApp🌓 manifest_update_title_Standalone_StandaloneUpdated_SkipUpdate🌑 check_update_dialog_not_shown🌑 await_manifest_update_Standalone🌑 launch_from_chrome_apps_Standalone🌑 check_app_title_Standalone_StandaloneUpdated🌑 @@ -1172,183 +1168,153 @@ install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 check_site_handles_file_FileHandler_Foo🌕 check_site_handles_file_FileHandler_Bar🌕 install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 check_site_handles_file_FileHandler_Foo🌕 check_site_handles_file_FileHandler_Bar🌕 install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 check_site_handles_file_FileHandler_Foo🌕 check_site_handles_file_FileHandler_Bar🌕 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneFooFile🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_MultipleFooFiles🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -install_menu_option_MinimalUi🌕 launch_file_MultipleFooFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_MultipleFooFiles🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_OneBarFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -install_menu_option_MinimalUi🌕 launch_file_OneBarFile🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_One🌑 check_files_loaded_in_site_MinimalUi_OneBarFile🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_MultipleBarFiles🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -install_menu_option_MinimalUi🌕 launch_file_MultipleBarFiles🌑 file_handling_dialog_Allow_AskAgain🌑 check_pwa_window_created_MinimalUi_Two🌑 check_files_loaded_in_site_MinimalUi_MultipleBarFiles🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_Remember🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Allow_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_MinimalUi_Foo🌑 check_site_handles_file_MinimalUi_Bar🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_AskAgain🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Windowed🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -create_shortcut_MinimalUi_Browser🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_omnibox_icon_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -install_menu_option_MinimalUi🌕 launch_file_OneFooFile🌑 file_handling_dialog_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_MinimalUi_Foo🌑 check_site_not_handles_file_MinimalUi_Bar🌑 -create_shortcut_MinimalUi_Windowed🌕 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -create_shortcut_MinimalUi_Browser🌕 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_omnibox_icon_MinimalUi🌕 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -install_menu_option_MinimalUi🌕 add_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_NotShown🌑 check_pwa_window_created_MinimalUi_One🌑 -create_shortcut_MinimalUi_Windowed🌕 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -create_shortcut_MinimalUi_Browser🌕 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_omnibox_icon_MinimalUi🌕 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_policy_app_MinimalUi_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 -install_menu_option_MinimalUi🌕 add_file_handling_policy_approval_MinimalUi🌑 remove_file_handling_policy_approval_MinimalUi🌑 launch_file_OneFooFile🌑 check_file_handling_dialog_Shown🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneFooFile🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_One🌑 check_files_loaded_in_site_FileHandler_OneBarFile🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑 check_pwa_window_created_FileHandler_Two🌑 check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 check_window_not_created🌑 check_site_handles_file_FileHandler_Foo🌑 check_site_handles_file_FileHandler_Bar🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Windowed🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +create_shortcut_FileHandler_Browser🌕 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑 check_window_not_created🌑 check_site_not_handles_file_FileHandler_Foo🌑 check_site_not_handles_file_FileHandler_Bar🌑 +create_shortcut_FileHandler_Windowed🌕 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +create_shortcut_FileHandler_Browser🌕 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 launch_file_expect_no_dialog_FileHandler_OneFooFile🌑 check_pwa_window_created_FileHandler_One🌑 +create_shortcut_FileHandler_Windowed🌕 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +create_shortcut_FileHandler_Browser🌕 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑 +install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓 add_file_handling_policy_approval_FileHandler🌑 remove_file_handling_policy_approval_FileHandler🌑 launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
diff --git a/chrome/test/webapps/data/framework_supported_actions.csv b/chrome/test/webapps/data/framework_supported_actions.csv index b5ee007..f35e27e9 100644 --- a/chrome/test/webapps/data/framework_supported_actions.csv +++ b/chrome/test/webapps/data/framework_supported_actions.csv
@@ -1,4 +1,5 @@ # Action base name, Mac,Win,Lin,Cros +check_app_icon, 🌕, 🌕, 🌕, 🌕, check_app_in_list_icon_correct, 🌕, 🌕, 🌕, 🌕, check_app_in_list_not_locally_installed, 🌓, 🌓, 🌓, 🌓, check_app_in_list_tabbed, 🌓, 🌓, 🌓, 🌓, @@ -40,6 +41,7 @@ launch_from_launch_icon, 🌕, 🌕, 🌕, 🌕, launch_from_menu_option, 🌕, 🌕, 🌕, 🌕, launch_from_platform_shortcut, 🌓, 🌓, 🌓, 🌑, +manifest_update_icon, 🌕, 🌕, 🌕, 🌕, manifest_update_display, 🌕, 🌕, 🌕, 🌕, manifest_update_scope_to, 🌕, 🌕, 🌕, 🌕, navigate_browser, 🌕, 🌕, 🌕, 🌕,
diff --git a/chrome/updater/test/integration_tests_win.cc b/chrome/updater/test/integration_tests_win.cc index 370ee2a..7c23541e 100644 --- a/chrome/updater/test/integration_tests_win.cc +++ b/chrome/updater/test/integration_tests_win.cc
@@ -25,6 +25,7 @@ #include "base/path_service.h" #include "base/process/launch.h" #include "base/process/process.h" +#include "base/process/process_iterator.h" #include "base/ranges/algorithm.h" #include "base/strings/strcat.h" #include "base/strings/string_util.h" @@ -540,6 +541,19 @@ return result; } +void PrintProcesses() { + const std::string demarcation(72, '='); + VLOG(0) << "Found processes:"; + VLOG(0) << demarcation; + base::ProcessIterator process_iterator(nullptr); + const base::ProcessIterator::ProcessEntries& process_entries = + process_iterator.Snapshot(); + for (const base::ProcessEntry& entry : process_entries) { + VLOG(0) << entry.exe_file(); + } + VLOG(0) << demarcation; +} + } // namespace base::FilePath GetSetupExecutablePath() { @@ -570,6 +584,8 @@ } void Clean(UpdaterScope scope) { + VLOG(0) << __func__; + CleanProcesses(); const HKEY root = UpdaterScopeToHKeyRoot(scope); @@ -620,12 +636,12 @@ absl::optional<base::FilePath> path = GetProductPath(scope); EXPECT_TRUE(path); - if (path) - EXPECT_TRUE(base::DeletePathRecursively(*path)); - path = GetDataDirPath(scope); - EXPECT_TRUE(path); - if (path) - EXPECT_TRUE(base::DeletePathRecursively(*path)); + if (path) { + EXPECT_TRUE(base::DeletePathRecursively(*path)) << [&path]() { + PrintProcesses(); + return path->value(); + }(); + } const absl::optional<base::FilePath> target_path = GetGoogleUpdateExePath(scope);
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt index de2f265..70fe46e 100644 --- a/chromeos/profiles/bigcore.afdo.newest.txt +++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@ -chromeos-chrome-amd64-bigcore-109-5391.0-1668445159-benchmark-109.0.5414.13-r1-redacted.afdo.xz +chromeos-chrome-amd64-bigcore-109-5412.2-1669029745-benchmark-109.0.5414.14-r1-redacted.afdo.xz
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc index 768efa136..76afbc9 100644 --- a/components/autofill/core/browser/form_structure.cc +++ b/components/autofill/core/browser/form_structure.cc
@@ -634,6 +634,11 @@ // TODO(crbug.com/1154080): By calling this with true, autocomplete section // attributes will be ignored. form->IdentifySections(/*ignore_autocomplete=*/true); + // Metrics are intentionally only emitted after the sever response, not when + // determining heuristic types. This is done to reduce noise in the metrics, + // since generally only this sectioning result is used. + LogSectioningMetrics(form->form_signature(), form->fields_, + form_interactions_ukm_logger); } AutofillMetrics::ServerQueryMetric metric;
diff --git a/components/autofill/core/browser/form_structure_sectioning_util.cc b/components/autofill/core/browser/form_structure_sectioning_util.cc index e0e14c8..9dbbb7d 100644 --- a/components/autofill/core/browser/form_structure_sectioning_util.cc +++ b/components/autofill/core/browser/form_structure_sectioning_util.cc
@@ -6,6 +6,7 @@ #include <iterator> #include <memory> +#include <sstream> #include <utility> #include "base/ranges/algorithm.h" @@ -205,4 +206,34 @@ } } +void LogSectioningMetrics( + FormSignature form_signature, + base::span<const std::unique_ptr<AutofillField>> fields, + AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger) { + // UMA: + base::flat_map<Section, size_t> fields_per_section; + for (auto& field : fields) + ++fields_per_section[field->section]; + AutofillMetrics::LogSectioningMetrics(fields_per_section); + // UKM: + if (form_interactions_ukm_logger) { + form_interactions_ukm_logger->LogSectioningHash( + form_signature, ComputeSectioningSignature(fields)); + } +} + +uint32_t ComputeSectioningSignature( + base::span<const std::unique_ptr<AutofillField>> fields) { + // Compute a signature by converting the fields' sections into integers and + // concatenating them. Finally, hash the result. + std::stringstream signature; + base::flat_map<Section, size_t> section_ids; + for (auto& field : fields) { + size_t section_id = + section_ids.emplace(field->section, section_ids.size()).first->second; + signature << section_id; + } + return StrToHash32Bit(signature.str()); +} + } // namespace autofill
diff --git a/components/autofill/core/browser/form_structure_sectioning_util.h b/components/autofill/core/browser/form_structure_sectioning_util.h index ad77236..b7235632 100644 --- a/components/autofill/core/browser/form_structure_sectioning_util.h +++ b/components/autofill/core/browser/form_structure_sectioning_util.h
@@ -5,10 +5,13 @@ #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_STRUCTURE_SECTIONING_UTIL_H_ #define COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_STRUCTURE_SECTIONING_UTIL_H_ +#include <stdint.h> #include <memory> #include "base/containers/span.h" #include "components/autofill/core/browser/autofill_field.h" +#include "components/autofill/core/browser/metrics/autofill_metrics.h" +#include "components/autofill/core/common/signatures.h" namespace autofill { @@ -111,6 +114,19 @@ // ------------------------------------------------------+------------------- void AssignSections(base::span<const std::unique_ptr<AutofillField>> fields); +// Logs UMA and UKM metrics about the `fields`' sections. +// UKM metrics are only logged if `form_interactions_ukm_logger` is available. +void LogSectioningMetrics( + FormSignature form_signature, + base::span<const std::unique_ptr<AutofillField>> fields, + AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger); + +// Computes a 32-bit signature of the `fields` sections. +// This is useful for logging Ukm metrics to detect on which sites different +// sectioning algorithms produce different results. +uint32_t ComputeSectioningSignature( + base::span<const std::unique_ptr<AutofillField>> fields); + } // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_STRUCTURE_SECTIONING_UTIL_H_
diff --git a/components/autofill/core/browser/form_structure_sectioning_util_unittest.cc b/components/autofill/core/browser/form_structure_sectioning_util_unittest.cc index 5d8eff52..6926af3 100644 --- a/components/autofill/core/browser/form_structure_sectioning_util_unittest.cc +++ b/components/autofill/core/browser/form_structure_sectioning_util_unittest.cc
@@ -10,6 +10,7 @@ #include "autofill_test_utils.h" #include "base/check_op.h" +#include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/autofill_type.h" @@ -18,6 +19,7 @@ #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/form_field_data.h" #include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h" +#include "components/autofill/core/common/signatures.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -31,6 +33,14 @@ namespace { +using base::Bucket; +using base::BucketsAre; + +constexpr char kNumberOfSectionsHistogram[] = + "Autofill.Sectioning.NumberOfSections"; +constexpr char kFieldsPerSectionHistogram[] = + "Autofill.Sectioning.FieldsPerSection"; + // The key information from which we build the `FormFieldData` objects for a // unittest. struct FieldTemplate { @@ -73,6 +83,17 @@ } class FormStructureSectioningTest : public testing::Test { + public: + void AssignSectionsAndLogMetrics( + const std::vector<std::unique_ptr<AutofillField>>& fields) { + AssignSections(fields); + // Since only the UMA metrics are tested, the form signature and UKM logger + // are irrelevant. + LogSectioningMetrics(FormSignature(0UL), fields, + /*form_interactions_ukm_logger=*/nullptr); + } + + private: test::AutofillEnvironment autofill_environment_; }; @@ -101,7 +122,8 @@ features::kAutofillUseParameterizedSectioning, feature_parameters); auto fields = CreateExampleFields(); - AssignSections(fields); + base::HistogramTester histogram_tester; + AssignSectionsAndLogMetrics(fields); // The evaluation order of the `Section::FromFieldIdentifier()` expressions // does not matter, as all `FormFieldData::host_frame` are identical. @@ -118,6 +140,10 @@ Section::FromFieldIdentifier(*fields[6], frame_token_ids), Section::FromFieldIdentifier(*fields[6], frame_token_ids), Section::FromFieldIdentifier(*fields[4], frame_token_ids))); + EXPECT_EQ(ComputeSectioningSignature(fields), StrToHash32Bit("001022332")); + histogram_tester.ExpectUniqueSample(kNumberOfSectionsHistogram, 4, 1); + EXPECT_THAT(histogram_tester.GetAllSamples(kFieldsPerSectionHistogram), + BucketsAre(Bucket(1, 1), Bucket(2, 1), Bucket(3, 2))); } TEST_F(FormStructureSectioningTest, @@ -132,7 +158,8 @@ features::kAutofillUseParameterizedSectioning, feature_parameters); auto fields = CreateExampleFields(); - AssignSections(fields); + base::HistogramTester histogram_tester; + AssignSectionsAndLogMetrics(fields); // The evaluation order of the `Section::FromFieldIdentifier()` expressions // does not matter, as all `FormFieldData::host_frame` are identical. @@ -148,6 +175,10 @@ Section::FromFieldIdentifier(*fields[6], frame_token_ids), Section::FromFieldIdentifier(*fields[6], frame_token_ids), Section::FromFieldIdentifier(*fields[4], frame_token_ids))); + EXPECT_EQ(ComputeSectioningSignature(fields), StrToHash32Bit("001122332")); + histogram_tester.ExpectUniqueSample(kNumberOfSectionsHistogram, 4, 1); + EXPECT_THAT(histogram_tester.GetAllSamples(kFieldsPerSectionHistogram), + BucketsAre(Bucket(2, 3), Bucket(3, 1))); } TEST_F(FormStructureSectioningTest, ExampleFormSectioningModeCreateGaps) { @@ -161,7 +192,8 @@ features::kAutofillUseParameterizedSectioning, feature_parameters); auto fields = CreateExampleFields(); - AssignSections(fields); + base::HistogramTester histogram_tester; + AssignSectionsAndLogMetrics(fields); // The evaluation order of the `Section::FromFieldIdentifier()` expressions // does not matter, as all `FormFieldData::host_frame` are identical. @@ -178,6 +210,10 @@ Section::FromFieldIdentifier(*fields[6], frame_token_ids), Section::FromFieldIdentifier(*fields[6], frame_token_ids), Section::FromFieldIdentifier(*fields[4], frame_token_ids))); + EXPECT_EQ(ComputeSectioningSignature(fields), StrToHash32Bit("001233443")); + histogram_tester.ExpectUniqueSample(kNumberOfSectionsHistogram, 5, 1); + EXPECT_THAT(histogram_tester.GetAllSamples(kFieldsPerSectionHistogram), + BucketsAre(Bucket(1, 2), Bucket(2, 2), Bucket(3, 1))); } TEST_F(FormStructureSectioningTest, ExampleFormSectioningModeExpand) { @@ -191,7 +227,8 @@ features::kAutofillUseParameterizedSectioning, feature_parameters); auto fields = CreateExampleFields(); - AssignSections(fields); + base::HistogramTester histogram_tester; + AssignSectionsAndLogMetrics(fields); // The evaluation order of the `Section::FromFieldIdentifier()` expressions // does not matter, as all `FormFieldData::host_frame` are identical. @@ -208,6 +245,10 @@ Section::FromFieldIdentifier(*fields[4], frame_token_ids), Section::FromFieldIdentifier(*fields[4], frame_token_ids), Section::FromFieldIdentifier(*fields[4], frame_token_ids))); + EXPECT_EQ(ComputeSectioningSignature(fields), StrToHash32Bit("001022222")); + histogram_tester.ExpectUniqueSample(kNumberOfSectionsHistogram, 3, 1); + EXPECT_THAT(histogram_tester.GetAllSamples(kFieldsPerSectionHistogram), + BucketsAre(Bucket(1, 1), Bucket(3, 1), Bucket(5, 1))); } } // namespace
diff --git a/components/autofill/core/browser/metrics/autofill_metrics.cc b/components/autofill/core/browser/metrics/autofill_metrics.cc index cda1f0981..96e22d80 100644 --- a/components/autofill/core/browser/metrics/autofill_metrics.cc +++ b/components/autofill/core/browser/metrics/autofill_metrics.cc
@@ -2033,6 +2033,18 @@ filling_stats.Total()); } +void AutofillMetrics::LogSectioningMetrics( + const base::flat_map<Section, size_t>& fields_per_section) { + constexpr base::StringPiece kBaseHistogramName = "Autofill.Sectioning."; + UMA_HISTOGRAM_COUNTS_100( + base::StrCat({kBaseHistogramName, "NumberOfSections"}), + fields_per_section.size()); + for (auto& [_, section_size] : fields_per_section) { + UMA_HISTOGRAM_COUNTS_100( + base::StrCat({kBaseHistogramName, "FieldsPerSection"}), section_size); + } +} + // static void AutofillMetrics::LogServerResponseHasDataForForm(bool has_data) { UMA_HISTOGRAM_BOOLEAN("Autofill.ServerResponseHasDataForForm", has_data); @@ -2724,6 +2736,15 @@ .Record(ukm_recorder_); } +void AutofillMetrics::FormInteractionsUkmLogger::LogSectioningHash( + FormSignature form_signature, + uint32_t sectioning_signature) { + ukm::builders::Autofill_Sectioning(source_id_) + .SetFormSignature(HashFormSignature(form_signature)) + .SetSectioningSignature(sectioning_signature % 1024) + .Record(ukm_recorder_); +} + int64_t AutofillMetrics::FormTypesToBitVector( const DenseSet<FormType>& form_types) { int64_t form_type_bv = 0;
diff --git a/components/autofill/core/browser/metrics/autofill_metrics.h b/components/autofill/core/browser/metrics/autofill_metrics.h index d67abaf7..23cd862 100644 --- a/components/autofill/core/browser/metrics/autofill_metrics.h +++ b/components/autofill/core/browser/metrics/autofill_metrics.h
@@ -12,6 +12,7 @@ #include <utility> #include <vector> +#include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/memory/raw_ptr.h" #include "base/memory/raw_ref.h" @@ -1015,6 +1016,14 @@ const AutofillField& field, ServerFieldType old_type); + // Logs a hash of the `sectioning_signature` for a specific + // `form_signature`. This is useful for detecting sites where different + // sectioning algorithms yield different results. Emitted every time + // sectioning is performed and only when + // `AutofillUseParameterizedSectioning` is enabled. + void LogSectioningHash(FormSignature form_signature, + uint32_t sectioning_signature); + private: bool CanLog() const; int64_t MillisecondsSinceFormParsed( @@ -1417,6 +1426,10 @@ static void LogFieldFillingStats(FormType form_type, const FormGroupFillingStats& filling_stats); + // Logs the number of sections and the number of fields/section. + static void LogSectioningMetrics( + const base::flat_map<Section, size_t>& fields_per_section); + // This should be called each time a server response is parsed for a form. static void LogServerResponseHasDataForForm(bool has_data);
diff --git a/components/browser_ui/styles/android/BUILD.gn b/components/browser_ui/styles/android/BUILD.gn index 96e38ed..fd99b68 100644 --- a/components/browser_ui/styles/android/BUILD.gn +++ b/components/browser_ui/styles/android/BUILD.gn
@@ -52,6 +52,7 @@ "java/res/color/switch_thumb_tint_list.xml", "java/res/color/switch_track_tint.xml", "java/res/color/switch_track_tint_incognito_baseline_list.xml", + "java/res/color/text_button_ripple_color_list.xml", "java/res/color/text_highlight_color.xml", "java/res/drawable-hdpi/btn_star_filled.png", "java/res/drawable-hdpi/ic_chrome.png",
diff --git a/components/browser_ui/styles/android/java/res/color/text_button_ripple_color_list.xml b/components/browser_ui/styles/android/java/res/color/text_button_ripple_color_list.xml new file mode 100644 index 0000000..9360730a --- /dev/null +++ b/components/browser_ui/styles/android/java/res/color/text_button_ripple_color_list.xml
@@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright 2022 The Chromium Authors +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:alpha="@dimen/default_pressed_alpha" + android:color="@macro/default_control_color_active" android:state_pressed="true"/> + <item android:alpha="@dimen/default_focused_hovered_alpha" + android:color="@macro/default_control_color_active" android:state_focused="true" android:state_hovered="true"/> + <item android:alpha="@dimen/default_focused_alpha" + android:color="@macro/default_control_color_active" android:state_focused="true"/> + <item android:alpha="@dimen/default_hovered_alpha" + android:color="@macro/default_control_color_active" android:state_hovered="true"/> + <item android:color="@android:color/transparent" /> +</selector>
diff --git a/components/browser_ui/styles/android/java/res/values/themes.xml b/components/browser_ui/styles/android/java/res/values/themes.xml index 9b6207e..8491bc2 100644 --- a/components/browser_ui/styles/android/java/res/values/themes.xml +++ b/components/browser_ui/styles/android/java/res/values/themes.xml
@@ -72,6 +72,7 @@ <item name="globalFilledButtonBgColor">@color/filled_button_bg_dynamic_list</item> <item name="globalFilledButtonTextColor">@color/default_text_color_on_accent1_list</item> <item name="globalTextButtonTextColor">@color/default_text_color_accent1_tint_list</item> + <item name="globalTextButtonRippleColor">@color/text_button_ripple_color_list</item> <item name="globalOutlinedButtonBorderColor">@macro/divider_line_bg_color</item> <item name="globalLinkTextColor">?attr/colorPrimary</item> <item name="globalClickableSpanColor">?attr/colorPrimary</item>
diff --git a/components/browser_ui/theme/android/java/res/values/themes.xml b/components/browser_ui/theme/android/java/res/values/themes.xml index 5e3b31a..182d1189 100644 --- a/components/browser_ui/theme/android/java/res/values/themes.xml +++ b/components/browser_ui/theme/android/java/res/values/themes.xml
@@ -52,6 +52,7 @@ <item name="globalFilledButtonBgColor">@color/filled_button_bg</item> <item name="globalFilledButtonTextColor">@color/default_text_color_on_accent1_baseline_list</item> <item name="globalTextButtonTextColor">@color/blue_when_enabled_list</item> + <item name="globalTextButtonRippleColor">@color/text_button_ripple_color_list_baseline</item> <item name="globalOutlinedButtonBorderColor">@color/divider_line_bg_color_baseline</item> <item name="globalLinkTextColor">@color/default_text_color_link_baseline</item> <item name="globalClickableSpanColor">@color/default_text_color_blue_baseline</item> @@ -141,6 +142,7 @@ <item name="globalFilledButtonBgColor">@color/filled_button_bg</item> <item name="globalFilledButtonTextColor">@color/default_text_color_on_accent1_baseline_list</item> <item name="globalTextButtonTextColor">@color/blue_when_enabled_list</item> + <item name="globalTextButtonRippleColor">@color/text_button_ripple_color_list_baseline</item> <item name="globalOutlinedButtonBorderColor">@color/divider_line_bg_color_baseline</item> <item name="globalLinkTextColor">@color/default_text_color_link_baseline</item> <item name="globalClickableSpanColor">@color/default_text_color_blue_baseline</item>
diff --git a/components/feed/core/v2/api_test/feed_api_stream_unittest.cc b/components/feed/core/v2/api_test/feed_api_stream_unittest.cc index 964c3060..3a2308bd 100644 --- a/components/feed/core/v2/api_test/feed_api_stream_unittest.cc +++ b/components/feed/core/v2/api_test/feed_api_stream_unittest.cc
@@ -2850,19 +2850,6 @@ 1); } -TEST_F(FeedApiTest, FeedIsAblated) { - base::test::ScopedFeatureList scoped_feature_list; - base::FieldTrialParams params; - scoped_feature_list.InitAndEnableFeature(kIsAblated); - - base::HistogramTester histograms; - CreateStream(); - histograms.ExpectUniqueSample("ContentSuggestions.Feed.UserSettingsOnStart", - UserSettingsOnStart::kFeedNotEnabled, 1); - - ASSERT_FALSE(network_.query_request_sent.has_value()); -} - TEST_F(FeedApiTest, ReportUserSettingsFromMetadataWaaOnDpOff) { // Fetch a feed, so that there's stored data. {
diff --git a/components/feed/core/v2/public/feed_service.cc b/components/feed/core/v2/public/feed_service.cc index 1e09774..f654a4af 100644 --- a/components/feed/core/v2/public/feed_service.cc +++ b/components/feed/core/v2/public/feed_service.cc
@@ -296,8 +296,7 @@ // static bool FeedService::IsEnabled(const PrefService& pref_service) { - return !base::FeatureList::IsEnabled(kIsAblated) && - pref_service.GetBoolean(feed::prefs::kEnableSnippets); + return pref_service.GetBoolean(feed::prefs::kEnableSnippets); } // static
diff --git a/components/feed/feed_feature_list.cc b/components/feed/feed_feature_list.cc index e21ccd9..5c8d75a 100644 --- a/components/feed/feed_feature_list.cc +++ b/components/feed/feed_feature_list.cc
@@ -149,8 +149,6 @@ "ShareCrowButton", base::FEATURE_DISABLED_BY_DEFAULT); -BASE_FEATURE(kIsAblated, "FeedAblation", base::FEATURE_DISABLED_BY_DEFAULT); - BASE_FEATURE(kFeedCloseRefresh, "FeedCloseRefresh", base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/components/feed/feed_feature_list.h b/components/feed/feed_feature_list.h index 6ed2df2d..a5e59de 100644 --- a/components/feed/feed_feature_list.h +++ b/components/feed/feed_feature_list.h
@@ -124,9 +124,6 @@ // component, since it is being used in the feed component. BASE_DECLARE_FEATURE(kShareCrowButton); -// Feature that when enabled completely removes all Feeds from chrome. -BASE_DECLARE_FEATURE(kIsAblated); - // When enabled, schedule a background refresh for a feed sometime after the // last user engagement with that feed. BASE_DECLARE_FEATURE(kFeedCloseRefresh);
diff --git a/components/history_clusters/core/BUILD.gn b/components/history_clusters/core/BUILD.gn index a7999a5..831d640b 100644 --- a/components/history_clusters/core/BUILD.gn +++ b/components/history_clusters/core/BUILD.gn
@@ -23,6 +23,8 @@ "content_annotations_cluster_processor.h", "content_visibility_cluster_finalizer.cc", "content_visibility_cluster_finalizer.h", + "context_clusterer_history_service_observer.cc", + "context_clusterer_history_service_observer.h", "features.cc", "features.h", "file_clustering_backend.cc", @@ -100,6 +102,7 @@ "config_unittest.cc", "content_annotations_cluster_processor_unittest.cc", "content_visibility_cluster_finalizer_unittest.cc", + "context_clusterer_history_service_observer_unittest.cc", "file_clustering_backend_unittest.cc", "full_membership_cluster_processor_unittest.cc", "history_clusters_db_tasks_unittest.cc",
diff --git a/components/history_clusters/core/DEPS b/components/history_clusters/core/DEPS index d6aec55..b727921 100644 --- a/components/history_clusters/core/DEPS +++ b/components/history_clusters/core/DEPS
@@ -5,6 +5,7 @@ "+components/pref_registry", "+components/prefs", "+components/query_parser", + "+components/search_engines", "+components/site_engagement/core", "+components/strings/grit/components_strings.h", "+components/url_formatter",
diff --git a/components/history_clusters/core/config.cc b/components/history_clusters/core/config.cc index 3b35882..99105f6 100644 --- a/components/history_clusters/core/config.cc +++ b/components/history_clusters/core/config.cc
@@ -371,6 +371,15 @@ DCHECK_GE(search_results_page_ranking_weight, 0.0f); } + // The `kHistoryClustersNavigationContextClustering` feature and child params. + { + context_clustering_clean_up_duration = + base::Minutes(GetFieldTrialParamByFeatureAsInt( + internal::kHistoryClustersNavigationContextClustering, + "clean_up_duration_minutes", + context_clustering_clean_up_duration.InMinutes())); + } + // Lonely features without child params. { non_user_visible_debug =
diff --git a/components/history_clusters/core/config.h b/components/history_clusters/core/config.h index 04de34b..e08f2de 100644 --- a/components/history_clusters/core/config.h +++ b/components/history_clusters/core/config.h
@@ -338,6 +338,9 @@ // visits within a cluster. Will always be greater than or equal to 0. float search_results_page_ranking_weight = 2.0; + // The `kHistoryClustersNavigationContextClustering` feature and child params. + base::TimeDelta context_clustering_clean_up_duration = base::Minutes(10); + // Lonely features without child params. // Enables debug info in non-user-visible surfaces, like Chrome Inspector.
diff --git a/components/history_clusters/core/context_clusterer_history_service_observer.cc b/components/history_clusters/core/context_clusterer_history_service_observer.cc new file mode 100644 index 0000000..0b8eba7a --- /dev/null +++ b/components/history_clusters/core/context_clusterer_history_service_observer.cc
@@ -0,0 +1,250 @@ +// Copyright 2022 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/history_clusters/core/context_clusterer_history_service_observer.h" + +#include "base/metrics/histogram_functions.h" +#include "base/time/default_clock.h" +#include "components/history/core/browser/history_service.h" +#include "components/history_clusters/core/config.h" +#include "components/optimization_guide/core/new_optimization_guide_decider.h" +#include "components/search_engines/template_url_service.h" + +namespace history_clusters { + +namespace { + +// Returns whether `visit` should be added to `cluster`. +bool ShouldAddVisitToCluster(const history::VisitRow& new_visit, + const std::u16string& search_terms, + const InProgressCluster& in_progress_cluster) { + if ((new_visit.visit_time - in_progress_cluster.last_visit_time) > + GetConfig().cluster_navigation_time_cutoff) { + return false; + } + + if (!search_terms.empty()) { + return search_terms == in_progress_cluster.search_terms; + } + + return true; +} + +} // namespace + +InProgressCluster::InProgressCluster() = default; +InProgressCluster::~InProgressCluster() = default; +InProgressCluster::InProgressCluster(const InProgressCluster&) = default; + +ContextClustererHistoryServiceObserver::ContextClustererHistoryServiceObserver( + history::HistoryService* history_service, + TemplateURLService* template_url_service, + optimization_guide::NewOptimizationGuideDecider* optimization_guide_decider) + : template_url_service_(template_url_service), + optimization_guide_decider_(optimization_guide_decider), + clock_(base::DefaultClock::GetInstance()) { + if (history_service) { + // History service is only null in tests. + history_service_observation_.Observe(history_service); + } + + if (optimization_guide_decider_) { + optimization_guide_decider_->RegisterOptimizationTypes( + {optimization_guide::proto::HISTORY_CLUSTERS}); + } + clean_up_clusters_repeating_timer_.Start( + FROM_HERE, GetConfig().context_clustering_clean_up_duration, this, + &ContextClustererHistoryServiceObserver::CleanUpClusters); +} +ContextClustererHistoryServiceObserver:: + ~ContextClustererHistoryServiceObserver() = default; + +void ContextClustererHistoryServiceObserver::OnURLVisited( + history::HistoryService* history_service, + const history::URLRow& url_row, + const history::VisitRow& new_visit) { + if (new_visit.is_known_to_sync) { + // Skip synced visits. + // + // Although local visits that have been synced can have this bit flipped, + // local visits do not automatically get sent to sync when they just get + // created. + return; + } + + if (optimization_guide_decider_ && + optimization_guide_decider_->CanApplyOptimization( + url_row.url(), optimization_guide::proto::HISTORY_CLUSTERS, + /*optimization_metadata=*/nullptr) != + optimization_guide::OptimizationGuideDecision::kTrue) { + // Skip visits that are on the blocklist. + return; + } + + // Update the normalized URL if it's a search URL. + std::string normalized_url = url_row.url().possibly_invalid_spec(); + std::u16string search_terms; + if (template_url_service_) { + absl::optional<TemplateURLService::SearchMetadata> search_metadata = + template_url_service_->ExtractSearchMetadata(url_row.url()); + if (search_metadata) { + normalized_url = search_metadata->normalized_url.possibly_invalid_spec(); + search_terms = search_metadata->search_terms; + } + } + + // See what cluster we should add it to. + absl::optional<int64_t> cluster_idx; + + std::vector<history::VisitID> previous_visit_ids_to_check; + if (new_visit.opener_visit != 0) { + previous_visit_ids_to_check.push_back(new_visit.opener_visit); + } + if (new_visit.referring_visit != 0) { + previous_visit_ids_to_check.push_back(new_visit.referring_visit); + } + if (!previous_visit_ids_to_check.empty()) { + // See if we have clustered any of the previous visits with opener taking + // precedence. + for (history::VisitID previous_visit_id : previous_visit_ids_to_check) { + auto it = visit_id_to_cluster_map_.find(previous_visit_id); + if (it != visit_id_to_cluster_map_.end()) { + cluster_idx = it->second; + break; + } + } + } else { + // See if we have clustered the URL. (forward-back, reload, etc.) + auto it = visit_url_to_cluster_map_.find(normalized_url); + if (it != visit_url_to_cluster_map_.end()) { + cluster_idx = it->second; + } + } + + // See if we should add to cluster. + if (cluster_idx) { + auto& in_progress_cluster = in_progress_clusters_.at(*cluster_idx); + if (!ShouldAddVisitToCluster(new_visit, search_terms, + in_progress_cluster)) { + FinalizeCluster(*cluster_idx); + + cluster_idx = absl::nullopt; + } + } + + // Add a new cluster if we haven't assigned one already. + if (!cluster_idx) { + cluster_idx_counter_++; + cluster_idx = cluster_idx_counter_; + + in_progress_clusters_.emplace(*cluster_idx, InProgressCluster()); + } + + // Add to cluster maps. + auto& in_progress_cluster = in_progress_clusters_.at(*cluster_idx); + in_progress_cluster.last_visit_time = new_visit.visit_time; + in_progress_cluster.visit_urls.insert(normalized_url); + in_progress_cluster.visit_ids.emplace_back(new_visit.visit_id); + in_progress_cluster.search_terms = search_terms; + visit_id_to_cluster_map_[new_visit.visit_id] = *cluster_idx; + visit_url_to_cluster_map_[normalized_url] = *cluster_idx; + + // TODO(b/259466296): Persist visit. +} + +void ContextClustererHistoryServiceObserver::OnURLsDeleted( + history::HistoryService* history_service, + const history::DeletionInfo& deletion_info) { + // Clear out everything if the user deleted all history. + if (deletion_info.IsAllHistory()) { + in_progress_clusters_.clear(); + visit_url_to_cluster_map_.clear(); + visit_id_to_cluster_map_.clear(); + return; + } + + // Delete relevant visits from in-progress clusters. + base::flat_set<int64_t> clusters_to_finalize; + for (const auto& deleted_url : deletion_info.deleted_rows()) { + std::string normalized_deleted_url = + deleted_url.url().possibly_invalid_spec(); + if (template_url_service_) { + absl::optional<TemplateURLService::SearchMetadata> search_metadata = + template_url_service_->ExtractSearchMetadata(deleted_url.url()); + if (search_metadata) { + normalized_deleted_url = + search_metadata->normalized_url.possibly_invalid_spec(); + } + } + auto it = visit_url_to_cluster_map_.find(normalized_deleted_url); + if (it != visit_url_to_cluster_map_.end()) { + // TODO(b/259466296): Maybe check time range. + clusters_to_finalize.insert(it->second); + } + } + + // Finalize clusters. + for (int64_t cluster_idx : clusters_to_finalize) { + FinalizeCluster(cluster_idx); + } +} + +void ContextClustererHistoryServiceObserver::CleanUpClusters() { + if (in_progress_clusters_.empty()) { + // Nothing to clean up, just return. + return; + } + + base::UmaHistogramCounts1000( + "History.Clusters.ContextClusterer.NumClusters.AtCleanUp", + in_progress_clusters_.size()); + + // See which clusters we need to clean up. + base::flat_set<int64_t> clusters_to_finalize; + for (const auto& cluster_id_and_cluster : in_progress_clusters_) { + if ((clock_->Now() - cluster_id_and_cluster.second.last_visit_time) > + GetConfig().cluster_navigation_time_cutoff) { + clusters_to_finalize.insert(cluster_id_and_cluster.first); + } + } + + // Finalize clusters. + for (int64_t cluster_idx : clusters_to_finalize) { + FinalizeCluster(cluster_idx); + } + + base::UmaHistogramCounts1000( + "History.Clusters.ContextClusterer.NumClusters.CleanedUp", + clusters_to_finalize.size()); + + base::UmaHistogramCounts1000( + "History.Clusters.ContextClusterer.NumClusters.PostCleanUp", + in_progress_clusters_.size()); +} + +void ContextClustererHistoryServiceObserver::FinalizeCluster( + int64_t cluster_idx) { + DCHECK(in_progress_clusters_.find(cluster_idx) != + in_progress_clusters_.end()); + + // Delete relevant visits from in-progress maps. + auto& cluster = in_progress_clusters_.at(cluster_idx); + for (const auto& visit_url : cluster.visit_urls) { + visit_url_to_cluster_map_.erase(visit_url); + } + for (const auto visit_id : cluster.visit_ids) { + visit_id_to_cluster_map_.erase(visit_id); + } + + // TODO(b/259466296): Kick off persisting keywords and prominence bits. + + in_progress_clusters_.erase(cluster_idx); +} + +void ContextClustererHistoryServiceObserver::OverrideClockForTesting( + const base::Clock* clock) { + clock_ = clock; +} + +} // namespace history_clusters
diff --git a/components/history_clusters/core/context_clusterer_history_service_observer.h b/components/history_clusters/core/context_clusterer_history_service_observer.h new file mode 100644 index 0000000..1afce648 --- /dev/null +++ b/components/history_clusters/core/context_clusterer_history_service_observer.h
@@ -0,0 +1,125 @@ +// Copyright 2022 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_HISTORY_CLUSTERS_CORE_CONTEXT_CLUSTERER_HISTORY_SERVICE_OBSERVER_H_ +#define COMPONENTS_HISTORY_CLUSTERS_CORE_CONTEXT_CLUSTERER_HISTORY_SERVICE_OBSERVER_H_ + +#include <map> +#include <vector> + +#include "base/containers/flat_set.h" +#include "base/memory/raw_ptr.h" +#include "base/scoped_observation.h" +#include "base/time/clock.h" +#include "base/timer/timer.h" +#include "components/history/core/browser/history_service_observer.h" + +class TemplateURLService; + +namespace history { +class HistoryService; +} // namespace history + +namespace optimization_guide { +class NewOptimizationGuideDecider; +} // namespace optimization_guide + +namespace history_clusters { + +// Information required for determine pending cluster. +struct InProgressCluster { + InProgressCluster(); + ~InProgressCluster(); + InProgressCluster(const InProgressCluster&); + + // The visit IDs that were added to this in-progress cluster. + std::vector<history::VisitID> visit_ids; + // The normalized URLs that are a part of this in-progress cluster. + base::flat_set<std::string> visit_urls; + // The visit time of the last visit added to this in-progress cluster. + base::Time last_visit_time; + // The search terms associated with this in-progress cluster. It will only be + // set once if a search visit is part of this in-progress cluster. + std::u16string search_terms; +}; + +// A HistoryServiceObserver responsible for grouping visits into clusters. +// +// This groups visits together based on their navigation graph (previous visit, +// forward-back, reload, etc.). It is responsible for determining when a cluster +// is closed based on navigational factors as well as a timer that regularly +// cleans up in-progress clusters. +// +// Still todo are to persist the visits to the clusters database as they come +// in. After this is fully rolled out, there should not be any concept of +// "incomplete" visits and that the on-device clustering backend will receive a +// vector of clusters to combine or add metadata to. +class ContextClustererHistoryServiceObserver + : public history::HistoryServiceObserver { + public: + ContextClustererHistoryServiceObserver( + history::HistoryService* history_service, + TemplateURLService* template_url_service, + optimization_guide::NewOptimizationGuideDecider* + optimization_guide_decider); + ~ContextClustererHistoryServiceObserver() override; + + // history::HistoryServiceObserver: + void OnURLVisited(history::HistoryService* history_service, + const history::URLRow& url_row, + const history::VisitRow& visit_row) override; + void OnURLsDeleted(history::HistoryService* history_service, + const history::DeletionInfo& deletion_info) override; + + private: + friend class ContextClustererHistoryServiceObserverTest; + + // Cleans up clusters that have not been interacted with for awhile. + void CleanUpClusters(); + + // Finalizes the cluster with index, `cluster_idx`. + void FinalizeCluster(int64_t cluster_idx); + + // Overrides `clock_` for testing. + void OverrideClockForTesting(const base::Clock* clock); + + // Returns the number of clusters created since the start of the session. + int64_t num_clusters_created() const { return cluster_idx_counter_; } + + // Mapping from cluster ID to the contents of the in-progress cluster. + std::map<int64_t, InProgressCluster> in_progress_clusters_; + + // Mapping from visit ID to the in-progress cluster ID it belongs to. + std::map<history::VisitID, int64_t> visit_id_to_cluster_map_; + + // Mapping from normalized URL spec to the in-progress cluster ID it belongs + // to. + std::map<std::string, int64_t> visit_url_to_cluster_map_; + + // A running counter that is used to index the in-progress clusters. + int64_t cluster_idx_counter_ = 0; + + // Used to invoke `CleanUpClusters()` periodically. + base::RepeatingTimer clean_up_clusters_repeating_timer_; + + // The Template URL Service used to determine if a visit is a search visit. + raw_ptr<TemplateURLService> template_url_service_; + + // The Optimization Guide decider used to determine whether to include a visit + // in a cluster. + raw_ptr<optimization_guide::NewOptimizationGuideDecider> + optimization_guide_decider_; + + // The clock used to schedule the clean up of clusters. + raw_ptr<const base::Clock> clock_; + + // Tracks the observed history service, for cleanup. + base::ScopedObservation<history::HistoryService, + history::HistoryServiceObserver> + history_service_observation_{this}; +}; + +} // namespace history_clusters + +#endif // COMPONENTS_HISTORY_CLUSTERS_CORE_CONTEXT_CLUSTERER_HISTORY_SERVICE_OBSERVER_H_
diff --git a/components/history_clusters/core/context_clusterer_history_service_observer_unittest.cc b/components/history_clusters/core/context_clusterer_history_service_observer_unittest.cc new file mode 100644 index 0000000..ef4999de --- /dev/null +++ b/components/history_clusters/core/context_clusterer_history_service_observer_unittest.cc
@@ -0,0 +1,350 @@ +// Copyright 2022 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/history_clusters/core/context_clusterer_history_service_observer.h" + +#include "base/test/metrics/histogram_tester.h" +#include "base/test/task_environment.h" +#include "components/history_clusters/core/config.h" +#include "components/optimization_guide/core/new_optimization_guide_decider.h" +#include "components/search_engines/template_url_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace history_clusters { + +namespace { + +history::URLRows CreateURLRows(const std::vector<GURL>& urls) { + history::URLRows url_rows; + for (const auto& url : urls) { + url_rows.emplace_back(history::URLRow(url)); + } + return url_rows; +} + +class TestOptimizationGuideDecider + : public optimization_guide::NewOptimizationGuideDecider { + public: + TestOptimizationGuideDecider() = default; + ~TestOptimizationGuideDecider() override = default; + + void RegisterOptimizationTypes( + const std::vector<optimization_guide::proto::OptimizationType>& + optimization_types) override { + ASSERT_EQ(optimization_types.size(), 1u); + ASSERT_EQ(optimization_guide::proto::HISTORY_CLUSTERS, + optimization_types[0]); + } + + void CanApplyOptimization( + const GURL& url, + optimization_guide::proto::OptimizationType optimization_type, + optimization_guide::OptimizationGuideDecisionCallback callback) override { + NOTREACHED(); + } + + optimization_guide::OptimizationGuideDecision CanApplyOptimization( + const GURL& url, + optimization_guide::proto::OptimizationType optimization_type, + optimization_guide::OptimizationMetadata* optimization_metadata) + override { + DCHECK_EQ(optimization_guide::proto::HISTORY_CLUSTERS, optimization_type); + return url.host() == "shouldskip.com" + ? optimization_guide::OptimizationGuideDecision::kFalse + : optimization_guide::OptimizationGuideDecision::kTrue; + } + + void CanApplyOptimizationOnDemand( + const std::vector<GURL>& urls, + const base::flat_set<optimization_guide::proto::OptimizationType>& + optimization_types, + optimization_guide::proto::RequestContext request_context, + optimization_guide::OnDemandOptimizationGuideDecisionRepeatingCallback + callback) override {} +}; + +const TemplateURLService::Initializer kTemplateURLData[] = { + {"default-engine.com", "http://default-engine.com/search?q={searchTerms}", + "Default"}, + {"non-default-engine.com", "http://non-default-engine.com?q={searchTerms}", + "Not Default"}, +}; + +} // namespace + +class ContextClustererHistoryServiceObserverTest : public testing::Test { + public: + ContextClustererHistoryServiceObserverTest() = default; + ~ContextClustererHistoryServiceObserverTest() override = default; + + void SetUp() override { + // Set up a simple template URL service with a default search engine. + template_url_service_ = std::make_unique<TemplateURLService>( + kTemplateURLData, std::size(kTemplateURLData)); + + optimization_guide_decider_ = + std::make_unique<TestOptimizationGuideDecider>(); + + // Instantiate observer. + observer_ = std::make_unique<ContextClustererHistoryServiceObserver>( + /*history_service=*/nullptr, template_url_service_.get(), + optimization_guide_decider_.get()); + observer_->OverrideClockForTesting(task_environment_.GetMockClock()); + } + + // Simulates a visit to URL. + void VisitURL(const GURL& url, + history::VisitID visit_id, + base::Time visit_time, + history::VisitID opener_visit = history::kInvalidVisitID, + history::VisitID referring_visit = history::kInvalidVisitID, + bool is_known_to_sync = false) { + history::URLRow url_row(url); + history::VisitRow new_visit; + new_visit.visit_id = visit_id; + new_visit.visit_time = visit_time; + new_visit.opener_visit = opener_visit; + new_visit.referring_visit = referring_visit; + new_visit.is_known_to_sync = is_known_to_sync; + observer_->OnURLVisited(/*history_service=*/nullptr, url_row, new_visit); + } + + // Simulates deleting `urls` from history. If `urls` is empty, we will + // simulate deleting all history. + void DeleteHistory(const std::vector<GURL>& urls) { + history::DeletionInfo deletion_info = + urls.empty() ? history::DeletionInfo::ForAllHistory() + : history::DeletionInfo::ForUrls(CreateURLRows(urls), + /*favicon_urls=*/{}); + observer_->OnURLsDeleted(/*history_service=*/nullptr, deletion_info); + } + + // Move clock forward by `time_delta`. + void MoveClockForwardBy(base::TimeDelta time_delta) { + task_environment_.FastForwardBy(time_delta); + task_environment_.RunUntilIdle(); + } + + // Returns the number of clusters created by |observer_|. + int64_t GetNumClustersCreated() const { + return observer_->num_clusters_created(); + } + + // Returns the current time of this task environment's mock clock. + base::Time Now() { return task_environment_.GetMockClock()->Now(); } + + private: + base::test::TaskEnvironment task_environment_{ + base::test::TaskEnvironment::TimeSource::MOCK_TIME}; + + std::unique_ptr<TemplateURLService> template_url_service_; + std::unique_ptr<TestOptimizationGuideDecider> optimization_guide_decider_; + std::unique_ptr<ContextClustererHistoryServiceObserver> observer_; +}; + +TEST_F(ContextClustererHistoryServiceObserverTest, ClusterOneVisit) { + VisitURL(GURL("https://example.com"), 1, base::Time::FromTimeT(123)); + + EXPECT_EQ(1, GetNumClustersCreated()); +} + +TEST_F(ContextClustererHistoryServiceObserverTest, + ClusterTwoVisitsTiedByReferringVisit) { + VisitURL(GURL("https://example.com"), 1, base::Time::FromTimeT(123)); + VisitURL(GURL("https://example.com/2"), 2, base::Time::FromTimeT(123), + /*opener_visit=*/history::kInvalidVisitID, /*referring_visit=*/1); + + EXPECT_EQ(1, GetNumClustersCreated()); +} + +TEST_F(ContextClustererHistoryServiceObserverTest, + ClusterTwoVisitsTiedByOpenerVisit) { + VisitURL(GURL("https://example.com"), 1, base::Time::FromTimeT(123)); + VisitURL(GURL("https://example.com/2"), 2, base::Time::FromTimeT(123), + /*opener_visit=*/1, /*referring_visit=*/history::kInvalidVisitID); + + EXPECT_EQ(1, GetNumClustersCreated()); +} + +TEST_F(ContextClustererHistoryServiceObserverTest, + ClusterTwoVisitsOpenerTakesPrecedenceOverReferrer) { + VisitURL(GURL("https://example.com"), 1, base::Time::FromTimeT(123)); + VisitURL(GURL("https://hasopenerbutbadreferrer.com"), 2, + base::Time::FromTimeT(123), + /*opener_visit=*/1, /*referring_visit=*/6); + VisitURL(GURL("https://hasbadopenerbutgoodreferrer.com"), 3, + base::Time::FromTimeT(123), + /*opener_visit=*/6, /*referring_visit=*/2); + + EXPECT_EQ(1, GetNumClustersCreated()); +} + +TEST_F(ContextClustererHistoryServiceObserverTest, ClusterTwoVisitsTiedByURL) { + VisitURL(GURL("https://example.com"), 1, base::Time::FromTimeT(123)); + VisitURL(GURL("https://example.com"), 2, base::Time::FromTimeT(123), + /*opener_visit=*/history::kInvalidVisitID, + /*referring_visit=*/history::kInvalidVisitID); + + EXPECT_EQ(1, GetNumClustersCreated()); +} + +TEST_F(ContextClustererHistoryServiceObserverTest, + ClusterTwoVisitsTiedBySearchURL) { + VisitURL(GURL("http://default-engine.com/search?q=foo"), 1, + base::Time::FromTimeT(123)); + VisitURL(GURL("http://default-engine.com/search?q=foo#whatever"), 2, + base::Time::FromTimeT(123), + /*opener_visit=*/history::kInvalidVisitID, + /*referring_visit=*/history::kInvalidVisitID); + + EXPECT_EQ(1, GetNumClustersCreated()); +} + +TEST_F(ContextClustererHistoryServiceObserverTest, SplitClusterOnSearchTerm) { + VisitURL(GURL("http://default-engine.com/search?q=foo"), 1, + base::Time::FromTimeT(123)); + VisitURL(GURL("http://default-engine.com/search?q=otherterm"), 2, + base::Time::FromTimeT(123), + /*opener_visit=*/1); + + EXPECT_EQ(2, GetNumClustersCreated()); +} + +TEST_F(ContextClustererHistoryServiceObserverTest, + SplitClusterOnNavigationTime) { + VisitURL(GURL("https://example.com"), 1, base::Time::FromTimeT(123)); + VisitURL(GURL("https://example.com/2"), 2, + base::Time::FromTimeT(123) + base::Milliseconds(1) + + GetConfig().cluster_navigation_time_cutoff, + /*opener_visit=*/1, /*referring_visit=*/history::kInvalidVisitID); + + EXPECT_EQ(2, GetNumClustersCreated()); +} + +TEST_F(ContextClustererHistoryServiceObserverTest, SkipsSyncedVisits) { + VisitURL(GURL("https://example.com"), 1, base::Time::FromTimeT(123), + history::kInvalidVisitID, history::kInvalidVisitID, + /*is_known_to_sync=*/true); + + EXPECT_EQ(0, GetNumClustersCreated()); +} + +TEST_F(ContextClustererHistoryServiceObserverTest, SkipsBlocklistedHost) { + VisitURL(GURL("https://shouldskip.com"), 1, base::Time::FromTimeT(123)); + + EXPECT_EQ(0, GetNumClustersCreated()); +} + +TEST_F(ContextClustererHistoryServiceObserverTest, MultipleClusters) { + VisitURL(GURL("https://example.com"), 1, base::Time::FromTimeT(1)); + VisitURL(GURL("https://example.com/2"), 2, base::Time::FromTimeT(2), 1); + VisitURL(GURL("https://whatever.com"), 3, base::Time::FromTimeT(3)); + VisitURL(GURL("https://example.com"), 4, base::Time::FromTimeT(4)); + VisitURL(GURL("https://nonexistentreferrer.com"), 10, + base::Time::FromTimeT(10), 6); + + // The clusters should be (1, 2, 4), (3), (10). + EXPECT_EQ(3, GetNumClustersCreated()); +} + +TEST_F(ContextClustererHistoryServiceObserverTest, CleansUpClusters) { + { + base::HistogramTester histogram_tester; + + // Simulate a now time that is "old"-ish to make sure these get cleaned up + // appropriately. + base::Time now = + Now() - GetConfig().cluster_navigation_time_cutoff - base::Minutes(1); + VisitURL(GURL("https://example.com"), 1, now); + VisitURL(GURL("https://example.com/2"), 2, now + base::Milliseconds(2), 1); + VisitURL(GURL("https://whatever.com"), 3, now + base::Milliseconds(4)); + VisitURL(GURL("https://example.com"), 4, now + base::Milliseconds(10)); + // Make sure this last cluster doesn't get cleaned up, so use the actual + // "Now" value. + VisitURL( + GURL("https://nonexistentreferrer.com"), 10, + Now() + base::Minutes( + GetConfig().cluster_navigation_time_cutoff.InMinutes() / 2), + 6); + // The clusters should be (1, 2, 4), (3), (10). + EXPECT_EQ(3, GetNumClustersCreated()); + + // Force a cleanup pass. + MoveClockForwardBy(GetConfig().context_clustering_clean_up_duration); + + histogram_tester.ExpectUniqueSample( + "History.Clusters.ContextClusterer.NumClusters.AtCleanUp", 3, 1); + histogram_tester.ExpectUniqueSample( + "History.Clusters.ContextClusterer.NumClusters.CleanedUp", 2, 1); + // Should not finalize cluster with visit 10. + histogram_tester.ExpectUniqueSample( + "History.Clusters.ContextClusterer.NumClusters.PostCleanUp", 1, 1); + } + + // This should create a new cluster. + VisitURL(GURL("https://example.com"), 11, Now(), 4); + // This should connect to the cluster with visit 10. + VisitURL(GURL("https://newvisit.com"), 12, Now(), 10); + + // Expect only one more cluster to be created, which makes 4 total. + EXPECT_EQ(4, GetNumClustersCreated()); + + // Make sure everything is cleaned up eventually. + { + base::HistogramTester histogram_tester; + + MoveClockForwardBy(2 * GetConfig().cluster_navigation_time_cutoff); + + histogram_tester.ExpectBucketCount( + "History.Clusters.ContextClusterer.NumClusters.PostCleanUp", 0, 1); + } +} + +TEST_F(ContextClustererHistoryServiceObserverTest, DeleteAllHistory) { + base::HistogramTester histogram_tester; + + VisitURL(GURL("https://example.com"), 1, Now()); + VisitURL(GURL("https://example.com/2"), 2, Now()); + + // Simulate deleting all history. + DeleteHistory(/*urls=*/{}); + + // Force a cleanup pass. + MoveClockForwardBy(GetConfig().context_clustering_clean_up_duration); + + // There should be nothing to clean up so it shouldn't even initiate a clean + // up pass. + histogram_tester.ExpectTotalCount( + "History.Clusters.ContextClusterer.NumClusters.AtCleanUp", 0); + histogram_tester.ExpectTotalCount( + "History.Clusters.ContextClusterer.NumClusters.CleanedUp", 0); + histogram_tester.ExpectTotalCount( + "History.Clusters.ContextClusterer.NumClusters.PostCleanUp", 0); +} + +TEST_F(ContextClustererHistoryServiceObserverTest, DeleteSelectURLs) { + base::HistogramTester histogram_tester; + + VisitURL(GURL("https://example.com"), 1, Now()); + VisitURL(GURL("https://example.com/2"), 2, Now()); + VisitURL(GURL("https://default-engine.com/search?q=foo"), 3, Now()); + VisitURL(GURL("https://default-engine.com/search?q=foo#whatever"), 4, Now()); + + // Simulate deleting one URL and one search URL. + DeleteHistory({GURL("https://example.com"), + GURL("https://default-engine.com/search?q=foo#whateverelse")}); + + // Force a cleanup pass. + MoveClockForwardBy(GetConfig().context_clustering_clean_up_duration); + + // There should be 1 cluster untouched by the deletion. + histogram_tester.ExpectUniqueSample( + "History.Clusters.ContextClusterer.NumClusters.AtCleanUp", 1, 1); + histogram_tester.ExpectUniqueSample( + "History.Clusters.ContextClusterer.NumClusters.CleanedUp", 0, 1); + histogram_tester.ExpectUniqueSample( + "History.Clusters.ContextClusterer.NumClusters.PostCleanUp", 1, 1); +} + +} // namespace history_clusters \ No newline at end of file
diff --git a/components/history_clusters/core/features.cc b/components/history_clusters/core/features.cc index f60b27e..23bc207 100644 --- a/components/history_clusters/core/features.cc +++ b/components/history_clusters/core/features.cc
@@ -81,6 +81,10 @@ "JourneysIncludeSyncedVisits", base::FEATURE_DISABLED_BY_DEFAULT); +BASE_FEATURE(kHistoryClustersNavigationContextClustering, + "HistoryClustersNavigationContextClustering", + base::FEATURE_DISABLED_BY_DEFAULT); + } // namespace internal BASE_FEATURE(kJourneysSurveyForHistoryEntrypoint,
diff --git a/components/history_clusters/core/features.h b/components/history_clusters/core/features.h index 251d45da..3ee66b98 100644 --- a/components/history_clusters/core/features.h +++ b/components/history_clusters/core/features.h
@@ -67,6 +67,10 @@ // Enables visits from other synced devices to be included in clusters. BASE_DECLARE_FEATURE(kJourneysIncludeSyncedVisits); +// Enables context clustering to be performed at navigation time rather than in +// batches. +BASE_DECLARE_FEATURE(kHistoryClustersNavigationContextClustering); + // Order consistently with config.h. } // namespace internal
diff --git a/components/history_clusters/core/history_clusters_service.cc b/components/history_clusters/core/history_clusters_service.cc index f36aec0..d192983 100644 --- a/components/history_clusters/core/history_clusters_service.cc +++ b/components/history_clusters/core/history_clusters_service.cc
@@ -58,12 +58,16 @@ optimization_guide::EntityMetadataProvider* entity_metadata_provider, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, site_engagement::SiteEngagementScoreProvider* engagement_score_provider, + TemplateURLService* template_url_service, optimization_guide::NewOptimizationGuideDecider* optimization_guide_decider) : is_journeys_enabled_( GetConfig().is_journeys_enabled_no_locale_check && IsApplicationLocaleSupportedByJourneys(application_locale)), history_service_(history_service), - visit_deletion_observer_(this) { + visit_deletion_observer_(this), + context_clusterer_observer_(history_service, + template_url_service, + optimization_guide_decider) { DCHECK(history_service_); visit_deletion_observer_.AttachToHistoryService(history_service);
diff --git a/components/history_clusters/core/history_clusters_service.h b/components/history_clusters/core/history_clusters_service.h index 2ae92e1..96ab6d32 100644 --- a/components/history_clusters/core/history_clusters_service.h +++ b/components/history_clusters/core/history_clusters_service.h
@@ -25,12 +25,15 @@ #include "components/history/core/browser/history_service_observer.h" #include "components/history/core/browser/history_types.h" #include "components/history_clusters/core/clustering_backend.h" +#include "components/history_clusters/core/context_clusterer_history_service_observer.h" #include "components/history_clusters/core/history_clusters_service_task_get_most_recent_clusters.h" #include "components/history_clusters/core/history_clusters_service_task_update_clusters.h" #include "components/history_clusters/core/history_clusters_types.h" #include "components/keyed_service/core/keyed_service.h" #include "services/network/public/cpp/shared_url_loader_factory.h" +class TemplateURLService; + namespace optimization_guide { class EntityMetadataProvider; class NewOptimizationGuideDecider; @@ -93,6 +96,7 @@ optimization_guide::EntityMetadataProvider* entity_metadata_provider, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, site_engagement::SiteEngagementScoreProvider* engagement_score_provider, + TemplateURLService* template_url_service, optimization_guide::NewOptimizationGuideDecider* optimization_guide_decider); HistoryClustersService(const HistoryClustersService&) = delete; @@ -282,6 +286,8 @@ VisitDeletionObserver visit_deletion_observer_; + ContextClustererHistoryServiceObserver context_clusterer_observer_; + // Weak pointers issued from this factory never get invalidated before the // service is destroyed. base::WeakPtrFactory<HistoryClustersService> weak_ptr_factory_{this};
diff --git a/components/history_clusters/core/history_clusters_service_unittest.cc b/components/history_clusters/core/history_clusters_service_unittest.cc index e8b61c3..05fe8ca 100644 --- a/components/history_clusters/core/history_clusters_service_unittest.cc +++ b/components/history_clusters/core/history_clusters_service_unittest.cc
@@ -110,6 +110,7 @@ /*entity_metadata_provider=*/nullptr, /*url_loader_factory=*/nullptr, /*engagement_score_provider=*/nullptr, + /*template_url_service=*/nullptr, /*optimization_guide_decider=*/nullptr); history_clusters_service_test_api_ =
diff --git a/components/history_clusters/history_clusters_internals/resources/BUILD.gn b/components/history_clusters/history_clusters_internals/resources/BUILD.gn index 208a1c3..ae159c9 100644 --- a/components/history_clusters/history_clusters_internals/resources/BUILD.gn +++ b/components/history_clusters/history_clusters_internals/resources/BUILD.gn
@@ -33,7 +33,7 @@ } copy("copy_mojo") { - deps = [ "//components/history_clusters/history_clusters_internals/webui:mojo_bindings_webui_js" ] + deps = [ "//components/history_clusters/history_clusters_internals/webui:mojo_bindings_js__generator" ] mojom_folder = "$root_gen_dir/mojom-webui/components/history_clusters/history_clusters_internals/webui/" sources = [ "$mojom_folder/history_clusters_internals.mojom-webui.js" ] outputs = [ "$target_gen_dir/{{source_file_part}}" ]
diff --git a/components/omnibox/browser/actions/history_clusters_action_unittest.cc b/components/omnibox/browser/actions/history_clusters_action_unittest.cc index 2ae6c2f5..f68aff6 100644 --- a/components/omnibox/browser/actions/history_clusters_action_unittest.cc +++ b/components/omnibox/browser/actions/history_clusters_action_unittest.cc
@@ -90,6 +90,7 @@ /*entity_metadata_provider=*/nullptr, /*url_loader_factory=*/nullptr, /*engagement_score_provider=*/nullptr, + /*template_url_service=*/nullptr, /*optimization_guide_decider=*/nullptr); history_clusters_service_test_api_ =
diff --git a/components/omnibox/browser/history_cluster_provider_unittest.cc b/components/omnibox/browser/history_cluster_provider_unittest.cc index 540d1a7..ee880cc7 100644 --- a/components/omnibox/browser/history_cluster_provider_unittest.cc +++ b/components/omnibox/browser/history_cluster_provider_unittest.cc
@@ -56,6 +56,7 @@ /*entity_metadata_provider=*/nullptr, /*url_loader_factory=*/nullptr, /*engagement_score_provider=*/nullptr, + /*template_url_service=*/nullptr, /*optimization_guide_decider=*/nullptr); history_clusters_service_test_api_ =
diff --git a/components/optimization_guide/core/BUILD.gn b/components/optimization_guide/core/BUILD.gn index ebc0b648..4148160 100644 --- a/components/optimization_guide/core/BUILD.gn +++ b/components/optimization_guide/core/BUILD.gn
@@ -268,6 +268,8 @@ static_library("prediction") { sources = [ + "model_store_metadata_entry.cc", + "model_store_metadata_entry.h", "prediction_manager.cc", "prediction_manager.h", "prediction_model_download_manager.cc", @@ -275,6 +277,8 @@ "prediction_model_download_observer.h", "prediction_model_override.cc", "prediction_model_override.h", + "prediction_model_store.cc", + "prediction_model_store.h", ] deps = [ "//components/crx_file", @@ -388,6 +392,7 @@ "prediction_manager_unittest.cc", "prediction_model_download_manager_unittest.cc", "prediction_model_fetcher_unittest.cc", + "prediction_model_store_unittest.cc", "push_notification_manager_unittest.cc", "store_update_data_unittest.cc", "url_pattern_with_wildcards_unittest.cc",
diff --git a/components/optimization_guide/core/model_store_metadata_entry.cc b/components/optimization_guide/core/model_store_metadata_entry.cc new file mode 100644 index 0000000..a89439bb --- /dev/null +++ b/components/optimization_guide/core/model_store_metadata_entry.cc
@@ -0,0 +1,104 @@ +// Copyright 2022 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/optimization_guide/core/model_store_metadata_entry.h" + +#include "base/files/file_path.h" +#include "base/json/values_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/time/time.h" +#include "components/optimization_guide/core/model_util.h" +#include "components/optimization_guide/core/optimization_guide_features.h" +#include "components/optimization_guide/core/optimization_guide_prefs.h" +#include "components/optimization_guide/core/optimization_guide_switches.h" +#include "components/optimization_guide/core/optimization_guide_util.h" +#include "components/optimization_guide/proto/models.pb.h" +#include "components/prefs/pref_service.h" +#include "model_store_metadata_entry.h" + +namespace optimization_guide { + +namespace { + +// Key names for the metadata entries. +const char kKeyModelBaseDir[] = "mbd"; +const char kKeyExpiryTime[] = "et"; +const char kKeyKeepBeyondValidDuration[] = "kbvd"; + +} // namespace + +// static +absl::optional<ModelStoreMetadataEntry> +ModelStoreMetadataEntry::GetModelMetadataEntryIfExists( + PrefService* local_state, + proto::OptimizationTarget optimization_target, + const proto::ModelCacheKey& model_cache_key) { + auto* metadata_target = + local_state->GetDict(prefs::localstate::kModelStoreMetadata) + .FindDict( + base::NumberToString(static_cast<int>(optimization_target))); + if (!metadata_target) + return absl::nullopt; + auto* metadata_entry = + metadata_target->FindDict(GetModelCacheKeyHash(model_cache_key)); + if (!metadata_entry) + return absl::nullopt; + return ModelStoreMetadataEntry(metadata_entry); +} + +ModelStoreMetadataEntry::ModelStoreMetadataEntry( + const base::Value::Dict* metadata_entry) + : metadata_entry_(metadata_entry) {} + +ModelStoreMetadataEntry::~ModelStoreMetadataEntry() = default; + +absl::optional<base::FilePath> ModelStoreMetadataEntry::GetModelBaseDir() + const { + return base::ValueToFilePath(metadata_entry_->Find(kKeyModelBaseDir)); +} + +base::Time ModelStoreMetadataEntry::GetExpiryTime() const { + return base::ValueToTime(metadata_entry_->Find(kKeyExpiryTime)) + .value_or(base::Time::Now() + features::StoredModelsValidDuration()); +} + +bool ModelStoreMetadataEntry::GetKeepBeyondValidDuration() const { + return metadata_entry_->FindBool(kKeyKeepBeyondValidDuration).value_or(false); +} + +void ModelStoreMetadataEntry::SetMetadataEntry( + const base::Value::Dict* metadata_entry) { + metadata_entry_ = metadata_entry; +} + +ModelStoreMetadataEntryUpdater::ModelStoreMetadataEntryUpdater( + PrefService* local_state, + proto::OptimizationTarget optimization_target, + const proto::ModelCacheKey& model_cache_key) + : ModelStoreMetadataEntry(/*metadata_entry=*/nullptr), + pref_updater_(local_state, prefs::localstate::kModelStoreMetadata) { + auto* metadata_target = pref_updater_->EnsureDict( + base::NumberToString(static_cast<int>(optimization_target))); + metadata_entry_updater_ = + metadata_target->EnsureDict(GetModelCacheKeyHash(model_cache_key)); + SetMetadataEntry(metadata_entry_updater_); +} + +void ModelStoreMetadataEntryUpdater::SetModelBaseDir( + base::FilePath model_base_dir) { + metadata_entry_updater_->Set(kKeyModelBaseDir, + base::FilePathToValue(model_base_dir)); +} + +void ModelStoreMetadataEntryUpdater::SetExpiryTime(base::Time expiry_time) { + metadata_entry_updater_->Set(kKeyExpiryTime, base::TimeToValue(expiry_time)); +} + +void ModelStoreMetadataEntryUpdater::SetKeepBeyondValidDuration( + bool keep_beyond_valid_duration) { + metadata_entry_updater_->Set(kKeyKeepBeyondValidDuration, + keep_beyond_valid_duration); +} + +} // namespace optimization_guide \ No newline at end of file
diff --git a/components/optimization_guide/core/model_store_metadata_entry.h b/components/optimization_guide/core/model_store_metadata_entry.h new file mode 100644 index 0000000..ff7731f --- /dev/null +++ b/components/optimization_guide/core/model_store_metadata_entry.h
@@ -0,0 +1,83 @@ +// Copyright 2022 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_OPTIMIZATION_GUIDE_CORE_MODEL_STORE_METADATA_ENTRY_H_ +#define COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_STORE_METADATA_ENTRY_H_ + +#include "base/values.h" +#include "components/optimization_guide/proto/models.pb.h" +#include "components/prefs/scoped_user_pref_update.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace base { +class FilePath; +class Time; +} // namespace base + +class PrefService; + +namespace optimization_guide { + +// Encapsulates the lightweight metadata entry that is stored in local state +// prefs for one model in the model store. The model is represented by the key +// pair OptimizationTarget and hash of ModelCacheKey. +class ModelStoreMetadataEntry { + public: + // Returns the metadata entry in the store if it exists. + static absl::optional<ModelStoreMetadataEntry> GetModelMetadataEntryIfExists( + PrefService* local_state, + proto::OptimizationTarget optimization_target, + const proto::ModelCacheKey& model_cache_key); + + ModelStoreMetadataEntry& operator=(const ModelStoreMetadataEntry&) = delete; + ~ModelStoreMetadataEntry(); + + // Gets the model base dir where the model files, its additional files + // and the model info files are stored. + absl::optional<base::FilePath> GetModelBaseDir() const; + + // Gets the expiry time. + base::Time GetExpiryTime() const; + + // Gets whether the model should be kept beyond the expiry duration. + bool GetKeepBeyondValidDuration() const; + + protected: + explicit ModelStoreMetadataEntry(const base::Value::Dict* metadata_entry); + + void SetMetadataEntry(const base::Value::Dict* metadata_entry); + + private: + // The root metadata entry for this model. + const base::Value::Dict* metadata_entry_; +}; + +// The pref updater for ModelStoreMetadataEntry. +class ModelStoreMetadataEntryUpdater : public ModelStoreMetadataEntry { + public: + // Returns the metadata entry in the store, creating it if it does not exist. + ModelStoreMetadataEntryUpdater(PrefService* local_state, + proto::OptimizationTarget optimization_target, + const proto::ModelCacheKey& model_cache_key); + + ModelStoreMetadataEntryUpdater(const ModelStoreMetadataEntryUpdater&) = + delete; + ModelStoreMetadataEntryUpdater& operator=( + const ModelStoreMetadataEntryUpdater&) = delete; + + // The setters for the various model metadata. + void SetModelBaseDir(base::FilePath model_base_dir); + void SetKeepBeyondValidDuration(bool keep_beyond_valid_duration); + void SetExpiryTime(base::Time expiry_time); + + private: + // The root metadata entry that is linked with the |pref_updater_|. + base::Value::Dict* metadata_entry_updater_; + + ScopedDictPrefUpdate pref_updater_; +}; + +} // namespace optimization_guide + +#endif // COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_STORE_METADATA_ENTRY_H_
diff --git a/components/optimization_guide/core/model_util.cc b/components/optimization_guide/core/model_util.cc index fc92be5..ab3d441 100644 --- a/components/optimization_guide/core/model_util.cc +++ b/components/optimization_guide/core/model_util.cc
@@ -7,8 +7,10 @@ #include "base/base64.h" #include "base/containers/flat_set.h" #include "base/files/file_util.h" +#include "base/hash/legacy_hash.h" #include "base/logging.h" #include "base/notreached.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" @@ -197,4 +199,14 @@ return true; } +std::string GetModelCacheKeyHash(proto::ModelCacheKey model_cache_key) { + std::string bytes; + model_cache_key.SerializeToString(&bytes); + uint64_t hash = + base::legacy::CityHash64(base::as_bytes(base::make_span(bytes))); + // Convert the hash to hex encoding and not as base64 and other encodings, + // since it will be used as filepath names. + return base::HexEncode(base::as_bytes(base::make_span(&hash, 1))); +} + } // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_util.h b/components/optimization_guide/core/model_util.h index aed1703b..4892b05 100644 --- a/components/optimization_guide/core/model_util.h +++ b/components/optimization_guide/core/model_util.h
@@ -50,6 +50,10 @@ // Checks all the files in |file_paths_to_check| exists. bool CheckAllPathsExist(const std::vector<base::FilePath>& file_paths_to_check); +// Returns the hash of |model_cache_key| that can be used as key in a +// persistent dict, or can be used as file paths. +std::string GetModelCacheKeyHash(proto::ModelCacheKey model_cache_key); + } // namespace optimization_guide #endif // COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_UTIL_H_
diff --git a/components/optimization_guide/core/model_util_unittest.cc b/components/optimization_guide/core/model_util_unittest.cc index f2f4bb3b..43fe104 100644 --- a/components/optimization_guide/core/model_util_unittest.cc +++ b/components/optimization_guide/core/model_util_unittest.cc
@@ -6,6 +6,8 @@ #include "base/base64.h" #include "base/command_line.h" +#include "base/ranges/algorithm.h" +#include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "components/optimization_guide/core/optimization_guide_switches.h" #include "components/optimization_guide/core/optimization_guide_test_util.h" @@ -21,6 +23,12 @@ const char kOtherAbsoluteFilePath[] = "/other/abs/file/path"; #endif +proto::ModelCacheKey CreateModelCacheKey(const std::string& locale) { + proto::ModelCacheKey model_cache_key; + model_cache_key.set_locale(locale); + return model_cache_key; +} + } // namespace TEST(ModelUtilTest, GetModelOverrideForOptimizationTargetSwitchNotSet) { @@ -169,4 +177,14 @@ EXPECT_EQ("sometypeurl", file_path_and_metadata->second->type_url()); } +TEST(ModelUtilTest, ModelCacheKeyHash) { + EXPECT_EQ(GetModelCacheKeyHash(CreateModelCacheKey("en-US")), + GetModelCacheKeyHash(CreateModelCacheKey("en-US"))); + EXPECT_NE(GetModelCacheKeyHash(CreateModelCacheKey("en-US")), + GetModelCacheKeyHash(CreateModelCacheKey("en-UK"))); + EXPECT_TRUE( + base::ranges::all_of(GetModelCacheKeyHash(CreateModelCacheKey("en-US")), + [](char ch) { return base::IsHexDigit(ch); })); +} + } // namespace optimization_guide \ No newline at end of file
diff --git a/components/optimization_guide/core/optimization_guide_prefs.cc b/components/optimization_guide/core/optimization_guide_prefs.cc index 9d070a7..530d34d 100644 --- a/components/optimization_guide/core/optimization_guide_prefs.cc +++ b/components/optimization_guide/core/optimization_guide_prefs.cc
@@ -54,6 +54,14 @@ const char kStoreFilePathsToDelete[] = "optimization_guide.store_file_paths_to_delete"; +namespace localstate { + +// A dictionary pref that stores the lightweight metadata of all the models in +// the store, keyed by the optimization target and ModelCacheKey. +const char kModelStoreMetadata[] = "optimization_guide.model_store_metadata"; + +} // namespace localstate + void RegisterProfilePrefs(PrefRegistrySimple* registry) { registry->RegisterInt64Pref( kHintsFetcherLastFetchAttempt, @@ -76,5 +84,9 @@ PrefRegistry::LOSSY_PREF); } +void RegisterLocalStatePrefs(PrefRegistrySimple* registry) { + registry->RegisterDictionaryPref(localstate::kModelStoreMetadata); +} + } // namespace prefs } // namespace optimization_guide
diff --git a/components/optimization_guide/core/optimization_guide_prefs.h b/components/optimization_guide/core/optimization_guide_prefs.h index 13f28680..178fadb 100644 --- a/components/optimization_guide/core/optimization_guide_prefs.h +++ b/components/optimization_guide/core/optimization_guide_prefs.h
@@ -10,6 +10,7 @@ namespace optimization_guide { namespace prefs { +// User profile prefs. extern const char kHintsFetcherLastFetchAttempt[]; extern const char kModelAndFeaturesLastFetchAttempt[]; extern const char kModelLastFetchSuccess[]; @@ -18,9 +19,19 @@ extern const char kPreviouslyRegisteredOptimizationTypes[]; extern const char kStoreFilePathsToDelete[]; +namespace localstate { + +// Local state prefs. +extern const char kModelStoreMetadata[]; + +} // namespace localstate + // Registers the optimization guide's prefs. void RegisterProfilePrefs(PrefRegistrySimple* registry); +// Registers the local state prefs. +void RegisterLocalStatePrefs(PrefRegistrySimple* registry); + } // namespace prefs } // namespace optimization_guide
diff --git a/components/optimization_guide/core/prediction_manager.h b/components/optimization_guide/core/prediction_manager.h index 2ecf3cc..0858c2a 100644 --- a/components/optimization_guide/core/prediction_manager.h +++ b/components/optimization_guide/core/prediction_manager.h
@@ -290,8 +290,7 @@ // The logger that plumbs the debug logs to the optimization guide // internals page. Not owned. Guaranteed to outlive |this|, since the logger // and |this| are owned by the optimization guide keyed service. - raw_ptr<OptimizationGuideLogger, DanglingUntriaged> - optimization_guide_logger_; + raw_ptr<OptimizationGuideLogger> optimization_guide_logger_; // A reference to the PrefService for this profile. Not owned. raw_ptr<PrefService> pref_service_ = nullptr;
diff --git a/components/optimization_guide/core/prediction_model_store.cc b/components/optimization_guide/core/prediction_model_store.cc new file mode 100644 index 0000000..e2a576fb --- /dev/null +++ b/components/optimization_guide/core/prediction_model_store.cc
@@ -0,0 +1,214 @@ +// Copyright 2022 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/optimization_guide/core/prediction_model_store.h" + +#include "base/files/file_util.h" +#include "base/guid.h" +#include "base/rand_util.h" +#include "base/task/thread_pool.h" +#include "components/optimization_guide/core/model_store_metadata_entry.h" +#include "components/optimization_guide/core/model_util.h" +#include "components/optimization_guide/core/optimization_guide_features.h" +#include "components/optimization_guide/core/optimization_guide_prefs.h" +#include "components/prefs/pref_service.h" +#include "model_store_metadata_entry.h" + +namespace optimization_guide { + +namespace { + +// Returns the model info parsed from |model_info_path|. +absl::optional<proto::ModelInfo> ParseModelInfoFromFile( + const base::FilePath& model_info_path) { + std::string binary_model_info; + if (!base::ReadFileToString(model_info_path, &binary_model_info)) + return absl::nullopt; + + proto::ModelInfo model_info; + if (!model_info.ParseFromString(binary_model_info)) + return absl::nullopt; + + DCHECK(model_info.has_version()); + DCHECK(model_info.has_optimization_target()); + return model_info; +} + +// Returns all the model file paths for the model |model_info| in +// |base_model_dir|. +std::vector<base::FilePath> GetModelFilePaths( + const proto::ModelInfo& model_info, + const base::FilePath& base_model_dir) { + std::vector<base::FilePath> model_file_paths; + model_file_paths.emplace_back( + base_model_dir.Append(GetBaseFileNameForModels())); + model_file_paths.emplace_back( + base_model_dir.Append(GetBaseFileNameForModelInfo())); + for (const auto& additional_file : model_info.additional_files()) { + auto additional_filepath = StringToFilePath(additional_file.file_path()); + if (!additional_filepath) + continue; + DCHECK(base_model_dir.IsParent(*additional_filepath)); + model_file_paths.emplace_back(*additional_filepath); + } + return model_file_paths; +} + +} // namespace + +PredictionModelStore::PredictionModelStore(PrefService* local_state, + const base::FilePath& base_store_dir) + : local_state_(local_state), + base_store_dir_(base_store_dir), + background_task_runner_(base::ThreadPool::CreateSequencedTaskRunner( + {base::MayBlock(), base::TaskPriority::BEST_EFFORT})) { + DCHECK(optimization_guide::features::IsInstallWideModelStoreEnabled()); + DCHECK(local_state); + DCHECK(!base_store_dir.empty()); +} + +PredictionModelStore::~PredictionModelStore() = default; + +bool PredictionModelStore::HasModel( + proto::OptimizationTarget optimization_target, + const proto::ModelCacheKey& model_cache_key) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return ModelStoreMetadataEntry::GetModelMetadataEntryIfExists( + local_state_, optimization_target, model_cache_key) + .has_value(); +} + +void PredictionModelStore::LoadModel( + proto::OptimizationTarget optimization_target, + const proto::ModelCacheKey& model_cache_key, + PredictionModelLoadedCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + auto metadata = ModelStoreMetadataEntry::GetModelMetadataEntryIfExists( + local_state_, optimization_target, model_cache_key); + if (!metadata) { + std::move(callback).Run(nullptr); + return; + } + if (!metadata->GetKeepBeyondValidDuration() && + metadata->GetExpiryTime() <= base::Time::Now()) { + // TODO(b/244649670): Remove the invalid model. + std::move(callback).Run(nullptr); + return; + } + auto base_model_dir = metadata->GetModelBaseDir(); + if (!base_model_dir) { + // TODO(b/244649670): Remove the invalid model. + std::move(callback).Run(nullptr); + return; + } + DCHECK(base_store_dir_.IsParent(*base_model_dir)); + + background_task_runner_->PostTaskAndReplyWithResult( + FROM_HERE, + base::BindOnce( + &PredictionModelStore::LoadAndVerifyModelInBackgroundThread, + optimization_target, *base_model_dir), + base::BindOnce(&PredictionModelStore::OnModelLoaded, + weak_ptr_factory_.GetWeakPtr(), optimization_target, + model_cache_key, std::move(callback))); +} + +// static +std::unique_ptr<proto::PredictionModel> +PredictionModelStore::LoadAndVerifyModelInBackgroundThread( + proto::OptimizationTarget optimization_target, + const base::FilePath& base_model_dir) { + auto model_info = ParseModelInfoFromFile( + base_model_dir.Append(GetBaseFileNameForModelInfo())); + if (!model_info) { + return nullptr; + } + DCHECK_EQ(optimization_target, model_info->optimization_target()); + // Make sure the model file, the full modelinfo file and all additional + // files still exist. + auto file_paths_to_check = GetModelFilePaths(*model_info, base_model_dir); + if (!CheckAllPathsExist(file_paths_to_check)) { + return nullptr; + } + std::unique_ptr<proto::PredictionModel> model = + std::make_unique<proto::PredictionModel>(); + *model->mutable_model_info() = *model_info; + model->mutable_model()->set_download_url( + FilePathToString(base_model_dir.Append(GetBaseFileNameForModels()))); + + return model; +} + +void PredictionModelStore::OnModelLoaded( + proto::OptimizationTarget optimization_target, + const proto::ModelCacheKey& model_cache_key, + PredictionModelLoadedCallback callback, + std::unique_ptr<proto::PredictionModel> model) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!model) { + // TODO(b/244649670): Remove the invalid model. + std::move(callback).Run(nullptr); + return; + } + std::move(callback).Run(std::move(model)); +} + +void PredictionModelStore::UpdateModel( + proto::OptimizationTarget optimization_target, + const proto::ModelCacheKey& model_cache_key, + const proto::ModelInfo& model_info, + const base::FilePath& base_model_dir, + base::OnceClosure callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(model_info.has_version()); + DCHECK_EQ(optimization_target, model_info.optimization_target()); + DCHECK(base_store_dir_.IsParent(base_model_dir)); + + ModelStoreMetadataEntryUpdater metadata(local_state_, optimization_target, + model_cache_key); + metadata.SetExpiryTime( + base::Time::Now() + + (model_info.has_valid_duration() + ? base::Seconds(model_info.valid_duration().seconds()) + : features::StoredModelsValidDuration())); + metadata.SetKeepBeyondValidDuration(model_info.keep_beyond_valid_duration()); + metadata.SetModelBaseDir(base_model_dir); + + background_task_runner_->PostTaskAndReplyWithResult( + FROM_HERE, + base::BindOnce(&CheckAllPathsExist, + GetModelFilePaths(model_info, base_model_dir)), + base::BindOnce(&PredictionModelStore::OnModelUpdateVerified, + weak_ptr_factory_.GetWeakPtr(), optimization_target, + model_cache_key, std::move(callback))); +} + +void PredictionModelStore::OnModelUpdateVerified( + proto::OptimizationTarget optimization_target, + const proto::ModelCacheKey& model_cache_key, + base::OnceClosure callback, + bool model_paths_exist) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!model_paths_exist) { + // TODO(b/244649670): Remove the invalid model. + } + std::move(callback).Run(); +} + +base::FilePath PredictionModelStore::GetBaseModelDirForModelCacheKey( + proto::OptimizationTarget optimization_target, + const proto::ModelCacheKey& model_cache_key) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + DCHECK(!base_store_dir_.empty()); + auto base_model_dir = base_store_dir_ + .AppendASCII(base::NumberToString( + static_cast<int>(optimization_target))) + .AppendASCII(GetModelCacheKeyHash(model_cache_key)); + return base_model_dir.AppendASCII(base::HexEncode( + base::as_bytes(base::make_span(base::RandBytesAsString(8))))); +} + +} // namespace optimization_guide
diff --git a/components/optimization_guide/core/prediction_model_store.h b/components/optimization_guide/core/prediction_model_store.h new file mode 100644 index 0000000..60599d63 --- /dev/null +++ b/components/optimization_guide/core/prediction_model_store.h
@@ -0,0 +1,114 @@ +// Copyright 2022 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_OPTIMIZATION_GUIDE_CORE_PREDICTION_MODEL_STORE_H_ +#define COMPONENTS_OPTIMIZATION_GUIDE_CORE_PREDICTION_MODEL_STORE_H_ + +#include "base/files/file_path.h" +#include "base/memory/scoped_refptr.h" +#include "base/memory/weak_ptr.h" +#include "base/sequence_checker.h" +#include "base/task/sequenced_task_runner.h" +#include "base/values.h" +#include "components/optimization_guide/proto/models.pb.h" + +class PrefService; + +namespace optimization_guide { + +// The new model store that manages the optimization guide prediction models. +// The model store is a key-value store where the optimization target, +// ModelCacheKey can be together considered as the key and the value is the +// model metadata and its files. For every model following are stored: +// * The prediction model file and its additional files are stored in the +// model dir. +// * The full model metadata as model_info.pb in the model dir. +// * Lightweight model metadata in the local state prefs, that is immediately +// needed for managing the store, and to avoid the full metadata read. +// The model store is meant to be shared across profiles. +class PredictionModelStore { + public: + using PredictionModelLoadedCallback = + base::OnceCallback<void(std::unique_ptr<proto::PredictionModel>)>; + + PredictionModelStore(PrefService* local_state, + const base::FilePath& base_store_dir); + + PredictionModelStore(const PredictionModelStore&) = delete; + PredictionModelStore& operator=(const PredictionModelStore&) = delete; + ~PredictionModelStore(); + + // Initializes the model store with |local_state| and the |base_store_dir|, if + // initialization hasn't happened already. Model store will be usable only + // after it is initialized. + + // Returns whether the model represented by |optimization_target| and + // |model_cache_key| is available in the store. + bool HasModel(proto::OptimizationTarget optimization_target, + const proto::ModelCacheKey& model_cache_key) const; + + // Loads the model represented by |optimization_target| and + // |model_cache_key|. Once the model is loaded and validated |callback| + // is invoked. On any failures, callback is run with nullptr. + void LoadModel(proto::OptimizationTarget optimization_target, + const proto::ModelCacheKey& model_cache_key, + PredictionModelLoadedCallback callback); + + // Update the model for |model_info| in the store represented by + // |optimization_target| and |model_cache_key|. The model files are stored in + // |base_model_dir|. |callback| is invoked on completion. + void UpdateModel(proto::OptimizationTarget optimization_target, + const proto::ModelCacheKey& model_cache_key, + const proto::ModelInfo& model_info, + const base::FilePath& base_model_dir, + base::OnceClosure callback); + + // Returns the base model dir where the model files, full modelinfo, etc + // should be stored, for the model represented by |optimization_target| and + // |model_cache_key|. + base::FilePath GetBaseModelDirForModelCacheKey( + proto::OptimizationTarget optimization_target, + const proto::ModelCacheKey& model_cache_key); + + private: + friend class PredictionModelStoreTest; + + // Loads the model and verifies if the model files exist and returns the + // model. Otherwise nullptr is returned on any failures. + static std::unique_ptr<proto::PredictionModel> + LoadAndVerifyModelInBackgroundThread( + proto::OptimizationTarget optimization_target, + const base::FilePath& base_model_dir); + + // Invoked when the model loaded. + void OnModelLoaded(proto::OptimizationTarget optimization_target, + const proto::ModelCacheKey& model_cache_key, + PredictionModelLoadedCallback callback, + std::unique_ptr<proto::PredictionModel> model); + + // Invoked when the model files are verified on a model update. + void OnModelUpdateVerified(proto::OptimizationTarget optimization_target, + const proto::ModelCacheKey& model_cache_key, + base::OnceClosure callback, + bool model_paths_exist); + + // Local state that stores the prefs across all profiles. Not owned and + // outlives |this|. + raw_ptr<PrefService> local_state_ GUARDED_BY_CONTEXT(sequence_checker_) = + nullptr; + + // The base dir where the prediction model dirs are saved. + base::FilePath base_store_dir_ GUARDED_BY_CONTEXT(sequence_checker_); + + SEQUENCE_CHECKER(sequence_checker_); + + // Background thread where file processing should be performed. + scoped_refptr<base::SequencedTaskRunner> background_task_runner_; + + base::WeakPtrFactory<PredictionModelStore> weak_ptr_factory_{this}; +}; + +} // namespace optimization_guide + +#endif // COMPONENTS_OPTIMIZATION_GUIDE_CORE_PREDICTION_MODEL_STORE_H_ \ No newline at end of file
diff --git a/components/optimization_guide/core/prediction_model_store_unittest.cc b/components/optimization_guide/core/prediction_model_store_unittest.cc new file mode 100644 index 0000000..4d2828e --- /dev/null +++ b/components/optimization_guide/core/prediction_model_store_unittest.cc
@@ -0,0 +1,209 @@ +// Copyright 2022 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/optimization_guide/core/prediction_model_store.h" +#include <memory> + +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/guid.h" +#include "base/rand_util.h" +#include "base/task/thread_pool.h" +#include "base/test/scoped_feature_list.h" +#include "base/test/task_environment.h" +#include "components/optimization_guide/core/model_store_metadata_entry.h" +#include "components/optimization_guide/core/model_util.h" +#include "components/optimization_guide/core/optimization_guide_features.h" +#include "components/optimization_guide/core/optimization_guide_prefs.h" +#include "components/optimization_guide/proto/models.pb.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/testing_pref_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace optimization_guide { + +namespace { + +const proto::OptimizationTarget kTestOptimizationTargetFoo = + proto::OptimizationTarget::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD; +const proto::OptimizationTarget kTestOptimizationTargetBar = + proto::OptimizationTarget::OPTIMIZATION_TARGET_MODEL_VALIDATION; + +const char kTestLocaleFoo[] = "foo"; + +proto::ModelCacheKey CreateModelCacheKey(const std::string& locale) { + proto::ModelCacheKey model_cache_key; + model_cache_key.set_locale(locale); + return model_cache_key; +} + +struct ModelDetail { + proto::ModelInfo model_info; + base::FilePath base_model_dir; +}; + +} // namespace + +class PredictionModelStoreTest : public testing::Test { + public: + PredictionModelStoreTest() { + feature_list_.InitWithFeatures( + {features::kRemoteOptimizationGuideFetching, + features::kOptimizationGuideModelDownloading, + features::kOptimizationGuideInstallWideModelStore}, + {}); + } + + void SetUp() override { + ASSERT_TRUE(temp_models_dir_.CreateUniqueTempDir()); + local_state_prefs_ = std::make_unique<TestingPrefServiceSimple>(); + prefs::RegisterLocalStatePrefs(local_state_prefs_->registry()); + prediction_model_store_ = std::make_unique<PredictionModelStore>( + local_state_prefs_.get(), temp_models_dir_.GetPath()); + } + + void OnPredictionModelLoaded( + std::unique_ptr<proto::PredictionModel> loaded_prediction_model) { + last_loaded_prediction_model_ = std::move(loaded_prediction_model); + } + + proto::PredictionModel* last_loaded_prediction_model() { + return last_loaded_prediction_model_.get(); + } + + void RunUntilIdle() { task_environment_.RunUntilIdle(); } + + // Creates model files and returns model details. + ModelDetail CreateTestModelFiles( + proto::OptimizationTarget optimization_target, + const proto::ModelCacheKey& model_cache_key, + const std::vector<const base::FilePath::CharType*> + additional_file_names) { + auto base_model_dir = + prediction_model_store_->GetBaseModelDirForModelCacheKey( + optimization_target, model_cache_key); + base::CreateDirectory(base_model_dir); + base::WriteFile(base_model_dir.Append(GetBaseFileNameForModels()), ""); + proto::ModelInfo model_info; + model_info.set_optimization_target(optimization_target); + model_info.set_version(1); + for (const auto* additional_file_name : additional_file_names) { + base::WriteFile(base_model_dir.Append(additional_file_name), ""); + model_info.add_additional_files()->set_file_path( + FilePathToString(base_model_dir.Append(additional_file_name))); + } + *model_info.mutable_model_cache_key() = model_cache_key; + std::string model_info_pb; + model_info.SerializeToString(&model_info_pb); + base::WriteFile(base_model_dir.Append(GetBaseFileNameForModelInfo()), + model_info_pb); + return {model_info, base_model_dir}; + } + + protected: + base::test::ScopedFeatureList feature_list_; + base::test::TaskEnvironment task_environment_; + base::ScopedTempDir temp_models_dir_; + std::unique_ptr<TestingPrefServiceSimple> local_state_prefs_; + std::unique_ptr<proto::PredictionModel> last_loaded_prediction_model_; + std::unique_ptr<PredictionModelStore> prediction_model_store_; +}; + +TEST_F(PredictionModelStoreTest, BaseModelDirs) { + auto model_cache_key = CreateModelCacheKey(kTestLocaleFoo); + auto base_model_dir = + prediction_model_store_->GetBaseModelDirForModelCacheKey( + kTestOptimizationTargetFoo, model_cache_key); + EXPECT_TRUE(base_model_dir.IsAbsolute()); + EXPECT_TRUE(temp_models_dir_.GetPath().IsParent(base_model_dir)); +} + +TEST_F(PredictionModelStoreTest, ModelUpdateAndLoad) { + auto model_cache_key = CreateModelCacheKey(kTestLocaleFoo); + + EXPECT_FALSE(prediction_model_store_->HasModel(kTestOptimizationTargetFoo, + model_cache_key)); + auto model_detail = + CreateTestModelFiles(kTestOptimizationTargetFoo, model_cache_key, {}); + prediction_model_store_->UpdateModel( + kTestOptimizationTargetFoo, model_cache_key, model_detail.model_info, + model_detail.base_model_dir, base::DoNothing()); + RunUntilIdle(); + EXPECT_TRUE(prediction_model_store_->HasModel(kTestOptimizationTargetFoo, + model_cache_key)); + EXPECT_FALSE(prediction_model_store_->HasModel(kTestOptimizationTargetBar, + model_cache_key)); + + prediction_model_store_->LoadModel( + kTestOptimizationTargetFoo, model_cache_key, + base::BindOnce(&PredictionModelStoreTest::OnPredictionModelLoaded, + base::Unretained(this))); + RunUntilIdle(); + + proto::PredictionModel* loaded_model = last_loaded_prediction_model(); + EXPECT_TRUE(loaded_model); + EXPECT_EQ(kTestOptimizationTargetFoo, + loaded_model->model_info().optimization_target()); + EXPECT_EQ(StringToFilePath(loaded_model->model().download_url()).value(), + model_detail.base_model_dir.Append(GetBaseFileNameForModels())); + EXPECT_EQ(0, loaded_model->model_info().additional_files_size()); + + prediction_model_store_->LoadModel( + kTestOptimizationTargetBar, model_cache_key, + base::BindOnce(&PredictionModelStoreTest::OnPredictionModelLoaded, + base::Unretained(this))); + RunUntilIdle(); + EXPECT_FALSE(last_loaded_prediction_model()); +} + +// Tests model with an additional file. +TEST_F(PredictionModelStoreTest, ModelWithAdditionalFile) { + auto model_cache_key = CreateModelCacheKey(kTestLocaleFoo); + auto model_detail = + CreateTestModelFiles(kTestOptimizationTargetFoo, model_cache_key, + {FILE_PATH_LITERAL("valid_additional_file.txt")}); + + prediction_model_store_->UpdateModel( + kTestOptimizationTargetFoo, model_cache_key, model_detail.model_info, + model_detail.base_model_dir, base::DoNothing()); + RunUntilIdle(); + EXPECT_TRUE(prediction_model_store_->HasModel(kTestOptimizationTargetFoo, + model_cache_key)); + + prediction_model_store_->LoadModel( + kTestOptimizationTargetFoo, model_cache_key, + base::BindOnce(&PredictionModelStoreTest::OnPredictionModelLoaded, + base::Unretained(this))); + RunUntilIdle(); + + EXPECT_TRUE(last_loaded_prediction_model()); +} + +// Tests model with invalid additional file. +TEST_F(PredictionModelStoreTest, InvalidModelAdditionalFile) { + auto model_cache_key = CreateModelCacheKey(kTestLocaleFoo); + auto model_detail = + CreateTestModelFiles(kTestOptimizationTargetFoo, model_cache_key, + {FILE_PATH_LITERAL("valid_additional_file.txt"), + FILE_PATH_LITERAL("invalid_additional_file.txt")}); + base::DeleteFile( + model_detail.base_model_dir.AppendASCII("invalid_additional_file.txt")); + + prediction_model_store_->UpdateModel( + kTestOptimizationTargetFoo, model_cache_key, model_detail.model_info, + model_detail.base_model_dir, base::DoNothing()); + RunUntilIdle(); + EXPECT_TRUE(prediction_model_store_->HasModel(kTestOptimizationTargetFoo, + model_cache_key)); + + prediction_model_store_->LoadModel( + kTestOptimizationTargetFoo, model_cache_key, + base::BindOnce(&PredictionModelStoreTest::OnPredictionModelLoaded, + base::Unretained(this))); + RunUntilIdle(); + + EXPECT_FALSE(last_loaded_prediction_model()); +} + +} // namespace optimization_guide
diff --git a/components/optimization_guide/optimization_guide_internals/resources/BUILD.gn b/components/optimization_guide/optimization_guide_internals/resources/BUILD.gn index d9a6e35..7592dc6 100644 --- a/components/optimization_guide/optimization_guide_internals/resources/BUILD.gn +++ b/components/optimization_guide/optimization_guide_internals/resources/BUILD.gn
@@ -35,7 +35,7 @@ copy("copy_mojo") { deps = [ "//components/optimization_guide/core:interfaces_js__generator", - "//components/optimization_guide/optimization_guide_internals/webui:mojo_bindings_webui_js", + "//components/optimization_guide/optimization_guide_internals/webui:mojo_bindings_js__generator", ] mojom_folder = "$root_gen_dir/mojom-webui/components/optimization_guide/optimization_guide_internals/webui/" core_mojom_folder =
diff --git a/components/page_load_metrics/browser/page_load_metrics_test_waiter.cc b/components/page_load_metrics/browser/page_load_metrics_test_waiter.cc index 579a9f4..34d0c54 100644 --- a/components/page_load_metrics/browser/page_load_metrics_test_waiter.cc +++ b/components/page_load_metrics/browser/page_load_metrics_test_waiter.cc
@@ -198,7 +198,7 @@ run_loop_->Run(); run_loop_ = nullptr; } - EXPECT_TRUE(ExpectationsSatisfied()); + AssertExpectationsSatisfied(); ResetExpectations(); } @@ -549,6 +549,23 @@ TotalInputDelayExpectationsSatisfied(); } +void PageLoadMetricsTestWaiter::AssertExpectationsSatisfied() const { + EXPECT_TRUE(expected_.page_fields_.AreAllSetIn(observed_.page_fields_)); + EXPECT_TRUE( + expected_.subframe_fields_.AreAllSetIn(observed_.subframe_fields_)); + EXPECT_TRUE(ResourceUseExpectationsSatisfied()); + EXPECT_TRUE(UseCounterExpectationsSatisfied()); + EXPECT_TRUE(SubframeNavigationExpectationsSatisfied()); + EXPECT_TRUE(SubframeDataExpectationsSatisfied()); + EXPECT_TRUE(IsSubset(expected_.frame_sizes_, observed_.frame_sizes_)); + EXPECT_TRUE(LoadingBehaviorExpectationsSatisfied()); + EXPECT_TRUE(CpuTimeExpectationsSatisfied()); + EXPECT_TRUE(MainFrameIntersectionExpectationsSatisfied()); + EXPECT_TRUE(MainFrameViewportRectExpectationsSatisfied()); + EXPECT_TRUE(MemoryUpdateExpectationsSatisfied()); + EXPECT_TRUE(TotalInputDelayExpectationsSatisfied()); +} + void PageLoadMetricsTestWaiter::ResetExpectations() { expected_ = State(); observed_ = State();
diff --git a/components/page_load_metrics/browser/page_load_metrics_test_waiter.h b/components/page_load_metrics/browser/page_load_metrics_test_waiter.h index c7feaa4..5d3dbcca 100644 --- a/components/page_load_metrics/browser/page_load_metrics_test_waiter.h +++ b/components/page_load_metrics/browser/page_load_metrics_test_waiter.h
@@ -132,6 +132,7 @@ protected: virtual bool ExpectationsSatisfied() const; + void AssertExpectationsSatisfied() const; // Intended to be overridden in tests to allow tests to wait on other resource // conditions.
diff --git a/components/reporting/metrics/periodic_event_collector.cc b/components/reporting/metrics/periodic_event_collector.cc index 5ca8175..1a54acc0 100644 --- a/components/reporting/metrics/periodic_event_collector.cc +++ b/components/reporting/metrics/periodic_event_collector.cc
@@ -45,6 +45,8 @@ void PeriodicEventCollector::SetReportingEnabled(bool is_enabled) { if (is_enabled) { + // Do initial collection at startup. + Collect(); rate_controller_->Start(); return; }
diff --git a/components/reporting/metrics/periodic_event_collector_unittest.cc b/components/reporting/metrics/periodic_event_collector_unittest.cc index 4fe12c3..a138d4c 100644 --- a/components/reporting/metrics/periodic_event_collector_unittest.cc +++ b/components/reporting/metrics/periodic_event_collector_unittest.cc
@@ -45,7 +45,6 @@ absl::optional<MetricData> previous_metric_data, const MetricData& current_metric_data) override { previous_metric_data_ = previous_metric_data; - run_loop_ptr_->Quit(); return event_type_; } @@ -57,14 +56,10 @@ return previous_metric_data_; } - void SetRunLoop(base::RunLoop* run_loop_ptr) { run_loop_ptr_ = run_loop_ptr; } - private: absl::optional<MetricData> previous_metric_data_; absl::optional<MetricEventType> event_type_; - - raw_ptr<base::RunLoop> run_loop_ptr_; }; class PeriodicEventCollectorTest : public ::testing::Test { @@ -94,7 +89,7 @@ settings_->SetInteger(kRateSettingPath, interval); MetricData sampler_data; - sampler_data.mutable_telemetry_data()->mutable_audio_telemetry(); + sampler_data.mutable_telemetry_data()->mutable_networks_telemetry(); PeriodicEventCollector periodic_event_collector( sampler_.get(), std::move(event_detector_), settings_.get(), @@ -108,6 +103,20 @@ sampler_->SetMetricData(sampler_data); { + // task_environment_.FastForwardBy(base::Milliseconds(interval)); + base::RunLoop().RunUntilIdle(); + + // Reporting enabled not set, sampler data is not collected and no events + // are observed. + EXPECT_THAT(sampler_->GetNumCollectCalls(), + testing::Eq(expected_collections)); + EXPECT_FALSE(event_observed_called); + } + + { + // Setting reporting enabled to initially to false should not cause + // event collection. + periodic_event_collector.SetReportingEnabled(false); task_environment_.FastForwardBy(base::Milliseconds(interval)); base::RunLoop().RunUntilIdle(); @@ -119,7 +128,26 @@ } { + // Reporting enabled, one initial collection should take place. periodic_event_collector.SetReportingEnabled(true); + ++expected_collections; + base::RunLoop().RunUntilIdle(); + + EXPECT_THAT(sampler_->GetNumCollectCalls(), Eq(expected_collections)); + EXPECT_FALSE(event_detector_ptr_->GetPreviousMetricData().has_value()); + EXPECT_TRUE(event_observed_called); + EXPECT_THAT(event_metric_data.event_data().type(), Eq(event_type)); + EXPECT_TRUE(event_metric_data.has_timestamp_ms()); + EXPECT_TRUE(event_metric_data.has_telemetry_data()); + EXPECT_TRUE(event_metric_data.telemetry_data().has_networks_telemetry()); + } + + { + event_observed_called = false; + sampler_data.Clear(); + sampler_data.mutable_telemetry_data()->mutable_audio_telemetry(); + sampler_->SetMetricData(sampler_data); + // Only forward time by half of the collection interval. task_environment_.FastForwardBy(base::Milliseconds(interval / 2)); base::RunLoop().RunUntilIdle(); @@ -130,14 +158,19 @@ EXPECT_FALSE(event_observed_called); // Forward time by the remaining half of the collection interval. - base::RunLoop run_loop; - event_detector_ptr_->SetRunLoop(&run_loop); task_environment_.FastForwardBy(base::Milliseconds(interval / 2)); - run_loop.Run(); + base::RunLoop().RunUntilIdle(); ++expected_collections; EXPECT_THAT(sampler_->GetNumCollectCalls(), Eq(expected_collections)); - EXPECT_FALSE(event_detector_ptr_->GetPreviousMetricData().has_value()); + // Assert previously collected data. + ASSERT_TRUE(event_detector_ptr_->GetPreviousMetricData().has_value()); + MetricData previous_metric_data = + event_detector_ptr_->GetPreviousMetricData().value(); + EXPECT_THAT(previous_metric_data.event_data().type(), Eq(event_type)); + EXPECT_TRUE(previous_metric_data.has_telemetry_data()); + EXPECT_TRUE(previous_metric_data.telemetry_data().has_networks_telemetry()); + // Assert current collected data. EXPECT_TRUE(event_observed_called); EXPECT_THAT(event_metric_data.event_data().type(), Eq(event_type)); EXPECT_TRUE(event_metric_data.has_timestamp_ms()); @@ -153,10 +186,8 @@ sampler_->SetMetricData(sampler_data); // Forward time by the the collection interval. - base::RunLoop run_loop; - event_detector_ptr_->SetRunLoop(&run_loop); task_environment_.FastForwardBy(base::Milliseconds(interval)); - run_loop.Run(); + base::RunLoop().RunUntilIdle(); ++expected_collections; EXPECT_THAT(sampler_->GetNumCollectCalls(), Eq(expected_collections));
diff --git a/components/search_engines/PRESUBMIT.py b/components/search_engines/PRESUBMIT.py new file mode 100644 index 0000000..053f944 --- /dev/null +++ b/components/search_engines/PRESUBMIT.py
@@ -0,0 +1,36 @@ +# Copyright 2022 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Presubmit script for changes affecting components/search_engines/ + +See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts +for more details about the presubmit API built into depot_tools. +""" + +USE_PYTHON3 = True + +import os + +def _CheckPrepopulatedEnginesVersion(input_api, output_api): + """Check that no changes were made to prepopulated_engines.json without + also updating kCurrentDataVersion""" + results = [] + file = next((f for f in input_api.AffectedFiles() + if os.path.basename(f.LocalPath()) == + "prepopulated_engines.json"), None) + + if file != None and not any(line for line in file.ChangedContents() + if "kCurrentDataVersion" in line[1]): + results.append(output_api.PresubmitPromptWarning( + ("prepopulated_engines.json changed but kCurrentDataVersion " + "did not. Please ensure the version is rolled up when making " + "meaningful changes to prepopulated_engines.json"))) + + return results + +def CheckChangeOnUpload(input_api, output_api): + results = [] + results.extend(_CheckPrepopulatedEnginesVersion(input_api, output_api)) + return results +
diff --git a/components/tab_groups/BUILD.gn b/components/tab_groups/BUILD.gn index f76cb303..98db0304 100644 --- a/components/tab_groups/BUILD.gn +++ b/components/tab_groups/BUILD.gn
@@ -17,7 +17,7 @@ deps = [ "//base", "//components/strings", - "//components/tab_groups/public/mojom:mojo_bindings_webui_js", + "//components/tab_groups/public/mojom:mojo_bindings_js__generator", "//skia", "//ui/base", "//ui/gfx",
diff --git a/components/update_client/update_checker.cc b/components/update_client/update_checker.cc index af4d6679..a44d536 100644 --- a/components/update_client/update_checker.cc +++ b/components/update_client/update_checker.cc
@@ -191,7 +191,7 @@ config_->GetLang(), metadata_->GetInstallDate(app_id), install_source, crx_component->install_location, crx_component->fingerprint, crx_component->installer_attributes, metadata_->GetCohort(app_id), - metadata_->GetCohortName(app_id), metadata_->GetCohortHint(app_id), + metadata_->GetCohortHint(app_id), metadata_->GetCohortName(app_id), crx_component->channel, crx_component->disabled_reasons, MakeProtocolUpdateCheck(!crx_component->updates_enabled, crx_component->target_version_prefix,
diff --git a/components/update_client/update_checker_unittest.cc b/components/update_client/update_checker_unittest.cc index 4b4638b..f457426 100644 --- a/components/update_client/update_checker_unittest.cc +++ b/components/update_client/update_checker_unittest.cc
@@ -242,6 +242,10 @@ {"updatepolicy", "1"}}; })); + metadata_->SetCohort(kUpdateItemId, "id3"); + metadata_->SetCohortHint(kUpdateItemId, "hint2"); + metadata_->SetCohortName(kUpdateItemId, "name1"); + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); update_context_->components[kUpdateItemId] = @@ -320,6 +324,9 @@ EXPECT_EQ("TEST", *app->FindString("brand")); ASSERT_TRUE(app->FindString("lang")); EXPECT_EQ("fake_lang", *app->FindString("lang")); + EXPECT_EQ("name1", *app->FindString("cohortname")); + EXPECT_EQ("hint2", *app->FindString("cohorthint")); + EXPECT_EQ("id3", *app->FindString("cohort")); ASSERT_TRUE(app->FindList("data")); ASSERT_FALSE(app->FindList("data")->empty());
diff --git a/components/version_ui/resources/about_version.js b/components/version_ui/resources/about_version.js index 5d7f315..22db5c6c 100644 --- a/components/version_ui/resources/about_version.js +++ b/components/version_ui/resources/about_version.js
@@ -11,7 +11,7 @@ // </if> import './strings.m.js'; -import {addWebUIListener, sendWithPromise} from 'chrome://resources/js/cr.js'; +import {addWebUiListener, sendWithPromise} from 'chrome://resources/js/cr.js'; import {$} from 'chrome://resources/js/util.js'; /** @@ -119,16 +119,16 @@ /* All the work we do onload. */ function onLoadWork() { // <if expr="chromeos_ash or is_win"> - addWebUIListener('return-os-version', returnOsVersion); + addWebUiListener('return-os-version', returnOsVersion); // </if> // <if expr="chromeos_ash"> - addWebUIListener('return-os-firmware-version', returnOsFirmwareVersion); - addWebUIListener( + addWebUiListener('return-os-firmware-version', returnOsFirmwareVersion); + addWebUiListener( 'return-arc-and-arc-android-sdk-versions', returnArcAndArcAndroidSdkVersions); // </if> // <if expr="is_chromeos"> - addWebUIListener('return-lacros-primary', returnLacrosPrimary); + addWebUiListener('return-lacros-primary', returnLacrosPrimary); // </if> chrome.send('requestVersionInfo');
diff --git a/content/BUILD.gn b/content/BUILD.gn index 618cb81..c96243b 100644 --- a/content/BUILD.gn +++ b/content/BUILD.gn
@@ -96,16 +96,16 @@ deps = [ "//components/ukm/debug:build_ts", "//content/browser/resources/histograms:build_ts", - "//gpu/ipc/common:vulkan_interface_webui_js", - "//ui/base/accelerators/mojom:mojom_webui_js", + "//gpu/ipc/common:vulkan_interface_js__generator", + "//ui/base/accelerators/mojom:mojom_js__generator", "//ui/base/mojom:mojom_js", - "//ui/events/mojom:mojom_webui_js", + "//ui/events/mojom:mojom_js__generator", "//ui/gfx/geometry/mojom:mojom_js", "//ui/gfx/image/mojom:mojom_js", "//ui/gfx/range/mojom:mojom_js", - "//ui/latency/mojom:mojom_webui_js", - "//url/mojom:url_mojom_origin_js", - "//url/mojom:url_mojom_origin_webui_js", + "//ui/latency/mojom:mojom_js__generator", + "//url/mojom:url_mojom_gurl_js__generator", + "//url/mojom:url_mojom_origin_js__generator", ] } @@ -117,15 +117,15 @@ "dev_ui_content_resources.pak", ] deps = [ - "//components/attribution_reporting:mojom_webui_js", - "//content/browser/aggregation_service:mojo_bindings_webui_js", - "//content/browser/attribution_reporting:internals_mojo_bindings_webui_js", - "//content/browser/attribution_reporting:mojo_bindings_webui_js", + "//components/attribution_reporting:mojom_js__generator", + "//content/browser/aggregation_service:mojo_bindings_js__generator", + "//content/browser/attribution_reporting:internals_mojo_bindings_js__generator", + "//content/browser/attribution_reporting:mojo_bindings_js__generator", "//content/browser/resources/aggregation_service:build_ts", "//content/browser/resources/attribution_reporting:build_ts", "//content/browser/resources/gpu:html_wrapper_files", "//content/browser/resources/process:build_ts", - "//storage/browser/quota:mojo_bindings_webui_js", + "//storage/browser/quota:mojo_bindings_js__generator", ] }
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index 3f65744..ac6cba6c 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -641,6 +641,10 @@ "browsing_topics/browsing_topics_site_data_manager_impl.h", "browsing_topics/browsing_topics_site_data_storage.cc", "browsing_topics/browsing_topics_site_data_storage.h", + "browsing_topics/browsing_topics_url_loader.cc", + "browsing_topics/browsing_topics_url_loader.h", + "browsing_topics/browsing_topics_url_loader_service.cc", + "browsing_topics/browsing_topics_url_loader_service.h", "browsing_topics/header_util.cc", "browsing_topics/header_util.h", "buckets/bucket_context.h",
diff --git a/content/browser/browsing_topics/browsing_topics_url_loader.cc b/content/browser/browsing_topics/browsing_topics_url_loader.cc new file mode 100644 index 0000000..623a4072 --- /dev/null +++ b/content/browser/browsing_topics/browsing_topics_url_loader.cc
@@ -0,0 +1,241 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/browsing_topics/browsing_topics_url_loader.h" + +#include "base/bind.h" +#include "content/browser/browsing_topics/header_util.h" +#include "content/browser/renderer_host/render_frame_host_impl.h" +#include "content/public/browser/content_browser_client.h" +#include "content/public/browser/page.h" +#include "content/public/common/content_client.h" +#include "content/public/common/content_features.h" +#include "services/network/public/cpp/features.h" +#include "services/network/public/cpp/is_potentially_trustworthy.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/network/public/mojom/early_hints.mojom.h" +#include "third_party/blink/public/common/features.h" +#include "third_party/blink/public/common/frame/frame_policy.h" +#include "third_party/blink/public/mojom/browsing_topics/browsing_topics.mojom.h" + +namespace content { + +namespace { + +bool GetTopicsHeaderValueForSubresourceRequest( + WeakDocumentPtr request_initiator_document, + const GURL& url, + std::string& header_value) { + DCHECK(header_value.empty()); + + // Due to the race between the subresource requests and navigations, this + // request may arrive before the commit confirmation is received (i.e. + // NavigationRequest::DidCommitNavigation()), or after the document is + // destroyed. We consider those cases to be ineligible for topics. + // + // TODO(yaoxia): measure how often this happens. + RenderFrameHost* request_initiator_frame = + request_initiator_document.AsRenderFrameHostIfValid(); + if (!request_initiator_frame) + return false; + + // Fenced frames disallow most permissions policies which would let this + // function return false regardless, but adding this check to be more + // explicit. + if (request_initiator_frame->IsNestedWithinFencedFrame()) + return false; + + if (!request_initiator_frame->GetPage().IsPrimary()) + return false; + + // TODO(crbug.com/1244137): IsPrimary() doesn't actually detect portals yet. + // Remove this when it does. + if (!static_cast<RenderFrameHostImpl*>(request_initiator_frame) + ->IsOutermostMainFrame()) { + return false; + } + + url::Origin origin = url::Origin::Create(url); + if (origin.opaque()) + return false; + + // TODO(yaoxia): should this be `ReportBadMessage`? On the renderer side, the + // fetch initiator context must be secure. Does it imply that the requested + // `origin` is always potentially trustworthy? + if (!network::IsOriginPotentiallyTrustworthy(origin)) + return false; + + const blink::PermissionsPolicy* permissions_policy = + static_cast<RenderFrameHostImpl*>(request_initiator_frame) + ->permissions_policy(); + + if (!permissions_policy->IsFeatureEnabledForOrigin( + blink::mojom::PermissionsPolicyFeature::kBrowsingTopics, origin) || + !permissions_policy->IsFeatureEnabledForOrigin( + blink::mojom::PermissionsPolicyFeature:: + kBrowsingTopicsBackwardCompatible, + origin)) { + return false; + } + + std::vector<blink::mojom::EpochTopicPtr> topics; + bool topics_eligible = GetContentClient()->browser()->HandleTopicsWebApi( + origin, request_initiator_frame->GetMainFrame(), + browsing_topics::ApiCallerSource::kFetch, + /*get_topics=*/true, + /*observe=*/false, topics); + + if (topics_eligible) + header_value = DeriveTopicsHeaderValue(topics); + + return topics_eligible; +} + +void ProcessResponseHeaders(const net::HttpResponseHeaders* response_headers, + WeakDocumentPtr document, + const GURL& url) { + if (!response_headers) + return; + + RenderFrameHost* rfh = document.AsRenderFrameHostIfValid(); + if (!rfh) + return; + + HandleTopicsEligibleResponse(*response_headers, url::Origin::Create(url), + *rfh, browsing_topics::ApiCallerSource::kFetch); +} + +} // namespace + +BrowsingTopicsURLLoader::BrowsingTopicsURLLoader( + WeakDocumentPtr document, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& resource_request, + mojo::PendingRemote<network::mojom::URLLoaderClient> client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation, + scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory) + : document_(std::move(document)), + url_(resource_request.url), + forwarding_client_(std::move(client)) { + DCHECK(network_loader_factory); + + network::ResourceRequest new_resource_request = resource_request; + + std::string header_value; + topics_eligible_ = GetTopicsHeaderValueForSubresourceRequest( + document_, new_resource_request.url, header_value); + + if (topics_eligible_) { + new_resource_request.headers.SetHeader(kBrowsingTopicsRequestHeaderKey, + header_value); + } + + network_loader_factory->CreateLoaderAndStart( + loader_.BindNewPipeAndPassReceiver(), request_id, options, + new_resource_request, client_receiver_.BindNewPipeAndPassRemote(), + traffic_annotation); + + client_receiver_.set_disconnect_handler( + base::BindOnce(&BrowsingTopicsURLLoader::OnNetworkConnectionError, + base::Unretained(this))); +} + +BrowsingTopicsURLLoader::~BrowsingTopicsURLLoader() = default; + +void BrowsingTopicsURLLoader::FollowRedirect( + const std::vector<std::string>& removed_headers, + const net::HttpRequestHeaders& modified_headers, + const net::HttpRequestHeaders& modified_cors_exempt_headers, + const absl::optional<GURL>& new_url) { + if (new_url) + url_ = new_url.value(); + + std::vector<std::string> new_removed_headers = removed_headers; + net::HttpRequestHeaders new_modified_headers = modified_headers; + + new_removed_headers.push_back(kBrowsingTopicsRequestHeaderKey); + + std::string header_value; + topics_eligible_ = + GetTopicsHeaderValueForSubresourceRequest(document_, url_, header_value); + + if (topics_eligible_) { + new_modified_headers.SetHeader(kBrowsingTopicsRequestHeaderKey, + header_value); + } + + loader_->FollowRedirect(new_removed_headers, new_modified_headers, + modified_cors_exempt_headers, new_url); +} + +void BrowsingTopicsURLLoader::SetPriority(net::RequestPriority priority, + int intra_priority_value) { + loader_->SetPriority(priority, intra_priority_value); +} + +void BrowsingTopicsURLLoader::PauseReadingBodyFromNet() { + loader_->PauseReadingBodyFromNet(); +} + +void BrowsingTopicsURLLoader::ResumeReadingBodyFromNet() { + loader_->ResumeReadingBodyFromNet(); +} + +void BrowsingTopicsURLLoader::OnReceiveEarlyHints( + network::mojom::EarlyHintsPtr early_hints) { + forwarding_client_->OnReceiveEarlyHints(std::move(early_hints)); +} + +void BrowsingTopicsURLLoader::OnReceiveResponse( + network::mojom::URLResponseHeadPtr head, + mojo::ScopedDataPipeConsumerHandle body, + absl::optional<mojo_base::BigBuffer> cached_metadata) { + if (topics_eligible_) { + ProcessResponseHeaders(head->headers.get(), document_, url_); + topics_eligible_ = false; + } + + forwarding_client_->OnReceiveResponse(std::move(head), std::move(body), + std::move(cached_metadata)); +} + +void BrowsingTopicsURLLoader::OnReceiveRedirect( + const net::RedirectInfo& redirect_info, + network::mojom::URLResponseHeadPtr head) { + if (topics_eligible_) { + ProcessResponseHeaders(head->headers.get(), document_, url_); + topics_eligible_ = false; + } + + url_ = redirect_info.new_url; + + forwarding_client_->OnReceiveRedirect(redirect_info, std::move(head)); +} + +void BrowsingTopicsURLLoader::OnUploadProgress( + int64_t current_position, + int64_t total_size, + base::OnceCallback<void()> callback) { + forwarding_client_->OnUploadProgress(current_position, total_size, + std::move(callback)); +} + +void BrowsingTopicsURLLoader::OnTransferSizeUpdated( + int32_t transfer_size_diff) { + forwarding_client_->OnTransferSizeUpdated(transfer_size_diff); +} + +void BrowsingTopicsURLLoader::OnComplete( + const network::URLLoaderCompletionStatus& status) { + forwarding_client_->OnComplete(status); +} + +void BrowsingTopicsURLLoader::OnNetworkConnectionError() { + // The network loader has an error; we should let the client know it's closed + // by dropping this, which will in turn make this loader destroyed. + forwarding_client_.reset(); +} + +} // namespace content
diff --git a/content/browser/browsing_topics/browsing_topics_url_loader.h b/content/browser/browsing_topics/browsing_topics_url_loader.h new file mode 100644 index 0000000..8576e64 --- /dev/null +++ b/content/browser/browsing_topics/browsing_topics_url_loader.h
@@ -0,0 +1,119 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_BROWSING_TOPICS_BROWSING_TOPICS_URL_LOADER_H_ +#define CONTENT_BROWSER_BROWSING_TOPICS_BROWSING_TOPICS_URL_LOADER_H_ + +#include <memory> +#include <string> + +#include "base/callback.h" +#include "base/memory/weak_ptr.h" +#include "content/public/browser/weak_document_ptr.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "net/traffic_annotation/network_traffic_annotation.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/mojom/url_loader.mojom.h" +#include "services/network/public/mojom/url_response_head.mojom.h" +#include "third_party/abseil-cpp/absl/types/optional.h" +#include "url/gurl.h" + +namespace network { +class SharedURLLoaderFactory; +} + +namespace content { + +// A URLLoader for handling a topics request, including +// fetch(<url>, {browsingTopics: true}). +// +// This loader works as follows: +// 1. Before making a network request (i.e. BrowsingTopicsURLLoader()), if the +// request is eligible for topics, calculates and adds the topics header. +// Starts the request with `loader_`. +// 2. For any redirect received (i.e. OnReceiveRedirect()), if the previous +// request or redirect was eligible for topics, and if the response header +// indicates an observation should be recorded, stores the observation. +// Forwards the original response back to `forwarding_client_`. +// 3. For any followed redirect (i.e. FollowRedirect()), if the redirect is +// eligible for topics, calculates and adds/updates the topics header. +// Forwards the updated redirect to `loader_`. +// 4. For the last response (i.e. OnReceiveResponse()), if the previous +// request or redirect was eligible for topics, and if the response header +// indicates an observation should be recorded, stores the observation. +// Forwards the original response (e.g. hands off fetching the body) back +// to `forwarding_client_`. +class CONTENT_EXPORT BrowsingTopicsURLLoader + : public network::mojom::URLLoader, + public network::mojom::URLLoaderClient { + public: + BrowsingTopicsURLLoader( + WeakDocumentPtr document, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& resource_request, + mojo::PendingRemote<network::mojom::URLLoaderClient> client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation, + scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory); + + BrowsingTopicsURLLoader(const BrowsingTopicsURLLoader&) = delete; + BrowsingTopicsURLLoader& operator=(const BrowsingTopicsURLLoader&) = delete; + + ~BrowsingTopicsURLLoader() override; + + private: + // network::mojom::URLLoader overrides: + void FollowRedirect( + const std::vector<std::string>& removed_headers, + const net::HttpRequestHeaders& modified_headers, + const net::HttpRequestHeaders& modified_cors_exempt_headers, + const absl::optional<GURL>& new_url) override; + void SetPriority(net::RequestPriority priority, + int intra_priority_value) override; + void PauseReadingBodyFromNet() override; + void ResumeReadingBodyFromNet() override; + + // network::mojom::URLLoaderClient overrides: + void OnReceiveEarlyHints(network::mojom::EarlyHintsPtr early_hints) override; + void OnReceiveResponse( + network::mojom::URLResponseHeadPtr head, + mojo::ScopedDataPipeConsumerHandle body, + absl::optional<mojo_base::BigBuffer> cached_metadata) override; + void OnReceiveRedirect(const net::RedirectInfo& redirect_info, + network::mojom::URLResponseHeadPtr head) override; + void OnUploadProgress(int64_t current_position, + int64_t total_size, + base::OnceCallback<void()> callback) override; + void OnTransferSizeUpdated(int32_t transfer_size_diff) override; + void OnComplete(const network::URLLoaderCompletionStatus& status) override; + + void OnNetworkConnectionError(); + + // Upon NavigationRequest::DidCommitNavigation(), `document_` will be set to + // the document that this `BrowsingTopicsURLLoader` is associated with. It + // will become null whenever the document navigates away. + WeakDocumentPtr document_; + + // The current request or redirect URL. + GURL url_; + + // Whether the ongoing request or redirect is eligible for topics. Set to the + // desired state when a request/redirect is made. Reset to false when the + // corresponding response is received. + bool topics_eligible_ = false; + + // For the actual request. + mojo::Remote<network::mojom::URLLoader> loader_; + + // The client to forward the response to. + mojo::Remote<network::mojom::URLLoaderClient> forwarding_client_; + + mojo::Receiver<network::mojom::URLLoaderClient> client_receiver_{this}; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_BROWSING_TOPICS_BROWSING_TOPICS_URL_LOADER_H_
diff --git a/content/browser/browsing_topics/browsing_topics_url_loader_service.cc b/content/browser/browsing_topics/browsing_topics_url_loader_service.cc new file mode 100644 index 0000000..79e3ef8 --- /dev/null +++ b/content/browser/browsing_topics/browsing_topics_url_loader_service.cc
@@ -0,0 +1,86 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/browsing_topics/browsing_topics_url_loader_service.h" + +#include "base/bind.h" +#include "content/browser/browsing_topics/browsing_topics_url_loader.h" +#include "content/public/browser/content_browser_client.h" +#include "mojo/public/cpp/bindings/message.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "mojo/public/cpp/bindings/self_owned_receiver.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" + +namespace content { + +BrowsingTopicsURLLoaderService::BindContext::BindContext( + scoped_refptr<network::SharedURLLoaderFactory> factory) + : factory(factory) {} + +BrowsingTopicsURLLoaderService::BindContext::BindContext( + const std::unique_ptr<BindContext>& other) + : document(other->document), factory(other->factory) {} + +void BrowsingTopicsURLLoaderService::BindContext::OnDidCommitNavigation( + WeakDocumentPtr committed_document) { + document = committed_document; +} + +BrowsingTopicsURLLoaderService::BindContext::~BindContext() = default; + +BrowsingTopicsURLLoaderService::BrowsingTopicsURLLoaderService() = default; + +BrowsingTopicsURLLoaderService::~BrowsingTopicsURLLoaderService() = default; + +base::WeakPtr<BrowsingTopicsURLLoaderService::BindContext> +BrowsingTopicsURLLoaderService::GetFactory( + mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver, + std::unique_ptr<network::PendingSharedURLLoaderFactory> pending_factory) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + auto factory_bundle = + network::SharedURLLoaderFactory::Create(std::move(pending_factory)); + + auto bind_context = std::make_unique<BindContext>(factory_bundle); + + base::WeakPtr<BindContext> weak_bind_context = + bind_context->weak_ptr_factory.GetWeakPtr(); + + loader_factory_receivers_.Add(this, std::move(receiver), + std::move(bind_context)); + + return weak_bind_context; +} + +void BrowsingTopicsURLLoaderService::CreateLoaderAndStart( + mojo::PendingReceiver<network::mojom::URLLoader> receiver, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& resource_request, + mojo::PendingRemote<network::mojom::URLLoaderClient> client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + const std::unique_ptr<BindContext>& current_context = + loader_factory_receivers_.current_context(); + + auto loader = std::make_unique<BrowsingTopicsURLLoader>( + current_context->document, request_id, options, resource_request, + std::move(client), traffic_annotation, current_context->factory); + + auto* raw_loader = loader.get(); + + loader_receivers_.Add(raw_loader, std::move(receiver), std::move(loader)); +} + +void BrowsingTopicsURLLoaderService::Clone( + mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + loader_factory_receivers_.Add( + this, std::move(receiver), + std::make_unique<BindContext>( + loader_factory_receivers_.current_context())); +} + +} // namespace content
diff --git a/content/browser/browsing_topics/browsing_topics_url_loader_service.h b/content/browser/browsing_topics/browsing_topics_url_loader_service.h new file mode 100644 index 0000000..64fefd77 --- /dev/null +++ b/content/browser/browsing_topics/browsing_topics_url_loader_service.h
@@ -0,0 +1,95 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_BROWSING_TOPICS_BROWSING_TOPICS_URL_LOADER_SERVICE_H_ +#define CONTENT_BROWSER_BROWSING_TOPICS_BROWSING_TOPICS_URL_LOADER_SERVICE_H_ + +#include <string> + +#include "base/callback.h" +#include "base/memory/raw_ptr.h" +#include "base/memory/ref_counted.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/weak_document_ptr.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 "services/network/public/mojom/url_loader_factory.mojom.h" +#include "third_party/blink/public/common/loader/url_loader_factory_bundle.h" + +namespace content { + +// A URLLoaderFactory that can be passed to a renderer to use for performing +// topics related requests. The renderer uses this to handle +// fetch(<url>, {browsingTopics: true}). +class CONTENT_EXPORT BrowsingTopicsURLLoaderService final + : public network::mojom::URLLoaderFactory { + public: + struct CONTENT_EXPORT BindContext { + // `factory` is a clone of the default factory bundle for document + // subresource requests. + explicit BindContext( + scoped_refptr<network::SharedURLLoaderFactory> factory); + + explicit BindContext(const std::unique_ptr<BindContext>& other); + + ~BindContext(); + + // Set `document` to `committed_document`. + void OnDidCommitNavigation(WeakDocumentPtr committed_document); + + // Upon NavigationRequest::DidCommitNavigation(), `document` will be set to + // the document that this `BindContext` is associated with. It will become + // null whenever the document navigates away. + WeakDocumentPtr document; + + // The factory to use for the requests initiated from this context. + scoped_refptr<network::SharedURLLoaderFactory> factory; + + // This must be the last member. + base::WeakPtrFactory<BrowsingTopicsURLLoaderService::BindContext> + weak_ptr_factory{this}; + }; + + BrowsingTopicsURLLoaderService(); + + ~BrowsingTopicsURLLoaderService() override; + + BrowsingTopicsURLLoaderService(const BrowsingTopicsURLLoaderService&) = + delete; + BrowsingTopicsURLLoaderService& operator=( + const BrowsingTopicsURLLoaderService&) = delete; + + // Binds `receiver`. Creates a `BindContext` to contain a factory constructed + // with `pending_factory`, and associates it to `receiver`. Returns the + // associated `BindContext`. + base::WeakPtr<BindContext> GetFactory( + mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver, + std::unique_ptr<network::PendingSharedURLLoaderFactory> pending_factory); + + private: + // network::mojom::URLLoaderFactory: + void CreateLoaderAndStart( + mojo::PendingReceiver<network::mojom::URLLoader> receiver, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& resource_request, + mojo::PendingRemote<network::mojom::URLLoaderClient> client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) + override; + void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) + override; + + mojo::ReceiverSet<network::mojom::URLLoaderFactory, + std::unique_ptr<BindContext>> + loader_factory_receivers_; + + mojo::ReceiverSet<network::mojom::URLLoader, + std::unique_ptr<network::mojom::URLLoader>> + loader_receivers_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_BROWSING_TOPICS_BROWSING_TOPICS_URL_LOADER_SERVICE_H_
diff --git a/content/browser/browsing_topics/browsing_topics_url_loader_service_unittest.cc b/content/browser/browsing_topics/browsing_topics_url_loader_service_unittest.cc new file mode 100644 index 0000000..11ebaf46 --- /dev/null +++ b/content/browser/browsing_topics/browsing_topics_url_loader_service_unittest.cc
@@ -0,0 +1,920 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/browsing_topics/browsing_topics_url_loader_service.h" + +#include "content/public/browser/browser_context.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/test/navigation_simulator.h" +#include "content/public/test/test_utils.h" +#include "content/public/test/web_contents_tester.h" +#include "content/test/test_render_view_host.h" +#include "net/traffic_annotation/network_traffic_annotation_test_helper.h" +#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h" +#include "services/network/test/test_url_loader_factory.h" +#include "third_party/blink/public/mojom/browsing_topics/browsing_topics.mojom.h" + +namespace content { + +namespace { + +using FollowRedirectParams = + network::TestURLLoaderFactory::TestURLLoader::FollowRedirectParams; + +constexpr char kExpectedHeaderForOrigin1[] = + "1;version=\"chrome.1:1:2\";config_version=\"chrome.1\";model_version=" + "\"2\";taxonomy_version=\"1\""; + +constexpr char kExpectedHeaderForOrigin2[] = + "2;version=\"chrome.3:4:5\";config_version=\"chrome.3\";model_version=" + "\"5\";taxonomy_version=\"4\""; + +class TopicsInterceptingContentBrowserClient : public ContentBrowserClient { + public: + bool HandleTopicsWebApi( + const url::Origin& context_origin, + content::RenderFrameHost* main_frame, + browsing_topics::ApiCallerSource caller_source, + bool get_topics, + bool observe, + std::vector<blink::mojom::EpochTopicPtr>& topics) override { + ++handle_topics_web_api_count_; + last_get_topics_param_ = get_topics; + last_observe_param_ = observe; + + if (get_topics) { + if (context_origin == url::Origin::Create(GURL("https://foo1.com"))) { + blink::mojom::EpochTopicPtr result_topic = + blink::mojom::EpochTopic::New(); + result_topic->topic = 1; + result_topic->config_version = "chrome.1"; + result_topic->taxonomy_version = "1"; + result_topic->model_version = "2"; + result_topic->version = "chrome.1:1:2"; + topics.push_back(std::move(result_topic)); + } else if (context_origin == + url::Origin::Create(GURL("https://foo2.com"))) { + blink::mojom::EpochTopicPtr result_topic = + blink::mojom::EpochTopic::New(); + result_topic->topic = 2; + result_topic->config_version = "chrome.3"; + result_topic->taxonomy_version = "4"; + result_topic->model_version = "5"; + result_topic->version = "chrome.3:4:5"; + topics.push_back(std::move(result_topic)); + } + } + + return true; + } + + size_t handle_topics_web_api_count() const { + return handle_topics_web_api_count_; + } + + bool last_get_topics_param() const { return last_get_topics_param_; } + + bool last_observe_param() const { return last_observe_param_; } + + private: + size_t handle_topics_web_api_count_ = 0; + bool last_get_topics_param_ = false; + bool last_observe_param_ = false; +}; + +} // namespace + +class BrowsingTopicsURLLoaderServiceTest : public RenderViewHostTestHarness { + public: + void SetUp() override { + content::RenderViewHostTestHarness::SetUp(); + + original_client_ = content::SetBrowserClientForTesting(&browser_client_); + } + + void TearDown() override { + SetBrowserClientForTesting(original_client_); + + content::RenderViewHostTestHarness::TearDown(); + } + + const TopicsInterceptingContentBrowserClient& browser_client() const { + return browser_client_; + } + + base::WeakPtr<BrowsingTopicsURLLoaderService::BindContext> CreateFactory( + network::TestURLLoaderFactory& proxied_url_loader_factory, + mojo::Remote<network::mojom::URLLoaderFactory>& + remote_url_loader_factory) { + if (!browsing_topics_url_loader_service_) { + browsing_topics_url_loader_service_ = + std::make_unique<BrowsingTopicsURLLoaderService>(); + } + + mojo::Remote<network::mojom::URLLoaderFactory> factory; + proxied_url_loader_factory.Clone(factory.BindNewPipeAndPassReceiver()); + auto pending_factory = + std::make_unique<network::WrapperPendingSharedURLLoaderFactory>( + factory.Unbind()); + + return browsing_topics_url_loader_service_->GetFactory( + remote_url_loader_factory.BindNewPipeAndPassReceiver(), + std::move(pending_factory)); + } + + network::mojom::URLResponseHeadPtr CreateResponseHead( + absl::optional<std::string> topics_header_value) { + auto head = network::mojom::URLResponseHead::New(); + head->headers = base::MakeRefCounted<net::HttpResponseHeaders>(""); + + if (topics_header_value) { + head->headers->AddHeader("Observe-Browsing-Topics", + topics_header_value.value()); + } + + return head; + } + + network::ResourceRequest CreateResourceRequest(const GURL& url) { + network::ResourceRequest request; + request.url = url; + return request; + } + + void NavigatePage(const GURL& url) { + auto simulator = + NavigationSimulator::CreateBrowserInitiated(url, web_contents()); + + blink::ParsedPermissionsPolicy policy; + policy.emplace_back(blink::mojom::PermissionsPolicyFeature::kBrowsingTopics, + /*allowed_origins=*/ + std::vector<blink::OriginWithPossibleWildcards>{ + blink::OriginWithPossibleWildcards( + url::Origin::Create(GURL("https://foo1.com")), + /*has_subdomain_wildcard=*/false), + blink::OriginWithPossibleWildcards( + url::Origin::Create(GURL("https://foo2.com")), + /*has_subdomain_wildcard=*/false), + blink::OriginWithPossibleWildcards( + url::Origin::Create(GURL("https://foo3.com")), + /*has_subdomain_wildcard=*/false)}, + /*matches_all_origins=*/false, + /*matches_opaque_src=*/false); + + simulator->SetPermissionsPolicyHeader(std::move(policy)); + + simulator->Commit(); + } + + private: + TopicsInterceptingContentBrowserClient browser_client_; + raw_ptr<ContentBrowserClient> original_client_ = nullptr; + + std::unique_ptr<BrowsingTopicsURLLoaderService> + browsing_topics_url_loader_service_; +}; + +TEST_F(BrowsingTopicsURLLoaderServiceTest, RequestArrivedBeforeCommit) { + NavigatePage(GURL("https://google.com")); + + mojo::Remote<network::mojom::URLLoaderFactory> remote_url_loader_factory; + network::TestURLLoaderFactory proxied_url_loader_factory; + mojo::Remote<network::mojom::URLLoader> remote_loader; + mojo::PendingReceiver<network::mojom::URLLoaderClient> client; + + CreateFactory(proxied_url_loader_factory, remote_url_loader_factory); + + // This request arrives before commit. It is thus not eligible for topics. + remote_url_loader_factory->CreateLoaderAndStart( + remote_loader.BindNewPipeAndPassReceiver(), + /*request_id=*/0, /*options=*/0, + CreateResourceRequest(GURL("https://foo1.com")), + client.InitWithNewPipeAndPassRemote(), + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); + remote_url_loader_factory.FlushForTesting(); + + EXPECT_EQ(1, proxied_url_loader_factory.NumPending()); + network::TestURLLoaderFactory::PendingRequest* pending_request = + &proxied_url_loader_factory.pending_requests()->back(); + + std::string topics_header_value; + bool has_topics_header = pending_request->request.headers.GetHeader( + "Sec-Browsing-Topics", &topics_header_value); + EXPECT_FALSE(has_topics_header); + + pending_request->client->OnReceiveResponse( + CreateResponseHead(/*topics_header_value=*/"?1"), /*body=*/{}, + absl::nullopt); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 0u); +} + +TEST_F(BrowsingTopicsURLLoaderServiceTest, RequestArrivedAfterCommit) { + NavigatePage(GURL("https://google.com")); + + mojo::Remote<network::mojom::URLLoaderFactory> remote_url_loader_factory; + network::TestURLLoaderFactory proxied_url_loader_factory; + mojo::Remote<network::mojom::URLLoader> remote_loader; + mojo::PendingReceiver<network::mojom::URLLoaderClient> client; + + base::WeakPtr<BrowsingTopicsURLLoaderService::BindContext> bind_context = + CreateFactory(proxied_url_loader_factory, remote_url_loader_factory); + bind_context->OnDidCommitNavigation( + web_contents()->GetPrimaryMainFrame()->GetWeakDocumentPtr()); + + // The request to `foo1.com` will cause the topics header value + // `kExpectedHeaderForOrigin1` to be added. + remote_url_loader_factory->CreateLoaderAndStart( + remote_loader.BindNewPipeAndPassReceiver(), + /*request_id=*/0, /*options=*/0, + CreateResourceRequest(GURL("https://foo1.com")), + client.InitWithNewPipeAndPassRemote(), + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); + remote_url_loader_factory.FlushForTesting(); + + EXPECT_EQ(1, proxied_url_loader_factory.NumPending()); + network::TestURLLoaderFactory::PendingRequest* pending_request = + &proxied_url_loader_factory.pending_requests()->back(); + + std::string topics_header_value; + bool has_topics_header = pending_request->request.headers.GetHeader( + "Sec-Browsing-Topics", &topics_header_value); + EXPECT_TRUE(has_topics_header); + EXPECT_EQ(topics_header_value, kExpectedHeaderForOrigin1); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 1u); + + // The topics response header value "?1" will cause an observation to be + // recorded. + pending_request->client->OnReceiveResponse( + CreateResponseHead(/*topics_header_value=*/"?1"), /*body=*/{}, + absl::nullopt); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 2u); + EXPECT_FALSE(browser_client().last_get_topics_param()); + EXPECT_TRUE(browser_client().last_observe_param()); +} + +TEST_F(BrowsingTopicsURLLoaderServiceTest, + RequestArrivedAfterDocumentDestroyed) { + NavigatePage(GURL("https://google.com")); + + mojo::Remote<network::mojom::URLLoaderFactory> remote_url_loader_factory; + network::TestURLLoaderFactory proxied_url_loader_factory; + mojo::Remote<network::mojom::URLLoader> remote_loader; + mojo::PendingReceiver<network::mojom::URLLoaderClient> client; + + base::WeakPtr<BrowsingTopicsURLLoaderService::BindContext> bind_context = + CreateFactory(proxied_url_loader_factory, remote_url_loader_factory); + bind_context->OnDidCommitNavigation( + web_contents()->GetPrimaryMainFrame()->GetWeakDocumentPtr()); + + // This second navigation will cause the initial document referenced by the + // factory to be destroyed. Thus the request won't be eligible for topics. + auto simulator = NavigationSimulator::CreateBrowserInitiated( + GURL("https://foo1.com"), web_contents()); + simulator->Commit(); + + remote_url_loader_factory->CreateLoaderAndStart( + remote_loader.BindNewPipeAndPassReceiver(), + /*request_id=*/0, /*options=*/0, + CreateResourceRequest(GURL("https://foo1.com")), + client.InitWithNewPipeAndPassRemote(), + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); + remote_url_loader_factory.FlushForTesting(); + + EXPECT_EQ(1, proxied_url_loader_factory.NumPending()); + network::TestURLLoaderFactory::PendingRequest* pending_request = + &proxied_url_loader_factory.pending_requests()->back(); + + std::string topics_header_value; + bool has_topics_header = pending_request->request.headers.GetHeader( + "Sec-Browsing-Topics", &topics_header_value); + EXPECT_FALSE(has_topics_header); + + pending_request->client->OnReceiveResponse( + CreateResponseHead(/*topics_header_value=*/"?1"), /*body=*/{}, + absl::nullopt); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 0u); +} + +TEST_F(BrowsingTopicsURLLoaderServiceTest, HasNoObserveResponseHeader) { + NavigatePage(GURL("https://google.com")); + + mojo::Remote<network::mojom::URLLoaderFactory> remote_url_loader_factory; + network::TestURLLoaderFactory proxied_url_loader_factory; + mojo::Remote<network::mojom::URLLoader> remote_loader; + mojo::PendingReceiver<network::mojom::URLLoaderClient> client; + + base::WeakPtr<BrowsingTopicsURLLoaderService::BindContext> bind_context = + CreateFactory(proxied_url_loader_factory, remote_url_loader_factory); + bind_context->OnDidCommitNavigation( + web_contents()->GetPrimaryMainFrame()->GetWeakDocumentPtr()); + + remote_url_loader_factory->CreateLoaderAndStart( + remote_loader.BindNewPipeAndPassReceiver(), + /*request_id=*/0, /*options=*/0, + CreateResourceRequest(GURL("https://foo1.com")), + client.InitWithNewPipeAndPassRemote(), + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); + remote_url_loader_factory.FlushForTesting(); + + EXPECT_EQ(1, proxied_url_loader_factory.NumPending()); + network::TestURLLoaderFactory::PendingRequest* pending_request = + &proxied_url_loader_factory.pending_requests()->back(); + + std::string topics_header_value; + bool has_topics_header = pending_request->request.headers.GetHeader( + "Sec-Browsing-Topics", &topics_header_value); + EXPECT_TRUE(has_topics_header); + EXPECT_EQ(topics_header_value, kExpectedHeaderForOrigin1); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 1u); + + // Expect no further handling for topics as the response does not contain the + // topics header. + pending_request->client->OnReceiveResponse( + CreateResponseHead(/*topics_header_value=*/absl::nullopt), + /*body=*/{}, absl::nullopt); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 1u); +} + +TEST_F(BrowsingTopicsURLLoaderServiceTest, HasFalseValueObserveResponseHeader) { + NavigatePage(GURL("https://google.com")); + + mojo::Remote<network::mojom::URLLoaderFactory> remote_url_loader_factory; + network::TestURLLoaderFactory proxied_url_loader_factory; + mojo::Remote<network::mojom::URLLoader> remote_loader; + mojo::PendingReceiver<network::mojom::URLLoaderClient> client; + + base::WeakPtr<BrowsingTopicsURLLoaderService::BindContext> bind_context = + CreateFactory(proxied_url_loader_factory, remote_url_loader_factory); + bind_context->OnDidCommitNavigation( + web_contents()->GetPrimaryMainFrame()->GetWeakDocumentPtr()); + + remote_url_loader_factory->CreateLoaderAndStart( + remote_loader.BindNewPipeAndPassReceiver(), + /*request_id=*/0, /*options=*/0, + CreateResourceRequest(GURL("https://foo1.com")), + client.InitWithNewPipeAndPassRemote(), + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); + remote_url_loader_factory.FlushForTesting(); + + EXPECT_EQ(1, proxied_url_loader_factory.NumPending()); + network::TestURLLoaderFactory::PendingRequest* pending_request = + &proxied_url_loader_factory.pending_requests()->back(); + + std::string topics_header_value; + bool has_topics_header = pending_request->request.headers.GetHeader( + "Sec-Browsing-Topics", &topics_header_value); + EXPECT_TRUE(has_topics_header); + EXPECT_EQ(topics_header_value, kExpectedHeaderForOrigin1); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 1u); + + // Expect no further handling for topics as the response header value is "?0" + // (i.e. false). + pending_request->client->OnReceiveResponse( + CreateResponseHead(/*topics_header_value=*/"?0"), + /*body=*/{}, absl::nullopt); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 1u); +} + +TEST_F(BrowsingTopicsURLLoaderServiceTest, HasMalformedObserveResponseHeader) { + NavigatePage(GURL("https://google.com")); + + mojo::Remote<network::mojom::URLLoaderFactory> remote_url_loader_factory; + network::TestURLLoaderFactory proxied_url_loader_factory; + mojo::Remote<network::mojom::URLLoader> remote_loader; + mojo::PendingReceiver<network::mojom::URLLoaderClient> client; + + base::WeakPtr<BrowsingTopicsURLLoaderService::BindContext> bind_context = + CreateFactory(proxied_url_loader_factory, remote_url_loader_factory); + bind_context->OnDidCommitNavigation( + web_contents()->GetPrimaryMainFrame()->GetWeakDocumentPtr()); + + remote_url_loader_factory->CreateLoaderAndStart( + remote_loader.BindNewPipeAndPassReceiver(), + /*request_id=*/0, /*options=*/0, + CreateResourceRequest(GURL("https://foo1.com")), + client.InitWithNewPipeAndPassRemote(), + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); + remote_url_loader_factory.FlushForTesting(); + + EXPECT_EQ(1, proxied_url_loader_factory.NumPending()); + network::TestURLLoaderFactory::PendingRequest* pending_request = + &proxied_url_loader_factory.pending_requests()->back(); + + std::string topics_header_value; + bool has_topics_header = pending_request->request.headers.GetHeader( + "Sec-Browsing-Topics", &topics_header_value); + EXPECT_TRUE(has_topics_header); + EXPECT_EQ(topics_header_value, kExpectedHeaderForOrigin1); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 1u); + + // Expect no further handling for topics as the response header value is + // malformed. + pending_request->client->OnReceiveResponse( + CreateResponseHead(/*topics_header_value=*/"?1, ?0"), + /*body=*/{}, absl::nullopt); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 1u); +} + +TEST_F(BrowsingTopicsURLLoaderServiceTest, EmptyTopics) { + NavigatePage(GURL("https://google.com")); + + mojo::Remote<network::mojom::URLLoaderFactory> remote_url_loader_factory; + network::TestURLLoaderFactory proxied_url_loader_factory; + mojo::Remote<network::mojom::URLLoader> remote_loader; + mojo::PendingReceiver<network::mojom::URLLoaderClient> client; + + base::WeakPtr<BrowsingTopicsURLLoaderService::BindContext> bind_context = + CreateFactory(proxied_url_loader_factory, remote_url_loader_factory); + bind_context->OnDidCommitNavigation( + web_contents()->GetPrimaryMainFrame()->GetWeakDocumentPtr()); + + // The request to `foo3.com` will cause an empty topics header value to be + // added. + remote_url_loader_factory->CreateLoaderAndStart( + remote_loader.BindNewPipeAndPassReceiver(), + /*request_id=*/0, /*options=*/0, + CreateResourceRequest(GURL("https://foo3.com")), + client.InitWithNewPipeAndPassRemote(), + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); + remote_url_loader_factory.FlushForTesting(); + + EXPECT_EQ(1, proxied_url_loader_factory.NumPending()); + network::TestURLLoaderFactory::PendingRequest* pending_request = + &proxied_url_loader_factory.pending_requests()->back(); + + std::string topics_header_value; + bool has_topics_header = pending_request->request.headers.GetHeader( + "Sec-Browsing-Topics", &topics_header_value); + EXPECT_TRUE(has_topics_header); + EXPECT_TRUE(topics_header_value.empty()); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 1u); + + // The topics response header value "?1" will cause an observation to be + // recorded. + pending_request->client->OnReceiveResponse( + CreateResponseHead(/*topics_header_value=*/"?1"), /*body=*/{}, + absl::nullopt); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 2u); + EXPECT_FALSE(browser_client().last_get_topics_param()); + EXPECT_TRUE(browser_client().last_observe_param()); +} + +TEST_F(BrowsingTopicsURLLoaderServiceTest, + TopicsNotEligibleDueToInactiveFrame) { + NavigatePage(GURL("https://google.com")); + + mojo::Remote<network::mojom::URLLoaderFactory> remote_url_loader_factory; + network::TestURLLoaderFactory proxied_url_loader_factory; + mojo::Remote<network::mojom::URLLoader> remote_loader; + mojo::PendingReceiver<network::mojom::URLLoaderClient> client; + + base::WeakPtr<BrowsingTopicsURLLoaderService::BindContext> bind_context = + CreateFactory(proxied_url_loader_factory, remote_url_loader_factory); + bind_context->OnDidCommitNavigation( + web_contents()->GetPrimaryMainFrame()->GetWeakDocumentPtr()); + + // Switch the frame to an inactive state. The request won't be eligible for + // topics. + RenderFrameHostImpl& rfh = + static_cast<RenderFrameHostImpl&>(*web_contents()->GetPrimaryMainFrame()); + rfh.SetLifecycleState( + RenderFrameHostImpl::LifecycleStateImpl::kReadyToBeDeleted); + + remote_url_loader_factory->CreateLoaderAndStart( + remote_loader.BindNewPipeAndPassReceiver(), + /*request_id=*/0, /*options=*/0, + CreateResourceRequest(GURL("https://foo1.com")), + client.InitWithNewPipeAndPassRemote(), + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); + remote_url_loader_factory.FlushForTesting(); + + EXPECT_EQ(1, proxied_url_loader_factory.NumPending()); + network::TestURLLoaderFactory::PendingRequest* pending_request = + &proxied_url_loader_factory.pending_requests()->back(); + + std::string topics_header_value; + bool has_topics_header = pending_request->request.headers.GetHeader( + "Sec-Browsing-Topics", &topics_header_value); + EXPECT_FALSE(has_topics_header); + + pending_request->client->OnReceiveResponse( + CreateResponseHead(/*topics_header_value=*/"?1"), /*body=*/{}, + absl::nullopt); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 0u); +} + +TEST_F(BrowsingTopicsURLLoaderServiceTest, + TopicsNotEligibleDueToPermissionsPolicy) { + NavigatePage(GURL("https://google.com")); + + mojo::Remote<network::mojom::URLLoaderFactory> remote_url_loader_factory; + network::TestURLLoaderFactory proxied_url_loader_factory; + mojo::Remote<network::mojom::URLLoader> remote_loader; + mojo::PendingReceiver<network::mojom::URLLoaderClient> client; + + base::WeakPtr<BrowsingTopicsURLLoaderService::BindContext> bind_context = + CreateFactory(proxied_url_loader_factory, remote_url_loader_factory); + bind_context->OnDidCommitNavigation( + web_contents()->GetPrimaryMainFrame()->GetWeakDocumentPtr()); + + // The permissions policy disallows `foo4.com`. The request won't be eligible + // for topics. + remote_url_loader_factory->CreateLoaderAndStart( + remote_loader.BindNewPipeAndPassReceiver(), + /*request_id=*/0, /*options=*/0, + CreateResourceRequest(GURL("https://foo4.com")), + client.InitWithNewPipeAndPassRemote(), + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); + remote_url_loader_factory.FlushForTesting(); + + EXPECT_EQ(1, proxied_url_loader_factory.NumPending()); + network::TestURLLoaderFactory::PendingRequest* pending_request = + &proxied_url_loader_factory.pending_requests()->back(); + + std::string topics_header_value; + bool has_topics_header = pending_request->request.headers.GetHeader( + "Sec-Browsing-Topics", &topics_header_value); + EXPECT_FALSE(has_topics_header); + + pending_request->client->OnReceiveResponse( + CreateResponseHead(/*topics_header_value=*/"?1"), /*body=*/{}, + absl::nullopt); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 0u); +} + +TEST_F(BrowsingTopicsURLLoaderServiceTest, RedirectTopicsUpdated) { + NavigatePage(GURL("https://google.com")); + + mojo::Remote<network::mojom::URLLoaderFactory> remote_url_loader_factory; + network::TestURLLoaderFactory proxied_url_loader_factory( + /*observe_loader_requests=*/true); + mojo::Remote<network::mojom::URLLoader> remote_loader; + mojo::PendingReceiver<network::mojom::URLLoaderClient> client; + + base::WeakPtr<BrowsingTopicsURLLoaderService::BindContext> bind_context = + CreateFactory(proxied_url_loader_factory, remote_url_loader_factory); + bind_context->OnDidCommitNavigation( + web_contents()->GetPrimaryMainFrame()->GetWeakDocumentPtr()); + + // The request to `foo1.com` will cause the topics header value + // `kExpectedHeaderForOrigin1` to be added. + remote_url_loader_factory->CreateLoaderAndStart( + remote_loader.BindNewPipeAndPassReceiver(), + /*request_id=*/0, /*options=*/0, + CreateResourceRequest(GURL("https://foo1.com")), + client.InitWithNewPipeAndPassRemote(), + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); + remote_url_loader_factory.FlushForTesting(); + + EXPECT_EQ(1, proxied_url_loader_factory.NumPending()); + network::TestURLLoaderFactory::PendingRequest* pending_request = + &proxied_url_loader_factory.pending_requests()->back(); + + std::string topics_header_value; + bool has_topics_header = pending_request->request.headers.GetHeader( + "Sec-Browsing-Topics", &topics_header_value); + EXPECT_TRUE(has_topics_header); + EXPECT_EQ(topics_header_value, kExpectedHeaderForOrigin1); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 1u); + + // Redirect to `foo2.com` will cause the topics header to be updated to + // `kExpectedHeaderForOrigin2` for the redirect request. + net::RedirectInfo redirect_info; + redirect_info.new_url = GURL("https://foo2.com"); + + // The topics response header value "?1" for the initial request will cause an + // observation to be recorded. + pending_request->client->OnReceiveRedirect( + redirect_info, CreateResponseHead(/*topics_header_value=*/"?1")); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 2u); + EXPECT_FALSE(browser_client().last_get_topics_param()); + EXPECT_TRUE(browser_client().last_observe_param()); + + remote_loader->FollowRedirect(/*removed_headers=*/{}, + /*modified_headers=*/{}, + /*modified_cors_exempt_headers=*/{}, + /*new_url=*/absl::nullopt); + base::RunLoop().RunUntilIdle(); + + const std::vector<FollowRedirectParams>& follow_redirect_params = + pending_request->test_url_loader->follow_redirect_params(); + EXPECT_EQ(follow_redirect_params.size(), 1u); + EXPECT_EQ(follow_redirect_params[0].removed_headers.size(), 1u); + EXPECT_EQ(follow_redirect_params[0].removed_headers[0], + "Sec-Browsing-Topics"); + + std::string redirect_topics_header_value; + bool redirect_has_topics_header = + follow_redirect_params[0].modified_headers.GetHeader( + "Sec-Browsing-Topics", &redirect_topics_header_value); + EXPECT_TRUE(redirect_has_topics_header); + EXPECT_EQ(redirect_topics_header_value, kExpectedHeaderForOrigin2); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 3u); + + // The topics response header value "?1" will cause an observation to be + // recorded. + pending_request->client->OnReceiveResponse( + CreateResponseHead(/*topics_header_value=*/"?1"), /*body=*/{}, + absl::nullopt); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 4u); + EXPECT_FALSE(browser_client().last_get_topics_param()); + EXPECT_TRUE(browser_client().last_observe_param()); +} + +TEST_F(BrowsingTopicsURLLoaderServiceTest, RedirectNotEligibleForTopics) { + NavigatePage(GURL("https://google.com")); + + mojo::Remote<network::mojom::URLLoaderFactory> remote_url_loader_factory; + network::TestURLLoaderFactory proxied_url_loader_factory( + /*observe_loader_requests=*/true); + mojo::Remote<network::mojom::URLLoader> remote_loader; + mojo::PendingReceiver<network::mojom::URLLoaderClient> client; + + base::WeakPtr<BrowsingTopicsURLLoaderService::BindContext> bind_context = + CreateFactory(proxied_url_loader_factory, remote_url_loader_factory); + bind_context->OnDidCommitNavigation( + web_contents()->GetPrimaryMainFrame()->GetWeakDocumentPtr()); + + // The request to `foo1.com` will cause the topics header value + // `kExpectedHeaderForOrigin1` to be added. + remote_url_loader_factory->CreateLoaderAndStart( + remote_loader.BindNewPipeAndPassReceiver(), + /*request_id=*/0, /*options=*/0, + CreateResourceRequest(GURL("https://foo1.com")), + client.InitWithNewPipeAndPassRemote(), + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); + remote_url_loader_factory.FlushForTesting(); + + EXPECT_EQ(1, proxied_url_loader_factory.NumPending()); + network::TestURLLoaderFactory::PendingRequest* pending_request = + &proxied_url_loader_factory.pending_requests()->back(); + + std::string topics_header_value; + bool has_topics_header = pending_request->request.headers.GetHeader( + "Sec-Browsing-Topics", &topics_header_value); + EXPECT_TRUE(has_topics_header); + EXPECT_EQ(topics_header_value, kExpectedHeaderForOrigin1); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 1u); + + // The permissions policy disallows `foo4.com`. The redirect is thus not + // eligible for topics. + net::RedirectInfo redirect_info; + redirect_info.new_url = GURL("https://foo4.com"); + + // The topics response header value "?1" for the initial request will cause an + // observation to be recorded. + pending_request->client->OnReceiveRedirect( + redirect_info, CreateResponseHead(/*topics_header_value=*/"?1")); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 2u); + EXPECT_FALSE(browser_client().last_get_topics_param()); + EXPECT_TRUE(browser_client().last_observe_param()); + + remote_loader->FollowRedirect(/*removed_headers=*/{}, + /*modified_headers=*/{}, + /*modified_cors_exempt_headers=*/{}, + /*new_url=*/absl::nullopt); + base::RunLoop().RunUntilIdle(); + + const std::vector<FollowRedirectParams>& follow_redirect_params = + pending_request->test_url_loader->follow_redirect_params(); + EXPECT_EQ(follow_redirect_params.size(), 1u); + EXPECT_EQ(follow_redirect_params[0].removed_headers.size(), 1u); + EXPECT_EQ(follow_redirect_params[0].removed_headers[0], + "Sec-Browsing-Topics"); + + std::string redirect_topics_header_value; + bool redirect_has_topics_header = + follow_redirect_params[0].modified_headers.GetHeader( + "Sec-Browsing-Topics", &redirect_topics_header_value); + EXPECT_FALSE(redirect_has_topics_header); + + pending_request->client->OnReceiveResponse( + CreateResponseHead(/*topics_header_value=*/"?1"), /*body=*/{}, + absl::nullopt); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 2u); +} + +TEST_F(BrowsingTopicsURLLoaderServiceTest, TwoRequests) { + NavigatePage(GURL("https://google.com")); + + mojo::Remote<network::mojom::URLLoaderFactory> remote_url_loader_factory; + network::TestURLLoaderFactory proxied_url_loader_factory; + + mojo::Remote<network::mojom::URLLoader> remote_loader1; + mojo::PendingReceiver<network::mojom::URLLoaderClient> client1; + + mojo::Remote<network::mojom::URLLoader> remote_loader2; + mojo::PendingReceiver<network::mojom::URLLoaderClient> client2; + + base::WeakPtr<BrowsingTopicsURLLoaderService::BindContext> bind_context = + CreateFactory(proxied_url_loader_factory, remote_url_loader_factory); + bind_context->OnDidCommitNavigation( + web_contents()->GetPrimaryMainFrame()->GetWeakDocumentPtr()); + + remote_url_loader_factory->CreateLoaderAndStart( + remote_loader1.BindNewPipeAndPassReceiver(), + /*request_id=*/1, /*options=*/0, + CreateResourceRequest(GURL("https://foo1.com")), + client1.InitWithNewPipeAndPassRemote(), + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); + remote_url_loader_factory.FlushForTesting(); + + EXPECT_EQ(1, proxied_url_loader_factory.NumPending()); + network::TestURLLoaderFactory::PendingRequest* pending_request1 = + &proxied_url_loader_factory.pending_requests()->back(); + + { + std::string topics_header_value; + bool has_topics_header = pending_request1->request.headers.GetHeader( + "Sec-Browsing-Topics", &topics_header_value); + EXPECT_TRUE(has_topics_header); + EXPECT_EQ(topics_header_value, kExpectedHeaderForOrigin1); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 1u); + } + + pending_request1->client->OnReceiveResponse( + CreateResponseHead(/*topics_header_value=*/"?1"), /*body=*/{}, + absl::nullopt); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 2u); + EXPECT_FALSE(browser_client().last_get_topics_param()); + EXPECT_TRUE(browser_client().last_observe_param()); + + remote_url_loader_factory->CreateLoaderAndStart( + remote_loader2.BindNewPipeAndPassReceiver(), + /*request_id=*/2, /*options=*/0, + CreateResourceRequest(GURL("https://foo4.com")), + client2.InitWithNewPipeAndPassRemote(), + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); + remote_url_loader_factory.FlushForTesting(); + + EXPECT_EQ(2, proxied_url_loader_factory.NumPending()); + network::TestURLLoaderFactory::PendingRequest* pending_request2 = + &proxied_url_loader_factory.pending_requests()->back(); + + EXPECT_TRUE(remote_url_loader_factory.is_connected()); + + { + std::string topics_header_value; + bool has_topics_header = pending_request2->request.headers.GetHeader( + "Sec-Browsing-Topics", &topics_header_value); + + // Topics not eligible due to permissions policy. + EXPECT_FALSE(has_topics_header); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 2u); + } + + pending_request2->client->OnReceiveResponse( + CreateResponseHead(/*topics_header_value=*/"?1"), /*body=*/{}, + absl::nullopt); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 2u); +} + +TEST_F(BrowsingTopicsURLLoaderServiceTest, TwoFactories) { + NavigatePage(GURL("https://google.com")); + + mojo::Remote<network::mojom::URLLoaderFactory> remote_url_loader_factory1; + network::TestURLLoaderFactory proxied_url_loader_factory1; + mojo::Remote<network::mojom::URLLoader> remote_loader1; + mojo::PendingReceiver<network::mojom::URLLoaderClient> client1; + + mojo::Remote<network::mojom::URLLoaderFactory> remote_url_loader_factory2; + network::TestURLLoaderFactory proxied_url_loader_factory2; + mojo::Remote<network::mojom::URLLoader> remote_loader2; + mojo::PendingReceiver<network::mojom::URLLoaderClient> client2; + + base::WeakPtr<BrowsingTopicsURLLoaderService::BindContext> bind_context1 = + CreateFactory(proxied_url_loader_factory1, remote_url_loader_factory1); + bind_context1->OnDidCommitNavigation( + web_contents()->GetPrimaryMainFrame()->GetWeakDocumentPtr()); + + base::WeakPtr<BrowsingTopicsURLLoaderService::BindContext> bind_context2 = + CreateFactory(proxied_url_loader_factory2, remote_url_loader_factory2); + bind_context2->OnDidCommitNavigation( + web_contents()->GetPrimaryMainFrame()->GetWeakDocumentPtr()); + + remote_url_loader_factory1->CreateLoaderAndStart( + remote_loader1.BindNewPipeAndPassReceiver(), + /*request_id=*/0, /*options=*/0, + CreateResourceRequest(GURL("https://foo1.com")), + client1.InitWithNewPipeAndPassRemote(), + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); + remote_url_loader_factory1.FlushForTesting(); + + EXPECT_EQ(1, proxied_url_loader_factory1.NumPending()); + network::TestURLLoaderFactory::PendingRequest* pending_request1 = + &proxied_url_loader_factory1.pending_requests()->back(); + + { + std::string topics_header_value; + bool has_topics_header = pending_request1->request.headers.GetHeader( + "Sec-Browsing-Topics", &topics_header_value); + EXPECT_TRUE(has_topics_header); + EXPECT_EQ(topics_header_value, kExpectedHeaderForOrigin1); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 1u); + } + + remote_url_loader_factory2->CreateLoaderAndStart( + remote_loader2.BindNewPipeAndPassReceiver(), + /*request_id=*/0, /*options=*/0, + CreateResourceRequest(GURL("https://foo4.com")), + client2.InitWithNewPipeAndPassRemote(), + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); + remote_url_loader_factory2.FlushForTesting(); + + EXPECT_EQ(1, proxied_url_loader_factory2.NumPending()); + network::TestURLLoaderFactory::PendingRequest* pending_request2 = + &proxied_url_loader_factory2.pending_requests()->back(); + + { + std::string topics_header_value; + bool has_topics_header = pending_request2->request.headers.GetHeader( + "Sec-Browsing-Topics", &topics_header_value); + + // Topics not eligible due to permissions policy. + EXPECT_FALSE(has_topics_header); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 1u); + } + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 1u); + + pending_request1->client->OnReceiveResponse( + CreateResponseHead(/*topics_header_value=*/"?1"), /*body=*/{}, + absl::nullopt); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 2u); + EXPECT_FALSE(browser_client().last_get_topics_param()); + EXPECT_TRUE(browser_client().last_observe_param()); + + pending_request2->client->OnReceiveResponse( + CreateResponseHead(/*topics_header_value=*/"?1"), /*body=*/{}, + absl::nullopt); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(browser_client().handle_topics_web_api_count(), 2u); +} + +TEST_F(BrowsingTopicsURLLoaderServiceTest, BindContextClearedDueToDisconnect) { + NavigatePage(GURL("https://google.com")); + + base::WeakPtr<BrowsingTopicsURLLoaderService::BindContext> bind_context; + + { + mojo::Remote<network::mojom::URLLoaderFactory> remote_url_loader_factory; + network::TestURLLoaderFactory proxied_url_loader_factory; + + bind_context = + CreateFactory(proxied_url_loader_factory, remote_url_loader_factory); + + EXPECT_TRUE(bind_context); + } + + // Destroying `remote_url_loader_factory` would reset `bind_context`. + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(bind_context); +} + +} // namespace content
diff --git a/content/browser/file_system_access/file_system_access_directory_handle_impl.cc b/content/browser/file_system_access/file_system_access_directory_handle_impl.cc index 83b334e..8f5c3d4 100644 --- a/content/browser/file_system_access/file_system_access_directory_handle_impl.cc +++ b/content/browser/file_system_access/file_system_access_directory_handle_impl.cc
@@ -4,6 +4,7 @@ #include "content/browser/file_system_access/file_system_access_directory_handle_impl.h" +#include "base/feature_list.h" #include "base/guid.h" #include "base/i18n/file_util_icu.h" #include "base/memory/ref_counted_delete_on_sequence.h" @@ -41,6 +42,12 @@ using blink::mojom::FileSystemAccessTransferToken; using storage::FileSystemOperationRunner; +namespace features { +BASE_FEATURE(kFileSystemAccessRemoveEntryExclusiveLock, + "FileSystemAccessRemoveEntryExclusiveLock", + base::FEATURE_ENABLED_BY_DEFAULT); +} // namespace features + namespace content { using HandleType = FileSystemAccessPermissionContext::HandleType; @@ -384,15 +391,13 @@ return; } - // TODO(crbug.com/1254078): Consider requiring an exclusive lock to match the - // behavior of `remove()`. - // - // Use a shared write lock to allow the file to be removed if it has an open - // writable, but not if it has an open access handle. + auto lock_type = base::FeatureList::IsEnabled( + features::kFileSystemAccessRemoveEntryExclusiveLock) + ? WriteLockType::kExclusive + : WriteLockType::kShared; RunWithWritePermission( base::BindOnce(&FileSystemAccessHandleBase::DoRemove, - weak_factory_.GetWeakPtr(), child_url, recurse, - WriteLockType::kShared), + weak_factory_.GetWeakPtr(), child_url, recurse, lock_type), base::BindOnce([](blink::mojom::FileSystemAccessErrorPtr result, RemoveEntryCallback callback) { std::move(callback).Run(std::move(result));
diff --git a/content/browser/file_system_access/file_system_access_directory_handle_impl.h b/content/browser/file_system_access/file_system_access_directory_handle_impl.h index fbbd15e8..de8a9403 100644 --- a/content/browser/file_system_access/file_system_access_directory_handle_impl.h +++ b/content/browser/file_system_access/file_system_access_directory_handle_impl.h
@@ -5,6 +5,7 @@ #ifndef CONTENT_BROWSER_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_DIRECTORY_HANDLE_IMPL_H_ #define CONTENT_BROWSER_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_DIRECTORY_HANDLE_IMPL_H_ +#include "base/feature_list.h" #include "base/files/file.h" #include "base/memory/weak_ptr.h" #include "base/thread_annotations.h" @@ -15,6 +16,13 @@ #include "storage/browser/file_system/file_system_url.h" #include "third_party/blink/public/mojom/file_system_access/file_system_access_directory_handle.mojom.h" +namespace features { +// TODO(crbug.com/1254078): Remove this flag eventually. +// When enabled, removeEntry() acquires an exclusive lock (as opposed to a +// shared lock when disabled). +BASE_DECLARE_FEATURE(kFileSystemAccessRemoveEntryExclusiveLock); +} // namespace features + namespace content { // This is the browser side implementation of the
diff --git a/content/browser/file_system_access/file_system_access_directory_handle_impl_unittest.cc b/content/browser/file_system_access/file_system_access_directory_handle_impl_unittest.cc index 4d45ae18..836dfd18 100644 --- a/content/browser/file_system_access/file_system_access_directory_handle_impl_unittest.cc +++ b/content/browser/file_system_access/file_system_access_directory_handle_impl_unittest.cc
@@ -399,7 +399,7 @@ } // Acquire a shared lock on a file before removing to simulate when the file - // has an open writable. + // has an open writable. This should also fail. { base::CreateTemporaryFileInDir(dir, &file); auto base_name = storage::FilePathToString(file.BaseName()); @@ -412,8 +412,10 @@ base::test::TestFuture<blink::mojom::FileSystemAccessErrorPtr> future; handle->RemoveEntry(base_name, /*recurse=*/false, future.GetCallback()); - EXPECT_EQ(future.Get()->status, blink::mojom::FileSystemAccessStatus::kOk); - EXPECT_FALSE(base::PathExists(file)); + EXPECT_EQ( + future.Get()->status, + blink::mojom::FileSystemAccessStatus::kNoModificationAllowedError); + EXPECT_TRUE(base::PathExists(file)); } }
diff --git a/content/browser/file_system_access/file_system_access_file_writer_impl.h b/content/browser/file_system_access/file_system_access_file_writer_impl.h index 2417dfd..d125f2e 100644 --- a/content/browser/file_system_access/file_system_access_file_writer_impl.h +++ b/content/browser/file_system_access/file_system_access_file_writer_impl.h
@@ -114,7 +114,7 @@ // most filesystems, this move operation is atomic. storage::FileSystemURL swap_url_ GUARDED_BY_CONTEXT(sequence_checker_); - // Shared write lock on the file. It is released on destruction. + // Exclusive write lock on the file. It is released on destruction. scoped_refptr<FileSystemAccessWriteLockManager::WriteLock> lock_ GUARDED_BY_CONTEXT(sequence_checker_);
diff --git a/content/browser/media/capture/web_contents_video_capture_device_browsertest.cc b/content/browser/media/capture/web_contents_video_capture_device_browsertest.cc index 20e006a9..4906b702d 100644 --- a/content/browser/media/capture/web_contents_video_capture_device_browsertest.cc +++ b/content/browser/media/capture/web_contents_video_capture_device_browsertest.cc
@@ -27,6 +27,7 @@ #include "content/public/browser/web_contents.h" #include "content/public/test/browser_test.h" #include "content/shell/browser/shell.h" +#include "media/base/media_switches.h" #include "media/base/video_frame.h" #include "media/base/video_types.h" #include "media/base/video_util.h" @@ -80,7 +81,10 @@ : public ContentCaptureDeviceBrowserTestBase, public FrameTestUtil { public: - WebContentsVideoCaptureDeviceBrowserTest() = default; + WebContentsVideoCaptureDeviceBrowserTest() { + // TODO(https://crbug.com/1324757): tests should work with HiDPI enabled. + scoped_feature_list_.InitAndDisableFeature(media::kWebContentsCaptureHiDpi); + } WebContentsVideoCaptureDeviceBrowserTest( const WebContentsVideoCaptureDeviceBrowserTest&) = delete; @@ -281,6 +285,9 @@ } void WaitForFirstFrame() final { WaitForFrameWithColor(SK_ColorBLACK); } + + private: + base::test::ScopedFeatureList scoped_feature_list_; }; // Tests that the device refuses to start if the WebContents target was
diff --git a/content/browser/resources/aggregation_service/BUILD.gn b/content/browser/resources/aggregation_service/BUILD.gn index 99fe826..00d2107d 100644 --- a/content/browser/resources/aggregation_service/BUILD.gn +++ b/content/browser/resources/aggregation_service/BUILD.gn
@@ -13,7 +13,7 @@ # Copy (via creating sym links) all the other files into the same folder for # ts_library. copy("copy_files") { - deps = [ "//content/browser/aggregation_service:mojo_bindings_webui_js" ] + deps = [ "//content/browser/aggregation_service:mojo_bindings_js__generator" ] sources = [ "$root_gen_dir/mojom-webui/content/browser/aggregation_service/aggregation_service_internals.mojom-webui.js", "aggregation_service_internals.ts",
diff --git a/content/browser/resources/attribution_reporting/BUILD.gn b/content/browser/resources/attribution_reporting/BUILD.gn index 069107c..61040daf 100644 --- a/content/browser/resources/attribution_reporting/BUILD.gn +++ b/content/browser/resources/attribution_reporting/BUILD.gn
@@ -14,9 +14,9 @@ # ts_library. copy("copy_files") { deps = [ - "//components/attribution_reporting:mojom_webui_js", - "//content/browser/attribution_reporting:internals_mojo_bindings_webui_js", - "//content/browser/attribution_reporting:mojo_bindings_webui_js", + "//components/attribution_reporting:mojom_js__generator", + "//content/browser/attribution_reporting:internals_mojo_bindings_js__generator", + "//content/browser/attribution_reporting:mojo_bindings_js__generator", ] sources = [ "$root_gen_dir/mojom-webui/components/attribution_reporting/source_registration_error.mojom-webui.js",
diff --git a/content/browser/resources/indexed_db/BUILD.gn b/content/browser/resources/indexed_db/BUILD.gn index bbe4102..62b37125 100644 --- a/content/browser/resources/indexed_db/BUILD.gn +++ b/content/browser/resources/indexed_db/BUILD.gn
@@ -38,12 +38,12 @@ ] mojo_files_deps = [ - "//$buckets_folder:buckets_webui_js", - "//$indexeddb_bucket_folder:mojom_bucket_webui_js", - "//$indexeddb_internal_folder:mojo_bindings_webui_js", - "//$quota_folder:quota_webui_js", - "//$schemeful_site_folder:mojom_schemeful_site_webui_js", - "//$storage_key_folder:storage_key_webui_js", + "//$buckets_folder:buckets_js__generator", + "//$indexeddb_bucket_folder:mojom_bucket_js__generator", + "//$indexeddb_internal_folder:mojo_bindings_js__generator", + "//$quota_folder:quota_js__generator", + "//$schemeful_site_folder:mojom_schemeful_site_js__generator", + "//$storage_key_folder:storage_key_js__generator", ] grit_output_dir = "$root_gen_dir/content"
diff --git a/content/browser/resources/process/BUILD.gn b/content/browser/resources/process/BUILD.gn index 3a3d47c..f8180fc 100644 --- a/content/browser/resources/process/BUILD.gn +++ b/content/browser/resources/process/BUILD.gn
@@ -7,7 +7,7 @@ # Copy (via creating sym links) all the other files into the same folder for # ts_library. copy("copy_files") { - deps = [ "//content/browser/process_internals:mojo_bindings_webui_js" ] + deps = [ "//content/browser/process_internals:mojo_bindings_js__generator" ] sources = [ "$root_gen_dir/mojom-webui/content/browser/process_internals/process_internals.mojom-webui.js", "process_internals.ts",
diff --git a/content/browser/resources/quota/BUILD.gn b/content/browser/resources/quota/BUILD.gn index fe04cca..42dfb4c3 100644 --- a/content/browser/resources/quota/BUILD.gn +++ b/content/browser/resources/quota/BUILD.gn
@@ -17,7 +17,7 @@ "quota_internals_browser_proxy.ts", ] - mojo_files_deps = [ "//storage/browser/quota:mojo_bindings_webui_js" ] + mojo_files_deps = [ "//storage/browser/quota:mojo_bindings_js__generator" ] mojo_files = [ "$root_gen_dir/mojom-webui/storage/browser/quota/quota_internals.mojom-webui.js" ] ts_deps = [
diff --git a/content/browser/sandbox_parameters_mac.mm b/content/browser/sandbox_parameters_mac.mm index 714c9f3..b14014e 100644 --- a/content/browser/sandbox_parameters_mac.mm +++ b/content/browser/sandbox_parameters_mac.mm
@@ -19,6 +19,7 @@ #include "base/strings/stringprintf.h" #include "base/strings/sys_string_conversions.h" #include "base/system/sys_info.h" +#include "components/services/screen_ai/buildflags/buildflags.h" #include "content/public/browser/content_browser_client.h" #include "content/public/common/content_client.h" #include "content/public/common/content_features.h" @@ -236,7 +237,9 @@ << static_cast<int>(sandbox_type); break; // Setup parameters for sandbox types handled by embedders below. +#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) case sandbox::mojom::Sandbox::kScreenAI: +#endif case sandbox::mojom::Sandbox::kSpeechRecognition: SetupCommonSandboxParameters(client); CHECK(GetContentClient()->browser()->SetupEmbedderSandboxParameters(
diff --git a/content/browser/tracing/background_tracing_active_scenario.cc b/content/browser/tracing/background_tracing_active_scenario.cc index 94a35e2..66d65dc 100644 --- a/content/browser/tracing/background_tracing_active_scenario.cc +++ b/content/browser/tracing/background_tracing_active_scenario.cc
@@ -248,7 +248,7 @@ // we abort tracing here. DCHECK_NE(config_->tracing_mode(), BackgroundTracingConfigImpl::SYSTEM); base::trace_event::TraceLog::GetInstance()->SetDisabled( - base::trace_event::TraceLog::GetInstance()->enabled_modes()); + base::trace_event::TraceLog::RECORDING_MODE); } if (scenario_state_ == State::kAborted) {
diff --git a/content/browser/utility_sandbox_delegate_win.cc b/content/browser/utility_sandbox_delegate_win.cc index fed4afb7..7ec09dd 100644 --- a/content/browser/utility_sandbox_delegate_win.cc +++ b/content/browser/utility_sandbox_delegate_win.cc
@@ -2,12 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/files/file_path.h" #include "content/browser/utility_sandbox_delegate.h" #include "base/check.h" #include "base/feature_list.h" +#include "base/files/file_path.h" #include "base/strings/utf_string_conversions.h" +#include "components/services/screen_ai/buildflags/buildflags.h" #include "components/services/screen_ai/public/cpp/utilities.h" #include "content/public/browser/content_browser_client.h" #include "content/public/common/content_client.h" @@ -209,6 +210,7 @@ return true; } +#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) bool ScreenAIPreSpawnTarget(sandbox::TargetConfig* config, sandbox::mojom::Sandbox sandbox_type) { DCHECK(!config->IsConfigured()); @@ -236,6 +238,7 @@ library_binary_path.value().c_str()); return result == sandbox::SBOX_ALL_OK; } +#endif // BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) } // namespace @@ -309,10 +312,12 @@ return false; } +#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) if (sandbox_type_ == sandbox::mojom::Sandbox::kScreenAI) { if (!ScreenAIPreSpawnTarget(config, sandbox_type_)) return false; } +#endif if (sandbox_type_ == sandbox::mojom::Sandbox::kSpeechRecognition) { auto result = config->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn index 9dc02705..91b876e 100644 --- a/content/public/android/BUILD.gn +++ b/content/public/android/BUILD.gn
@@ -642,6 +642,7 @@ "junit/src/org/chromium/content/browser/selection/SmartSelectionEventProcessorTest.java", "junit/src/org/chromium/content/browser/sms/SmsProviderGmsTest.java", "junit/src/org/chromium/content/browser/webcontents/WebContentsImplTest.java", + "junit/src/org/chromium/content/browser/webcontents/WebContentsObserverProxyTest.java", "junit/src/org/chromium/content_public/browser/MessagePayloadTest.java", ]
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java index 3b374b4..c6c019d 100644 --- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java +++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java
@@ -19,6 +19,8 @@ import org.chromium.ui.mojom.VirtualKeyboardMode; import org.chromium.url.GURL; +import java.util.Iterator; + /** * Serves as a compound observer proxy for dispatching WebContentsObserver callbacks, * avoiding redundant JNI-related work when there are multiple Java-based observers. @@ -27,7 +29,6 @@ class WebContentsObserverProxy extends WebContentsObserver { private long mNativeWebContentsObserverProxy; private final ObserverList<WebContentsObserver> mObservers; - private final RewindableIterator<WebContentsObserver> mObserversIterator; private int mObserverCallsCurrentlyHandling; /** @@ -42,7 +43,6 @@ mNativeWebContentsObserverProxy = WebContentsObserverProxyJni.get().init(WebContentsObserverProxy.this, webContents); mObservers = new ObserverList<WebContentsObserver>(); - mObserversIterator = mObservers.rewindableIterator(); mObserverCallsCurrentlyHandling = 0; } @@ -90,8 +90,9 @@ @Override public void renderFrameCreated(GlobalRenderFrameHostId id) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().renderFrameCreated(id); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().renderFrameCreated(id); } finishObserverCall(); } @@ -104,8 +105,9 @@ @Override public void renderFrameDeleted(GlobalRenderFrameHostId id) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().renderFrameDeleted(id); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().renderFrameDeleted(id); } finishObserverCall(); } @@ -116,8 +118,9 @@ // Don't call handleObserverCall() and finishObserverCall() to explicitly allow a // WebContents to be destroyed while handling an this observer call. See // https://chromium-review.googlesource.com/c/chromium/src/+/2343269 for details - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().renderProcessGone(); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().renderProcessGone(); } } @@ -125,8 +128,9 @@ @CalledByNative public void didStartNavigationInPrimaryMainFrame(NavigationHandle navigation) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().didStartNavigationInPrimaryMainFrame(navigation); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().didStartNavigationInPrimaryMainFrame(navigation); } finishObserverCall(); } @@ -135,8 +139,9 @@ @CalledByNative public void didStartNavigationNoop(NavigationHandle navigation) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().didStartNavigationNoop(navigation); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().didStartNavigationNoop(navigation); } finishObserverCall(); } @@ -145,8 +150,9 @@ @CalledByNative public void didRedirectNavigation(NavigationHandle navigation) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().didRedirectNavigation(navigation); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().didRedirectNavigation(navigation); } finishObserverCall(); } @@ -155,8 +161,9 @@ @CalledByNative public void didFinishNavigationInPrimaryMainFrame(NavigationHandle navigation) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().didFinishNavigationInPrimaryMainFrame(navigation); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().didFinishNavigationInPrimaryMainFrame(navigation); } finishObserverCall(); } @@ -165,8 +172,9 @@ @CalledByNative public void didFinishNavigationNoop(NavigationHandle navigation) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().didFinishNavigationNoop(navigation); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().didFinishNavigationNoop(navigation); } finishObserverCall(); } @@ -175,8 +183,9 @@ @CalledByNative public void didStartLoading(GURL url) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().didStartLoading(url); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().didStartLoading(url); } finishObserverCall(); } @@ -185,8 +194,9 @@ @CalledByNative public void didStopLoading(GURL url, boolean isKnownValid) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().didStopLoading(url, isKnownValid); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().didStopLoading(url, isKnownValid); } finishObserverCall(); } @@ -195,8 +205,9 @@ @CalledByNative public void loadProgressChanged(float progress) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().loadProgressChanged(progress); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().loadProgressChanged(progress); } finishObserverCall(); } @@ -205,8 +216,9 @@ @CalledByNative public void didChangeVisibleSecurityState() { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().didChangeVisibleSecurityState(); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().didChangeVisibleSecurityState(); } finishObserverCall(); } @@ -216,8 +228,9 @@ public void didFailLoad(boolean isInPrimaryMainFrame, int errorCode, GURL failingUrl, @LifecycleState int frameLifecycleState) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().didFailLoad( + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().didFailLoad( isInPrimaryMainFrame, errorCode, failingUrl, frameLifecycleState); } finishObserverCall(); @@ -227,8 +240,9 @@ @CalledByNative public void didFirstVisuallyNonEmptyPaint() { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().didFirstVisuallyNonEmptyPaint(); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().didFirstVisuallyNonEmptyPaint(); } finishObserverCall(); } @@ -237,8 +251,9 @@ @CalledByNative public void wasShown() { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().wasShown(); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().wasShown(); } finishObserverCall(); } @@ -247,8 +262,9 @@ @CalledByNative public void wasHidden() { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().wasHidden(); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().wasHidden(); } finishObserverCall(); } @@ -257,8 +273,9 @@ @CalledByNative public void titleWasSet(String title) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().titleWasSet(title); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().titleWasSet(title); } finishObserverCall(); } @@ -267,8 +284,9 @@ @CalledByNative public void primaryMainDocumentElementAvailable() { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().primaryMainDocumentElementAvailable(); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().primaryMainDocumentElementAvailable(); } finishObserverCall(); } @@ -284,8 +302,9 @@ public void didFinishLoadInPrimaryMainFrame(GlobalRenderFrameHostId rfhId, GURL url, boolean isKnownValid, @LifecycleState int rfhLifecycleState) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().didFinishLoadInPrimaryMainFrame( + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().didFinishLoadInPrimaryMainFrame( rfhId, url, isKnownValid, rfhLifecycleState); } finishObserverCall(); @@ -303,8 +322,9 @@ public void didFinishLoadNoop(GlobalRenderFrameHostId rfhId, GURL url, boolean isKnownValid, boolean isInPrimaryMainFrame, @LifecycleState int rfhLifecycleState) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().didFinishLoadNoop( + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().didFinishLoadNoop( rfhId, url, isKnownValid, isInPrimaryMainFrame, rfhLifecycleState); } finishObserverCall(); @@ -321,8 +341,9 @@ public void documentLoadedInPrimaryMainFrame( GlobalRenderFrameHostId rfhId, @LifecycleState int rfhLifecycleState) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().documentLoadedInPrimaryMainFrame(rfhId, rfhLifecycleState); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().documentLoadedInPrimaryMainFrame(rfhId, rfhLifecycleState); } finishObserverCall(); } @@ -338,8 +359,9 @@ public void documentLoadedInFrameNoop(GlobalRenderFrameHostId rfhId, boolean isInPrimaryMainFrame, @LifecycleState int rfhLifecycleState) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().documentLoadedInFrameNoop( + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().documentLoadedInFrameNoop( rfhId, isInPrimaryMainFrame, rfhLifecycleState); } finishObserverCall(); @@ -349,8 +371,9 @@ @CalledByNative public void navigationEntryCommitted(LoadCommittedDetails details) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().navigationEntryCommitted(details); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().navigationEntryCommitted(details); } finishObserverCall(); } @@ -359,8 +382,9 @@ @CalledByNative public void navigationEntriesDeleted() { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().navigationEntriesDeleted(); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().navigationEntriesDeleted(); } finishObserverCall(); } @@ -369,8 +393,9 @@ @CalledByNative public void navigationEntriesChanged() { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().navigationEntriesChanged(); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().navigationEntriesChanged(); } finishObserverCall(); } @@ -379,8 +404,9 @@ @CalledByNative public void frameReceivedUserActivation() { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().frameReceivedUserActivation(); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().frameReceivedUserActivation(); } finishObserverCall(); } @@ -389,8 +415,9 @@ @CalledByNative public void didChangeThemeColor() { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().didChangeThemeColor(); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().didChangeThemeColor(); } finishObserverCall(); } @@ -399,8 +426,9 @@ @CalledByNative public void mediaStartedPlaying() { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().mediaStartedPlaying(); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().mediaStartedPlaying(); } finishObserverCall(); } @@ -409,8 +437,9 @@ @CalledByNative public void mediaStoppedPlaying() { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().mediaStoppedPlaying(); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().mediaStoppedPlaying(); } finishObserverCall(); } @@ -419,8 +448,9 @@ @CalledByNative public void hasEffectivelyFullscreenVideoChange(boolean isFullscreen) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().hasEffectivelyFullscreenVideoChange(isFullscreen); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().hasEffectivelyFullscreenVideoChange(isFullscreen); } finishObserverCall(); } @@ -429,8 +459,9 @@ @CalledByNative public void didToggleFullscreenModeForTab(boolean enteredFullscreen, boolean willCauseResize) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().didToggleFullscreenModeForTab( + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().didToggleFullscreenModeForTab( enteredFullscreen, willCauseResize); } finishObserverCall(); @@ -440,8 +471,9 @@ @CalledByNative public void viewportFitChanged(@WebContentsObserver.ViewportFitType int value) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().viewportFitChanged(value); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().viewportFitChanged(value); } finishObserverCall(); } @@ -450,8 +482,9 @@ @CalledByNative public void virtualKeyboardModeChanged(@VirtualKeyboardMode.EnumType int mode) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().virtualKeyboardModeChanged(mode); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().virtualKeyboardModeChanged(mode); } finishObserverCall(); } @@ -460,8 +493,9 @@ @CalledByNative public void onWebContentsFocused() { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().onWebContentsFocused(); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().onWebContentsFocused(); } finishObserverCall(); } @@ -470,8 +504,9 @@ @CalledByNative public void onWebContentsLostFocus() { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().onWebContentsLostFocus(); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().onWebContentsLostFocus(); } finishObserverCall(); } @@ -479,8 +514,9 @@ @Override public void onTopLevelNativeWindowChanged(WindowAndroid windowAndroid) { handleObserverCall(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().onTopLevelNativeWindowChanged(windowAndroid); + Iterator<WebContentsObserver> observersIterator = mObservers.iterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().onTopLevelNativeWindowChanged(windowAndroid); } finishObserverCall(); } @@ -492,15 +528,16 @@ // Java-based WebContents) are quite different, so we explicitly avoid // calling it here. ThreadUtils.assertOnUiThread(); - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - mObserversIterator.next().destroy(); + RewindableIterator<WebContentsObserver> observersIterator = mObservers.rewindableIterator(); + for (; observersIterator.hasNext();) { + observersIterator.next().destroy(); } // All observer destroy() implementations should result in their removal // from the proxy. String remainingObservers = "These observers were not removed: "; if (!mObservers.isEmpty()) { - for (mObserversIterator.rewind(); mObserversIterator.hasNext();) { - remainingObservers += mObserversIterator.next().getClass().getName() + " "; + for (observersIterator.rewind(); observersIterator.hasNext();) { + remainingObservers += observersIterator.next().getClass().getName() + " "; } } assert mObservers.isEmpty() : remainingObservers;
diff --git a/content/public/android/junit/src/org/chromium/content/browser/webcontents/WebContentsObserverProxyTest.java b/content/public/android/junit/src/org/chromium/content/browser/webcontents/WebContentsObserverProxyTest.java new file mode 100644 index 0000000..623ae67 --- /dev/null +++ b/content/public/android/junit/src/org/chromium/content/browser/webcontents/WebContentsObserverProxyTest.java
@@ -0,0 +1,73 @@ +// Copyright 2022 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.content.browser.webcontents; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.robolectric.annotation.Config; + +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.base.test.util.JniMocker; +import org.chromium.content_public.browser.WebContentsObserver; + +/** + * Unit tests for {@link WebContentsObserverProxy}. + */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class WebContentsObserverProxyTest { + @Mock + private WebContentsObserver mWebContentsObserver; + @Mock + private WebContentsObserver mWebContentsObserver2; + @Mock + private WebContentsObserverProxy.Natives mWebContentsObserverProxyJni; + @Rule + public JniMocker mJniMocker = new JniMocker(); + + private WebContentsImpl mWebContentsImpl; + private final long mNativeWebContentsAndroid = 1; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mJniMocker.mock(WebContentsObserverProxyJni.TEST_HOOKS, mWebContentsObserverProxyJni); + when(mWebContentsObserverProxyJni.init(any(), any())).thenReturn(1L); + } + + @Test + public void testChainingEvents() { + InOrder inOrder = Mockito.inOrder(mWebContentsObserver, mWebContentsObserver2); + final WebContentsObserverProxy proxy = new WebContentsObserverProxy(null); + proxy.addObserver(mWebContentsObserver); + proxy.addObserver(mWebContentsObserver2); + Mockito.doAnswer(new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocation) { + proxy.navigationEntriesChanged(); + return null; + } + }) + .when(mWebContentsObserver) + .navigationEntriesDeleted(); + proxy.navigationEntriesDeleted(); + inOrder.verify(mWebContentsObserver).navigationEntriesDeleted(); + inOrder.verify(mWebContentsObserver).navigationEntriesChanged(); + inOrder.verify(mWebContentsObserver2).navigationEntriesChanged(); + inOrder.verify(mWebContentsObserver2).navigationEntriesDeleted(); + inOrder.verifyNoMoreInteractions(); + } +}
diff --git a/content/renderer/media/renderer_webaudiodevice_impl.cc b/content/renderer/media/renderer_webaudiodevice_impl.cc index 10aa412..fca5b131 100644 --- a/content/renderer/media/renderer_webaudiodevice_impl.cc +++ b/content/renderer/media/renderer_webaudiodevice_impl.cc
@@ -97,6 +97,11 @@ .output_params(); } +scoped_refptr<media::AudioRendererSink> GetNullAudioSink( + const scoped_refptr<base::SequencedTaskRunner>& task_runner) { + return base::MakeRefCounted<media::NullAudioSink>(task_runner); +} + } // namespace std::unique_ptr<RendererWebAudioDeviceImpl> RendererWebAudioDeviceImpl::Create( @@ -109,7 +114,8 @@ return std::unique_ptr<RendererWebAudioDeviceImpl>( new RendererWebAudioDeviceImpl( sink_descriptor, layout, number_of_output_channels, latency_hint, - callback, session_id, base::BindOnce(&GetOutputDeviceParameters))); + callback, session_id, base::BindOnce(&GetOutputDeviceParameters), + base::BindRepeating(&GetNullAudioSink))); } RendererWebAudioDeviceImpl::RendererWebAudioDeviceImpl( @@ -119,12 +125,14 @@ const blink::WebAudioLatencyHint& latency_hint, WebAudioDevice::RenderCallback* callback, const base::UnguessableToken& session_id, - OutputDeviceParamsCallback device_params_cb) + OutputDeviceParamsCallback device_params_cb, + CreateSilentSinkCallback create_silent_sink_cb) : sink_descriptor_(sink_descriptor), latency_hint_(latency_hint), client_callback_(callback), session_id_(session_id), - frame_token_(sink_descriptor.Token()) { + frame_token_(sink_descriptor.Token()), + create_silent_sink_cb_(std::move(create_silent_sink_cb)) { DCHECK(client_callback_); SendLogMessage(base::StringPrintf("%s", __func__)); @@ -201,8 +209,7 @@ sink_->Initialize(sink_params_, silent_sink_suspender_.get()); break; case blink::WebAudioSinkDescriptor::kSilent: - sink_ = - base::MakeRefCounted<media::NullAudioSink>(GetSilentSinkTaskRunner()); + sink_ = create_silent_sink_cb_.Run(GetSilentSinkTaskRunner()); sink_->Initialize(sink_params_, this); break; }
diff --git a/content/renderer/media/renderer_webaudiodevice_impl.h b/content/renderer/media/renderer_webaudiodevice_impl.h index e567a61..62fc7d37 100644 --- a/content/renderer/media/renderer_webaudiodevice_impl.h +++ b/content/renderer/media/renderer_webaudiodevice_impl.h
@@ -85,6 +85,10 @@ const base::UnguessableToken& session_id, const std::string& device_id)>; + using CreateSilentSinkCallback = + base::RepeatingCallback<scoped_refptr<media::AudioRendererSink>( + const scoped_refptr<base::SequencedTaskRunner>& task_runner)>; + RendererWebAudioDeviceImpl( const blink::WebAudioSinkDescriptor& sink_descriptor, media::ChannelLayout layout, @@ -92,7 +96,8 @@ const blink::WebAudioLatencyHint& latency_hint, blink::WebAudioDevice::RenderCallback* callback, const base::UnguessableToken& session_id, - OutputDeviceParamsCallback device_params_cb); + OutputDeviceParamsCallback device_params_cb, + CreateSilentSinkCallback create_silent_sink_cb); private: scoped_refptr<base::SingleThreadTaskRunner> GetSilentSinkTaskRunner(); @@ -135,6 +140,8 @@ // Used to trigger one single textlog indicating that rendering started as // intended. Set to true once in the first call to the Render callback. bool is_rendering_ = false; + + CreateSilentSinkCallback create_silent_sink_cb_; }; } // namespace content
diff --git a/content/renderer/media/renderer_webaudiodevice_impl_unittest.cc b/content/renderer/media/renderer_webaudiodevice_impl_unittest.cc index 90b7e7df..9d707633 100644 --- a/content/renderer/media/renderer_webaudiodevice_impl_unittest.cc +++ b/content/renderer/media/renderer_webaudiodevice_impl_unittest.cc
@@ -20,12 +20,40 @@ #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h" #include "third_party/blink/public/web/modules/media/audio/audio_device_factory.h" -using testing::_; +using ::testing::_; +using ::testing::InSequence; namespace content { namespace { +class MockAudioRendererSink : public media::AudioRendererSink { + public: + explicit MockAudioRendererSink() = default; + void Initialize(const media::AudioParameters& params, + RenderCallback* callback) override { + callback_ = callback; + } + MOCK_METHOD(void, Start, (), (override)); + MOCK_METHOD(void, Stop, (), (override)); + MOCK_METHOD(void, Pause, (), (override)); + MOCK_METHOD(void, Play, (), (override)); + MOCK_METHOD(void, Flush, (), (override)); + MOCK_METHOD(bool, SetVolume, (double volume), (override)); + MOCK_METHOD(media::OutputDeviceInfo, GetOutputDeviceInfo, (), (override)); + MOCK_METHOD(void, + GetOutputDeviceInfoAsync, + (OutputDeviceInfoCB info_cb), + (override)); + MOCK_METHOD(bool, IsOptimizedForHardwareParameters, (), (override)); + MOCK_METHOD(bool, CurrentThreadIsRenderingThread, (), (override)); + + media::AudioRendererSink::RenderCallback* callback_ = nullptr; + + private: + ~MockAudioRendererSink() override = default; +}; + constexpr int kHardwareSampleRate = 44100; constexpr int kHardwareBufferSize = 128; const blink::LocalFrameToken kFrameToken; @@ -47,7 +75,8 @@ int number_of_output_channels, const blink::WebAudioLatencyHint& latency_hint, blink::WebAudioDevice::RenderCallback* callback, - const base::UnguessableToken& session_id) + const base::UnguessableToken& session_id, + CreateSilentSinkCallback silent_sink_callback) : RendererWebAudioDeviceImpl( sink_descriptor, layout, @@ -55,7 +84,8 @@ latency_hint, callback, session_id, - base::BindOnce(&MockGetOutputDeviceParameters)) {} + base::BindOnce(&MockGetOutputDeviceParameters), + std::move(silent_sink_callback)) {} }; } // namespace @@ -65,23 +95,36 @@ public blink::AudioDeviceFactory, public testing::Test { public: - void Render(const blink::WebVector<float*>& destination_data, - uint32_t number_of_frames, - double delay, - double delay_timestamp, - size_t prior_frames_skipped) override { - called_render_ = true; - } + MOCK_METHOD(void, + Render, + (const blink::WebVector<float*>& destination_data, + uint32_t number_of_frames, + double delay, + double delay_timestamp, + size_t prior_frames_skipped), + (override)); protected: - RendererWebAudioDeviceImplTest() {} + RendererWebAudioDeviceImplTest() { + mock_audio_renderer_sink_ = base::MakeRefCounted<MockAudioRendererSink>(); + } + + scoped_refptr<media::AudioRendererSink> MockCreateSilentSink( + const scoped_refptr<base::SequencedTaskRunner>& task_runner) { + return mock_audio_renderer_sink_; + } void SetupDevice(blink::WebAudioLatencyHint latencyHint) { blink::WebAudioSinkDescriptor sink_descriptor( blink::WebString::FromUTF8(std::string()), kFrameToken); webaudio_device_ = std::make_unique<RendererWebAudioDeviceImplUnderTest>( sink_descriptor, media::CHANNEL_LAYOUT_MONO, 1, latencyHint, this, - base::UnguessableToken()); + base::UnguessableToken(), + base::BindRepeating( + &RendererWebAudioDeviceImplTest::MockCreateSilentSink, + // Guaranteed to be valid because |this| owns |webaudio_device_| and + // so will outlive it. + base::Unretained(this))); webaudio_device_->SetSilentSinkTaskRunnerForTesting( blink::scheduler::GetSingleThreadTaskRunnerForTesting()); } @@ -93,7 +136,12 @@ sink_descriptor, layout, channels, blink::WebAudioLatencyHint( blink::WebAudioLatencyHint::kCategoryInteractive), - this, base::UnguessableToken()); + this, base::UnguessableToken(), + base::BindRepeating( + &RendererWebAudioDeviceImplTest::MockCreateSilentSink, + // Guaranteed to be valid because |this| owns |webaudio_device_| and + // so will outlive it. + base::Unretained(this))); webaudio_device_->SetSilentSinkTaskRunnerForTesting( blink::scheduler::GetSingleThreadTaskRunnerForTesting()); } @@ -103,7 +151,12 @@ sink_descriptor, media::CHANNEL_LAYOUT_MONO, 1, blink::WebAudioLatencyHint( blink::WebAudioLatencyHint::kCategoryInteractive), - this, base::UnguessableToken()); + this, base::UnguessableToken(), + base::BindRepeating( + &RendererWebAudioDeviceImplTest::MockCreateSilentSink, + // Guaranteed to be valid because |this| owns |webaudio_device_| and + // so will outlive it. + base::Unretained(this))); webaudio_device_->SetSilentSinkTaskRunnerForTesting( blink::scheduler::GetSingleThreadTaskRunnerForTesting()); } @@ -129,7 +182,7 @@ std::unique_ptr<RendererWebAudioDeviceImpl> webaudio_device_; base::test::SingleThreadTaskEnvironment task_environment_; - bool called_render_ = false; + scoped_refptr<MockAudioRendererSink> mock_audio_renderer_sink_; }; TEST_F(RendererWebAudioDeviceImplTest, ChannelLayout) { @@ -229,39 +282,148 @@ EXPECT_GE(balancedBufferSize, interactiveBufferSize); } -TEST_F(RendererWebAudioDeviceImplTest, TestSilent) { - std::unique_ptr<media::AudioBus> audio_bus = - media::AudioBus::Create(1, kHardwareBufferSize); +TEST_F(RendererWebAudioDeviceImplTest, NullSink_RenderWorks) { + { + InSequence s; + + EXPECT_CALL(*mock_audio_renderer_sink_, Start).Times(1); + EXPECT_CALL(*mock_audio_renderer_sink_, Play).Times(1); + EXPECT_CALL(*this, Render).Times(1); + EXPECT_CALL(*mock_audio_renderer_sink_, Stop).Times(1); + } // The WebAudioSinkDescriptor constructor with frame token will construct a // silent sink. - blink::WebAudioSinkDescriptor sink_descriptor(kFrameToken); - SetupDevice(sink_descriptor); - - // Test public interface. - EXPECT_EQ(called_render_, false); + SetupDevice(blink::WebAudioSinkDescriptor(kFrameToken)); webaudio_device_->Start(); - task_environment_.RunUntilIdle(); - EXPECT_EQ(called_render_, true); - webaudio_device_->SampleRate(); - webaudio_device_->FramesPerBuffer(); - webaudio_device_->SetDetectSilence(true); - webaudio_device_->SetDetectSilence(false); + mock_audio_renderer_sink_->callback_->Render( + base::TimeDelta::Min(), base::TimeTicks::Now(), 0, + media::AudioBus::Create(1, kHardwareBufferSize).get()); + webaudio_device_->Stop(); +} + +TEST_F(RendererWebAudioDeviceImplTest, NullSink_PauseResumeWorks) { + { + InSequence s; + + EXPECT_CALL(*mock_audio_renderer_sink_, Start).Times(1); + EXPECT_CALL(*mock_audio_renderer_sink_, Play).Times(1); + EXPECT_CALL(*mock_audio_renderer_sink_, Pause).Times(1); + EXPECT_CALL(*mock_audio_renderer_sink_, Play).Times(1); + EXPECT_CALL(*mock_audio_renderer_sink_, Stop).Times(1); + } + + // The WebAudioSinkDescriptor constructor with frame token will construct a + // silent sink. + SetupDevice(blink::WebAudioSinkDescriptor(kFrameToken)); + webaudio_device_->Start(); webaudio_device_->Pause(); webaudio_device_->Resume(); webaudio_device_->Stop(); +} - // Test repeated calls. +TEST_F(RendererWebAudioDeviceImplTest, + NullSink_StartRenderStopStartRenderStopWorks) { + { + InSequence s; + + EXPECT_CALL(*mock_audio_renderer_sink_, Start).Times(1); + EXPECT_CALL(*mock_audio_renderer_sink_, Play).Times(1); + EXPECT_CALL(*this, Render).Times(1); + EXPECT_CALL(*mock_audio_renderer_sink_, Stop).Times(1); + EXPECT_CALL(*mock_audio_renderer_sink_, Start).Times(1); + EXPECT_CALL(*mock_audio_renderer_sink_, Play).Times(1); + EXPECT_CALL(*this, Render).Times(1); + EXPECT_CALL(*mock_audio_renderer_sink_, Stop).Times(1); + } + + // The WebAudioSinkDescriptor constructor with frame token will construct a + // silent sink. + SetupDevice(blink::WebAudioSinkDescriptor(kFrameToken)); webaudio_device_->Start(); + mock_audio_renderer_sink_->callback_->Render( + base::TimeDelta::Min(), base::TimeTicks::Now(), 0, + media::AudioBus::Create(1, kHardwareBufferSize).get()); + webaudio_device_->Stop(); + webaudio_device_->Start(); + mock_audio_renderer_sink_->callback_->Render( + base::TimeDelta::Min(), base::TimeTicks::Now(), 0, + media::AudioBus::Create(1, kHardwareBufferSize).get()); + webaudio_device_->Stop(); +} + +TEST_F(RendererWebAudioDeviceImplTest, NullSink_RepeatedStartWorks) { + { + InSequence s; + + EXPECT_CALL(*mock_audio_renderer_sink_, Start).Times(1); + EXPECT_CALL(*mock_audio_renderer_sink_, Play).Times(1); + EXPECT_CALL(*mock_audio_renderer_sink_, Stop).Times(1); + } + + // The WebAudioSinkDescriptor constructor with frame token will construct a + // silent sink. + SetupDevice(blink::WebAudioSinkDescriptor(kFrameToken)); + webaudio_device_->Start(); + webaudio_device_->Start(); + webaudio_device_->Stop(); +} + +TEST_F(RendererWebAudioDeviceImplTest, NullSink_RepeatedPauseWorks) { + { + InSequence s; + + EXPECT_CALL(*mock_audio_renderer_sink_, Start).Times(1); + EXPECT_CALL(*mock_audio_renderer_sink_, Play).Times(1); + EXPECT_CALL(*mock_audio_renderer_sink_, Pause).Times(2); + EXPECT_CALL(*mock_audio_renderer_sink_, Stop).Times(1); + } + + // The WebAudioSinkDescriptor constructor with frame token will construct a + // silent sink. + SetupDevice(blink::WebAudioSinkDescriptor(kFrameToken)); webaudio_device_->Start(); webaudio_device_->Pause(); webaudio_device_->Pause(); + webaudio_device_->Stop(); +} + +TEST_F(RendererWebAudioDeviceImplTest, NullSink_RepeatedResumeWorks) { + { + InSequence s; + + EXPECT_CALL(*mock_audio_renderer_sink_, Start).Times(1); + EXPECT_CALL(*mock_audio_renderer_sink_, Play).Times(1); + EXPECT_CALL(*mock_audio_renderer_sink_, Pause).Times(1); + EXPECT_CALL(*mock_audio_renderer_sink_, Play).Times(2); + EXPECT_CALL(*mock_audio_renderer_sink_, Stop).Times(1); + } + + // The WebAudioSinkDescriptor constructor with frame token will construct a + // silent sink. + SetupDevice(blink::WebAudioSinkDescriptor(kFrameToken)); + webaudio_device_->Start(); + webaudio_device_->Pause(); webaudio_device_->Resume(); webaudio_device_->Resume(); webaudio_device_->Stop(); - webaudio_device_->Stop(); +} - task_environment_.RunUntilIdle(); +TEST_F(RendererWebAudioDeviceImplTest, NullSink_RepeatedStopWorks) { + { + InSequence s; + + EXPECT_CALL(*mock_audio_renderer_sink_, Start).Times(1); + EXPECT_CALL(*mock_audio_renderer_sink_, Play).Times(1); + EXPECT_CALL(*mock_audio_renderer_sink_, Stop).Times(1); + } + + // The WebAudioSinkDescriptor constructor with frame token will construct a + // silent sink. + SetupDevice(blink::WebAudioSinkDescriptor(kFrameToken)); + webaudio_device_->Start(); + webaudio_device_->Stop(); + webaudio_device_->Stop(); } } // namespace content
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index 31d12b8..e117d16 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -2034,7 +2034,7 @@ "grit/web_ui_mojo_test_resources_map.h", "web_ui_mojo_test_resources.pak", ] - deps = [ ":web_ui_test_mojo_bindings_webui_js" ] + deps = [ ":web_ui_test_mojo_bindings_js__generator" ] } static_library("run_all_unittests") { @@ -2134,6 +2134,7 @@ "../browser/browsing_data/same_site_data_remover_impl_unittest.cc", "../browser/browsing_topics/browsing_topics_site_data_manager_impl_unittest.cc", "../browser/browsing_topics/browsing_topics_site_data_storage_unittest.cc", + "../browser/browsing_topics/browsing_topics_url_loader_service_unittest.cc", "../browser/browsing_topics/header_util_unittest.cc", "../browser/buckets/bucket_manager_host_unittest.cc", "../browser/buckets/bucket_utils_unittest.cc",
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 d633872..da1a026 100644 --- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt +++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -375,6 +375,7 @@ crbug.com/1382332 [ chromeos ] Pixel_MediaRecorderFromVideoElementWithOoprCanvasDisabled [ Failure ] crbug.com/1382332 [ android ] Pixel_MediaRecorderFromVideoElement [ Failure ] crbug.com/1382332 [ android ] Pixel_MediaRecorderFromVideoElementWithOoprCanvasDisabled [ Failure ] +crbug.com/1382332 [ fuchsia ] Pixel_MediaRecorderFromVideoElementWithOoprCanvasDisabled [ Failure ] ####################################################################### # Automated Entries After This Point - Do Not Manually Add Below Here #
diff --git a/device/bluetooth/floss/exported_callback_manager.h b/device/bluetooth/floss/exported_callback_manager.h index 410ad974..e49da3d 100644 --- a/device/bluetooth/floss/exported_callback_manager.h +++ b/device/bluetooth/floss/exported_callback_manager.h
@@ -9,6 +9,8 @@ #include <unordered_map> #include <utility> +#include "base/bind.h" +#include "base/callback.h" #include "base/containers/contains.h" #include "base/logging.h" #include "dbus/bus.h" @@ -20,10 +22,9 @@ template <typename T> using MethodDelegate = - std::function<void(dbus::MethodCall*, - base::WeakPtr<T>, - dbus::ExportedObject::ResponseSender)>; - + base::RepeatingCallback<void(dbus::MethodCall*, + base::WeakPtr<T>, + dbus::ExportedObject::ResponseSender)>; } // Private class helper. @@ -110,17 +111,20 @@ } public: - // Returns a lambda with captured |func| that parses D-Bus parameters and - // forwards it to |func|. + // Returns a RepeatingCallback with captured |func| that parses D-Bus + // parameters and forwards it to |func|. // - // Being a lambda has the benefit that the lambda invoker does not need to + // Being a RepeatingCallback has the benefit that the invoker does not need to // know the signature of |func| at compile time. static MethodDelegate<T> CreateForwarder(void (T::*func)(Args...)) { - return [func](dbus::MethodCall* method_call, base::WeakPtr<T> callback, - dbus::ExportedObject::ResponseSender response_sender) { - Forward(method_call, base::BindOnce(func, callback), - std::move(response_sender)); - }; + return base::BindRepeating( + [](void (T::*func)(Args...), dbus::MethodCall* method_call, + base::WeakPtr<T> target, + dbus::ExportedObject::ResponseSender response_sender) { + Forward(method_call, base::BindOnce(func, target), + std::move(response_sender)); + }, + func); } }; @@ -242,7 +246,7 @@ DCHECK(method_name == method_call->GetMember()) << "Method name from D-Bus does not match with the registered name"; - delegate(method_call, exported_callback, std::move(response_sender)); + delegate.Run(method_call, exported_callback, std::move(response_sender)); } scoped_refptr<dbus::Bus> bus_;
diff --git a/extensions/browser/api/feedback_private/feedback_private_api.cc b/extensions/browser/api/feedback_private/feedback_private_api.cc index 6ef7bb8..3670353c 100644 --- a/extensions/browser/api/feedback_private/feedback_private_api.cc +++ b/extensions/browser/api/feedback_private/feedback_private_api.cc
@@ -375,4 +375,15 @@ } } +ExtensionFunction::ResponseAction FeedbackPrivateOpenFeedbackFunction::Run() { + std::unique_ptr<feedback_private::OpenFeedback::Params> params( + feedback_private::OpenFeedback::Params::Create(args())); + EXTENSION_FUNCTION_VALIDATE(params); + + ExtensionsAPIClient::Get()->GetFeedbackPrivateDelegate()->OpenFeedback( + browser_context(), params->source); + + return RespondNow(NoArguments()); +} + } // namespace extensions
diff --git a/extensions/browser/api/feedback_private/feedback_private_api.h b/extensions/browser/api/feedback_private/feedback_private_api.h index 67aff72e..0dcc665 100644 --- a/extensions/browser/api/feedback_private/feedback_private_api.h +++ b/extensions/browser/api/feedback_private/feedback_private_api.h
@@ -130,6 +130,16 @@ void OnCompleted(api::feedback_private::LandingPageType type, bool success); }; +class FeedbackPrivateOpenFeedbackFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("feedbackPrivate.openFeedback", + FEEDBACKPRIVATE_OPENFEEDBACK) + + protected: + ~FeedbackPrivateOpenFeedbackFunction() override = default; + ResponseAction Run() override; +}; + } // namespace extensions #endif // EXTENSIONS_BROWSER_API_FEEDBACK_PRIVATE_FEEDBACK_PRIVATE_API_H_
diff --git a/extensions/browser/api/feedback_private/feedback_private_delegate.h b/extensions/browser/api/feedback_private/feedback_private_delegate.h index 7113c30ee..7fb309f 100644 --- a/extensions/browser/api/feedback_private/feedback_private_delegate.h +++ b/extensions/browser/api/feedback_private/feedback_private_delegate.h
@@ -85,6 +85,11 @@ // feedback reports to the feedback server. virtual feedback::FeedbackUploader* GetFeedbackUploaderForContext( content::BrowserContext* context) const = 0; + + // Opens the feedback report window. + virtual void OpenFeedback( + content::BrowserContext* context, + api::feedback_private::FeedbackSource source) const = 0; }; } // namespace extensions
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h index 5db97a5..6961d7d 100644 --- a/extensions/browser/extension_function_histogram_value.h +++ b/extensions/browser/extension_function_histogram_value.h
@@ -1797,6 +1797,7 @@ OS_DIAGNOSTICS_RUNNVMESELFTESTROUTINE = 1734, AUTOTESTPRIVATE_STARTFRAMECOUNTING = 1735, AUTOTESTPRIVATE_STOPFRAMECOUNTING = 1736, + FEEDBACKPRIVATE_OPENFEEDBACK = 1737, // Last entry: Add new entries above, then run: // tools/metrics/histograms/update_extension_histograms.py ENUM_BOUNDARY
diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json index 9d47807..9567b15 100644 --- a/extensions/common/api/_api_features.json +++ b/extensions/common/api/_api_features.json
@@ -202,6 +202,17 @@ ] } ], + "feedbackPrivate.openFeedback": { + "dependencies": [], + "channel": "stable", + "contexts": ["blessed_extension"], + "extension_types": ["extension"], + "allowlist": [ + "2FC374607C2DF285634B67C64A2E356C607091C3", // http://crbug.com/1352358 + "3727DD3E564B6055387425027AD74C58784ACC15", // http://crbug.com/1352358 + "12E618C3C6E97495AAECF2AC12DEB082353241C6" // http://crbug.com/1352358 + ] + }, "feedbackPrivate.readLogSource": [ { "platforms": ["chromeos"],
diff --git a/extensions/common/api/extension_action/action_info.cc b/extensions/common/api/extension_action/action_info.cc index f112a79..254cfaa 100644 --- a/extensions/common/api/extension_action/action_info.cc +++ b/extensions/common/api/extension_action/action_info.cc
@@ -58,10 +58,12 @@ ActionInfo::~ActionInfo() {} // static -std::unique_ptr<ActionInfo> ActionInfo::Load(const Extension* extension, - Type type, - const base::DictionaryValue* dict, - std::u16string* error) { +std::unique_ptr<ActionInfo> ActionInfo::Load( + const Extension* extension, + Type type, + const base::DictionaryValue* dict, + std::vector<InstallWarning>* install_warnings, + std::u16string* error) { auto result = std::make_unique<ActionInfo>(type); // Read the page action |default_icon| (optional). @@ -113,16 +115,25 @@ } if (!url_str->empty()) { - // An empty string is treated as having no popup. - result->default_popup_url = - Extension::GetResourceURL(extension->url(), *url_str); - if (!result->default_popup_url.is_valid()) { + GURL popup_url = Extension::GetResourceURL(extension->url(), *url_str); + + if (!popup_url.is_valid()) { *error = errors::kInvalidActionDefaultPopup; return nullptr; } + + // Check popup is only for this extension. + if (extension->origin().IsSameOriginWith(popup_url)) { + result->default_popup_url = popup_url; + } else { + install_warnings->push_back(extensions::InstallWarning( + extensions::manifest_errors::kInvalidExtensionOriginPopup, + GetManifestKeyForActionType(type), + extensions::manifest_keys::kActionDefaultPopup)); + } } else { - DCHECK(result->default_popup_url.is_empty()) - << "Shouldn't be possible for the popup to be set."; + // An empty string is treated as having no popup. + DCHECK(result->default_popup_url.is_empty()); } }
diff --git a/extensions/common/api/extension_action/action_info.h b/extensions/common/api/extension_action/action_info.h index e4cdb58d..d3bec75 100644 --- a/extensions/common/api/extension_action/action_info.h +++ b/extensions/common/api/extension_action/action_info.h
@@ -37,11 +37,14 @@ ActionInfo(const ActionInfo& other); ~ActionInfo(); - // Loads an ActionInfo from the given DictionaryValue. - static std::unique_ptr<ActionInfo> Load(const Extension* extension, - Type type, - const base::DictionaryValue* dict, - std::u16string* error); + // Loads an ActionInfo from the given DictionaryValue. Populating + // `install_warnings` if issues are encountered when parsing the manifest. + static std::unique_ptr<ActionInfo> Load( + const Extension* extension, + Type type, + const base::DictionaryValue* dict, + std::vector<InstallWarning>* install_warnings, + std::u16string* error); // TODO(jlulejian): Rather than continue to grow this list of static helper // methods, move them to a action_helper.h class similar to
diff --git a/extensions/common/api/feedback_private.idl b/extensions/common/api/feedback_private.idl index 3ae66ff..8ca3d7db 100644 --- a/extensions/common/api/feedback_private.idl +++ b/extensions/common/api/feedback_private.idl
@@ -160,6 +160,9 @@ uptime }; + // Source of the feedback. + enum FeedbackSource {quickoffice}; + // Input parameters for a readLogSource() call. dictionary ReadLogSourceParams { // The log source from which to read. @@ -208,6 +211,9 @@ // Returns the system information dictionary. static void getSystemInformation(GetSystemInformationCallback callback); + // Opens the feedback report window. + static void openFeedback(FeedbackSource source); + // Sends a feedback report. // |loadSystemInfo|: Optional flag when present and is true, the backend // should load system information before sending the report. This is added
diff --git a/extensions/common/api/sockets/sockets_manifest_permission_unittest.cc b/extensions/common/api/sockets/sockets_manifest_permission_unittest.cc index b6808ca..c35d559 100644 --- a/extensions/common/api/sockets/sockets_manifest_permission_unittest.cc +++ b/extensions/common/api/sockets/sockets_manifest_permission_unittest.cc
@@ -40,11 +40,10 @@ EXPECT_EQ(0u, permission->entries().size()); } -static std::unique_ptr<base::Value> ParsePermissionJSON( - const std::string& json) { - std::unique_ptr<base::Value> result(base::JSONReader::ReadDeprecated(json)); +static base::Value ParsePermissionJSON(const std::string& json) { + absl::optional<base::Value> result = base::JSONReader::Read(json); EXPECT_TRUE(result) << "Invalid JSON string: " << json; - return result; + return std::move(result.value()); } static std::unique_ptr<SocketsManifestPermission> PermissionFromValue( @@ -58,8 +57,8 @@ static std::unique_ptr<SocketsManifestPermission> PermissionFromJSON( const std::string& json) { - std::unique_ptr<base::Value> value(ParsePermissionJSON(json)); - return PermissionFromValue(*value); + base::Value value = ParsePermissionJSON(json); + return PermissionFromValue(value); } struct CheckFormatEntry { @@ -270,34 +269,31 @@ } TEST(SocketsManifestPermissionTest, FromToValue) { - std::unique_ptr<base::Value> udp_send( - ParsePermissionJSON(kUdpBindPermission)); - std::unique_ptr<base::Value> udp_bind( - ParsePermissionJSON(kUdpSendPermission)); - std::unique_ptr<base::Value> tcp_connect( - ParsePermissionJSON(kTcpConnectPermission)); - std::unique_ptr<base::Value> tcp_server_listen( - ParsePermissionJSON(kTcpServerListenPermission)); + base::Value udp_send = ParsePermissionJSON(kUdpBindPermission); + base::Value udp_bind = ParsePermissionJSON(kUdpSendPermission); + base::Value tcp_connect = ParsePermissionJSON(kTcpConnectPermission); + base::Value tcp_server_listen = + ParsePermissionJSON(kTcpServerListenPermission); // FromValue() std::unique_ptr<SocketsManifestPermission> permission1( new SocketsManifestPermission()); - EXPECT_TRUE(permission1->FromValue(udp_send.get())); + EXPECT_TRUE(permission1->FromValue(&udp_send)); EXPECT_EQ(2u, permission1->entries().size()); std::unique_ptr<SocketsManifestPermission> permission2( new SocketsManifestPermission()); - EXPECT_TRUE(permission2->FromValue(udp_bind.get())); + EXPECT_TRUE(permission2->FromValue(&udp_bind)); EXPECT_EQ(2u, permission2->entries().size()); std::unique_ptr<SocketsManifestPermission> permission3( new SocketsManifestPermission()); - EXPECT_TRUE(permission3->FromValue(tcp_connect.get())); + EXPECT_TRUE(permission3->FromValue(&tcp_connect)); EXPECT_EQ(2u, permission3->entries().size()); std::unique_ptr<SocketsManifestPermission> permission4( new SocketsManifestPermission()); - EXPECT_TRUE(permission4->FromValue(tcp_server_listen.get())); + EXPECT_TRUE(permission4->FromValue(&tcp_server_listen)); EXPECT_EQ(2u, permission4->entries().size()); // ToValue()
diff --git a/extensions/common/manifest_handlers/extension_action_handler.cc b/extensions/common/manifest_handlers/extension_action_handler.cc index 59444530..e890beca 100644 --- a/extensions/common/manifest_handlers/extension_action_handler.cc +++ b/extensions/common/manifest_handlers/extension_action_handler.cc
@@ -16,10 +16,9 @@ #include "extensions/common/image_util.h" #include "extensions/common/manifest_constants.h" -// Adds `extensions::InstallWarning`s to an `extensions::Extension` if the -// `default_popup` value for the action is not same origin, or doesn't -// exist in the filesystem. -void SetWarningsForInvalidDefaultPopup( +// Adds `extensions::InstallWarning`s to `warnings` if the`default_popup` value +// for the action doesn't exist in the filesystem. +void SetWarningsForNonExistentDefaultPopup( const extensions::ActionInfo* action, const char* manifest_key, const extensions::Extension* extension, @@ -35,13 +34,7 @@ base::FilePath resource_path = extension->GetResource(relative_path).GetFilePath(); - // Check popup is only for this extension. - if (!extension->origin().IsSameOriginWith(default_popup_url)) { - warnings->push_back(extensions::InstallWarning( - extensions::manifest_errors::kInvalidExtensionOriginPopup, manifest_key, - extensions::manifest_keys::kActionDefaultPopup)); - // Check that the popup file actually exists on filesystem. - } else if (resource_path.empty() || !base::PathExists(resource_path)) { + if (resource_path.empty() || !base::PathExists(resource_path)) { warnings->push_back(extensions::InstallWarning( extensions::manifest_errors::kNonexistentDefaultPopup, manifest_key, extensions::manifest_keys::kActionDefaultPopup)); @@ -94,8 +87,10 @@ return false; } + std::vector<InstallWarning> install_warnings; std::unique_ptr<ActionInfo> action_info = - ActionInfo::Load(extension, type, dict, error); + ActionInfo::Load(extension, type, dict, &install_warnings, error); + extension->AddInstallWarnings(std::move(install_warnings)); if (!action_info) return false; // Failed to parse extension action definition. @@ -137,7 +132,8 @@ ActionInfo::GetManifestKeyForActionType(action->type); DCHECK(manifest_key); - SetWarningsForInvalidDefaultPopup(action, manifest_key, extension, warnings); + SetWarningsForNonExistentDefaultPopup(action, manifest_key, extension, + warnings); // Empty default icon is valid. if (action->default_icon.empty())
diff --git a/extensions/shell/browser/api/feedback_private/shell_feedback_private_delegate.cc b/extensions/shell/browser/api/feedback_private/shell_feedback_private_delegate.cc index 78c9a396..3a652f6 100644 --- a/extensions/shell/browser/api/feedback_private/shell_feedback_private_delegate.cc +++ b/extensions/shell/browser/api/feedback_private/shell_feedback_private_delegate.cc
@@ -77,4 +77,10 @@ return feedback::FeedbackUploaderFactory::GetForBrowserContext(context); } +void ShellFeedbackPrivateDelegate::OpenFeedback( + content::BrowserContext* context, + api::feedback_private::FeedbackSource source) const { + NOTIMPLEMENTED(); +} + } // namespace extensions
diff --git a/extensions/shell/browser/api/feedback_private/shell_feedback_private_delegate.h b/extensions/shell/browser/api/feedback_private/shell_feedback_private_delegate.h index eb002a28..3135843 100644 --- a/extensions/shell/browser/api/feedback_private/shell_feedback_private_delegate.h +++ b/extensions/shell/browser/api/feedback_private/shell_feedback_private_delegate.h
@@ -42,6 +42,9 @@ void NotifyFeedbackDelayed() const override; feedback::FeedbackUploader* GetFeedbackUploaderForContext( content::BrowserContext* context) const override; + void OpenFeedback( + content::BrowserContext* context, + api::feedback_private::FeedbackSource source) const override; }; } // namespace extensions
diff --git a/fuchsia_web/runners/cast/cast_component.cc b/fuchsia_web/runners/cast/cast_component.cc index 927f1cd..c79a8aa 100644 --- a/fuchsia_web/runners/cast/cast_component.cc +++ b/fuchsia_web/runners/cast/cast_component.cc
@@ -195,8 +195,13 @@ fuchsia::web::ContentAreaSettings settings; // Disable scrollbars on all Cast applications. settings.set_hide_scrollbars(true); - // Get the theme from the system service. - settings.set_theme(fuchsia::settings::ThemeType::DEFAULT); + + // Get the theme from `fuchsia.settings.Display`, except in headless mode + // where the service may not be available. + if (!is_headless_) { + settings.set_theme(fuchsia::settings::ThemeType::DEFAULT); + } + frame()->SetContentAreaSettings(std::move(settings)); }
diff --git a/gpu/command_buffer/service/skia_utils.cc b/gpu/command_buffer/service/skia_utils.cc index 2b4f0c958..9920232a 100644 --- a/gpu/command_buffer/service/skia_utils.cc +++ b/gpu/command_buffer/service/skia_utils.cc
@@ -107,12 +107,22 @@ const gles2::FeatureInfo* feature_info, viz::ResourceFormat resource_format, sk_sp<GrContextThreadSafeProxy> gr_context_thread_safe) { + GLenum gl_storage_format = viz::TextureStorageFormat( + resource_format, + feature_info->feature_flags().angle_rgbx_internal_format); + return GetGrGLBackendTextureFormat(feature_info, gl_storage_format, + gr_context_thread_safe); +} + +GLuint GetGrGLBackendTextureFormat( + const gles2::FeatureInfo* feature_info, + GLenum gl_storage_format, + sk_sp<GrContextThreadSafeProxy> gr_context_thread_safe) { + // TODO(hitawala): Internalize the skia version specifics to a + // SharedImageFormat util function after getting the TextureStorageFormat. const gl::GLVersionInfo* version_info = &feature_info->gl_version_info(); - GLuint internal_format = gl::GetInternalFormat( - version_info, - viz::TextureStorageFormat( - resource_format, - feature_info->feature_flags().angle_rgbx_internal_format)); + GLuint internal_format = + gl::GetInternalFormat(version_info, gl_storage_format); bool use_version_es2 = false; #if BUILDFLAG(IS_ANDROID) @@ -135,7 +145,7 @@ } // Map ETC1 to ETC2 type depending on conversion by skia - if (resource_format == viz::ResourceFormat::ETC1) { + if (gl_storage_format == GL_ETC1_RGB8_OES) { GrGLFormat gr_gl_format = gr_context_thread_safe ->compressedBackendFormat(SkImage::kETC1_CompressionType) @@ -163,6 +173,21 @@ viz::ResourceFormat resource_format, sk_sp<GrContextThreadSafeProxy> gr_context_thread_safe, GrBackendTexture* gr_texture) { + GLenum gl_storage_format = viz::TextureStorageFormat( + resource_format, + feature_info->feature_flags().angle_rgbx_internal_format); + return GetGrBackendTexture(feature_info, target, size, service_id, + gl_storage_format, gr_context_thread_safe, + gr_texture); +} + +bool GetGrBackendTexture(const gles2::FeatureInfo* feature_info, + GLenum target, + const gfx::Size& size, + GLuint service_id, + GLenum gl_storage_format, + sk_sp<GrContextThreadSafeProxy> gr_context_thread_safe, + GrBackendTexture* gr_texture) { if (target != GL_TEXTURE_2D && target != GL_TEXTURE_RECTANGLE_ARB && target != GL_TEXTURE_EXTERNAL_OES) { LOG(ERROR) << "GetGrBackendTexture: invalid texture target."; @@ -173,7 +198,7 @@ texture_info.fID = service_id; texture_info.fTarget = target; texture_info.fFormat = GetGrGLBackendTextureFormat( - feature_info, resource_format, gr_context_thread_safe); + feature_info, gl_storage_format, gr_context_thread_safe); *gr_texture = GrBackendTexture(size.width(), size.height(), GrMipMapped::kNo, texture_info); return true;
diff --git a/gpu/command_buffer/service/skia_utils.h b/gpu/command_buffer/service/skia_utils.h index 70631f53..8a580fad 100644 --- a/gpu/command_buffer/service/skia_utils.h +++ b/gpu/command_buffer/service/skia_utils.h
@@ -51,12 +51,31 @@ GPU_GLES2_EXPORT GrContextOptions GetDefaultGrContextOptions(GrContextType type); -// Returns internal gl format of texture for Skia +// Returns internal gl format of texture for Skia for given `resource_format`. +// NOTE: This is being deprecated. Use below function with gl_storage_format. GPU_GLES2_EXPORT GLuint GetGrGLBackendTextureFormat( const gles2::FeatureInfo* feature_info, viz::ResourceFormat resource_format, sk_sp<GrContextThreadSafeProxy> gr_context_thread_safe); +// Returns internal gl format of texture for Skia for given `gl_storage_format`. +GPU_GLES2_EXPORT GLuint GetGrGLBackendTextureFormat( + const gles2::FeatureInfo* feature_info, + GLenum gl_storage_format, + sk_sp<GrContextThreadSafeProxy> gr_context_thread_safe); + +// Creates a GrBackendTexture from a service ID. Skia does not take ownership. +// Returns true on success. +// NOTE: This is being deprecated. Use below function with gl_storage_format. +GPU_GLES2_EXPORT bool GetGrBackendTexture( + const gles2::FeatureInfo* feature_info, + GLenum target, + const gfx::Size& size, + GLuint service_id, + viz::ResourceFormat resource_format, + sk_sp<GrContextThreadSafeProxy> gr_context_thread_safe, + GrBackendTexture* gr_texture); + // Creates a GrBackendTexture from a service ID. Skia does not take ownership. // Returns true on success. GPU_GLES2_EXPORT bool GetGrBackendTexture( @@ -64,7 +83,7 @@ GLenum target, const gfx::Size& size, GLuint service_id, - viz::ResourceFormat resource_format, + GLenum gl_storage_format, sk_sp<GrContextThreadSafeProxy> gr_context_thread_safe, GrBackendTexture* gr_texture);
diff --git a/headless/app/headless_shell.cc b/headless/app/headless_shell.cc index e48031d1..23e4d5b 100644 --- a/headless/app/headless_shell.cc +++ b/headless/app/headless_shell.cc
@@ -58,9 +58,15 @@ namespace { -// Default file name for screenshot. Can be overriden by "--screenshot" switch. +#if BUILDFLAG(IS_WIN) +const wchar_t kAboutBlank[] = L"about:blank"; +#else +const char kAboutBlank[] = "about:blank"; +#endif + +// Default file name for screenshot. Can be overridden by "--screenshot" switch. const char kDefaultScreenshotFileName[] = "screenshot.png"; -// Default file name for pdf. Can be overriden by "--print-to-pdf" switch. +// Default file name for pdf. Can be overridden by "--print-to-pdf" switch. const char kDefaultPDFFileName[] = "output.pdf"; GURL ConvertArgumentToURL(const base::CommandLine::StringType& arg) { @@ -136,15 +142,9 @@ base::CommandLine::ForCurrentProcess()->GetArgs(); // If no explicit URL is present, navigate to about:blank, unless we're being - // driven by debugger. - if (args.empty() && !base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kRemoteDebuggingPipe)) { -#if BUILDFLAG(IS_WIN) - args.push_back(L"about:blank"); -#else - args.push_back("about:blank"); -#endif - } + // driven by a debugger. + if (args.empty() && !IsRemoteDebuggingEnabled()) + args.push_back(kAboutBlank); if (!args.empty()) { file_task_runner_->PostTaskAndReplyWithResult(
diff --git a/infra/config/generated/builders/ci/Linux MSan Focal/properties.json b/infra/config/generated/builders/ci/Linux MSan Focal/properties.json deleted file mode 100644 index 5fc9bed..0000000 --- a/infra/config/generated/builders/ci/Linux MSan Focal/properties.json +++ /dev/null
@@ -1,58 +0,0 @@ -{ - "$build/chromium_tests_builder_config": { - "builder_config": { - "builder_db": { - "entries": [ - { - "builder_id": { - "bucket": "ci", - "builder": "Linux MSan Focal", - "project": "chromium" - }, - "builder_spec": { - "builder_group": "chromium.fyi", - "execution_mode": "COMPILE_AND_TEST", - "legacy_chromium_config": { - "apply_configs": [ - "mb" - ], - "build_config": "Release", - "config": "chromium_msan" - }, - "legacy_gclient_config": { - "config": "chromium" - } - } - } - ] - }, - "builder_ids": [ - { - "bucket": "ci", - "builder": "Linux MSan Focal", - "project": "chromium" - } - ], - "mirroring_builder_group_and_names": [ - { - "builder": "linux_chromium_msan_focal", - "group": "tryserver.chromium.linux" - } - ] - } - }, - "$build/reclient": { - "instance": "rbe-chromium-trusted", - "jobs": 500, - "metrics_project": "chromium-reclient-metrics" - }, - "$recipe_engine/resultdb/test_presentation": { - "column_keys": [], - "grouping_keys": [ - "status", - "v.test_suite" - ] - }, - "builder_group": "chromium.fyi", - "recipe": "chromium" -} \ No newline at end of file
diff --git a/infra/config/generated/builders/ci/linux-lacros-tester-rel-reviver/properties.json b/infra/config/generated/builders/ci/linux-lacros-tester-rel-reviver/properties.json deleted file mode 100644 index feb8841..0000000 --- a/infra/config/generated/builders/ci/linux-lacros-tester-rel-reviver/properties.json +++ /dev/null
@@ -1,64 +0,0 @@ -{ - "$build/chromium_tests_builder_config": { - "builder_config": { - "builder_db": { - "entries": [ - { - "builder_id": { - "bucket": "ci", - "builder": "linux-lacros-tester-rel-reviver", - "project": "chromium" - }, - "builder_spec": { - "build_gs_bucket": "chromium-chromiumos-archive", - "builder_group": "chromium.fyi", - "execution_mode": "COMPILE_AND_TEST", - "legacy_chromium_config": { - "apply_configs": [ - "mb" - ], - "build_config": "Release", - "config": "chromium", - "target_arch": "intel", - "target_bits": 64 - }, - "legacy_gclient_config": { - "apply_configs": [ - "chromeos" - ], - "config": "chromium_no_telemetry_dependencies" - } - } - } - ] - }, - "builder_ids": [ - { - "bucket": "ci", - "builder": "linux-lacros-tester-rel-reviver", - "project": "chromium" - } - ], - "mirroring_builder_group_and_names": [ - { - "builder": "linux-lacros-tester-rel-reviver", - "group": "tryserver.chromium.chromiumos" - } - ] - } - }, - "$build/reclient": { - "instance": "rbe-chromium-trusted", - "jobs": 250, - "metrics_project": "chromium-reclient-metrics" - }, - "$recipe_engine/resultdb/test_presentation": { - "column_keys": [], - "grouping_keys": [ - "status", - "v.test_suite" - ] - }, - "builder_group": "chromium.fyi", - "recipe": "chromium" -} \ No newline at end of file
diff --git a/infra/config/generated/builders/try/fuchsia-arm64-cast-receiver-rel/properties.json b/infra/config/generated/builders/try/fuchsia-arm64-cast-receiver-rel/properties.json index fc64ef6..d8982c4 100644 --- a/infra/config/generated/builders/try/fuchsia-arm64-cast-receiver-rel/properties.json +++ b/infra/config/generated/builders/try/fuchsia-arm64-cast-receiver-rel/properties.json
@@ -43,14 +43,9 @@ ] } }, - "$build/goma": { - "enable_ats": true, - "rpc_extra_params": "?prod", - "server_host": "goma.chromium.org" - }, "$build/reclient": { "instance": "rbe-chromium-untrusted", - "jobs": 150, + "jobs": 300, "metrics_project": "chromium-reclient-metrics" }, "$recipe_engine/resultdb/test_presentation": {
diff --git a/infra/config/generated/builders/try/fuchsia-arm64-chrome-rel/properties.json b/infra/config/generated/builders/try/fuchsia-arm64-chrome-rel/properties.json index f8dc5d5..d5dfc3bc 100644 --- a/infra/config/generated/builders/try/fuchsia-arm64-chrome-rel/properties.json +++ b/infra/config/generated/builders/try/fuchsia-arm64-chrome-rel/properties.json
@@ -51,7 +51,7 @@ }, "$build/reclient": { "instance": "rbe-chromium-untrusted", - "jobs": 150, + "jobs": 300, "metrics_project": "chromium-reclient-metrics" }, "$recipe_engine/resultdb/test_presentation": {
diff --git a/infra/config/generated/builders/try/fuchsia-compile-x64-dbg/properties.json b/infra/config/generated/builders/try/fuchsia-compile-x64-dbg/properties.json index 425f812..7d8cdca 100644 --- a/infra/config/generated/builders/try/fuchsia-compile-x64-dbg/properties.json +++ b/infra/config/generated/builders/try/fuchsia-compile-x64-dbg/properties.json
@@ -42,14 +42,9 @@ "is_compile_only": true } }, - "$build/goma": { - "enable_ats": true, - "rpc_extra_params": "?prod", - "server_host": "goma.chromium.org" - }, "$build/reclient": { "instance": "rbe-chromium-untrusted", - "jobs": 150, + "jobs": 300, "metrics_project": "chromium-reclient-metrics" }, "$recipe_engine/resultdb/test_presentation": {
diff --git a/infra/config/generated/builders/try/fuchsia-fyi-arm64-dbg/properties.json b/infra/config/generated/builders/try/fuchsia-fyi-arm64-dbg/properties.json index 86da2cf..fe36dcb 100644 --- a/infra/config/generated/builders/try/fuchsia-fyi-arm64-dbg/properties.json +++ b/infra/config/generated/builders/try/fuchsia-fyi-arm64-dbg/properties.json
@@ -44,14 +44,9 @@ ] } }, - "$build/goma": { - "enable_ats": true, - "rpc_extra_params": "?prod", - "server_host": "goma.chromium.org" - }, "$build/reclient": { "instance": "rbe-chromium-untrusted", - "jobs": 150, + "jobs": 300, "metrics_project": "chromium-reclient-metrics" }, "$recipe_engine/resultdb/test_presentation": {
diff --git a/infra/config/generated/builders/try/fuchsia-fyi-x64-dbg/properties.json b/infra/config/generated/builders/try/fuchsia-fyi-x64-dbg/properties.json index 25138b23..edeadbf5 100644 --- a/infra/config/generated/builders/try/fuchsia-fyi-x64-dbg/properties.json +++ b/infra/config/generated/builders/try/fuchsia-fyi-x64-dbg/properties.json
@@ -41,14 +41,9 @@ ] } }, - "$build/goma": { - "enable_ats": true, - "rpc_extra_params": "?prod", - "server_host": "goma.chromium.org" - }, "$build/reclient": { "instance": "rbe-chromium-untrusted", - "jobs": 150, + "jobs": 300, "metrics_project": "chromium-reclient-metrics" }, "$recipe_engine/resultdb/test_presentation": {
diff --git a/infra/config/generated/builders/try/fuchsia-official/properties.json b/infra/config/generated/builders/try/fuchsia-official/properties.json index ce10abd..e2c3ae4c 100644 --- a/infra/config/generated/builders/try/fuchsia-official/properties.json +++ b/infra/config/generated/builders/try/fuchsia-official/properties.json
@@ -39,10 +39,10 @@ ] } }, - "$build/goma": { - "enable_ats": true, - "rpc_extra_params": "?prod", - "server_host": "goma.chromium.org" + "$build/reclient": { + "instance": "rbe-chromium-untrusted", + "jobs": 300, + "metrics_project": "chromium-reclient-metrics" }, "$recipe_engine/resultdb/test_presentation": { "column_keys": [],
diff --git a/infra/config/generated/builders/try/fuchsia-x64-cast-receiver-rel-compilator/properties.json b/infra/config/generated/builders/try/fuchsia-x64-cast-receiver-rel-compilator/properties.json index 69e3416a..3c93621 100644 --- a/infra/config/generated/builders/try/fuchsia-x64-cast-receiver-rel-compilator/properties.json +++ b/infra/config/generated/builders/try/fuchsia-x64-cast-receiver-rel-compilator/properties.json
@@ -41,12 +41,6 @@ ] } }, - "$build/goma": { - "enable_ats": true, - "jobs": 150, - "rpc_extra_params": "?prod", - "server_host": "goma.chromium.org" - }, "$build/reclient": { "instance": "rbe-chromium-untrusted", "jobs": 300,
diff --git a/infra/config/generated/builders/try/fuchsia-x64-chrome-rel/properties.json b/infra/config/generated/builders/try/fuchsia-x64-chrome-rel/properties.json index 46d7d88f..b5ea470a 100644 --- a/infra/config/generated/builders/try/fuchsia-x64-chrome-rel/properties.json +++ b/infra/config/generated/builders/try/fuchsia-x64-chrome-rel/properties.json
@@ -49,7 +49,7 @@ }, "$build/reclient": { "instance": "rbe-chromium-untrusted", - "jobs": 150, + "jobs": 300, "metrics_project": "chromium-reclient-metrics" }, "$recipe_engine/resultdb/test_presentation": {
diff --git a/infra/config/generated/builders/try/fuchsia-x64-rel/properties.json b/infra/config/generated/builders/try/fuchsia-x64-rel/properties.json index 7ccfe95b..da8d0a11 100644 --- a/infra/config/generated/builders/try/fuchsia-x64-rel/properties.json +++ b/infra/config/generated/builders/try/fuchsia-x64-rel/properties.json
@@ -48,7 +48,7 @@ }, "$build/reclient": { "instance": "rbe-chromium-untrusted", - "jobs": 150, + "jobs": 300, "metrics_project": "chromium-reclient-metrics" }, "$recipe_engine/resultdb/test_presentation": {
diff --git a/infra/config/generated/builders/try/fuchsia-x64-workstation/properties.json b/infra/config/generated/builders/try/fuchsia-x64-workstation/properties.json index f334520..3ca2c42 100644 --- a/infra/config/generated/builders/try/fuchsia-x64-workstation/properties.json +++ b/infra/config/generated/builders/try/fuchsia-x64-workstation/properties.json
@@ -49,7 +49,7 @@ }, "$build/reclient": { "instance": "rbe-chromium-untrusted", - "jobs": 150, + "jobs": 300, "metrics_project": "chromium-reclient-metrics" }, "$recipe_engine/resultdb/test_presentation": {
diff --git a/infra/config/generated/builders/try/linux-dawn-rel/properties.json b/infra/config/generated/builders/try/linux-dawn-rel/properties.json index 6efce04..a4b14b2 100644 --- a/infra/config/generated/builders/try/linux-dawn-rel/properties.json +++ b/infra/config/generated/builders/try/linux-dawn-rel/properties.json
@@ -114,11 +114,6 @@ ] } }, - "$build/goma": { - "enable_ats": true, - "rpc_extra_params": "?prod", - "server_host": "goma.chromium.org" - }, "$build/reclient": { "instance": "rbe-chromium-untrusted", "jobs": 150,
diff --git a/infra/config/generated/builders/try/linux-lacros-tester-rel-reviver/properties.json b/infra/config/generated/builders/try/linux-lacros-tester-rel-reviver/properties.json deleted file mode 100644 index 57e2e775..0000000 --- a/infra/config/generated/builders/try/linux-lacros-tester-rel-reviver/properties.json +++ /dev/null
@@ -1,63 +0,0 @@ -{ - "$build/chromium_tests_builder_config": { - "builder_config": { - "builder_db": { - "entries": [ - { - "builder_id": { - "bucket": "ci", - "builder": "linux-lacros-tester-rel-reviver", - "project": "chromium" - }, - "builder_spec": { - "build_gs_bucket": "chromium-chromiumos-archive", - "builder_group": "chromium.fyi", - "execution_mode": "COMPILE_AND_TEST", - "legacy_chromium_config": { - "apply_configs": [ - "mb" - ], - "build_config": "Release", - "config": "chromium", - "target_arch": "intel", - "target_bits": 64 - }, - "legacy_gclient_config": { - "apply_configs": [ - "chromeos" - ], - "config": "chromium_no_telemetry_dependencies" - } - } - } - ] - }, - "builder_ids": [ - { - "bucket": "ci", - "builder": "linux-lacros-tester-rel-reviver", - "project": "chromium" - } - ] - } - }, - "$build/goma": { - "enable_ats": true, - "rpc_extra_params": "?prod", - "server_host": "goma.chromium.org" - }, - "$build/reclient": { - "instance": "rbe-chromium-untrusted", - "jobs": 150, - "metrics_project": "chromium-reclient-metrics" - }, - "$recipe_engine/resultdb/test_presentation": { - "column_keys": [], - "grouping_keys": [ - "status", - "v.test_suite" - ] - }, - "builder_group": "tryserver.chromium.chromiumos", - "recipe": "chromium_trybot" -} \ No newline at end of file
diff --git a/infra/config/generated/builders/try/linux_chromium_msan_focal/properties.json b/infra/config/generated/builders/try/linux_chromium_msan_focal/properties.json deleted file mode 100644 index 79e84aff..0000000 --- a/infra/config/generated/builders/try/linux_chromium_msan_focal/properties.json +++ /dev/null
@@ -1,52 +0,0 @@ -{ - "$build/chromium_tests_builder_config": { - "builder_config": { - "builder_db": { - "entries": [ - { - "builder_id": { - "bucket": "ci", - "builder": "Linux MSan Focal", - "project": "chromium" - }, - "builder_spec": { - "builder_group": "chromium.fyi", - "execution_mode": "COMPILE_AND_TEST", - "legacy_chromium_config": { - "apply_configs": [ - "mb" - ], - "build_config": "Release", - "config": "chromium_msan" - }, - "legacy_gclient_config": { - "config": "chromium" - } - } - } - ] - }, - "builder_ids": [ - { - "bucket": "ci", - "builder": "Linux MSan Focal", - "project": "chromium" - } - ] - } - }, - "$build/reclient": { - "instance": "rbe-chromium-untrusted", - "jobs": 300, - "metrics_project": "chromium-reclient-metrics" - }, - "$recipe_engine/resultdb/test_presentation": { - "column_keys": [], - "grouping_keys": [ - "status", - "v.test_suite" - ] - }, - "builder_group": "tryserver.chromium.linux", - "recipe": "chromium_trybot" -} \ No newline at end of file
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg index 0721a769..e123a0a 100644 --- a/infra/config/generated/luci/commit-queue.cfg +++ b/infra/config/generated/luci/commit-queue.cfg
@@ -2606,10 +2606,6 @@ includable_only: true } builders { - name: "chromium/try/linux-lacros-tester-rel-reviver" - includable_only: true - } - builders { name: "chromium/try/linux-lacros-version-skew-fyi" includable_only: true } @@ -2974,10 +2970,6 @@ } } builders { - name: "chromium/try/linux_chromium_msan_focal" - includable_only: true - } - builders { name: "chromium/try/linux_chromium_msan_rel_ng" includable_only: true }
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg index 195594a..0281c56 100644 --- a/infra/config/generated/luci/cr-buildbucket.cfg +++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -13839,7 +13839,7 @@ dimensions: "cores:8" dimensions: "cpu:x86-64" dimensions: "free_space:standard" - dimensions: "os:Ubuntu-18.04" + dimensions: "os:Ubuntu-20.04" dimensions: "pool:luci.chromium.ci" dimensions: "ssd:1" exe { @@ -13924,102 +13924,13 @@ } } builders { - name: "Linux MSan Focal" - swarming_host: "chromium-swarm.appspot.com" - dimensions: "builderless:1" - dimensions: "cores:8" - dimensions: "cpu:x86-64" - dimensions: "free_space:standard" - dimensions: "os:Ubuntu-20.04" - dimensions: "pool:luci.chromium.ci" - dimensions: "ssd:0" - exe { - cipd_package: "infra/chromium/bootstrapper/${platform}" - cipd_version: "latest" - cmd: "bootstrapper" - } - properties: - '{' - ' "$bootstrap/exe": {' - ' "exe": {' - ' "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",' - ' "cipd_version": "refs/heads/main",' - ' "cmd": [' - ' "luciexe"' - ' ]' - ' }' - ' },' - ' "$bootstrap/properties": {' - ' "properties_file": "infra/config/generated/builders/ci/Linux MSan Focal/properties.json",' - ' "top_level_project": {' - ' "ref": "refs/heads/main",' - ' "repo": {' - ' "host": "chromium.googlesource.com",' - ' "project": "chromium/src"' - ' }' - ' }' - ' },' - ' "builder_group": "chromium.fyi",' - ' "led_builder_is_bootstrapped": true,' - ' "recipe": "chromium"' - '}' - priority: 35 - execution_timeout_secs: 57600 - build_numbers: YES - service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com" - experiments { - key: "chromium_swarming.expose_merge_script_failures" - value: 100 - } - experiments { - key: "luci.buildbucket.omit_python2" - value: 100 - } - experiments { - key: "luci.recipes.use_python3" - value: 100 - } - resultdb { - enable: true - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "ci_test_results" - test_results {} - } - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "gpu_ci_test_results" - test_results { - predicate { - test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+" - } - } - } - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "blink_web_tests_ci_test_results" - test_results { - predicate { - test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*blink_wpt_tests/.+)" - } - } - } - history_options { - use_invocation_timestamp: true - } - } - } - builders { name: "Linux MSan Tests" swarming_host: "chromium-swarm.appspot.com" dimensions: "builderless:1" dimensions: "cores:8" dimensions: "cpu:x86-64" dimensions: "free_space:standard" - dimensions: "os:Ubuntu-18.04" + dimensions: "os:Ubuntu-20.04" dimensions: "pool:luci.chromium.ci" dimensions: "ssd:0" exe { @@ -26371,6 +26282,34 @@ } properties: '{' + ' "$build/avd_packager": {' + ' "avd_configs": [' + ' "tools/android/avd/proto/creation/generic_android19.textpb",' + ' "tools/android/avd/proto/creation/generic_android22.textpb",' + ' "tools/android/avd/proto/creation/generic_android23.textpb",' + ' "tools/android/avd/proto/creation/generic_android24.textpb",' + ' "tools/android/avd/proto/creation/generic_playstore_android24.textpb",' + ' "tools/android/avd/proto/creation/generic_android25.textpb",' + ' "tools/android/avd/proto/creation/generic_playstore_android25.textpb",' + ' "tools/android/avd/proto/creation/generic_android27.textpb",' + ' "tools/android/avd/proto/creation/generic_playstore_android27.textpb",' + ' "tools/android/avd/proto/creation/generic_android28.textpb",' + ' "tools/android/avd/proto/creation/generic_playstore_android28.textpb",' + ' "tools/android/avd/proto/creation/generic_android29.textpb",' + ' "tools/android/avd/proto/creation/generic_android30.textpb",' + ' "tools/android/avd/proto/creation/generic_playstore_android30.textpb",' + ' "tools/android/avd/proto/creation/generic_android31.textpb",' + ' "tools/android/avd/proto/creation/generic_playstore_android31.textpb",' + ' "tools/android/avd/proto/creation/generic_android32_foldable.textpb",' + ' "tools/android/avd/proto/creation/generic_playstore_android32_foldable.textpb",' + ' "tools/android/avd/proto/creation/generic_android33.textpb",' + ' "tools/android/avd/proto/creation/generic_playstore_android33.textpb"' + ' ],' + ' "gclient_apply_config": [' + ' "android"' + ' ],' + ' "gclient_config": "chromium"' + ' },' ' "$recipe_engine/resultdb/test_presentation": {' ' "column_keys": [],' ' "grouping_keys": [' @@ -39788,95 +39727,6 @@ } } builders { - name: "linux-lacros-tester-rel-reviver" - swarming_host: "chromium-swarm.appspot.com" - dimensions: "builderless:1" - dimensions: "cores:8" - dimensions: "cpu:x86-64" - dimensions: "free_space:standard" - dimensions: "os:Ubuntu-18.04" - dimensions: "pool:luci.chromium.ci" - dimensions: "ssd:0" - exe { - cipd_package: "infra/chromium/bootstrapper/${platform}" - cipd_version: "latest" - cmd: "bootstrapper" - } - properties: - '{' - ' "$bootstrap/exe": {' - ' "exe": {' - ' "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",' - ' "cipd_version": "refs/heads/main",' - ' "cmd": [' - ' "luciexe"' - ' ]' - ' }' - ' },' - ' "$bootstrap/properties": {' - ' "properties_file": "infra/config/generated/builders/ci/linux-lacros-tester-rel-reviver/properties.json",' - ' "top_level_project": {' - ' "ref": "refs/heads/main",' - ' "repo": {' - ' "host": "chromium.googlesource.com",' - ' "project": "chromium/src"' - ' }' - ' }' - ' },' - ' "builder_group": "chromium.fyi",' - ' "led_builder_is_bootstrapped": true,' - ' "recipe": "chromium"' - '}' - priority: 35 - execution_timeout_secs: 36000 - build_numbers: YES - service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com" - experiments { - key: "chromium_swarming.expose_merge_script_failures" - value: 100 - } - experiments { - key: "luci.buildbucket.omit_python2" - value: 100 - } - experiments { - key: "luci.recipes.use_python3" - value: 100 - } - resultdb { - enable: true - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "ci_test_results" - test_results {} - } - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "gpu_ci_test_results" - test_results { - predicate { - test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+" - } - } - } - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "blink_web_tests_ci_test_results" - test_results { - predicate { - test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*blink_wpt_tests/.+)" - } - } - } - history_options { - use_invocation_timestamp: true - } - } - } - builders { name: "linux-lacros-version-skew-fyi" swarming_host: "chromium-swarm.appspot.com" dimensions: "builderless:1" @@ -54821,6 +54671,53 @@ } } builders { + name: "lacros-coordinator" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "builderless:1" + dimensions: "os:Ubuntu-18.04" + dimensions: "pool:luci.chromium.ci" + dimensions: "ssd:0" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "recipe": "chromium_polymorphic/launcher",' + ' "runner_builder": {' + ' "bucket": "reviver",' + ' "builder": "runner",' + ' "project": "chromium"' + ' },' + ' "target_builders": [' + ' {' + ' "builder_id": {' + ' "bucket": "ci",' + ' "builder": "linux-lacros-tester-rel",' + ' "project": "chromium"' + ' }' + ' }' + ' ]' + '}' + service_account: "reviver-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.buildbucket.omit_python2" + value: 0 + } + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { name: "runner" swarming_host: "chromium-swarm.appspot.com" dimensions: "builderless:1" @@ -65385,15 +65282,9 @@ ' "fuchsia_sizes"' ' ]' ' },' - ' "$build/goma": {' - ' "enable_ats": true,' - ' "jobs": 150,' - ' "rpc_extra_params": "?prod",' - ' "server_host": "goma.chromium.org"' - ' },' ' "$build/reclient": {' ' "instance": "rbe-chromium-untrusted",' - ' "jobs": 150,' + ' "jobs": 300,' ' "metrics_project": "chromium-reclient-metrics"' ' },' ' "$recipe_engine/resultdb/test_presentation": {' @@ -65493,12 +65384,6 @@ } properties: '{' - ' "$build/goma": {' - ' "enable_ats": true,' - ' "jobs": 150,' - ' "rpc_extra_params": "?prod",' - ' "server_host": "goma.chromium.org"' - ' },' ' "$build/reclient": {' ' "instance": "rbe-chromium-untrusted",' ' "jobs": 150,' @@ -65711,14 +65596,9 @@ } properties: '{' - ' "$build/goma": {' - ' "enable_ats": true,' - ' "rpc_extra_params": "?prod",' - ' "server_host": "goma.chromium.org"' - ' },' ' "$build/reclient": {' ' "instance": "rbe-chromium-untrusted",' - ' "jobs": 150,' + ' "jobs": 300,' ' "metrics_project": "chromium-reclient-metrics"' ' },' ' "$recipe_engine/resultdb/test_presentation": {' @@ -78799,116 +78679,6 @@ description_html: "This is the compilator half of an orchestrator + compilator pair of builders. The orchestrator is <a href=\"https://ci.chromium.org/p/chromium/builders/try/linux-lacros-rel\">linux-lacros-rel</a>." } builders { - name: "linux-lacros-tester-rel-reviver" - swarming_host: "chromium-swarm.appspot.com" - dimensions: "builderless:1" - dimensions: "cores:8" - dimensions: "cpu:x86-64" - dimensions: "os:Ubuntu-18.04" - dimensions: "pool:luci.chromium.try" - dimensions: "ssd:0" - exe { - cipd_package: "infra/chromium/bootstrapper/${platform}" - cipd_version: "latest" - cmd: "bootstrapper" - } - properties: - '{' - ' "$bootstrap/exe": {' - ' "exe": {' - ' "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",' - ' "cipd_version": "refs/heads/main",' - ' "cmd": [' - ' "luciexe"' - ' ]' - ' }' - ' },' - ' "$bootstrap/properties": {' - ' "properties_file": "infra/config/generated/builders/try/linux-lacros-tester-rel-reviver/properties.json",' - ' "top_level_project": {' - ' "ref": "refs/heads/main",' - ' "repo": {' - ' "host": "chromium.googlesource.com",' - ' "project": "chromium/src"' - ' }' - ' }' - ' },' - ' "builder_group": "tryserver.chromium.chromiumos",' - ' "led_builder_is_bootstrapped": true,' - ' "recipe": "chromium_trybot"' - '}' - execution_timeout_secs: 14400 - expiration_secs: 7200 - grace_period { - seconds: 120 - } - caches { - name: "win_toolchain" - path: "win_toolchain" - } - build_numbers: YES - service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com" - task_template_canary_percentage { - value: 5 - } - experiments { - key: "chromium_swarming.expose_merge_script_failures" - value: 100 - } - experiments { - key: "enable_weetbix_queries" - value: 100 - } - experiments { - key: "luci.buildbucket.omit_python2" - value: 100 - } - experiments { - key: "luci.recipes.use_python3" - value: 100 - } - experiments { - key: "weetbix.enable_weetbix_exonerations" - value: 100 - } - experiments { - key: "weetbix.retry_weak_exonerations" - value: 100 - } - resultdb { - enable: true - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "try_test_results" - test_results {} - } - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "gpu_try_test_results" - test_results { - predicate { - test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+" - } - } - } - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "blink_web_tests_try_test_results" - test_results { - predicate { - test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*blink_wpt_tests/.+)" - } - } - } - history_options { - use_invocation_timestamp: true - } - } - } - builders { name: "linux-lacros-version-skew-fyi" swarming_host: "chromium-swarm.appspot.com" dimensions: "builderless:1" @@ -83600,122 +83370,12 @@ } } builders { - name: "linux_chromium_msan_focal" - swarming_host: "chromium-swarm.appspot.com" - dimensions: "builderless:1" - dimensions: "cores:8" - dimensions: "cpu:x86-64" - dimensions: "os:Ubuntu-20.04" - dimensions: "pool:luci.chromium.try" - dimensions: "ssd:0" - exe { - cipd_package: "infra/chromium/bootstrapper/${platform}" - cipd_version: "latest" - cmd: "bootstrapper" - } - properties: - '{' - ' "$bootstrap/exe": {' - ' "exe": {' - ' "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",' - ' "cipd_version": "refs/heads/main",' - ' "cmd": [' - ' "luciexe"' - ' ]' - ' }' - ' },' - ' "$bootstrap/properties": {' - ' "properties_file": "infra/config/generated/builders/try/linux_chromium_msan_focal/properties.json",' - ' "top_level_project": {' - ' "ref": "refs/heads/main",' - ' "repo": {' - ' "host": "chromium.googlesource.com",' - ' "project": "chromium/src"' - ' }' - ' }' - ' },' - ' "builder_group": "tryserver.chromium.linux",' - ' "led_builder_is_bootstrapped": true,' - ' "recipe": "chromium_trybot"' - '}' - execution_timeout_secs: 57600 - expiration_secs: 7200 - grace_period { - seconds: 120 - } - caches { - name: "win_toolchain" - path: "win_toolchain" - } - build_numbers: YES - service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com" - task_template_canary_percentage { - value: 5 - } - experiments { - key: "chromium_swarming.expose_merge_script_failures" - value: 100 - } - experiments { - key: "enable_weetbix_queries" - value: 100 - } - experiments { - key: "luci.buildbucket.omit_python2" - value: 100 - } - experiments { - key: "luci.recipes.use_python3" - value: 100 - } - experiments { - key: "weetbix.enable_weetbix_exonerations" - value: 100 - } - experiments { - key: "weetbix.retry_weak_exonerations" - value: 100 - } - resultdb { - enable: true - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "try_test_results" - test_results {} - } - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "gpu_try_test_results" - test_results { - predicate { - test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+" - } - } - } - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "blink_web_tests_try_test_results" - test_results { - predicate { - test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*blink_wpt_tests/.+)" - } - } - } - history_options { - use_invocation_timestamp: true - } - } - } - builders { name: "linux_chromium_msan_rel_ng" swarming_host: "chromium-swarm.appspot.com" dimensions: "builderless:1" dimensions: "cores:8" dimensions: "cpu:x86-64" - dimensions: "os:Ubuntu-18.04" + dimensions: "os:Ubuntu-20.04" dimensions: "pool:luci.chromium.try" dimensions: "ssd:0" exe { @@ -89460,11 +89120,6 @@ } properties: '{' - ' "$build/goma": {' - ' "enable_ats": true,' - ' "rpc_extra_params": "?prod",' - ' "server_host": "goma.chromium.org"' - ' },' ' "$build/reclient": {' ' "instance": "rbe-chromium-untrusted",' ' "metrics_project": "chromium-reclient-metrics"' @@ -89566,11 +89221,6 @@ } properties: '{' - ' "$build/goma": {' - ' "enable_ats": true,' - ' "rpc_extra_params": "?prod",' - ' "server_host": "goma.chromium.org"' - ' },' ' "$build/reclient": {' ' "instance": "rbe-chromium-untrusted",' ' "metrics_project": "chromium-reclient-metrics"' @@ -89672,11 +89322,6 @@ } properties: '{' - ' "$build/goma": {' - ' "enable_ats": true,' - ' "rpc_extra_params": "?prod",' - ' "server_host": "goma.chromium.org"' - ' },' ' "$build/reclient": {' ' "instance": "rbe-chromium-untrusted",' ' "metrics_project": "chromium-reclient-metrics"'
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg index 8efe6d3b..334dd62 100644 --- a/infra/config/generated/luci/luci-milo.cfg +++ b/infra/config/generated/luci/luci-milo.cfg
@@ -2701,9 +2701,6 @@ name: "buildbucket/luci.chromium.try/linux-lacros-rel-compilator" } builders { - name: "buildbucket/luci.chromium.try/linux-lacros-tester-rel-reviver" - } - builders { name: "buildbucket/luci.chromium.try/linux-libfuzzer-asan-rel" } builders { @@ -8716,11 +8713,6 @@ category: "default" } builders { - name: "buildbucket/luci.chromium.ci/linux-lacros-tester-rel-reviver" - category: "default" - short_name: "rev" - } - builders { name: "buildbucket/luci.chromium.ci/Comparison ios (reclient)" category: "ios" short_name: "cmp" @@ -8759,11 +8751,6 @@ short_name: "crs" } builders { - name: "buildbucket/luci.chromium.ci/Linux MSan Focal" - category: "msan" - short_name: "lin" - } - builders { name: "buildbucket/luci.chromium.ci/linux-upload-perfetto" category: "perfetto" short_name: "lnx" @@ -16842,9 +16829,6 @@ name: "buildbucket/luci.chromium.try/linux-lacros-rel-compilator" } builders { - name: "buildbucket/luci.chromium.try/linux-lacros-tester-rel-reviver" - } - builders { name: "buildbucket/luci.chromium.try/linux-lacros-version-skew-fyi" } builders { @@ -16971,9 +16955,6 @@ name: "buildbucket/luci.chromium.try/linux_chromium_dbg_ng" } builders { - name: "buildbucket/luci.chromium.try/linux_chromium_msan_focal" - } - builders { name: "buildbucket/luci.chromium.try/linux_chromium_msan_rel_ng" } builders { @@ -17268,6 +17249,9 @@ name: "buildbucket/luci.chromium.reviver/fuchsia-coordinator" } builders { + name: "buildbucket/luci.chromium.reviver/lacros-coordinator" + } + builders { name: "buildbucket/luci.chromium.reviver/runner" } builder_view_only: true @@ -17672,9 +17656,6 @@ builders { name: "buildbucket/luci.chromium.try/linux-lacros-rel-compilator" } - builders { - name: "buildbucket/luci.chromium.try/linux-lacros-tester-rel-reviver" - } builder_view_only: true } consoles { @@ -17990,9 +17971,6 @@ name: "buildbucket/luci.chromium.try/linux_chromium_dbg_ng" } builders { - name: "buildbucket/luci.chromium.try/linux_chromium_msan_focal" - } - builders { name: "buildbucket/luci.chromium.try/linux_chromium_msan_rel_ng" } builders {
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg index 019490a..769dd14 100644 --- a/infra/config/generated/luci/luci-scheduler.cfg +++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -1739,15 +1739,6 @@ } } job { - id: "Linux MSan Focal" - realm: "ci" - buildbucket { - server: "cr-buildbucket.appspot.com" - bucket: "ci" - builder: "Linux MSan Focal" - } -} -job { id: "Linux MSan Tests" realm: "ci" buildbucket { @@ -4552,6 +4543,16 @@ } } job { + id: "lacros-coordinator" + realm: "reviver" + schedule: "0 3,5,7,9 * * *" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "reviver" + builder: "lacros-coordinator" + } +} +job { id: "lacros64-archive-rel" realm: "ci" buildbucket { @@ -4907,16 +4908,6 @@ } } job { - id: "linux-lacros-tester-rel-reviver" - realm: "ci" - schedule: "0 3,5,7,9 * * *" - buildbucket { - server: "cr-buildbucket.appspot.com" - bucket: "ci" - builder: "linux-lacros-tester-rel-reviver" - } -} -job { id: "linux-lacros-version-skew-fyi" realm: "ci" buildbucket { @@ -5928,7 +5919,6 @@ triggers: "Linux ChromiumOS MSan Focal" triggers: "Linux FYI GPU TSAN Release" triggers: "Linux MSan Builder" - triggers: "Linux MSan Focal" triggers: "Linux TSan Builder" triggers: "Linux Viz" triggers: "MSAN Release (chained origins)" @@ -6120,7 +6110,6 @@ triggers: "linux-lacros-code-coverage" triggers: "linux-lacros-dbg" triggers: "linux-lacros-dbg-fyi" - triggers: "linux-lacros-tester-rel-reviver" triggers: "linux-lacros-version-skew-fyi" triggers: "linux-official" triggers: "linux-perfetto-rel"
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star index c9f191c..c519380 100644 --- a/infra/config/subprojects/chromium/ci/chromium.fyi.star +++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -334,36 +334,6 @@ ) ci.builder( - name = "linux-lacros-tester-rel-reviver", - builder_spec = builder_config.builder_spec( - gclient_config = builder_config.gclient_config( - config = "chromium_no_telemetry_dependencies", - apply_configs = [ - "chromeos", - ], - ), - chromium_config = builder_config.chromium_config( - config = "chromium", - apply_configs = [ - "mb", - ], - build_config = builder_config.build_config.RELEASE, - target_arch = builder_config.target_arch.INTEL, - target_bits = 64, - ), - build_gs_bucket = "chromium-chromiumos-archive", - ), - console_view_entry = consoles.console_view_entry( - category = "default", - short_name = "rev", - ), - os = os.LINUX_DEFAULT, - # To avoid peak hours, we run it from 8PM TO 4AM PST. It is - # 3 AM to 11 AM UTC. - schedule = "0 3,5,7,9 * * *", -) - -ci.builder( name = "linux-lacros-version-skew-fyi", console_view_entry = consoles.console_view_entry( category = "default", @@ -1957,32 +1927,6 @@ ci.builder( # An FYI version of the following builders that runs on Focal: - # https://ci.chromium.org/p/chromium/builders/ci/Linux%20MSan%20Builder - # https://ci.chromium.org/p/chromium/builders/ci/Linux%20MSan%20Tests - # TODO(crbug.com/1260217): Remove this builder when the main MSAN builder - # has migrated to focal. - name = "Linux MSan Focal", - builder_spec = builder_config.builder_spec( - gclient_config = builder_config.gclient_config( - config = "chromium", - ), - chromium_config = builder_config.chromium_config( - config = "chromium_msan", - apply_configs = ["mb"], - build_config = builder_config.build_config.RELEASE, - ), - ), - console_view_entry = consoles.console_view_entry( - category = "msan", - short_name = "lin", - ), - reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CI, - os = os.LINUX_FOCAL, - execution_timeout = 16 * time.hour, -) - -ci.builder( - # An FYI version of the following builders that runs on Focal: # https://ci.chromium.org/p/chromium/builders/ci/Linux%20ChromiumOS%20MSan%20Builder # https://ci.chromium.org/p/chromium/builders/ci/Linux%20ChromiumOS%20MSan%20Tests # TODO(crbug.com/1260217): Remove this builder when the main MSAN builder
diff --git a/infra/config/subprojects/chromium/ci/chromium.memory.star b/infra/config/subprojects/chromium/ci/chromium.memory.star index 950bfdba..5b7ad835 100644 --- a/infra/config/subprojects/chromium/ci/chromium.memory.star +++ b/infra/config/subprojects/chromium/ci/chromium.memory.star
@@ -321,6 +321,7 @@ short_name = "bld", ), ssd = True, + os = os.LINUX_FOCAL, ) linux_memory_builder( @@ -348,6 +349,7 @@ ), reclient_jobs = reclient.jobs.LOW_JOBS_FOR_CI, triggered_by = ["Linux MSan Builder"], + os = os.LINUX_FOCAL, ) linux_memory_builder(
diff --git a/infra/config/subprojects/chromium/ci/chromium.packager.star b/infra/config/subprojects/chromium/ci/chromium.packager.star index 45ec91b..15959bae 100644 --- a/infra/config/subprojects/chromium/ci/chromium.packager.star +++ b/infra/config/subprojects/chromium/ci/chromium.packager.star
@@ -95,6 +95,34 @@ ), executable = "recipe:android/avd_packager", properties = { + "$build/avd_packager": { + "avd_configs": [ + "tools/android/avd/proto/creation/generic_android19.textpb", + "tools/android/avd/proto/creation/generic_android22.textpb", + "tools/android/avd/proto/creation/generic_android23.textpb", + "tools/android/avd/proto/creation/generic_android24.textpb", + "tools/android/avd/proto/creation/generic_playstore_android24.textpb", + "tools/android/avd/proto/creation/generic_android25.textpb", + "tools/android/avd/proto/creation/generic_playstore_android25.textpb", + "tools/android/avd/proto/creation/generic_android27.textpb", + "tools/android/avd/proto/creation/generic_playstore_android27.textpb", + "tools/android/avd/proto/creation/generic_android28.textpb", + "tools/android/avd/proto/creation/generic_playstore_android28.textpb", + "tools/android/avd/proto/creation/generic_android29.textpb", + "tools/android/avd/proto/creation/generic_android30.textpb", + "tools/android/avd/proto/creation/generic_playstore_android30.textpb", + "tools/android/avd/proto/creation/generic_android31.textpb", + "tools/android/avd/proto/creation/generic_playstore_android31.textpb", + "tools/android/avd/proto/creation/generic_android32_foldable.textpb", + "tools/android/avd/proto/creation/generic_playstore_android32_foldable.textpb", + "tools/android/avd/proto/creation/generic_android33.textpb", + "tools/android/avd/proto/creation/generic_playstore_android33.textpb", + ], + "gclient_config": "chromium", + "gclient_apply_config": ["android"], + }, + # TODO(crbug.com/1381614): Remove properties below after + # avd_packager is refactored to recipe_module in crrev.com/c/4021719 "avd_configs": [ "tools/android/avd/proto/creation/generic_android19.textpb", "tools/android/avd/proto/creation/generic_android22.textpb",
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star b/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star index f9ab8c6..6268c27 100644 --- a/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star +++ b/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
@@ -204,15 +204,6 @@ ) try_.builder( - name = "linux-lacros-tester-rel-reviver", - mirrors = [ - "ci/linux-lacros-tester-rel-reviver", - ], - builderless = True, - main_list_view = "try", -) - -try_.builder( name = "chromeos-jacuzzi-rel", branch_selector = branches.CROS_LTS_MILESTONE, mirrors = [
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star b/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star index 2dd0121f..fda343c 100644 --- a/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star +++ b/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star
@@ -195,6 +195,7 @@ test_presentation = resultdb.test_presentation( grouping_keys = ["status", "v.test_suite", "v.gpu"], ), + goma_backend = None, ) try_.builder(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star b/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star index 4371483..cb14c6f3 100644 --- a/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star +++ b/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star
@@ -22,7 +22,7 @@ os = os.LINUX_DEFAULT, pool = try_.DEFAULT_POOL, reclient_instance = reclient.instance.DEFAULT_UNTRUSTED, - reclient_jobs = reclient.jobs.LOW_JOBS_FOR_CQ, + reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CQ, compilator_reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CQ, service_account = try_.DEFAULT_SERVICE_ACCOUNT, @@ -49,6 +49,7 @@ mirrors = [ "ci/fuchsia-arm64-cast-receiver-rel", ], + goma_backend = None, ) try_.builder( @@ -102,6 +103,7 @@ }, }, tryjob = try_.job(), + goma_backend = None, ) try_.builder( @@ -120,21 +122,25 @@ include_all_triggered_testers = True, is_compile_only = True, ), + goma_backend = None, ) try_.builder( name = "fuchsia-deterministic-dbg", executable = "recipe:swarming/deterministic_build", + goma_backend = None, ) try_.builder( name = "fuchsia-fyi-arm64-dbg", mirrors = ["ci/fuchsia-fyi-arm64-dbg"], + goma_backend = None, ) try_.builder( name = "fuchsia-fyi-x64-dbg", mirrors = ["ci/fuchsia-fyi-x64-dbg"], + goma_backend = None, ) try_.orchestrator_builder( @@ -159,6 +165,7 @@ cores = 16, ssd = True, main_list_view = "try", + goma_backend = None, ) try_.builder(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star index fe28d37c..f0762a5 100644 --- a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star +++ b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
@@ -522,17 +522,6 @@ ) try_.builder( - name = "linux_chromium_msan_focal", - mirrors = [ - "ci/Linux MSan Focal", - ], - execution_timeout = 16 * time.hour, - goma_backend = None, - os = os.LINUX_FOCAL, - reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CQ, -) - -try_.builder( name = "linux_chromium_msan_rel_ng", mirrors = [ "ci/Linux MSan Builder", @@ -540,6 +529,7 @@ ], execution_timeout = 6 * time.hour, goma_backend = None, + os = os.LINUX_FOCAL, reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CQ, ) @@ -681,16 +671,19 @@ try_.builder( name = "tricium-metrics-analysis", executable = "recipe:tricium_metrics", + goma_backend = None, ) try_.builder( name = "tricium-oilpan-analysis", executable = "recipe:tricium_oilpan", + goma_backend = None, ) try_.builder( name = "tricium-simple", executable = "recipe:tricium_simple", + goma_backend = None, ) try_.gpu.optional_tests_builder(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.star b/infra/config/subprojects/chromium/try/tryserver.chromium.star index f51fbc60..205b56c 100644 --- a/infra/config/subprojects/chromium/try/tryserver.chromium.star +++ b/infra/config/subprojects/chromium/try/tryserver.chromium.star
@@ -45,6 +45,9 @@ mirrors = [ "ci/fuchsia-official", ], + goma_backend = None, + reclient_instance = reclient.instance.DEFAULT_UNTRUSTED, + reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CQ, ) try_.builder(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.tricium.star b/infra/config/subprojects/chromium/try/tryserver.chromium.tricium.star index 379121c1..1ef9ff8 100644 --- a/infra/config/subprojects/chromium/try/tryserver.chromium.tricium.star +++ b/infra/config/subprojects/chromium/try/tryserver.chromium.tricium.star
@@ -61,6 +61,7 @@ name = "fuchsia-clang-tidy-rel", executable = "recipe:tricium_clang_tidy_wrapper", os = os.LINUX_DEFAULT, + goma_backend = None, ) try_.builder(
diff --git a/infra/config/subprojects/reviver/reviver.star b/infra/config/subprojects/reviver/reviver.star index 10a65fe..0b53afc 100644 --- a/infra/config/subprojects/reviver/reviver.star +++ b/infra/config/subprojects/reviver/reviver.star
@@ -73,6 +73,20 @@ schedule = "0 1,3,5,7,9,11,13 * * *", ) +# A coordinator for lacros. +polymorphic.launcher( + name = "lacros-coordinator", + runner = "reviver/runner", + target_builders = [ + "ci/linux-lacros-tester-rel", + ], + os = os.LINUX_DEFAULT, + pool = ci.DEFAULT_POOL, + # To avoid peak hours, we run it from 8PM TO 4AM PST. It is + # 3 AM to 11 AM UTC. + schedule = "0 3,5,7,9 * * *", +) + builder( name = "runner", executable = "recipe:reviver/chromium/runner",
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_destination_view.swift b/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_destination_view.swift index d6fc001..fb3992a 100644 --- a/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_destination_view.swift +++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_destination_view.swift
@@ -164,7 +164,7 @@ ) // Pad the color circle by 0.5, otherwise the color shows up faintly // around the border. - .background(Circle().foregroundColor(.blue500).padding(0.5)) + .background(Circle().foregroundColor(.blue600).padding(0.5)) .frame(width: Dimensions.badgeWidth, height: Dimensions.badgeWidth) .offset(x: Dimensions.iconWidth / 2, y: -Dimensions.iconWidth / 2) } else if destination.badge == .newLabel {
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 index f45fc48..d3e1dbc 100644 --- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@ -1fab80f8d30a950d916d2f5fa7a9ee4598ba51bb \ No newline at end of file +58bc21bb40977a250250443b23534d0f32684d85 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 index 71a3ad7..08c56923 100644 --- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@ -c54f66a553042d785c1332646ad43ba4be4b093d \ No newline at end of file +9ac10e93fbb498550f647e0e12665c35a1394008 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 index dcb2734..7f55562 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -62d2991bec0bc791de1eda733bdaf9bc85810ca5 \ No newline at end of file +0528fd7b028dbd915238625ac3aa561248a674a8 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 index c8f4cf3..59d4403 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -5ac80fd19444d1ab46105a6cb6c6488c118c2ae1 \ No newline at end of file +f721009610b3ab78cecc2f28c63e920cddae0609 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 index 98325356..790268c 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -2a312d62c79ba6bff052798a08c0df65897e7010 \ No newline at end of file +87994f69c51e206c051261c3181ebec3cd7b3b4d \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 index e272dada..b767ab4 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -5815f5e6e7b498bf9811991cdf1f97ec87acc7db \ No newline at end of file +64785d4bf68a9326ea0c8a8591ba3beda0ef3c78 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 index 34e531d..6d429c4b 100644 --- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -26ca56d0f0cce70fb8fb67273d092092260f0914 \ No newline at end of file +d9a62341e0a244866311ffe9967baef2e67d5f0d \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 index b938421..68e0e0f 100644 --- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -da7e589cc79803c9be82bf91dd7875a945c4d4bc \ No newline at end of file +6115887860f00219f1ce4783e13989a0b304a19e \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 index 90406c7..348c7c1 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -922056ab8f76f6d76865f4632fa697658644dabb \ No newline at end of file +cc3a0454ec48d95fc502b00044cd06dc89bf30c6 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 index de71285..9133d8d72 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -af452f809a5e12711874d6f769f00625bfaf0325 \ No newline at end of file +bb3cfc5448bc3e6b895504e9f878982c2faa0e6b \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 index c8740495..f04ef32 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -80e9873604bb0a97afda669b59356f28f519f4f7 \ No newline at end of file +a44f465bfd44091eba2f32a05568e08fac5752f4 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 index f25bb18..0238282 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -5d1b7d2f1735fd23b20c40f798c392c4a1eee5b4 \ No newline at end of file +6fa84f8efa31468f084a5800f976106801640140 \ No newline at end of file
diff --git a/ios/third_party/webkit/BUILD.gn b/ios/third_party/webkit/BUILD.gn index fa606621..9ecbe26 100644 --- a/ios/third_party/webkit/BUILD.gn +++ b/ios/third_party/webkit/BUILD.gn
@@ -183,14 +183,14 @@ "--output_dir", rebase_path("$_webkit_mac_out_base_dir"), - # The arguments below allow building with Xcode 12.5 (with the macOS 11.3 - # SDK). - "MACOSX_DEPLOYMENT_TARGET=11.3", - "TARGET_MAC_OS_X_VERSION_MAJOR=110000", - "MAC_OS_X_VERSION_ACTUAL=110300", - "MAC_OS_X_VERSION_MAJOR=110000", - "MAC_OS_X_VERSION_MINOR=110300", - "MAC_OS_X_PRODUCT_BUILD_VERSION=20E232", + # The arguments below allow building with Xcode 13.4 (with the macOS 12.3 + # SDK) on macOS 12.6.1. + "MACOSX_DEPLOYMENT_TARGET=12.3", + "TARGET_MAC_OS_X_VERSION_MAJOR=120000", + "MAC_OS_X_VERSION_ACTUAL=120601", + "MAC_OS_X_VERSION_MAJOR=120000", + "MAC_OS_X_VERSION_MINOR=120600", + "MAC_OS_X_PRODUCT_BUILD_VERSION=21G217", ] }
diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn index 37d99a63..3926010 100644 --- a/ios/web_view/BUILD.gn +++ b/ios/web_view/BUILD.gn
@@ -72,7 +72,6 @@ "public/cwv_password.h", "public/cwv_preferences.h", "public/cwv_preview_element_info.h", - "public/cwv_script_command.h", "public/cwv_ssl_error_handler.h", "public/cwv_ssl_status.h", "public/cwv_sync_controller.h", @@ -153,8 +152,6 @@ "internal/cwv_preferences_internal.h", "internal/cwv_preview_element_info.mm", "internal/cwv_preview_element_info_internal.h", - "internal/cwv_script_command.mm", - "internal/cwv_script_command_internal.h", "internal/cwv_ssl_error_handler.mm", "internal/cwv_ssl_error_handler_internal.h", "internal/cwv_ssl_status.mm",
diff --git a/ios/web_view/internal/cwv_script_command.mm b/ios/web_view/internal/cwv_script_command.mm deleted file mode 100644 index e68e37242..0000000 --- a/ios/web_view/internal/cwv_script_command.mm +++ /dev/null
@@ -1,29 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "ios/web_view/public/cwv_script_command.h" - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -@implementation CWVScriptCommand - -@synthesize content = _content; -@synthesize mainDocumentURL = _mainDocumentURL; -@synthesize userInteracting = _userInteracting; - -- (instancetype)initWithContent:(nullable NSDictionary*)content - mainDocumentURL:(NSURL*)mainDocumentURL - userInteracting:(BOOL)userInteracting { - self = [super init]; - if (self) { - _content = [content copy]; - _mainDocumentURL = [mainDocumentURL copy]; - _userInteracting = userInteracting; - } - return self; -} - -@end
diff --git a/ios/web_view/internal/cwv_script_command_internal.h b/ios/web_view/internal/cwv_script_command_internal.h deleted file mode 100644 index 449af36..0000000 --- a/ios/web_view/internal/cwv_script_command_internal.h +++ /dev/null
@@ -1,31 +0,0 @@ -// 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. - -#ifndef IOS_WEB_VIEW_INTERNAL_CWV_SCRIPT_COMMAND_INTERNAL_H_ -#define IOS_WEB_VIEW_INTERNAL_CWV_SCRIPT_COMMAND_INTERNAL_H_ - -#import "ios/web_view/public/cwv_script_command.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface CWVScriptCommand () - -/** - * Designated initializer. - * - * @param content Content of the command. nil in case of an error converting the - * content object. - * @param mainDocumentURL URL of the document in the main web view frame. - * @param userInteracting YES if the user is currently interacting with the - * page. - */ -- (instancetype)initWithContent:(nullable NSDictionary*)content - mainDocumentURL:(NSURL*)mainDocumentURL - userInteracting:(BOOL)userInteracting NS_DESIGNATED_INITIALIZER; - -@end - -NS_ASSUME_NONNULL_END - -#endif // IOS_WEB_VIEW_INTERNAL_CWV_SCRIPT_COMMAND_INTERNAL_H_
diff --git a/ios/web_view/internal/cwv_web_view.mm b/ios/web_view/internal/cwv_web_view.mm index 8ddfaae8..acb7ae08 100644 --- a/ios/web_view/internal/cwv_web_view.mm +++ b/ios/web_view/internal/cwv_web_view.mm
@@ -52,7 +52,6 @@ #import "ios/web_view/internal/cwv_favicon_internal.h" #import "ios/web_view/internal/cwv_html_element_internal.h" #import "ios/web_view/internal/cwv_navigation_action_internal.h" -#import "ios/web_view/internal/cwv_script_command_internal.h" #import "ios/web_view/internal/cwv_ssl_status_internal.h" #import "ios/web_view/internal/cwv_web_view_configuration_internal.h" #import "ios/web_view/internal/language/web_view_url_language_histogram_factory.h" @@ -123,16 +122,6 @@ return nil; } -// Converts base::Value expected to be a dictionary to NSDictionary. -NSDictionary* NSDictionaryFromDictionaryValue(const base::Value& value) { - DCHECK(value.is_dict()) << "Incorrect value type: " << value.type(); - - NSDictionary* ns_dictionary = base::mac::ObjCCastStrict<NSDictionary>( - NSObjectFromCollectionValue(&value)); - DCHECK(ns_dictionary) << "Failed to convert JSON to NSDictionary"; - return ns_dictionary; -} - // Converts base::Value::Dict to NSDictionary. NSDictionary* NSDictionaryFromDictValue(const base::Value::Dict& value) { std::string json; @@ -176,11 +165,6 @@ // Handles presentation of JavaScript dialogs. std::unique_ptr<ios_web_view::WebViewJavaScriptDialogPresenter> _javaScriptDialogPresenter; - // Stores the script command callbacks with subscriptions. - std::unordered_map<std::string, - std::pair<web::WebState::ScriptCommandCallback, - base::CallbackListSubscription>> - _scriptCommandCallbacks; CRWSessionStorage* _cachedSessionStorage; } @@ -391,12 +375,6 @@ } - (void)evaluateJavaScript:(NSString*)javaScriptString - completionHandler:(void (^)(id, NSError*))completionHandler { - [_webState->GetJSInjectionReceiver() executeJavaScript:javaScriptString - completionHandler:completionHandler]; -} - -- (void)evaluateJavaScript:(NSString*)javaScriptString completion:(void (^)(id, NSError*))completion { web::WebFrame* mainFrame = web::GetMainFrame(_webState.get()); if (!mainFrame) { @@ -647,34 +625,6 @@ } } -- (void)addScriptCommandHandler:(id<CWVScriptCommandHandler>)handler - commandPrefix:(NSString*)commandPrefix { - CWVWebView* __weak weakSelf = self; - const web::WebState::ScriptCommandCallback callback = base::BindRepeating( - ^(const base::Value& content, const GURL& mainDocumentURL, - bool userInteracting, web::WebFrame* senderFrame) { - NSDictionary* nsContent = NSDictionaryFromDictionaryValue(content); - CWVScriptCommand* command = [[CWVScriptCommand alloc] - initWithContent:nsContent - mainDocumentURL:net::NSURLWithGURL(mainDocumentURL) - userInteracting:userInteracting]; - [handler webView:weakSelf - handleScriptCommand:command - fromMainFrame:senderFrame->IsMainFrame()]; - }); - - std::string stdCommandPrefix = base::SysNSStringToUTF8(commandPrefix); - auto subscription = - _webState->AddScriptCommandCallback(callback, stdCommandPrefix); - _scriptCommandCallbacks[stdCommandPrefix] = {callback, - std::move(subscription)}; -} - -- (void)removeScriptCommandHandlerForCommandPrefix:(NSString*)commandPrefix { - std::string stdCommandPrefix = base::SysNSStringToUTF8(commandPrefix); - _scriptCommandCallbacks.erase(stdCommandPrefix); -} - - (void)addMessageHandler:(void (^)(NSDictionary* payload))handler forCommand:(NSString*)nsCommand { DCHECK(handler); @@ -885,11 +835,6 @@ std::make_unique<ios_web_view::WebViewJavaScriptDialogPresenter>(self, nullptr); - for (auto& pair : _scriptCommandCallbacks) { - pair.second.second = - _webState->AddScriptCommandCallback(pair.second.first, pair.first); - } - _webState->GetWebViewProxy().allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
diff --git a/ios/web_view/public/cwv_script_command.h b/ios/web_view/public/cwv_script_command.h deleted file mode 100644 index 10a278f..0000000 --- a/ios/web_view/public/cwv_script_command.h +++ /dev/null
@@ -1,46 +0,0 @@ -// 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. - -#ifndef IOS_WEB_VIEW_PUBLIC_CWV_SCRIPT_COMMAND_H_ -#define IOS_WEB_VIEW_PUBLIC_CWV_SCRIPT_COMMAND_H_ - -#import <Foundation/Foundation.h> - -#import "cwv_export.h" - -NS_ASSUME_NONNULL_BEGIN - -@class CWVWebView; - -// A script command passed to CWVScriptCommandHandler. -CWV_EXPORT -@interface CWVScriptCommand : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -// Content of the command. nil in case of an error converting the content -// object. -@property(nonatomic, readonly, nullable, copy) NSDictionary* content; - -// URL of the document in the main web view frame. -@property(nonatomic, readonly, copy) NSURL* mainDocumentURL; - -// YES if the user is currently interacting with the page. -@property(nonatomic, readonly, getter=isUserInteracting) BOOL userInteracting; - -@end - -// Provides a method for receiving commands from JavaScript running in a web -// page. -@protocol CWVScriptCommandHandler<NSObject> - -- (BOOL)webView:(CWVWebView*)webView - handleScriptCommand:(CWVScriptCommand*)command - fromMainFrame:(BOOL)fromMainFrame; - -@end - -NS_ASSUME_NONNULL_END - -#endif // IOS_WEB_VIEW_PUBLIC_CWV_SCRIPT_COMMAND_H_
diff --git a/ios/web_view/public/cwv_web_view.h b/ios/web_view/public/cwv_web_view.h index 7f79f39..0877d95 100644 --- a/ios/web_view/public/cwv_web_view.h +++ b/ios/web_view/public/cwv_web_view.h
@@ -19,7 +19,6 @@ @class CWVTranslationController; @class CWVWebViewConfiguration; @protocol CWVNavigationDelegate; -@protocol CWVScriptCommandHandler; @protocol CWVUIDelegate; @class CWVSSLStatus; @@ -218,38 +217,6 @@ // Unlike WKWebView, this method supports HTTPBody. - (void)loadRequest:(NSURLRequest*)request; -// Evaluates a JavaScript string. -// The completion handler is invoked when script evaluation completes. -// -// Note that |javaScriptString| is wrapped with: -// if (<implementation defined>) { ... } -// before evaluation, which causes some tricky side effect when you use |let| or -// |const| in the script. -// -// 1. Variables defined with |let| or |const| at the top level of the script -// do NOT become a global variable. i.e., It is accessible neither from -// scripts in the page nor another call to -// -evaluateJavaScript:completionHandler:. Variables defined with |var| -// DOES become a global variable. -// -// 2. Variables defined with |let| or |const| at the top level are not -// accessible from top level functions, even in the same script. Variable -// defined with |var| doesn't have this issue either. e.g., evaluation of -// this script causes an error: -// -// let a = 3; -// function f() { -// console.log(a); // ReferenceError: Can't find variable: a -// } -// f(); -// -// To workaround the issue, you can use |var| instead, or an explicit reference -// to window.xxx. This is because |let| and |const| are scoped by braces while -// |var| isn't, and due to tricky behavior of WebKit in non-strict mode. -// DEPRECATED. Use `evaluateJavaScript:completion:` instead. -- (void)evaluateJavaScript:(NSString*)javaScriptString - completionHandler:(void (^)(id, NSError*))completionHandler; - // Evaluates a JavaScript string in the main frame of the page content world. // `completion` is invoked with the result of evaluating the script and a // boolean representing success (`YES`) or failure (`NO`) of the evaluation. @@ -261,29 +228,6 @@ - (void)evaluateJavaScript:(NSString*)javaScriptString completion:(void (^)(id result, NSError* error))completion; -// Registers a handler that will be called when a command matching -// |commandPrefix| is received. -// -// Web pages can send a command by executing JavaScript like this: -// __gCrWeb.message.invokeOnHost( -// {'command': 'test.command1', 'key1':'value1', 'key2': 42}); -// And receive it by: -// [webView addScriptCommandHandler:handler commandPrefix:@"test"]; -// -// Make sure to call -removeScriptCommandHandlerForCommandPrefix: with the same -// prefix before deallocating a CWVWebView instance. Otherwise it causes an -// assertion failure. -// -// This provides a similar functionarity to -[WKUserContentController -// addScriptMessageHandler:name:]. -// DEPRECATED: Use `addMessageHandler:forCommand:` instead. -- (void)addScriptCommandHandler:(id<CWVScriptCommandHandler>)handler - commandPrefix:(NSString*)commandPrefix; - -// Removes the handler associated with |commandPrefix|. -// DEPRECATED: Use `removeMessageHandlerForCommand:` instead. -- (void)removeScriptCommandHandlerForCommandPrefix:(NSString*)commandPrefix; - // Adds a message handler for messages sent from JavaScript. // `handler` will be called each time a message is sent with the corresponding // value of `command`. To send messages from JavaScript, use the WebKit
diff --git a/ios/web_view/shell/shell_view_controller.m b/ios/web_view/shell/shell_view_controller.m index 8818d3f..ef6d621 100644 --- a/ios/web_view/shell/shell_view_controller.m +++ b/ios/web_view/shell/shell_view_controller.m
@@ -28,7 +28,6 @@ CWVLeakCheckServiceObserver, CWVNavigationDelegate, CWVUIDelegate, - CWVScriptCommandHandler, CWVSyncControllerDelegate, UIScrollViewDelegate, UITextFieldDelegate> @@ -1020,8 +1019,6 @@ NSKeyValueObservingOptionInitial context:nil]; - [webView addScriptCommandHandler:self commandPrefix:@"test"]; - [webView addMessageHandler:^(NSDictionary* payload) { NSLog(@"message handler payload received =\n%@", payload); @@ -1036,7 +1033,6 @@ [_webView removeObserver:self forKeyPath:@"canGoBack"]; [_webView removeObserver:self forKeyPath:@"canGoForward"]; [_webView removeObserver:self forKeyPath:@"loading"]; - [_webView removeScriptCommandHandlerForCommandPrefix:@"test"]; [_webView removeMessageHandlerForCommand:@"messageHandlerCommand"]; _webView = nil; @@ -1046,7 +1042,6 @@ [_webView removeObserver:self forKeyPath:@"canGoBack"]; [_webView removeObserver:self forKeyPath:@"canGoForward"]; [_webView removeObserver:self forKeyPath:@"loading"]; - [_webView removeScriptCommandHandlerForCommandPrefix:@"test"]; [_webView removeMessageHandlerForCommand:@"messageHandlerCommand"]; } @@ -1421,15 +1416,6 @@ [task startDownloadToLocalFileAtPath:self.downloadFilePath]; } -#pragma mark CWVScriptCommandHandler - -- (BOOL)webView:(CWVWebView*)webView - handleScriptCommand:(nonnull CWVScriptCommand*)command - fromMainFrame:(BOOL)fromMainFrame { - NSLog(@"%@ command.content=%@", NSStringFromSelector(_cmd), command.content); - return YES; -} - #pragma mark CWVAutofillDataManagerObserver - (void)autofillDataManagerDataDidChange:
diff --git a/ios/web_view/test/BUILD.gn b/ios/web_view/test/BUILD.gn index 27fbd26..72a88edd 100644 --- a/ios/web_view/test/BUILD.gn +++ b/ios/web_view/test/BUILD.gn
@@ -28,7 +28,6 @@ "web_view_inttest_base.mm", "web_view_kvo_inttest.mm", "web_view_restorable_state_inttest.mm", - "web_view_script_command_inttest.mm", "web_view_script_message_handler_inttest.mm", ] }
diff --git a/ios/web_view/test/web_view_script_command_inttest.mm b/ios/web_view/test/web_view_script_command_inttest.mm deleted file mode 100644 index b2d07fd14..0000000 --- a/ios/web_view/test/web_view_script_command_inttest.mm +++ /dev/null
@@ -1,125 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <ChromeWebView/ChromeWebView.h> -#import <Foundation/Foundation.h> - -#import "base/test/ios/wait_util.h" -#import "ios/web_view/test/web_view_inttest_base.h" -#import "ios/web_view/test/web_view_test_util.h" -#import "net/base/mac/url_conversions.h" -#include "net/test/embedded_test_server/embedded_test_server.h" -#include "testing/gtest_mac.h" -#include "url/gurl.h" - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -@interface CWVFakeScriptCommandHandler : NSObject<CWVScriptCommandHandler> - -@property(nonatomic) CWVScriptCommand* lastReceivedCommand; - -- (BOOL)webView:(CWVWebView*)webView - handleScriptCommand:(CWVScriptCommand*)command - fromMainFrame:(BOOL)fromMainFrame; - -@end - -@implementation CWVFakeScriptCommandHandler - -@synthesize lastReceivedCommand = _lastReceivedCommand; - -- (BOOL)webView:(CWVWebView*)webView - handleScriptCommand:(CWVScriptCommand*)command - fromMainFrame:(BOOL)fromMainFrame { - self.lastReceivedCommand = command; - return YES; -} - -@end - -namespace ios_web_view { - -// Tests the script command feature in CWVWebView. -using WebViewScriptCommandTest = WebViewInttestBase; - -// Tests that a handler added by -[CWVWebView -// addScriptCommandHandler:commandPrefix] is invoked by JavaScript. -TEST_F(WebViewScriptCommandTest, TestScriptCommand) { - ASSERT_TRUE(test_server_->Start()); - CWVFakeScriptCommandHandler* handler = - [[CWVFakeScriptCommandHandler alloc] init]; - [web_view_ addScriptCommandHandler:handler commandPrefix:@"test"]; - - // Uses GetUrlForPageWithHtmlBody() instead of simply using about:blank - // because it looks __gCrWeb may not be available on about:blank. - // TODO(crbug.com/836114): Analyze why. - NSURL* url = net::NSURLWithGURL(GetUrlForPageWithHtmlBody("")); - ASSERT_TRUE(test::LoadUrl(web_view_, url)); - ASSERT_TRUE(test::WaitForWebViewLoadCompletionOrTimeout(web_view_)); - - NSString* script = - @"__gCrWeb.message.invokeOnHost(" - @"{'command': 'test.command1', 'key1': 'value1', 'key2': 42});"; - NSError* error; - test::EvaluateJavaScript(web_view_, script, &error); - EXPECT_FALSE(error); - - EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout( - base::test::ios::kWaitForJSCompletionTimeout, ^{ - return handler.lastReceivedCommand != nil; - })); - - EXPECT_NSEQ(@"test.command1", - handler.lastReceivedCommand.content[@"command"]); - EXPECT_NSEQ(@"value1", handler.lastReceivedCommand.content[@"key1"]); - EXPECT_NSEQ(@42, handler.lastReceivedCommand.content[@"key2"]); - EXPECT_NSEQ(url, handler.lastReceivedCommand.mainDocumentURL); - EXPECT_FALSE(handler.lastReceivedCommand.userInteracting); - - [web_view_ removeScriptCommandHandlerForCommandPrefix:@"test"]; -} - -// Tests that added script commands are still valid after state restoration. -// Tests the same thing as TestScriptCommand() after state restoration. -TEST_F(WebViewScriptCommandTest, TestScriptCommandAfterStateRestoration) { - ASSERT_TRUE(test_server_->Start()); - CWVFakeScriptCommandHandler* handler = - [[CWVFakeScriptCommandHandler alloc] init]; - [web_view_ addScriptCommandHandler:handler commandPrefix:@"test"]; - - CWVWebView* source_web_view = test::CreateWebView(); - test::CopyWebViewState(source_web_view, web_view_); - - // Uses GetUrlForPageWithHtmlBody() instead of simply using about:blank - // because it looks __gCrWeb may not be available on about:blank. - // TODO(crbug.com/836114): Analyze why. - NSURL* url = net::NSURLWithGURL(GetUrlForPageWithHtmlBody("")); - ASSERT_TRUE(test::LoadUrl(web_view_, url)); - ASSERT_TRUE(test::WaitForWebViewLoadCompletionOrTimeout(web_view_)); - - NSString* script = - @"__gCrWeb.message.invokeOnHost(" - @"{'command': 'test.command1', 'key1': 'value1', 'key2': 42});"; - NSError* error; - test::EvaluateJavaScript(web_view_, script, &error); - ASSERT_FALSE(error); - - EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout( - base::test::ios::kWaitForJSCompletionTimeout, ^{ - return handler.lastReceivedCommand != nil; - })); - - EXPECT_NSEQ(@"test.command1", - handler.lastReceivedCommand.content[@"command"]); - EXPECT_NSEQ(@"value1", handler.lastReceivedCommand.content[@"key1"]); - EXPECT_NSEQ(@42, handler.lastReceivedCommand.content[@"key2"]); - EXPECT_NSEQ(url, handler.lastReceivedCommand.mainDocumentURL); - EXPECT_FALSE(handler.lastReceivedCommand.userInteracting); - - [web_view_ removeScriptCommandHandlerForCommandPrefix:@"test"]; -} - -} // namespace ios_web_view
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn index 7dcd29a..8e473fe 100644 --- a/media/gpu/BUILD.gn +++ b/media/gpu/BUILD.gn
@@ -361,7 +361,7 @@ "av1_picture.cc", "av1_picture.h", ] - public_deps += [ "//third_party/libgav1:libgav1" ] + public_deps += [ "//third_party/libgav1:libgav1_parser" ] } }
diff --git a/media/gpu/v4l2/BUILD.gn b/media/gpu/v4l2/BUILD.gn index 71c7d5d..cfc2d8c 100644 --- a/media/gpu/v4l2/BUILD.gn +++ b/media/gpu/v4l2/BUILD.gn
@@ -125,9 +125,8 @@ ] } - # TODO(b/243970152): update use_libgav1_parser flag if (use_libgav1_parser) { - deps += [ "//third_party/libgav1:libgav1" ] + deps += [ "//third_party/libgav1:libgav1_parser" ] } } @@ -194,6 +193,6 @@ ] if (use_libgav1_parser) { - deps += [ "//third_party/libgav1:libgav1" ] + deps += [ "//third_party/libgav1:libgav1_parser" ] } }
diff --git a/media/gpu/vaapi/BUILD.gn b/media/gpu/vaapi/BUILD.gn index 29aa09b..924f778 100644 --- a/media/gpu/vaapi/BUILD.gn +++ b/media/gpu/vaapi/BUILD.gn
@@ -414,7 +414,7 @@ "//media:test_support", "//media/gpu:common", "//media/parsers:parsers", - "//third_party/libgav1:libgav1", + "//third_party/libgav1:libgav1_parser", "//third_party/libyuv", "//ui/gfx/codec:codec", "//ui/gfx/geometry",
diff --git a/media/gpu/vaapi/vaapi_video_decoder.cc b/media/gpu/vaapi/vaapi_video_decoder.cc index 5ec4b61..3e4d9e3 100644 --- a/media/gpu/vaapi/vaapi_video_decoder.cc +++ b/media/gpu/vaapi/vaapi_video_decoder.cc
@@ -315,6 +315,7 @@ } aspect_ratio_ = config.aspect_ratio(); + is_rtc_ = config.is_rtc(); output_cb_ = std::move(output_cb); waiting_cb_ = std::move(waiting_cb); @@ -1110,8 +1111,22 @@ encryption_scheme_); decoder_delegate_ = accelerator.get(); - decoder_ = std::make_unique<VP9Decoder>(std::move(accelerator), profile_, - color_space_); + // The VaapiVideoDecoder can generally deal with larger-to-smaller VP9 + // resolution changes without re-configuring the VA-API context. However, we + // exclude two use cases: + // + // - WebRTC: resolution changes are only expected to occur on key frames. + // Therefore, if we ignore larger-to-smaller changes, we would be + // introducing a memory usage regression on the common case. + // + // - Protected content: the scaling logic in + // ApplyResolutionChangeWithScreenSizes() assumes that we have a fresh + // picture size so that we can calculate the correct scaling factor. + const bool ignore_resolution_changes_to_smaller = + !(cdm_context_ref_ && !transcryption_) && !is_rtc_; + decoder_ = std::make_unique<VP9Decoder>( + std::move(accelerator), profile_, color_space_, + ignore_resolution_changes_to_smaller); } #if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER) else if (profile_ >= HEVCPROFILE_MIN && profile_ <= HEVCPROFILE_MAX) {
diff --git a/media/gpu/vaapi/vaapi_video_decoder.h b/media/gpu/vaapi/vaapi_video_decoder.h index c76b2c5d..26f279a 100644 --- a/media/gpu/vaapi/vaapi_video_decoder.h +++ b/media/gpu/vaapi/vaapi_video_decoder.h
@@ -217,6 +217,7 @@ VideoCodecProfile profile_ = VIDEO_CODEC_PROFILE_UNKNOWN; VideoColorSpace color_space_; absl::optional<gfx::HDRMetadata> hdr_metadata_; + bool is_rtc_; // Aspect ratio from the config. VideoAspectRatio aspect_ratio_;
diff --git a/media/gpu/vp9_decoder.cc b/media/gpu/vp9_decoder.cc index ca87587c..a2cf762 100644 --- a/media/gpu/vp9_decoder.cc +++ b/media/gpu/vp9_decoder.cc
@@ -113,9 +113,12 @@ VP9Decoder::VP9Decoder(std::unique_ptr<VP9Accelerator> accelerator, VideoCodecProfile profile, - const VideoColorSpace& container_color_space) + const VideoColorSpace& container_color_space, + bool ignore_resolution_changes_to_smaller) : state_(kNeedStreamMetadata), container_color_space_(container_color_space), + ignore_resolution_changes_to_smaller_( + ignore_resolution_changes_to_smaller), // TODO(hiroh): Set profile to UNKNOWN. profile_(profile), accelerator_(std::move(accelerator)), @@ -291,11 +294,19 @@ } DCHECK(!new_pic_size.IsEmpty()); - if (new_pic_size != pic_size_ || new_profile != profile_ || - curr_frame_hdr_->bit_depth != bit_depth_) { + + const bool is_pic_size_different = new_pic_size != pic_size_; + const bool is_pic_size_larger = new_pic_size.width() > pic_size_.width() || + new_pic_size.height() > pic_size_.height(); + const bool is_new_configuration_different_enough = + (ignore_resolution_changes_to_smaller_ ? is_pic_size_larger + : is_pic_size_different) || + new_profile != profile_ || curr_frame_hdr_->bit_depth != bit_depth_; + + if (is_new_configuration_different_enough) { DVLOG(1) << "New profile: " << GetProfileName(new_profile) - << ", New resolution: " << new_pic_size.ToString() - << ", New bit depth: " + << ", new resolution: " << new_pic_size.ToString() + << ", new bit depth: " << base::strict_cast<int>(curr_frame_hdr_->bit_depth); if (!curr_frame_hdr_->IsKeyframe() &&
diff --git a/media/gpu/vp9_decoder.h b/media/gpu/vp9_decoder.h index 89fff46..4067123678 100644 --- a/media/gpu/vp9_decoder.h +++ b/media/gpu/vp9_decoder.h
@@ -119,7 +119,8 @@ explicit VP9Decoder( std::unique_ptr<VP9Accelerator> accelerator, VideoCodecProfile profile, - const VideoColorSpace& container_color_space = VideoColorSpace()); + const VideoColorSpace& container_color_space = VideoColorSpace(), + bool ignore_resolution_changes_to_smaller = false); VP9Decoder(const VP9Decoder&) = delete; VP9Decoder& operator=(const VP9Decoder&) = delete; @@ -177,6 +178,11 @@ // Color space provided by the container. const VideoColorSpace container_color_space_; + // Many implementors (e.g. Intel and AMD via VA-API) will support changes of + // resolution to smaller without reconfiguring the driver (e.g. keeping the + // reference frames etc), but others won't. + const bool ignore_resolution_changes_to_smaller_ = false; + // Reference frames currently in use. Vp9ReferenceFrameVector ref_frames_;
diff --git a/net/cert/pki/path_builder_unittest.cc b/net/cert/pki/path_builder_unittest.cc index d333db81..5366801 100644 --- a/net/cert/pki/path_builder_unittest.cc +++ b/net/cert/pki/path_builder_unittest.cc
@@ -4,6 +4,7 @@ #include "net/cert/pki/path_builder.h" +#include <algorithm> #include "base/base_paths.h" #include "base/callback_forward.h" #include "base/containers/span.h" @@ -957,10 +958,9 @@ EXPECT_TRUE(AreCertsEq(e_by_e_, path.certs[2])); // Should only be one valid path, the one above. - int valid_paths = 0; - for (const auto& candidate_path : result.paths) { - valid_paths += candidate_path->IsValid() ? 1 : 0; - } + const int valid_paths = std::count_if( + result.paths.begin(), result.paths.end(), + [](const auto& candidate_path) { return candidate_path->IsValid(); }); ASSERT_EQ(1, valid_paths); }
diff --git a/net/spdy/fuzzing/hpack_fuzz_util.cc b/net/spdy/fuzzing/hpack_fuzz_util.cc index d72aff8..7de999de 100644 --- a/net/spdy/fuzzing/hpack_fuzz_util.cc +++ b/net/spdy/fuzzing/hpack_fuzz_util.cc
@@ -102,7 +102,8 @@ // static size_t HpackFuzzUtil::SampleExponential(size_t mean, size_t sanity_bound) { - return std::min(static_cast<size_t>(-std::log(base::RandDouble()) * mean), + // Use `1-base::RandDouble()` to avoid log(0). + return std::min(static_cast<size_t>(-std::log(1 - base::RandDouble()) * mean), sanity_bound); }
diff --git a/net/test/embedded_test_server/embedded_test_server_unittest.cc b/net/test/embedded_test_server/embedded_test_server_unittest.cc index dc13494..09fc1b6 100644 --- a/net/test/embedded_test_server/embedded_test_server_unittest.cc +++ b/net/test/embedded_test_server/embedded_test_server_unittest.cc
@@ -17,9 +17,12 @@ #include "base/synchronization/lock.h" #include "base/task/single_thread_task_executor.h" #include "base/task/single_thread_task_runner.h" +#include "base/test/bind.h" #include "base/threading/thread.h" #include "build/build_config.h" +#include "net/base/elements_upload_data_stream.h" #include "net/base/test_completion_callback.h" +#include "net/base/upload_bytes_element_reader.h" #include "net/http/http_response_headers.h" #include "net/log/net_log_source.h" #include "net/socket/client_socket_factory.h" @@ -621,6 +624,35 @@ } } +TEST_P(EmbeddedTestServerTest, LargePost) { + // HTTP/2's default flow-control window is 65K. Send a larger request body + // than that to verify the server correctly updates flow control. + std::string large_post_body(100 * 1024, 'a'); + server_->RegisterRequestMonitor( + base::BindLambdaForTesting([=](const HttpRequest& request) { + EXPECT_EQ(request.method, METHOD_POST); + EXPECT_TRUE(request.has_content); + EXPECT_EQ(large_post_body, request.content); + })); + + server_->SetSSLConfig(net::EmbeddedTestServer::CERT_OK); + ASSERT_TRUE(server_->Start()); + + auto reader = std::make_unique<UploadBytesElementReader>( + large_post_body.data(), large_post_body.size()); + auto stream = ElementsUploadDataStream::CreateWithReader(std::move(reader), + /*identifier=*/0); + + TestDelegate delegate; + std::unique_ptr<URLRequest> request( + context_->CreateRequest(server_->GetURL("/test"), DEFAULT_PRIORITY, + &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); + request->set_method("POST"); + request->set_upload(std::move(stream)); + request->Start(); + delegate.RunUntilComplete(); +} + INSTANTIATE_TEST_SUITE_P(EmbeddedTestServerTestInstantiation, EmbeddedTestServerTest, testing::ValuesIn(EmbeddedTestServerConfigs()));
diff --git a/net/test/embedded_test_server/http2_connection.cc b/net/test/embedded_test_server/http2_connection.cc index 2fca983..ee82fdc 100644 --- a/net/test/embedded_test_server/http2_connection.cc +++ b/net/test/embedded_test_server/http2_connection.cc
@@ -411,11 +411,21 @@ bool Http2Connection::OnDataForStream(StreamId stream_id, absl::string_view data) { + auto request = request_map_.find(stream_id); + if (request == request_map_.end()) { + // We should not receive data before receiving headers. + return false; + } + + request->second->has_content = true; + request->second->content.append(data.data(), data.size()); + adapter_->MarkDataConsumedForStream(stream_id, data.size()); return true; } bool Http2Connection::OnDataPaddingLength(StreamId stream_id, size_t padding_length) { + adapter_->MarkDataConsumedForStream(stream_id, padding_length); return true; }
diff --git a/sandbox/policy/mac/sandbox_mac.mm b/sandbox/policy/mac/sandbox_mac.mm index bf0490a..228f84ae 100644 --- a/sandbox/policy/mac/sandbox_mac.mm +++ b/sandbox/policy/mac/sandbox_mac.mm
@@ -12,6 +12,7 @@ #include "base/files/scoped_file.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" +#include "components/services/screen_ai/buildflags/buildflags.h" #include "ppapi/buildflags/buildflags.h" #include "printing/buildflags/buildflags.h" #include "sandbox/policy/mac/audio.sb.h" @@ -25,7 +26,9 @@ #include "sandbox/policy/mac/print_backend.sb.h" #include "sandbox/policy/mac/print_compositor.sb.h" #include "sandbox/policy/mac/renderer.sb.h" +#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) #include "sandbox/policy/mac/screen_ai.sb.h" +#endif #include "sandbox/policy/mac/speech_recognition.sb.h" #include "sandbox/policy/mac/utility.sb.h" #include "sandbox/policy/mojom/sandbox.mojom.h" @@ -84,9 +87,11 @@ case sandbox::mojom::Sandbox::kPrintCompositor: profile += kSeatbeltPolicyString_print_compositor; break; +#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) case sandbox::mojom::Sandbox::kScreenAI: profile += kSeatbeltPolicyString_screen_ai; break; +#endif case sandbox::mojom::Sandbox::kSpeechRecognition: profile += kSeatbeltPolicyString_speech_recognition; break;
diff --git a/sandbox/policy/win/sandbox_win.cc b/sandbox/policy/win/sandbox_win.cc index 88ba3bd..f20c7697 100644 --- a/sandbox/policy/win/sandbox_win.cc +++ b/sandbox/policy/win/sandbox_win.cc
@@ -42,6 +42,7 @@ #include "base/win/sid.h" #include "base/win/win_util.h" #include "base/win/windows_version.h" +#include "components/services/screen_ai/buildflags/buildflags.h" #include "ppapi/buildflags/buildflags.h" #include "printing/buildflags/buildflags.h" #include "sandbox/features.h" @@ -714,7 +715,9 @@ // Post-startup mitigations. mitigations = MITIGATION_DLL_SEARCH_ORDER; if (!cmd_line.HasSwitch(switches::kAllowThirdPartyModules) && +#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) sandbox_type != Sandbox::kScreenAI && +#endif sandbox_type != Sandbox::kSpeechRecognition && sandbox_type != Sandbox::kMediaFoundationCdm) { mitigations |= MITIGATION_FORCE_MS_SIGNED_BINS; @@ -1212,8 +1215,10 @@ #endif case Sandbox::kAudio: return "Audio"; +#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) case Sandbox::kScreenAI: return "Screen AI"; +#endif case Sandbox::kSpeechRecognition: return "Speech Recognition"; case Sandbox::kPdfConversion:
diff --git a/sandbox/win/src/broker_services.cc b/sandbox/win/src/broker_services.cc index 5e7f098..ecc0a635 100644 --- a/sandbox/win/src/broker_services.cc +++ b/sandbox/win/src/broker_services.cc
@@ -604,13 +604,6 @@ JobTracker* tracker = new JobTracker(std::move(policy_base), process_info.process_id()); - // Verify that the process is actually in the specified job. This should - // only fail if something has gone wrong during process creation. - BOOL in_job; - CHECK( - job_handle && - ::IsProcessInJob(process_info.process_handle(), job_handle, &in_job) && - in_job); // Post the tracker to the tracking thread, then associate the job with // the tracker. The worker thread takes ownership of these objects. CHECK(::PostQueuedCompletionStatus(
diff --git a/sandbox/win/src/sandbox_policy_base.h b/sandbox/win/src/sandbox_policy_base.h index c80221d..b898491 100644 --- a/sandbox/win/src/sandbox_policy_base.h +++ b/sandbox/win/src/sandbox_policy_base.h
@@ -175,8 +175,8 @@ // SetJobLevel(). ResultCode InitJob(); - // Returns the handle for this policy's job, or INVALID_HANDLE_VALUE if the - // job is not initialized. + // Returns the handle for this policy's job, or nullptr if the job is + // not initialized. HANDLE GetJobHandle(); // Returns true if a job is associated with this policy.
diff --git a/services/network/cors/cors_url_loader.cc b/services/network/cors/cors_url_loader.cc index d5fd2bc..2856522 100644 --- a/services/network/cors/cors_url_loader.cc +++ b/services/network/cors/cors_url_loader.cc
@@ -402,11 +402,6 @@ } } - network::URLLoader::LogConcerningRequestHeaders( - modified_headers, /*added_during_redirect=*/true); - network::URLLoader::LogConcerningRequestHeaders( - modified_cors_exempt_headers, /*added_during_redirect=*/true); - for (const auto& name : removed_headers) { request_.headers.RemoveHeader(name); request_.cors_exempt_headers.RemoveHeader(name);
diff --git a/services/network/cors/cors_url_loader_factory.cc b/services/network/cors/cors_url_loader_factory.cc index 8a3680ce..48af778 100644 --- a/services/network/cors/cors_url_loader_factory.cc +++ b/services/network/cors/cors_url_loader_factory.cc
@@ -534,9 +534,6 @@ return false; } - URLLoader::LogConcerningRequestHeaders(request.headers, - false /* added_during_redirect */); - // Specifying CredentialsMode::kSameOrigin without an initiator origin doesn't // make sense. if (request.credentials_mode == mojom::CredentialsMode::kSameOrigin &&
diff --git a/services/network/cors/cors_url_loader_unittest.cc b/services/network/cors/cors_url_loader_unittest.cc index 9e566d1..2ae010d 100644 --- a/services/network/cors/cors_url_loader_unittest.cc +++ b/services/network/cors/cors_url_loader_unittest.cc
@@ -10,7 +10,6 @@ #include "base/callback_helpers.h" #include "base/strings/string_piece.h" #include "base/strings/stringprintf.h" -#include "base/test/metrics/histogram_tester.h" #include "mojo/public/cpp/bindings/message.h" #include "mojo/public/cpp/system/functions.h" #include "net/base/load_flags.h" @@ -1714,75 +1713,6 @@ EXPECT_EQ(net::ERR_INVALID_ARGUMENT, client().completion_status().error_code); } -TEST_F(CorsURLLoaderTest, NoConcerningRequestHeadersLoggedCorrectly) { - base::HistogramTester histograms; - - ResourceRequest request; - request.mode = mojom::RequestMode::kNoCors; - request.credentials_mode = mojom::CredentialsMode::kInclude; - request.url = GURL("https://example.com/"); - request.request_initiator = url::Origin::Create(GURL("https://example.com")); - request.headers.SetHeader("Not", "Concerning"); - request.headers.SetHeader("Totally", "Fine"); - - CreateLoaderAndStart(request); - RunUntilCreateLoaderAndStartCalled(); - NotifyLoaderClientOnReceiveResponse(); - NotifyLoaderClientOnComplete(net::OK); - RunUntilComplete(); - - EXPECT_TRUE(IsNetworkLoaderStarted()); - EXPECT_FALSE(client().has_received_redirect()); - EXPECT_TRUE(client().has_received_response()); - EXPECT_TRUE(client().has_received_completion()); - EXPECT_EQ(net::OK, client().completion_status().error_code); - - histograms.ExpectBucketCount( - "NetworkService.ConcerningRequestHeader.PresentOnStart", true, 0); - histograms.ExpectBucketCount( - "NetworkService.ConcerningRequestHeader.PresentOnStart", false, 1); -} - -TEST_F(CorsURLLoaderTest, ConcerningRequestHeadersLoggedCorrectly) { - using ConcerningHeaderId = URLLoader::ConcerningHeaderId; - base::HistogramTester histograms; - - ResourceRequest request; - request.mode = mojom::RequestMode::kNoCors; - request.credentials_mode = mojom::CredentialsMode::kInclude; - request.url = GURL("https://example.com/"); - request.request_initiator = url::Origin::Create(GURL("https://example.com")); - request.headers.SetHeader(net::HttpRequestHeaders::kConnection, "Close"); - request.headers.SetHeader(net::HttpRequestHeaders::kCookie, "BadIdea=true"); - - CreateLoaderAndStart(request); - RunUntilCreateLoaderAndStartCalled(); - NotifyLoaderClientOnReceiveResponse(); - NotifyLoaderClientOnComplete(net::OK); - RunUntilComplete(); - - EXPECT_TRUE(IsNetworkLoaderStarted()); - EXPECT_FALSE(client().has_received_redirect()); - EXPECT_TRUE(client().has_received_response()); - EXPECT_TRUE(client().has_received_completion()); - EXPECT_EQ(net::OK, client().completion_status().error_code); - - histograms.ExpectBucketCount( - "NetworkService.ConcerningRequestHeader.PresentOnStart", true, 1); - histograms.ExpectBucketCount( - "NetworkService.ConcerningRequestHeader.PresentOnStart", false, 0); - for (int i = 0; i < static_cast<int>(ConcerningHeaderId::kMaxValue); ++i) { - if (i == static_cast<int>(ConcerningHeaderId::kConnection) || - i == static_cast<int>(ConcerningHeaderId::kCookie)) { - histograms.ExpectBucketCount( - "NetworkService.ConcerningRequestHeader.HeaderPresentOnStart", i, 1); - } else { - histograms.ExpectBucketCount( - "NetworkService.ConcerningRequestHeader.HeaderPresentOnStart", i, 0); - } - } -} - TEST_F(CorsURLLoaderTest, SetHostHeaderOnRedirectFails) { CreateLoaderAndStart(GURL("https://example.com/"), GURL("https://example.com/path"),
diff --git a/services/network/test/test_url_loader_factory.cc b/services/network/test/test_url_loader_factory.cc index cb551f7..94fe2e1 100644 --- a/services/network/test/test_url_loader_factory.cc +++ b/services/network/test/test_url_loader_factory.cc
@@ -17,6 +17,38 @@ namespace network { +TestURLLoaderFactory::TestURLLoader::FollowRedirectParams:: + FollowRedirectParams() = default; +TestURLLoaderFactory::TestURLLoader::FollowRedirectParams:: + ~FollowRedirectParams() = default; + +TestURLLoaderFactory::TestURLLoader::FollowRedirectParams::FollowRedirectParams( + FollowRedirectParams&& other) = default; + +TestURLLoaderFactory::TestURLLoader::FollowRedirectParams& +TestURLLoaderFactory::TestURLLoader::FollowRedirectParams::operator=( + FollowRedirectParams&& other) = default; + +TestURLLoaderFactory::TestURLLoader::TestURLLoader( + mojo::PendingReceiver<network::mojom::URLLoader> url_loader_receiver) + : receiver_(this, std::move(url_loader_receiver)) {} + +TestURLLoaderFactory::TestURLLoader::~TestURLLoader() = default; + +void TestURLLoaderFactory::TestURLLoader::FollowRedirect( + const std::vector<std::string>& removed_headers, + const net::HttpRequestHeaders& modified_headers, + const net::HttpRequestHeaders& modified_cors_exempt_headers, + const absl::optional<GURL>& new_url) { + FollowRedirectParams params; + params.removed_headers = removed_headers; + params.modified_headers = modified_headers; + params.modified_cors_exempt_headers = modified_cors_exempt_headers; + params.new_url = new_url; + + follow_redirect_params_.emplace_back(std::move(params)); +} + TestURLLoaderFactory::PendingRequest::PendingRequest() = default; TestURLLoaderFactory::PendingRequest::~PendingRequest() = default; @@ -31,10 +63,11 @@ TestURLLoaderFactory::Response& TestURLLoaderFactory::Response::operator=( Response&&) = default; -TestURLLoaderFactory::TestURLLoaderFactory() +TestURLLoaderFactory::TestURLLoaderFactory(bool observe_loader_requests) : weak_wrapper_( base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( - this)) {} + this)), + observe_loader_requests_(observe_loader_requests) {} TestURLLoaderFactory::~TestURLLoaderFactory() { weak_wrapper_->Detach(); @@ -132,6 +165,12 @@ return; PendingRequest pending_request; + + if (observe_loader_requests_) { + pending_request.test_url_loader = + std::make_unique<TestURLLoader>(std::move(receiver)); + } + pending_request.client = std::move(client_remote); pending_request.request_id = request_id; pending_request.options = options;
diff --git a/services/network/test/test_url_loader_factory.h b/services/network/test/test_url_loader_factory.h index 18a4cc1..fee4e73 100644 --- a/services/network/test/test_url_loader_factory.h +++ b/services/network/test/test_url_loader_factory.h
@@ -28,12 +28,55 @@ // would prime it with response data for arbitrary URLs. class TestURLLoaderFactory : public mojom::URLLoaderFactory { public: + // A helper class to bind a URLLoader observe method invocations on it. + class TestURLLoader final : public network::mojom::URLLoader { + public: + struct FollowRedirectParams { + FollowRedirectParams(); + ~FollowRedirectParams(); + FollowRedirectParams(FollowRedirectParams&& other); + FollowRedirectParams& operator=(FollowRedirectParams&& other); + + std::vector<std::string> removed_headers; + net::HttpRequestHeaders modified_headers; + net::HttpRequestHeaders modified_cors_exempt_headers; + absl::optional<GURL> new_url; + }; + + explicit TestURLLoader( + mojo::PendingReceiver<network::mojom::URLLoader> url_loader_receiver); + ~TestURLLoader() override; + + TestURLLoader(const TestURLLoader&) = delete; + TestURLLoader& operator=(const TestURLLoader&) = delete; + + // network::mojom::URLLoader overrides. + void FollowRedirect( + const std::vector<std::string>& removed_headers, + const net::HttpRequestHeaders& modified_headers, + const net::HttpRequestHeaders& modified_cors_exempt_headers, + const absl::optional<GURL>& new_url) override; + void SetPriority(net::RequestPriority priority, + int32_t intra_priority_value) override {} + void PauseReadingBodyFromNet() override {} + void ResumeReadingBodyFromNet() override {} + + const std::vector<FollowRedirectParams>& follow_redirect_params() const { + return follow_redirect_params_; + } + + private: + std::vector<FollowRedirectParams> follow_redirect_params_; + mojo::Receiver<network::mojom::URLLoader> receiver_; + }; + struct PendingRequest { PendingRequest(); ~PendingRequest(); PendingRequest(PendingRequest&& other); PendingRequest& operator=(PendingRequest&& other); + std::unique_ptr<TestURLLoader> test_url_loader; mojo::Remote<mojom::URLLoaderClient> client; int32_t request_id; uint32_t options; @@ -58,7 +101,7 @@ kSendHeadersOnNetworkError = 0x2, }; - TestURLLoaderFactory(); + explicit TestURLLoaderFactory(bool observe_loader_requests = false); TestURLLoaderFactory(const TestURLLoaderFactory&) = delete; TestURLLoaderFactory& operator=(const TestURLLoaderFactory&) = delete; @@ -207,6 +250,10 @@ Interceptor interceptor_; mojo::ReceiverSet<network::mojom::URLLoaderFactory> receivers_; size_t total_requests_ = 0; + + // Whether the pending URLLoader in `CreateLoaderAndStart()` should be bound + // to observe the method invocations to it (e.g. FollowRedirect). + const bool observe_loader_requests_; }; } // namespace network
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc index 84f72973..0237297 100644 --- a/services/network/url_loader.cc +++ b/services/network/url_loader.cc
@@ -106,8 +106,6 @@ namespace { -using ConcerningHeaderId = URLLoader::ConcerningHeaderId; - // Cannot use 0, because this means "default" in // mojo::core::Core::CreateDataPipe constexpr size_t kBlockedBodyAllocationSize = 1; @@ -287,22 +285,6 @@ net::CookieInclusionStatus::EXCLUDE_DOMAIN_NON_ASCII); } -// Concerning headers that consumers probably shouldn't be allowed to set. -// Gathering numbers on these before adding them to kUnsafeHeaders. -const struct { - const char* name; - ConcerningHeaderId histogram_id; -} kConcerningHeaders[] = { - {net::HttpRequestHeaders::kConnection, ConcerningHeaderId::kConnection}, - {net::HttpRequestHeaders::kCookie, ConcerningHeaderId::kCookie}, - {"Date", ConcerningHeaderId::kDate}, - {"Expect", ConcerningHeaderId::kExpect}, - // The referer is passed in from the caller on a per-request basis, but - // there's a separate field for it that should be used instead. - {net::HttpRequestHeaders::kReferer, ConcerningHeaderId::kReferer}, - {"Via", ConcerningHeaderId::kVia}, -}; - // Parses AcceptCHFrame and removes client hints already in the headers. std::vector<mojom::WebClientHintsType> ComputeAcceptCHFrameHints( const std::string& accept_ch_frame, @@ -982,7 +964,6 @@ URLLoader::~URLLoader() { TRACE_EVENT("loading", "URLLoader::~URLLoader", perfetto::TerminatingFlow::FromPointer(this)); - RecordBodyReadFromNetBeforePausedIfNeeded(); if (keepalive_ && keepalive_statistics_recorder_) { keepalive_statistics_recorder_->OnLoadFinished( *factory_params_->top_frame_id, keepalive_request_size_); @@ -1021,10 +1002,6 @@ return; } - if (!modified_headers.IsEmpty()) - LogConcerningRequestHeaders(modified_headers, - true /* added_during_redirect */); - deferred_redirect_url_.reset(); new_redirect_url_ = new_url; @@ -1053,17 +1030,7 @@ // request indicates that the response was cached, there could still be // network activity involved. For example, the response was only partially // cached. - // - // On the other hand, we only report BodyReadFromNetBeforePaused histogram - // when we are sure that the response body hasn't been read from cache. This - // avoids polluting the histogram data with data points from cached responses. should_pause_reading_body_ = true; - - if (read_in_progress_) { - update_body_read_before_paused_ = true; - } else { - body_read_before_paused_ = url_request_->GetRawBodyBytes(); - } } void URLLoader::ResumeReadingBodyFromNet() { @@ -1687,10 +1654,6 @@ reported_total_encoded_bytes_ = total_encoded_bytes; } } - if (update_body_read_before_paused_) { - update_body_read_before_paused_ = false; - body_read_before_paused_ = url_request_->GetRawBodyBytes(); - } bool complete_read = true; if (consumer_handle_.is_valid()) { @@ -1875,42 +1838,6 @@ return pointer->get(); } -// static -void URLLoader::LogConcerningRequestHeaders( - const net::HttpRequestHeaders& request_headers, - bool added_during_redirect) { - net::HttpRequestHeaders::Iterator it(request_headers); - - bool concerning_header_found = false; - - while (it.GetNext()) { - for (const auto& header : kConcerningHeaders) { - if (base::EqualsCaseInsensitiveASCII(header.name, it.name())) { - concerning_header_found = true; - if (added_during_redirect) { - UMA_HISTOGRAM_ENUMERATION( - "NetworkService.ConcerningRequestHeader.HeaderAddedOnRedirect", - header.histogram_id); - } else { - UMA_HISTOGRAM_ENUMERATION( - "NetworkService.ConcerningRequestHeader.HeaderPresentOnStart", - header.histogram_id); - } - } - } - } - - if (added_during_redirect) { - UMA_HISTOGRAM_BOOLEAN( - "NetworkService.ConcerningRequestHeader.AddedOnRedirect", - concerning_header_found); - } else { - UMA_HISTOGRAM_BOOLEAN( - "NetworkService.ConcerningRequestHeader.PresentOnStart", - concerning_header_found); - } -} - void URLLoader::OnAuthCredentials( const absl::optional<net::AuthCredentials>& credentials) { auth_challenge_responder_receiver_.reset(); @@ -2244,22 +2171,6 @@ return pending_write_ || response_body_stream_.is_valid(); } -void URLLoader::RecordBodyReadFromNetBeforePausedIfNeeded() { - if (update_body_read_before_paused_) - body_read_before_paused_ = url_request_->GetRawBodyBytes(); - if (body_read_before_paused_ != -1) { - if (!url_request_->was_cached()) { - UMA_HISTOGRAM_COUNTS_1M("Network.URLLoader.BodyReadFromNetBeforePaused", - body_read_before_paused_); - } else { - DVLOG(1) << "The request has been paused, but " - << "Network.URLLoader.BodyReadFromNetBeforePaused is not " - << "reported because the response body may be from cache. " - << "body_read_before_paused_: " << body_read_before_paused_; - } - } -} - void URLLoader::ResumeStart() { url_request_->LogUnblocked(); url_request_->Start();
diff --git a/services/network/url_loader.h b/services/network/url_loader.h index ca904eb..766c643 100644 --- a/services/network/url_loader.h +++ b/services/network/url_loader.h
@@ -96,25 +96,6 @@ public mojom::AuthChallengeResponder, public mojom::ClientCertificateResponder { public: - // Enumeration for UMA histograms logged by LogConcerningRequestHeaders(). - // Entries should not be renumbered and numeric values should never be reused. - // Please keep in sync with "NetworkServiceConcerningRequestHeaders" in - // src/tools/metrics/histograms/enums.xml. - enum class ConcerningHeaderId { - kConnection = 0, - kCookie = 1, - kCookie2 = 2, - kContentTransferEncoding = 3, - kDate = 4, - kExpect = 5, - kKeepAlive = 6, - kReferer = 7, - kTe = 8, - kTransferEncoding = 9, - kVia = 10, - kMaxValue = kVia, - }; - using DeleteCallback = base::OnceCallback<void(URLLoader* loader)>; // Holds a sync and async implementation of URLLoaderClient. The sync @@ -288,10 +269,6 @@ static const void* const kUserDataKey; - static void LogConcerningRequestHeaders( - const net::HttpRequestHeaders& request_headers, - bool added_during_redirect); - static bool HasFetchStreamingUploadBody(const ResourceRequest*); static absl::optional<net::IsolationInfo> GetIsolationInfo( @@ -551,16 +528,6 @@ // The response body stream is open, but transferring data is paused. bool paused_reading_body_ = false; - // Whether to update |body_read_before_paused_| after the pending read is - // completed (or when the response body stream is closed). - bool update_body_read_before_paused_ = false; - // The number of bytes obtained by the reads initiated before the last - // PauseReadingBodyFromNet() call. -1 means the request hasn't been paused. - // The body may be read from cache or network. So even if this value is not - // -1, we still need to check whether it is from network before reporting it - // as BodyReadFromNetBeforePaused. - int64_t body_read_before_paused_ = -1; - // This is used to compute the delta since last time received // encoded body size was reported to the client. int64_t reported_total_encoded_bytes_ = 0;
diff --git a/services/network/url_loader_unittest.cc b/services/network/url_loader_unittest.cc index fbaf103..8b1bb24 100644 --- a/services/network/url_loader_unittest.cc +++ b/services/network/url_loader_unittest.cc
@@ -20,7 +20,6 @@ #include "base/memory/raw_ptr.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" -#include "base/metrics/statistics_recorder.h" #include "base/no_destructor.h" #include "base/path_service.h" #include "base/run_loop.h" @@ -160,9 +159,6 @@ return base::BindOnce([](URLLoader* /* loader*/) { NOTREACHED(); }); } -constexpr char kBodyReadFromNetBeforePausedHistogram[] = - "Network.URLLoader.BodyReadFromNetBeforePaused"; - constexpr char kTestAuthURL[] = "/auth-basic?password=PASS&realm=REALM"; constexpr char kInsecureHost[] = "othersite.test"; @@ -602,25 +598,6 @@ resource_address_space); } -// Returns whether monitoring was successfully set up. -// |*output_sample| needs to stay valid until monitoring is stopped. -std::unique_ptr<base::StatisticsRecorder::ScopedHistogramSampleObserver> -StartMonitorBodyReadFromNetBeforePausedHistogram( - const base::RepeatingClosure& quit_closure, - base::HistogramBase::Sample* output_sample) { - return std::make_unique< - base::StatisticsRecorder::ScopedHistogramSampleObserver>( - kBodyReadFromNetBeforePausedHistogram, - base::BindRepeating( - [](const base::RepeatingClosure& quit_closure, - base::HistogramBase::Sample* output, const char* histogram_name, - uint64_t name_hash, base::HistogramBase::Sample sample) { - *output = sample; - quit_closure.Run(); - }, - quit_closure, output_sample)); -} - std::string CookieOrLineToString(const mojom::CookieOrLinePtr& cookie_or_line) { switch (cookie_or_line->which()) { case mojom::CookieOrLine::Tag::kCookie: @@ -2607,11 +2584,6 @@ const char* const kPath = "/hello.html"; const char* const kBodyContents = "This is the data as you requested."; - base::HistogramBase::Sample output_sample = -1; - base::RunLoop histogram_run_loop; - auto histogram_callback = StartMonitorBodyReadFromNetBeforePausedHistogram( - histogram_run_loop.QuitClosure(), &output_sample); - net::EmbeddedTestServer server; net::test_server::ControllableHttpResponse response_controller(&server, kPath); @@ -2667,8 +2639,6 @@ delete_run_loop.Run(); client()->Unbind(); - histogram_run_loop.Run(); - EXPECT_EQ(0, output_sample); } TEST_F(URLLoaderTest, PauseReadingBodyFromNetWhenReadIsPending) { @@ -2676,11 +2646,6 @@ const char* const kBodyContentsFirstHalf = "This is the first half."; const char* const kBodyContentsSecondHalf = "This is the second half."; - base::HistogramBase::Sample output_sample = -1; - base::RunLoop histogram_run_loop; - auto histogram_callback = StartMonitorBodyReadFromNetBeforePausedHistogram( - histogram_run_loop.QuitClosure(), &output_sample); - net::EmbeddedTestServer server; net::test_server::ControllableHttpResponse response_controller(&server, kPath); @@ -2727,19 +2692,12 @@ delete_run_loop.Run(); client()->Unbind(); - histogram_run_loop.Run(); - EXPECT_LE(0, output_sample); } TEST_F(URLLoaderTest, ResumeReadingBodyFromNetAfterClosingConsumer) { const char* const kPath = "/hello.html"; const char* const kBodyContentsFirstHalf = "This is the first half."; - base::HistogramBase::Sample output_sample = -1; - base::RunLoop histogram_run_loop; - auto histogram_callback = StartMonitorBodyReadFromNetBeforePausedHistogram( - histogram_run_loop.QuitClosure(), &output_sample); - net::EmbeddedTestServer server; net::test_server::ControllableHttpResponse response_controller(&server, kPath); @@ -2780,8 +2738,6 @@ loader.reset(); client()->Unbind(); delete_run_loop.Run(); - histogram_run_loop.Run(); - EXPECT_EQ(0, output_sample); } TEST_F(URLLoaderTest, MultiplePauseResumeReadingBodyFromNet) { @@ -2789,11 +2745,6 @@ const char* const kBodyContentsFirstHalf = "This is the first half."; const char* const kBodyContentsSecondHalf = "This is the second half."; - base::HistogramBase::Sample output_sample = -1; - base::RunLoop histogram_run_loop; - auto histogram_callback = StartMonitorBodyReadFromNetBeforePausedHistogram( - histogram_run_loop.QuitClosure(), &output_sample); - net::EmbeddedTestServer server; net::test_server::ControllableHttpResponse response_controller(&server, kPath); @@ -2847,8 +2798,6 @@ loader.reset(); client()->Unbind(); - histogram_run_loop.Run(); - EXPECT_LE(0, output_sample); } TEST_F(URLLoaderTest, UploadBytes) { @@ -3397,54 +3346,6 @@ } } -TEST_F(URLLoaderTest, RedirectLogsModifiedConcerningHeader) { - using ConcerningHeaderId = URLLoader::ConcerningHeaderId; - base::HistogramTester histograms; - - TestURLLoaderClient client; - - ResourceRequest request = CreateResourceRequest( - "GET", test_server()->GetURL("/redirect307-to-echo")); - - base::RunLoop delete_run_loop; - mojo::Remote<mojom::URLLoader> loader; - std::unique_ptr<URLLoader> url_loader; - context().mutable_factory_params().process_id = mojom::kBrowserProcessId; - context().mutable_factory_params().is_corb_enabled = false; - url_loader = URLLoaderOptions().MakeURLLoader( - context(), DeleteLoaderCallback(&delete_run_loop, &url_loader), - loader.BindNewPipeAndPassReceiver(), request, client.CreateRemote()); - - client.RunUntilRedirectReceived(); - - net::HttpRequestHeaders redirect_headers; - redirect_headers.SetHeader(net::HttpRequestHeaders::kReferer, - "https://somewhere.test/"); - redirect_headers.SetHeader("Via", "Albuquerque"); - loader->FollowRedirect({}, redirect_headers, {}, absl::nullopt); - - client.RunUntilComplete(); - delete_run_loop.Run(); - - EXPECT_TRUE(client.has_received_completion()); - EXPECT_EQ(net::OK, client.completion_status().error_code); - - histograms.ExpectBucketCount( - "NetworkService.ConcerningRequestHeader.AddedOnRedirect", true, 1); - histograms.ExpectBucketCount( - "NetworkService.ConcerningRequestHeader.AddedOnRedirect", false, 0); - for (int i = 0; i < static_cast<int>(ConcerningHeaderId::kMaxValue); ++i) { - if (i == static_cast<int>(ConcerningHeaderId::kReferer) || - i == static_cast<int>(ConcerningHeaderId::kVia)) { - histograms.ExpectBucketCount( - "NetworkService.ConcerningRequestHeader.HeaderAddedOnRedirect", i, 1); - } else { - histograms.ExpectBucketCount( - "NetworkService.ConcerningRequestHeader.HeaderAddedOnRedirect", i, 0); - } - } -} - // Test the client can remove headers during a redirect. TEST_F(URLLoaderTest, RedirectRemoveHeader) { ResourceRequest request = CreateResourceRequest(
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/browser_metrics.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/browser_metrics.cc index 6a83b44..9847122 100644 --- a/services/resource_coordinator/public/cpp/memory_instrumentation/browser_metrics.cc +++ b/services/resource_coordinator/public/cpp/memory_instrumentation/browser_metrics.cc
@@ -58,9 +58,10 @@ #else base::TimeDelta mean_time = base::Minutes(30); #endif - // Compute the actual delay before sampling using a Poisson process. + // Compute the actual delay before sampling using a Poisson process. Use + // `1-RandDouble()` to avoid log(0). double uniform = base::RandDouble(); - return -std::log(uniform) * mean_time; + return -std::log(1 - uniform) * mean_time; } } // namespace memory_instrumentation
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc index a0ac255..cfb9ad2 100644 --- a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc +++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
@@ -776,11 +776,8 @@ trace_config, privacy_filtering_enabled); #if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) - uint8_t modes = base::trace_event::TraceLog::RECORDING_MODE; - if (!trace_config.event_filters().empty()) { - modes |= base::trace_event::TraceLog::FILTERING_MODE; - } - base::trace_event::TraceLog::GetInstance()->SetEnabled(trace_config, modes); + base::trace_event::TraceLog::GetInstance()->SetEnabled( + trace_config, base::trace_event::TraceLog::RECORDING_MODE); #endif }
diff --git a/skia/BUILD.gn b/skia/BUILD.gn index b2c8b79..b330273 100644 --- a/skia/BUILD.gn +++ b/skia/BUILD.gn
@@ -944,8 +944,5 @@ "skia_resources.pak", ] - deps = [ - "//skia/public/mojom:mojom_js", - "//skia/public/mojom:mojom_webui_js", - ] + deps = [ "//skia/public/mojom:mojom_js__generator" ] }
diff --git a/storage/browser/quota/quota_database_migrations.cc b/storage/browser/quota/quota_database_migrations.cc index c7930c58..4a40b762 100644 --- a/storage/browser/quota/quota_database_migrations.cc +++ b/storage/browser/quota/quota_database_migrations.cc
@@ -6,7 +6,10 @@ #include <string> +#include "base/metrics/histogram_functions.h" #include "base/sequence_checker.h" +#include "base/strings/strcat.h" +#include "base/strings/string_number_conversions.h" #include "components/services/storage/public/cpp/buckets/bucket_id.h" #include "components/services/storage/public/cpp/buckets/constants.h" #include "sql/database.h" @@ -78,28 +81,46 @@ return quota_database.ResetStorage(); if (quota_database.meta_table_->GetVersionNumber() == 5) { - if (!MigrateFromVersion5ToVersion7(quota_database)) + bool success = MigrateFromVersion5ToVersion7(quota_database); + RecordMigrationHistogram(/*old_version=*/5, /*new_version=*/7, success); + if (!success) return false; } if (quota_database.meta_table_->GetVersionNumber() == 6) { - if (!MigrateFromVersion6ToVersion7(quota_database)) + bool success = MigrateFromVersion6ToVersion7(quota_database); + RecordMigrationHistogram(/*old_version=*/6, /*new_version=*/7, success); + if (!success) return false; } if (quota_database.meta_table_->GetVersionNumber() == 7) { - if (!MigrateFromVersion7ToVersion8(quota_database)) + bool success = MigrateFromVersion7ToVersion8(quota_database); + RecordMigrationHistogram(/*old_version=*/7, /*new_version=*/8, success); + if (!success) return false; } if (quota_database.meta_table_->GetVersionNumber() == 8) { - if (!MigrateFromVersion8ToVersion9(quota_database)) + bool success = MigrateFromVersion8ToVersion9(quota_database); + RecordMigrationHistogram(/*old_version=*/8, /*new_version=*/9, success); + if (!success) return false; } return quota_database.meta_table_->GetVersionNumber() == 9; } +void QuotaDatabaseMigrations::RecordMigrationHistogram(int old_version, + int new_version, + bool success) { + base::UmaHistogramBoolean( + base::StrCat({"Quota.DatabaseMigrationFromV", + base::NumberToString(old_version), "ToV", + base::NumberToString(new_version)}), + success); +} + bool QuotaDatabaseMigrations::MigrateFromVersion5ToVersion7( QuotaDatabase& quota_database) { DCHECK_CALLED_ON_VALID_SEQUENCE(quota_database.sequence_checker_);
diff --git a/storage/browser/quota/quota_database_migrations.h b/storage/browser/quota/quota_database_migrations.h index 633f195..d09cc24 100644 --- a/storage/browser/quota/quota_database_migrations.h +++ b/storage/browser/quota/quota_database_migrations.h
@@ -23,6 +23,10 @@ static bool UpgradeSchema(QuotaDatabase& quota_database); private: + static void RecordMigrationHistogram(int old_version, + int new_version, + bool success); + static bool MigrateFromVersion5ToVersion7(QuotaDatabase& quota_database); static bool MigrateFromVersion6ToVersion7(QuotaDatabase& quota_database); static bool MigrateFromVersion7ToVersion8(QuotaDatabase& quota_database);
diff --git a/storage/browser/quota/quota_database_migrations_unittest.cc b/storage/browser/quota/quota_database_migrations_unittest.cc index 2268b2d..0e40be1 100644 --- a/storage/browser/quota/quota_database_migrations_unittest.cc +++ b/storage/browser/quota/quota_database_migrations_unittest.cc
@@ -2,9 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <memory> + #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/path_service.h" +#include "base/test/metrics/histogram_tester.h" #include "components/services/storage/public/cpp/constants.h" #include "sql/database.h" #include "sql/meta_table.h" @@ -30,7 +33,10 @@ class QuotaDatabaseMigrationsTest : public testing::Test { public: - void SetUp() override { ASSERT_TRUE(temp_directory_.CreateUniqueTempDir()); } + void SetUp() override { + ASSERT_TRUE(temp_directory_.CreateUniqueTempDir()); + histograms_ = std::make_unique<base::HistogramTester>(); + } base::FilePath ProfilePath() { return temp_directory_.GetPath(); } @@ -93,7 +99,13 @@ return db.db_->GetSchema(); } + size_t GetTotalHistogramCount() { + return histograms_->GetTotalCountsForPrefix("Quota.DatabaseMigration") + .size(); + } + base::ScopedTempDir temp_directory_; + std::unique_ptr<base::HistogramTester> histograms_; }; // Verify that the schema created by a new `QuotaDatabase` instance matches the @@ -172,6 +184,14 @@ &db, "SELECT host FROM quota ORDER BY host ASC", "|", ",")); EXPECT_EQ(GetCurrentSchema(), RemoveQuotes(db.GetSchema())); + + EXPECT_EQ(GetTotalHistogramCount(), 3u); + histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV5ToV7", + /*sample=*/true, /*expected_count=*/1); + histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV7ToV8", + /*sample=*/true, /*expected_count=*/1); + histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV8ToV9", + /*sample=*/true, /*expected_count=*/1); } } @@ -244,6 +264,14 @@ &db, "SELECT host FROM quota ORDER BY host ASC", "|", ",")); EXPECT_EQ(GetCurrentSchema(), RemoveQuotes(db.GetSchema())); + + EXPECT_EQ(GetTotalHistogramCount(), 3u); + histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV6ToV7", + /*sample=*/true, /*expected_count=*/1); + histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV7ToV8", + /*sample=*/true, /*expected_count=*/1); + histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV8ToV9", + /*sample=*/true, /*expected_count=*/1); } } @@ -315,6 +343,12 @@ &db, "SELECT host FROM quota ORDER BY host ASC", "|", ",")); EXPECT_EQ(GetCurrentSchema(), RemoveQuotes(db.GetSchema())); + + EXPECT_EQ(GetTotalHistogramCount(), 2u); + histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV7ToV8", + /*sample=*/true, /*expected_count=*/1); + histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV8ToV9", + /*sample=*/true, /*expected_count=*/1); } } @@ -392,6 +426,10 @@ &db, "SELECT host FROM quota ORDER BY host ASC", "|", ",")); EXPECT_EQ(GetCurrentSchema(), RemoveQuotes(db.GetSchema())); + + EXPECT_EQ(GetTotalHistogramCount(), 1u); + histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV8ToV9", + /*sample=*/true, /*expected_count=*/1); } }
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json index 0a37826e..4d36701 100644 --- a/testing/buildbot/chromium.chromiumos.json +++ b/testing/buildbot/chromium.chromiumos.json
@@ -5923,9 +5923,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5431.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5432.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5431.0", + "description": "Run with ash-chrome version 110.0.5432.0", "isolate_profile_data": true, "merge": { "args": [], @@ -5937,8 +5937,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5431.0", - "revision": "version:110.0.5431.0" + "location": "lacros_version_skew_tests_v110.0.5432.0", + "revision": "version:110.0.5432.0" } ], "dimension_sets": [ @@ -6090,9 +6090,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5431.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5432.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5431.0", + "description": "Run with ash-chrome version 110.0.5432.0", "isolate_profile_data": true, "merge": { "args": [], @@ -6104,8 +6104,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5431.0", - "revision": "version:110.0.5431.0" + "location": "lacros_version_skew_tests_v110.0.5432.0", + "revision": "version:110.0.5432.0" } ], "dimension_sets": [ @@ -6242,9 +6242,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5431.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5432.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5431.0", + "description": "Run with ash-chrome version 110.0.5432.0", "isolate_profile_data": true, "merge": { "args": [], @@ -6256,8 +6256,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5431.0", - "revision": "version:110.0.5431.0" + "location": "lacros_version_skew_tests_v110.0.5432.0", + "revision": "version:110.0.5432.0" } ], "dimension_sets": [
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index 32454d3..b3209b2 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -3670,1949 +3670,6 @@ } ] }, - "Linux MSan Focal": { - "gtest_tests": [ - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "absl_hardening_tests", - "test_id_prefix": "ninja://third_party/abseil-cpp:absl_hardening_tests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "accessibility_unittests", - "test_id_prefix": "ninja://ui/accessibility:accessibility_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "angle_unittests", - "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/", - "use_isolated_scripts_api": true - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "app_shell_unittests", - "test_id_prefix": "ninja://extensions/shell:app_shell_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "aura_unittests", - "test_id_prefix": "ninja://ui/aura:aura_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "base_unittests", - "test_id_prefix": "ninja://base:base_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "blink_common_unittests", - "test_id_prefix": "ninja://third_party/blink/common:blink_common_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "blink_fuzzer_unittests", - "test_id_prefix": "ninja://third_party/blink/renderer/platform:blink_fuzzer_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "blink_heap_unittests", - "test_id_prefix": "ninja://third_party/blink/renderer/platform/heap:blink_heap_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "blink_platform_unittests", - "test_id_prefix": "ninja://third_party/blink/renderer/platform:blink_platform_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "name": "webkit_unit_tests", - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "blink_unittests", - "test_id_prefix": "ninja://third_party/blink/renderer/controller:blink_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "boringssl_crypto_tests", - "test_id_prefix": "ninja://third_party/boringssl:boringssl_crypto_tests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "boringssl_ssl_tests", - "test_id_prefix": "ninja://third_party/boringssl:boringssl_ssl_tests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 10 - }, - "test": "browser_tests", - "test_id_prefix": "ninja://chrome/test:browser_tests/" - }, - { - "args": [ - "--gtest_filter=-*UsingRealWebcam*", - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "capture_unittests", - "test_id_prefix": "ninja://media/capture:capture_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "cast_unittests", - "test_id_prefix": "ninja://media/cast:cast_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "cc_unittests", - "test_id_prefix": "ninja://cc:cc_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "chrome_app_unittests", - "test_id_prefix": "ninja://chrome/test:chrome_app_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "chromedriver_unittests", - "test_id_prefix": "ninja://chrome/test/chromedriver:chromedriver_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "color_unittests", - "test_id_prefix": "ninja://ui/color:color_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "components_browsertests", - "test_id_prefix": "ninja://components:components_browsertests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "components_unittests", - "test_id_prefix": "ninja://components:components_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "compositor_unittests", - "test_id_prefix": "ninja://ui/compositor:compositor_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 8 - }, - "test": "content_browsertests", - "test_id_prefix": "ninja://content/test:content_browsertests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "content_unittests", - "test_id_prefix": "ninja://content/test:content_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "crashpad_tests", - "test_id_prefix": "ninja://third_party/crashpad/crashpad:crashpad_tests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "cronet_tests", - "test_id_prefix": "ninja://components/cronet:cronet_tests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "cronet_unittests", - "test_id_prefix": "ninja://components/cronet:cronet_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "crypto_unittests", - "test_id_prefix": "ninja://crypto:crypto_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "dbus_unittests", - "test_id_prefix": "ninja://dbus:dbus_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "device_unittests", - "test_id_prefix": "ninja://device:device_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "display_unittests", - "test_id_prefix": "ninja://ui/display:display_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "events_unittests", - "test_id_prefix": "ninja://ui/events:events_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "extensions_browsertests", - "test_id_prefix": "ninja://extensions:extensions_browsertests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "extensions_unittests", - "test_id_prefix": "ninja://extensions:extensions_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "filesystem_service_unittests", - "test_id_prefix": "ninja://components/services/filesystem:filesystem_service_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gcm_unit_tests", - "test_id_prefix": "ninja://google_apis/gcm:gcm_unit_tests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gfx_unittests", - "test_id_prefix": "ninja://ui/gfx:gfx_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gin_unittests", - "test_id_prefix": "ninja://gin:gin_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "google_apis_unittests", - "test_id_prefix": "ninja://google_apis:google_apis_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gpu_unittests", - "test_id_prefix": "ninja://gpu:gpu_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gwp_asan_unittests", - "test_id_prefix": "ninja://components/gwp_asan:gwp_asan_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "headless_browsertests", - "test_id_prefix": "ninja://headless:headless_browsertests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "headless_unittests", - "test_id_prefix": "ninja://headless:headless_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 3 - }, - "test": "interactive_ui_tests", - "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "ipc_tests", - "test_id_prefix": "ninja://ipc:ipc_tests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "latency_unittests", - "test_id_prefix": "ninja://ui/latency:latency_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "libjingle_xmpp_unittests", - "test_id_prefix": "ninja://third_party/libjingle_xmpp:libjingle_xmpp_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "liburlpattern_unittests", - "test_id_prefix": "ninja://third_party/liburlpattern:liburlpattern_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "media_unittests", - "test_id_prefix": "ninja://media:media_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "message_center_unittests", - "test_id_prefix": "ninja://ui/message_center:message_center_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "midi_unittests", - "test_id_prefix": "ninja://media/midi:midi_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "mojo_core_unittests", - "test_id_prefix": "ninja://mojo/core:mojo_core_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "mojo_unittests", - "test_id_prefix": "ninja://mojo:mojo_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "nacl_loader_unittests", - "test_id_prefix": "ninja://components/nacl/loader:nacl_loader_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "native_theme_unittests", - "test_id_prefix": "ninja://ui/native_theme:native_theme_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "net_unittests", - "test_id_prefix": "ninja://net:net_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "openscreen_unittests", - "test_id_prefix": "ninja://chrome/browser/media/router:openscreen_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "ozone_x11_unittests", - "test_id_prefix": "ninja://ui/ozone:ozone_x11_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "pdf_unittests", - "test_id_prefix": "ninja://pdf:pdf_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "perfetto_unittests", - "test_id_prefix": "ninja://third_party/perfetto:perfetto_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "ppapi_unittests", - "test_id_prefix": "ninja://ppapi:ppapi_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "printing_unittests", - "test_id_prefix": "ninja://printing:printing_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "remoting_unittests", - "test_id_prefix": "ninja://remoting:remoting_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "sandbox_linux_unittests", - "test_id_prefix": "ninja://sandbox/linux:sandbox_linux_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "service_manager_unittests", - "test_id_prefix": "ninja://services/service_manager/tests:service_manager_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "shell_dialogs_unittests", - "test_id_prefix": "ninja://ui/shell_dialogs:shell_dialogs_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "skia_unittests", - "test_id_prefix": "ninja://skia:skia_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "snapshot_unittests", - "test_id_prefix": "ninja://ui/snapshot:snapshot_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "sql_unittests", - "test_id_prefix": "ninja://sql:sql_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "storage_unittests", - "test_id_prefix": "ninja://storage:storage_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "sync_integration_tests", - "test_id_prefix": "ninja://chrome/test:sync_integration_tests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "ui_base_unittests", - "test_id_prefix": "ninja://ui/base:ui_base_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "ui_touch_selection_unittests", - "test_id_prefix": "ninja://ui/touch_selection:ui_touch_selection_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "unit_tests", - "test_id_prefix": "ninja://chrome/test:unit_tests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "url_unittests", - "test_id_prefix": "ninja://url:url_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "views_unittests", - "test_id_prefix": "ninja://ui/views:views_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "viz_unittests", - "test_id_prefix": "ninja://components/viz:viz_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "vr_common_unittests", - "test_id_prefix": "ninja://chrome/browser/vr:vr_common_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "vr_pixeltests", - "test_id_prefix": "ninja://chrome/browser/vr:vr_pixeltests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "wm_unittests", - "test_id_prefix": "ninja://ui/wm:wm_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "wtf_unittests", - "test_id_prefix": "ninja://third_party/blink/renderer/platform/wtf:wtf_unittests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "name": "xr_browser_tests", - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "xr_browser_tests", - "test_id_prefix": "ninja://chrome/test:xr_browser_tests/" - }, - { - "args": [ - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-20.04" - } - ], - "expiration": 10800, - "hard_timeout": 7200, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "zlib_unittests", - "test_id_prefix": "ninja://third_party/zlib:zlib_unittests/" - } - ] - }, "Linux Viz": { "additional_compile_targets": [ "all" @@ -87397,9 +85454,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5431.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5432.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5431.0", + "description": "Run with ash-chrome version 110.0.5432.0", "isolate_profile_data": true, "merge": { "args": [], @@ -87411,8 +85468,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5431.0", - "revision": "version:110.0.5431.0" + "location": "lacros_version_skew_tests_v110.0.5432.0", + "revision": "version:110.0.5432.0" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", @@ -87534,9 +85591,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5431.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5432.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5431.0", + "description": "Run with ash-chrome version 110.0.5432.0", "isolate_profile_data": true, "merge": { "args": [], @@ -87548,8 +85605,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5431.0", - "revision": "version:110.0.5431.0" + "location": "lacros_version_skew_tests_v110.0.5432.0", + "revision": "version:110.0.5432.0" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -87661,9 +85718,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5431.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5432.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5431.0", + "description": "Run with ash-chrome version 110.0.5432.0", "isolate_profile_data": true, "merge": { "args": [], @@ -87675,8 +85732,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5431.0", - "revision": "version:110.0.5431.0" + "location": "lacros_version_skew_tests_v110.0.5432.0", + "revision": "version:110.0.5432.0" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", @@ -89017,9 +87074,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5431.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5432.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5431.0", + "description": "Run with ash-chrome version 110.0.5432.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -89030,8 +87087,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5431.0", - "revision": "version:110.0.5431.0" + "location": "lacros_version_skew_tests_v110.0.5432.0", + "revision": "version:110.0.5432.0" } ], "dimension_sets": [ @@ -89184,9 +87241,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5431.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5432.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5431.0", + "description": "Run with ash-chrome version 110.0.5432.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -89197,8 +87254,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5431.0", - "revision": "version:110.0.5431.0" + "location": "lacros_version_skew_tests_v110.0.5432.0", + "revision": "version:110.0.5432.0" } ], "dimension_sets": [ @@ -89336,9 +87393,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5431.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5432.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5431.0", + "description": "Run with ash-chrome version 110.0.5432.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -89349,8 +87406,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5431.0", - "revision": "version:110.0.5431.0" + "location": "lacros_version_skew_tests_v110.0.5432.0", + "revision": "version:110.0.5432.0" } ], "dimension_sets": [ @@ -90872,9 +88929,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5431.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5432.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5431.0", + "description": "Run with ash-chrome version 110.0.5432.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -90885,8 +88942,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5431.0", - "revision": "version:110.0.5431.0" + "location": "lacros_version_skew_tests_v110.0.5432.0", + "revision": "version:110.0.5432.0" } ], "dimension_sets": [ @@ -91039,9 +89096,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5431.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5432.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5431.0", + "description": "Run with ash-chrome version 110.0.5432.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -91052,8 +89109,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5431.0", - "revision": "version:110.0.5431.0" + "location": "lacros_version_skew_tests_v110.0.5432.0", + "revision": "version:110.0.5432.0" } ], "dimension_sets": [ @@ -91191,9 +89248,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5431.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5432.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5431.0", + "description": "Run with ash-chrome version 110.0.5432.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -91204,8 +89261,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5431.0", - "revision": "version:110.0.5431.0" + "location": "lacros_version_skew_tests_v110.0.5432.0", + "revision": "version:110.0.5432.0" } ], "dimension_sets": [ @@ -91871,37 +89928,6 @@ } ] }, - "linux-lacros-tester-rel-reviver": { - "additional_compile_targets": [ - "chrome", - "chrome_sandbox", - "symupload" - ], - "gtest_tests": [ - { - "args": [ - "--gtest_also_run_disabled_tests" - ], - "isolate_profile_data": true, - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "os": "Ubuntu-18.04" - } - ], - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 10 - }, - "test": "browser_tests", - "test_id_prefix": "ninja://chrome/test:browser_tests/" - } - ] - }, "linux-lacros-version-skew-fyi": { "additional_compile_targets": [ "chrome" @@ -92004,9 +90030,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5431.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5432.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5431.0", + "description": "Run with ash-chrome version 110.0.5432.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -92017,8 +90043,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5431.0", - "revision": "version:110.0.5431.0" + "location": "lacros_version_skew_tests_v110.0.5432.0", + "revision": "version:110.0.5432.0" } ], "dimension_sets": [ @@ -100188,44 +98214,6 @@ }, { "args": [ - "--num-retries=3", - "--write-run-histories-to=${ISOLATED_OUTDIR}/run_histories.json", - "--git-revision=${got_revision}" - ], - "check_flakiness_for_new_tests": false, - "isolate_name": "blink_wpt_tests", - "merge": { - "args": [ - "--verbose" - ], - "script": "//third_party/blink/tools/merge_web_test_results.py" - }, - "name": "blink_wpt_tests", - "precommit_args": [ - "--gerrit-issue=${patch_issue}", - "--gerrit-patchset=${patch_set}", - "--buildbucket-id=${buildbucket_build_id}" - ], - "resultdb": { - "enable": true - }, - "results_handler": "layout tests", - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Mac-13" - } - ], - "expiration": 21600, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 7 - }, - "test_id_prefix": "ninja://:blink_wpt_tests/" - }, - { - "args": [ "--test-type=integration" ], "check_flakiness_for_new_tests": false,
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json index b0673d0..eec7875 100644 --- a/testing/buildbot/chromium.memory.json +++ b/testing/buildbot/chromium.memory.json
@@ -7123,7 +7123,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7144,7 +7144,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7165,7 +7165,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7187,7 +7187,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7208,7 +7208,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7229,7 +7229,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7250,7 +7250,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7271,7 +7271,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7292,7 +7292,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7313,7 +7313,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7335,7 +7335,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7356,7 +7356,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7377,7 +7377,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7398,7 +7398,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", @@ -7421,7 +7421,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7442,7 +7442,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7463,7 +7463,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7484,7 +7484,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7505,7 +7505,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7526,7 +7526,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7547,7 +7547,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7568,7 +7568,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7589,7 +7589,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7610,7 +7610,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", @@ -7632,7 +7632,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7653,7 +7653,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7674,7 +7674,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7695,7 +7695,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7716,7 +7716,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7737,7 +7737,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7758,7 +7758,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7779,7 +7779,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7800,7 +7800,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7821,7 +7821,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7842,7 +7842,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7863,7 +7863,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7884,7 +7884,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7905,7 +7905,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7926,7 +7926,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7947,7 +7947,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7968,7 +7968,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -7989,7 +7989,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8010,7 +8010,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8031,7 +8031,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8052,7 +8052,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", @@ -8095,7 +8095,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8116,7 +8116,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8137,7 +8137,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8158,7 +8158,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8179,7 +8179,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8200,7 +8200,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8221,7 +8221,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8242,7 +8242,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8263,7 +8263,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8284,7 +8284,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8305,7 +8305,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8326,7 +8326,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8347,7 +8347,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8368,7 +8368,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8389,7 +8389,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8410,7 +8410,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8431,7 +8431,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8452,7 +8452,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8473,7 +8473,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8494,7 +8494,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8515,7 +8515,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8536,7 +8536,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8557,7 +8557,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8578,7 +8578,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8599,7 +8599,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8620,7 +8620,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8641,7 +8641,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8662,7 +8662,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8683,7 +8683,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8704,7 +8704,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8725,7 +8725,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8746,7 +8746,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8767,7 +8767,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8788,7 +8788,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8809,7 +8809,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8830,7 +8830,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8852,7 +8852,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -8873,7 +8873,7 @@ "dimension_sets": [ { "cpu": "x86-64", - "os": "Ubuntu-16.04" + "os": "Ubuntu-20.04" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -18604,11 +18604,11 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5431.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5432.0/test_ash_chrome", "--test-launcher-print-test-stdio=always", "--combine-ash-logs-on-bots" ], - "description": "Run with ash-chrome version 110.0.5431.0", + "description": "Run with ash-chrome version 110.0.5432.0", "isolate_profile_data": true, "merge": { "args": [], @@ -18620,8 +18620,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5431.0", - "revision": "version:110.0.5431.0" + "location": "lacros_version_skew_tests_v110.0.5432.0", + "revision": "version:110.0.5432.0" } ], "dimension_sets": [ @@ -18785,11 +18785,11 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5431.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5432.0/test_ash_chrome", "--test-launcher-print-test-stdio=always", "--combine-ash-logs-on-bots" ], - "description": "Run with ash-chrome version 110.0.5431.0", + "description": "Run with ash-chrome version 110.0.5432.0", "isolate_profile_data": true, "merge": { "args": [], @@ -18801,8 +18801,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5431.0", - "revision": "version:110.0.5431.0" + "location": "lacros_version_skew_tests_v110.0.5432.0", + "revision": "version:110.0.5432.0" } ], "dimension_sets": [ @@ -18947,11 +18947,11 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5431.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5432.0/test_ash_chrome", "--test-launcher-print-test-stdio=always", "--combine-ash-logs-on-bots" ], - "description": "Run with ash-chrome version 110.0.5431.0", + "description": "Run with ash-chrome version 110.0.5432.0", "isolate_profile_data": true, "merge": { "args": [], @@ -18963,8 +18963,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5431.0", - "revision": "version:110.0.5431.0" + "location": "lacros_version_skew_tests_v110.0.5432.0", + "revision": "version:110.0.5432.0" } ], "dimension_sets": [
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl index e23031b..619c712 100644 --- a/testing/buildbot/mixins.pyl +++ b/testing/buildbot/mixins.pyl
@@ -533,13 +533,6 @@ }, }, }, - 'gtest_run_disabled': { - '$mixin_append': { - 'args': [ - '--gtest_also_run_disabled_tests', - ], - }, - }, # Use of this mixin signals to the recipe that the test uploads its results # to result-sink and doesn't need to be wrapped by result_adapter. 'has_native_resultdb_integration': {
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl index e7c8c5c..c240bee 100644 --- a/testing/buildbot/test_suite_exceptions.pyl +++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -549,6 +549,7 @@ 'Win10 Tests x64 (dbg)', 'devtools_frontend_linux_blink_light_rel', 'devtools_frontend_linux_blink_light_rel_fastbuild', + 'mac-osxbeta-rel', ], 'modifications': { 'Linux Tests': { @@ -2125,7 +2126,6 @@ # Can't run on MSAN because gl_tests uses the hardware driver, # which isn't instrumented. 'Linux MSan Tests', - 'Linux MSan Focal', ], 'modifications': { # TODO(kbr): figure out a better way to specify blocks of @@ -2251,7 +2251,6 @@ # Can't run on MSAN because gl_unittests uses the hardware driver, # which isn't instrumented. 'Linux MSan Tests', - 'Linux MSan Focal', ], 'modifications': { 'Win10 FYI x64 Exp Release (Intel HD 630)': { @@ -3191,7 +3190,6 @@ 'Linux ChromiumOS MSan Tests', # https://crbug.com/831676 'Linux ChromiumOS MSan Focal', 'Linux MSan Tests', # https://crbug.com/831676 - 'Linux MSan Focal', # https://crbug.com/831676 ], 'replacements': { # TODO(crbug.com/1078982): Remove once the test is fixed on 10.15.4.
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl index 9e14bcc..7a86a62 100644 --- a/testing/buildbot/test_suites.pyl +++ b/testing/buildbot/test_suites.pyl
@@ -4140,14 +4140,6 @@ }, }, - 'linux_lacros_reviver_tests': { - 'browser_tests': { - 'swarming': { - 'shards': 10, - }, - }, - }, - 'linux_lacros_specific_gtests': { 'lacros_chrome_unittests': {}, 'ozone_unittests': {},
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl index 8d5c3e9..763fd76c 100644 --- a/testing/buildbot/variants.pyl +++ b/testing/buildbot/variants.pyl
@@ -22,16 +22,16 @@ }, 'LACROS_VERSION_SKEW_CANARY': { 'args': [ - '--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5431.0/test_ash_chrome', + '--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5432.0/test_ash_chrome', ], - 'description': 'Run with ash-chrome version 110.0.5431.0', + 'description': 'Run with ash-chrome version 110.0.5432.0', 'identifier': 'Lacros version skew testing ash canary', 'swarming': { 'cipd_packages': [ { 'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip', - 'location': 'lacros_version_skew_tests_v110.0.5431.0', - 'revision': 'version:110.0.5431.0', + 'location': 'lacros_version_skew_tests_v110.0.5432.0', + 'revision': 'version:110.0.5432.0', }, ], },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl index d9f2f5d1..a6c216de 100644 --- a/testing/buildbot/waterfalls.pyl +++ b/testing/buildbot/waterfalls.pyl
@@ -2724,22 +2724,6 @@ '--test-launcher-print-test-stdio=always', ], }, - 'Linux MSan Focal': { - 'mixins': [ - 'linux-focal', - 'x86-64', - ], - 'swarming': { - 'expiration': 10800, - 'hard_timeout': 7200, - }, - 'test_suites': { - 'gtest_tests': 'chromium_linux_and_gl_gtests', - }, - 'args': [ - '--test-launcher-print-test-stdio=always', - ], - }, 'Linux Viz': { 'mixins': [ 'linux-bionic', @@ -3421,21 +3405,6 @@ 'gtest_tests': 'linux_lacros_gtests', }, }, - 'linux-lacros-tester-rel-reviver': { - 'mixins': [ - 'isolate_profile_data', - 'linux-bionic', - 'gtest_run_disabled', - ], - 'additional_compile_targets': [ - 'chrome', - 'chrome_sandbox', - 'symupload' - ], - 'test_suites': { - 'gtest_tests': 'linux_lacros_reviver_tests', - }, - }, 'linux-lacros-version-skew-fyi': { 'additional_compile_targets': [ 'chrome', @@ -5055,7 +5024,7 @@ }, 'Linux MSan Tests': { 'mixins': [ - 'linux-xenial', + 'linux-focal', 'x86-64', ], 'test_suites': {
diff --git a/testing/scripts/wpt_common.py b/testing/scripts/wpt_common.py index cc9281c..d978012f 100644 --- a/testing/scripts/wpt_common.py +++ b/testing/scripts/wpt_common.py
@@ -362,7 +362,7 @@ if self.wptreport: command.extend(['--wpt-report', self.wptreport]) - common.run_command(command) + return common.run_command(command) def clean_up_after_test_run(self): if self._include_filename:
diff --git a/testing/test.gni b/testing/test.gni index cd37d02d..2be7351 100644 --- a/testing/test.gni +++ b/testing/test.gni
@@ -494,9 +494,6 @@ } use_v2_script = use_v2_script_default - if (target_cpu == "x64") { - use_v2_script = false - } if (defined(invoker.fuchsia_legacy_script_required) && invoker.fuchsia_legacy_script_required) { use_v2_script = false
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 1419be8..b024a2b 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -2011,31 +2011,6 @@ ] } ], - "BrokeringDiskCacheOnAndroid": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "OutOfProcessNetworkService", - "disable_features": [ - "BrokerFileOperationsOnDiskCacheInNetworkService", - "NetworkServiceInProcess2" - ] - }, - { - "name": "BrokerDiskCache", - "enable_features": [ - "BrokerFileOperationsOnDiskCacheInNetworkService" - ], - "disable_features": [ - "NetworkServiceInProcess2" - ] - } - ] - } - ], "BrowserPeriodicYieldingToNative": [ { "platforms": [ @@ -5695,37 +5670,6 @@ ] } ], - "IOSNewOverflowMenu": [ - { - "platforms": [ - "ios" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "NewOverflowMenu" - ] - } - ] - } - ], - "IOSNewOverflowMenuExperiments": [ - { - "platforms": [ - "ios" - ], - "experiments": [ - { - "name": "BothActions", - "enable_features": [ - "NewOverflowMenuCBDAction", - "NewOverflowMenuSettingsAction" - ] - } - ] - } - ], "IOSNewOverflowMenuIPH": [ { "platforms": [ @@ -6527,6 +6471,44 @@ ] } ], + "LocalWebApprovals": [ + { + "platforms": [ + "android" + ], + "experiments": [ + { + "name": "PreferLocal_481152618", + "params": { + "preferred_button": "local" + }, + "enable_features": [ + "LocalWebApprovals", + "WebFilterInterstitialRefresh" + ] + }, + { + "name": "PreferRemote_481152618", + "params": { + "preferred_button": "remote" + }, + "enable_features": [ + "LocalWebApprovals", + "WebFilterInterstitialRefresh" + ] + }, + { + "name": "RemoteOnly_481152618", + "enable_features": [ + "WebFilterInterstitialRefresh" + ], + "disable_features": [ + "LocalWebApprovals" + ] + } + ] + } + ], "LowerMemoryLimitForNonMainRenderers": [ { "platforms": [ @@ -7805,6 +7787,26 @@ ] } ], + "OriginAgentClusterDefaultEnabled": [ + { + "platforms": [ + "android", + "chromeos", + "chromeos_lacros", + "linux", + "mac", + "windows" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "OriginAgentClusterDefaultEnabled" + ] + } + ] + } + ], "OutOfProcessSystemDnsResolution": [ { "platforms": [ @@ -12161,6 +12163,25 @@ ] } ], + "WebContentsCaptureHiDPI": [ + { + "platforms": [ + "chromeos", + "chromeos_lacros", + "linux", + "mac", + "windows" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "WebContentsCaptureHiDPI" + ] + } + ] + } + ], "WebFeedsMVP": [ { "platforms": [
diff --git a/third_party/blink/common/notifications/notification_mojom_traits.cc b/third_party/blink/common/notifications/notification_mojom_traits.cc index f6ee5e7..2fa1944 100644 --- a/third_party/blink/common/notifications/notification_mojom_traits.cc +++ b/third_party/blink/common/notifications/notification_mojom_traits.cc
@@ -66,7 +66,8 @@ !notification_data.ReadActions(&platform_notification_data->actions) || !notification_data.ReadData(&data) || !notification_data.ReadShowTriggerTimestamp( - &platform_notification_data->show_trigger_timestamp)) { + &platform_notification_data->show_trigger_timestamp) || + !notification_data.ReadScenario(&platform_notification_data->scenario)) { return false; }
diff --git a/third_party/blink/common/notifications/notification_mojom_traits_unittest.cc b/third_party/blink/common/notifications/notification_mojom_traits_unittest.cc index 34b6b446..0a7a634 100644 --- a/third_party/blink/common/notifications/notification_mojom_traits_unittest.cc +++ b/third_party/blink/common/notifications/notification_mojom_traits_unittest.cc
@@ -55,6 +55,7 @@ notification_data.silent = true; notification_data.require_interaction = true; notification_data.show_trigger_timestamp = base::Time::Now(); + notification_data.scenario = mojom::NotificationScenario::INCOMING_CALL; const char data[] = "mock binary notification data"; notification_data.data.assign(data, data + std::size(data)); @@ -115,6 +116,7 @@ } EXPECT_EQ(roundtrip_notification_data.show_trigger_timestamp, notification_data.show_trigger_timestamp); + EXPECT_EQ(roundtrip_notification_data.scenario, notification_data.scenario); } // Check upper bound on vibration entries (99).
diff --git a/third_party/blink/common/notifications/platform_notification_data.cc b/third_party/blink/common/notifications/platform_notification_data.cc index b838e21..16a6813 100644 --- a/third_party/blink/common/notifications/platform_notification_data.cc +++ b/third_party/blink/common/notifications/platform_notification_data.cc
@@ -8,7 +8,8 @@ namespace blink { PlatformNotificationData::PlatformNotificationData() - : direction(mojom::NotificationDirection::LEFT_TO_RIGHT) {} + : direction(mojom::NotificationDirection::LEFT_TO_RIGHT), + scenario(mojom::NotificationScenario::DEFAULT) {} PlatformNotificationData::PlatformNotificationData( const PlatformNotificationData& other) { @@ -37,6 +38,7 @@ for (auto& action : other.actions) actions.push_back(action.Clone()); show_trigger_timestamp = other.show_trigger_timestamp; + scenario = other.scenario; return *this; }
diff --git a/third_party/blink/perf_tests/base64/atob-whitespace.html b/third_party/blink/perf_tests/base64/atob-whitespace.html index 8acc2f6..fb36458 100644 --- a/third_party/blink/perf_tests/base64/atob-whitespace.html +++ b/third_party/blink/perf_tests/base64/atob-whitespace.html
@@ -5,15 +5,15 @@ <script> var longStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ "; for (var i = 0; i < 15; i++) - longStr = longStr + longStr; + longStr = longStr + longStr; PerfTestRunner.measureRunsPerSecond({ - description: "This benchmark covers `atob` where the input has whitespace.", - setup: function() { - }, - run: function() { - atob(longStr); -}}); +description: "This benchmark covers `atob` where the input has whitespace.", + setup: function() {}, + run: function() { + atob(longStr); + } +}); </script> </body> </html>
diff --git a/third_party/blink/perf_tests/base64/atob.html b/third_party/blink/perf_tests/base64/atob.html index 0cbe62a8..4ba7d902 100644 --- a/third_party/blink/perf_tests/base64/atob.html +++ b/third_party/blink/perf_tests/base64/atob.html
@@ -5,15 +5,15 @@ <script> var longStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; for (var i = 0; i < 15; i++) - longStr = longStr + longStr; + longStr = longStr + longStr; PerfTestRunner.measureRunsPerSecond({ - description: "This benchmark covers `atob`.", - setup: function() { - }, - run: function() { - atob(longStr); -}}); +description: "This benchmark covers `atob`.", + setup: function() {}, + run: function() { + atob(longStr); + } +}); </script> </body> </html>
diff --git a/third_party/blink/perf_tests/base64/btoa.html b/third_party/blink/perf_tests/base64/btoa.html index 7c14760..5346a89 100644 --- a/third_party/blink/perf_tests/base64/btoa.html +++ b/third_party/blink/perf_tests/base64/btoa.html
@@ -5,16 +5,16 @@ <script> var longStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; for (var i = 0; i < 15; i++) - longStr = longStr + longStr; + longStr = longStr + longStr; var binary = atob(longStr); PerfTestRunner.measureRunsPerSecond({ - description: "This benchmark covers `btoa`.", - setup: function() { - }, - run: function() { - btoa(binary); -}}); +description: "This benchmark covers `btoa`.", + setup: function() {}, + run: function() { + btoa(binary); + } +}); </script> </body> </html>
diff --git a/third_party/blink/public/common/notifications/notification_mojom_traits.h b/third_party/blink/public/common/notifications/notification_mojom_traits.h index b358eac..22af574 100644 --- a/third_party/blink/public/common/notifications/notification_mojom_traits.h +++ b/third_party/blink/public/common/notifications/notification_mojom_traits.h
@@ -100,6 +100,11 @@ return data.show_trigger_timestamp; } + static blink::mojom::NotificationScenario scenario( + const blink::PlatformNotificationData& data) { + return data.scenario; + } + static bool Read(blink::mojom::NotificationDataDataView notification_data, blink::PlatformNotificationData* platform_notification_data); };
diff --git a/third_party/blink/public/common/notifications/platform_notification_data.h b/third_party/blink/public/common/notifications/platform_notification_data.h index 43a3394..a541e69 100644 --- a/third_party/blink/public/common/notifications/platform_notification_data.h +++ b/third_party/blink/public/common/notifications/platform_notification_data.h
@@ -80,6 +80,8 @@ // The time at which the notification should be shown. absl::optional<base::Time> show_trigger_timestamp; + + mojom::NotificationScenario scenario; }; } // namespace blink
diff --git a/third_party/blink/public/mojom/notifications/notification.mojom b/third_party/blink/public/mojom/notifications/notification.mojom index 6aa8b4d..bd002fc 100644 --- a/third_party/blink/public/mojom/notifications/notification.mojom +++ b/third_party/blink/public/mojom/notifications/notification.mojom
@@ -22,6 +22,12 @@ TEXT }; +// Scenarios that define the notification behavior. +enum NotificationScenario { + DEFAULT, + INCOMING_CALL +}; + // Structure representing an action button associated with a Notification. struct NotificationAction { // Type of action this structure represents. @@ -107,6 +113,13 @@ // Time when to show this notification. mojo_base.mojom.Time? show_trigger_timestamp; + + // The notification scenario can be either DEFAULT or INCOMING_CALL. If the + // latter was selected and the Notification was triggered by an installed web + // app, it should have a ringtone, customized action buttons, and increased + // priority. See the proposal's explainer at: + // https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Notifications/notifications_actions_customization.md + NotificationScenario scenario = DEFAULT; }; // Structure representing the resources associated with a Web Notification.
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni index d3eabc0..1177bcc 100644 --- a/third_party/blink/renderer/bindings/generated_in_modules.gni +++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -1304,6 +1304,8 @@ "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_notification_direction.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_notification_permission.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_notification_permission.h", + "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_notification_scenario.cc", + "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_notification_scenario.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_offscreen_rendering_context_type.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_offscreen_rendering_context_type.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_opus_bitstream_format.cc",
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect.cc b/third_party/blink/renderer/core/animation/keyframe_effect.cc index ba14466..20564a3 100644 --- a/third_party/blink/renderer/core/animation/keyframe_effect.cc +++ b/third_party/blink/renderer/core/animation/keyframe_effect.cc
@@ -783,11 +783,15 @@ if (removed) ApplyEffects(); + auto property_pass_filter = [](const PropertyHandle& property) { + return property.IsCSSProperty(); + }; + ActiveInterpolationsMap results = EffectStack::ActiveInterpolations( &target()->GetElementAnimations()->GetEffectStack(), /*new_animations=*/nullptr, - /*suppressed_animations=*/nullptr, kDefaultPriority, - /*property_pass_filter=*/nullptr, this); + /*suppressed_animations=*/nullptr, kDefaultPriority, property_pass_filter, + this); if (removed) ClearEffects();
diff --git a/third_party/blink/renderer/core/css/invalidation/style_invalidator.cc b/third_party/blink/renderer/core/css/invalidation/style_invalidator.cc index 818338af..26bb444 100644 --- a/third_party/blink/renderer/core/css/invalidation/style_invalidator.cc +++ b/third_party/blink/renderer/core/css/invalidation/style_invalidator.cc
@@ -295,14 +295,6 @@ if (UNLIKELY(element.NeedsStyleInvalidation())) PushInvalidationSetsForContainerNode(element, sibling_data); - // When a slot element is invalidated, the slotted elements are also - // invalidated by HTMLSlotElement::DidRecalcStyle. So if WholeSubtreeInvalid - // is true, they will be included even though they are not part of the - // subtree. It's not necessary to fully recalc style for the slotted - // elements in that case as they just need to pick up changed inherited - // styles but we do it. If we ever stop doing that then this code and the - // PushInvalidationSetsForContainerNode above need to move out of the - // if-block. auto* html_slot_element = DynamicTo<HTMLSlotElement>(element); if (html_slot_element && InvalidatesSlotted()) InvalidateSlotDistributedElements(*html_slot_element);
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc index 4cf21ba..3c66539 100644 --- a/third_party/blink/renderer/core/dom/element.cc +++ b/third_party/blink/renderer/core/dom/element.cc
@@ -2970,11 +2970,18 @@ const StyleRecalcContext& style_recalc_context) { DCHECK(GetDocument().InStyleRecalc()); - // FIXME: Instead of clearing updates that may have been added from calls to - // ResolveStyle outside RecalcStyle, we should just never set them if we're - // not inside RecalcStyle. - if (ElementAnimations* element_animations = GetElementAnimations()) + if (ElementAnimations* element_animations = GetElementAnimations()) { + // For multiple style recalc passes for the same element in the same + // lifecycle, which can happen for container queries, we may end up having + // pending updates from the previous pass. In that case the update from the + // previous pass should be dropped as it will be re-added if necessary. It + // may be that an update detected in the previous pass would no longer be + // necessary if the animated property flipped back to the old style with no + // change as the result. + DCHECK(GetDocument().GetStyleEngine().InContainerQueryStyleRecalc() || + element_animations->CssAnimations().PendingUpdate().IsEmpty()); element_animations->CssAnimations().ClearPendingUpdate(); + } scoped_refptr<ComputedStyle> style = HasCustomStyleCallbacks()
diff --git a/third_party/blink/renderer/core/html/forms/resources/color_picker.js b/third_party/blink/renderer/core/html/forms/resources/color_picker.js index 3237527..6b2fa7d 100644 --- a/third_party/blink/renderer/core/html/forms/resources/color_picker.js +++ b/third_party/blink/renderer/core/html/forms/resources/color_picker.js
@@ -442,12 +442,13 @@ this.systemColorPicker_, this.colorValueAXAnnouncer_); this.visualColorPicker_.addEventListener( - 'visual-color-picker-initialized', this.initializeListeners_); + 'visual-color-picker-initialized', + this.onVisualColorPickerInitialized_); window.addEventListener('resize', this.onWindowResize_, {once: true}); } - initializeListeners_ = () => { + onVisualColorPickerInitialized_ = () => { this.manualColorPicker_ .addEventListener('manual-color-change', this.onManualColorChange_); @@ -460,7 +461,11 @@ window.addEventListener('message', this.onMessageReceived_); document.documentElement.addEventListener('keydown', this.onKeyDown_); - } + + // Announce color now as any fired visual-color-change event would not have + // been caught by the listener as it was just added in this method. + this.colorValueAXAnnouncer_.announceColor(this.selectedColor); + }; get selectedColor() { return this.selectedColor_;
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc index c8e3543..8fb15ca2 100644 --- a/third_party/blink/renderer/core/html/html_element.cc +++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -1491,14 +1491,16 @@ GetPopoverData()->setInvoker(nullptr); GetPopoverData()->setNeedsRepositioningForSelectMenu(false); - // Stop matching `:open`: - GetPopoverData()->setVisibilityState(PopoverVisibilityState::kTransitioning); - PseudoStateChanged(CSSSelector::kPseudoOpen); // Fire the popoverhide event (bubbles, not cancelable). Event* event = Event::CreateBubble(event_type_names::kPopoverhide); event->SetTarget(this); if (force_hide) { + // Stop matching `:open` now: + GetPopoverData()->setVisibilityState( + PopoverVisibilityState::kTransitioning); + PseudoStateChanged(CSSSelector::kPseudoOpen); + // We will be force-hidden when the popover element is being removed from // the document, during which event dispatch is prohibited. GetDocument().EnqueueAnimationFrameEvent(event); @@ -1508,6 +1510,10 @@ auto result = DispatchEvent(*event); DCHECK_EQ(result, DispatchEventResult::kNotCanceled); + // Stop matching `:open`: + GetPopoverData()->setVisibilityState(PopoverVisibilityState::kTransitioning); + PseudoStateChanged(CSSSelector::kPseudoOpen); + // The 'popoverhide' event handler could have changed this popover, e.g. by // changing its type, removing it from the document, or calling showPopover(). if (!isConnected() || !HasPopoverAttribute() ||
diff --git a/third_party/blink/renderer/core/html/html_image_element.h b/third_party/blink/renderer/core/html/html_image_element.h index fe6beeb..f54ca4fe 100644 --- a/third_party/blink/renderer/core/html/html_image_element.h +++ b/third_party/blink/renderer/core/html/html_image_element.h
@@ -100,11 +100,14 @@ ImageResourceContent* CachedImage() const { return GetImageLoader().GetContent(); } - void LoadDeferredImage() { - GetImageLoader().LoadDeferredImage(referrer_policy_); + void LoadDeferredImageFromMicrotask() { + GetImageLoader().LoadDeferredImage(referrer_policy_, + /*force_blocking*/ false, + /*update_from_microtask*/ true); } void LoadDeferredImageBlockingLoad() { - GetImageLoader().LoadDeferredImage(referrer_policy_, true); + GetImageLoader().LoadDeferredImage(referrer_policy_, + /*force_blocking*/ true); } void SetImageForTest(ImageResourceContent* content) { GetImageLoader().SetImageForTest(content);
diff --git a/third_party/blink/renderer/core/html/lazy_load_image_observer.cc b/third_party/blink/renderer/core/html/lazy_load_image_observer.cc index c7f05e9..6bc5e75 100644 --- a/third_party/blink/renderer/core/html/lazy_load_image_observer.cc +++ b/third_party/blink/renderer/core/html/lazy_load_image_observer.cc
@@ -224,14 +224,14 @@ // Check that style was null because it was not computed since the // element was in an invisible subtree. DCHECK(style || IsElementInInvisibleSubTree(*element)); - image_element->LoadDeferredImage(); + image_element->LoadDeferredImageFromMicrotask(); lazy_load_intersection_observer_->unobserve(element); } } if (!entry->isIntersecting()) continue; if (image_element) - image_element->LoadDeferredImage(); + image_element->LoadDeferredImageFromMicrotask(); // Load the background image if the element has one deferred. if (const ComputedStyle* style = element->GetComputedStyle())
diff --git a/third_party/blink/renderer/core/loader/image_loader.cc b/third_party/blink/renderer/core/loader/image_loader.cc index ca5eae0..897f36d 100644 --- a/third_party/blink/renderer/core/loader/image_loader.cc +++ b/third_party/blink/renderer/core/loader/image_loader.cc
@@ -674,7 +674,8 @@ delay_until_do_update_from_element_ = nullptr; } - if (ShouldLoadImmediately(ImageSourceToKURL(image_source_url))) { + if (ShouldLoadImmediately(ImageSourceToKURL(image_source_url)) && + update_behavior != kUpdateFromMicrotask) { DoUpdateFromElement(element_->GetExecutionContext()->GetCurrentWorld(), update_behavior, referrer_policy, UpdateType::kSync, force_blocking); @@ -960,7 +961,8 @@ void ImageLoader::LoadDeferredImage( network::mojom::ReferrerPolicy referrer_policy, - bool force_blocking) { + bool force_blocking, + bool update_from_microtask) { if (lazy_image_load_state_ != LazyImageLoadState::kDeferred) return; DCHECK(!image_complete_); @@ -968,7 +970,9 @@ // If the image has been fully deferred (no placeholder fetch), report it as // fully loaded now. - UpdateFromElement(kUpdateNormal, referrer_policy, force_blocking); + UpdateFromElement( + update_from_microtask ? kUpdateFromMicrotask : kUpdateNormal, + referrer_policy, force_blocking); } void ImageLoader::ElementDidMoveToNewDocument() {
diff --git a/third_party/blink/renderer/core/loader/image_loader.h b/third_party/blink/renderer/core/loader/image_loader.h index 1714e7d..50bc6a9 100644 --- a/third_party/blink/renderer/core/loader/image_loader.h +++ b/third_party/blink/renderer/core/loader/image_loader.h
@@ -61,6 +61,10 @@ // document, or when DOM mutations trigger a new load. Starts loading if a // load hasn't already been started. kUpdateNormal, + // This is the behavior when the update is triggered by the lazy loading + // mechanism. We can't update synchronously, because doing so may invalidate + // style, which is forbidden from lazy load callbacks. + kUpdateFromMicrotask, // This should be the update behavior when the resource was changed (via // 'src', 'srcset' or 'sizes'). Starts a new load even if a previous load of // the same resource have failed, to match Firefox's behavior. @@ -142,7 +146,8 @@ // force_blocking ensures that the image will block the load event. void LoadDeferredImage(network::mojom::ReferrerPolicy, - bool force_blocking = false); + bool force_blocking = false, + bool update_from_microtask = false); protected: void ImageChanged(ImageResourceContent*, CanDeferInvalidation) override;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc index 02990a5..d9adf34c 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -2969,18 +2969,6 @@ SCOPED_DISALLOW_LIFECYCLE_TRANSITION(); #endif // DCHECK_IS_ON() - if (event_type == ax::mojom::blink::Event::kChildrenChanged && - obj->CachedParentObject()) { - const bool was_ignored = obj->LastKnownIsIgnoredValue(); - const bool was_in_tree = obj->LastKnownIsIncludedInTreeValue(); - obj->UpdateCachedAttributeValuesIfNeeded(false); - const bool is_ignored = obj->LastKnownIsIgnoredValue(); - const bool is_in_tree = obj->LastKnownIsIncludedInTreeValue(); - - if (is_ignored != was_ignored || was_in_tree != is_in_tree) - ChildrenChangedWithCleanLayout(obj->CachedParentObject()); - } - PostPlatformNotification(obj, event_type, event_from, event_from_action, event_intents); }
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_underlying_sink.cc b/third_party/blink/renderer/modules/file_system_access/file_system_underlying_sink.cc index 49ee4a9..fdf7bd0 100644 --- a/third_party/blink/renderer/modules/file_system_access/file_system_underlying_sink.cc +++ b/third_party/blink/renderer/modules/file_system_access/file_system_underlying_sink.cc
@@ -21,6 +21,7 @@ #include "third_party/blink/renderer/core/fileapi/blob.h" #include "third_party/blink/renderer/modules/file_system_access/file_system_access_error.h" #include "third_party/blink/renderer/modules/file_system_access/file_system_writable_file_stream.h" +#include "third_party/blink/renderer/platform/bindings/exception_code.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/blob/blob_data.h" #include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h" @@ -67,8 +68,9 @@ ScriptPromise FileSystemUnderlyingSink::close(ScriptState* script_state, ExceptionState& exception_state) { if (!writer_remote_.is_bound() || pending_operation_) { - exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, - "Object reached an invalid state"); + ThrowDOMExceptionAndInvalidateSink(exception_state, + DOMExceptionCode::kInvalidStateError, + "Object reached an invalid state"); return ScriptPromise(); } pending_operation_ = @@ -97,8 +99,8 @@ ExceptionState& exception_state) { if (params.type() == "truncate") { if (!params.hasSizeNonNull()) { - exception_state.ThrowDOMException( - DOMExceptionCode::kSyntaxError, + ThrowDOMExceptionAndInvalidateSink( + exception_state, DOMExceptionCode::kSyntaxError, "Invalid params passed. truncate requires a size argument"); return ScriptPromise(); } @@ -107,8 +109,8 @@ if (params.type() == "seek") { if (!params.hasPositionNonNull()) { - exception_state.ThrowDOMException( - DOMExceptionCode::kSyntaxError, + ThrowDOMExceptionAndInvalidateSink( + exception_state, DOMExceptionCode::kSyntaxError, "Invalid params passed. seek requires a position argument"); return ScriptPromise(); } @@ -119,21 +121,23 @@ uint64_t position = params.hasPositionNonNull() ? params.positionNonNull() : offset_; if (!params.hasData()) { - exception_state.ThrowDOMException( - DOMExceptionCode::kSyntaxError, + ThrowDOMExceptionAndInvalidateSink( + exception_state, DOMExceptionCode::kSyntaxError, "Invalid params passed. write requires a data argument"); return ScriptPromise(); } if (!params.data()) { - exception_state.ThrowTypeError( + ThrowTypeErrorAndInvalidateSink( + exception_state, "Invalid params passed. write requires a non-null data"); return ScriptPromise(); } return WriteData(script_state, position, params.data(), exception_state); } - exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, - "Object reached an invalid state"); + ThrowDOMExceptionAndInvalidateSink(exception_state, + DOMExceptionCode::kInvalidStateError, + "Object reached an invalid state"); return ScriptPromise(); } @@ -313,16 +317,26 @@ options.capacity_num_bytes = BlobUtils::GetDataPipeCapacity(data_size); MojoResult rv = CreateDataPipe(&options, producer, consumer); - if (rv != MOJO_RESULT_OK) { - exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, - "Failed to create datapipe"); - return false; - } - return true; + return rv == MOJO_RESULT_OK; } } // namespace +void FileSystemUnderlyingSink::ThrowDOMExceptionAndInvalidateSink( + ExceptionState& exception_state, + DOMExceptionCode error, + const char* message) { + exception_state.ThrowDOMException(error, message); + writer_remote_.reset(); +} + +void FileSystemUnderlyingSink::ThrowTypeErrorAndInvalidateSink( + ExceptionState& exception_state, + const char* message) { + exception_state.ThrowTypeError(message); + writer_remote_.reset(); +} + ScriptPromise FileSystemUnderlyingSink::WriteData( ScriptState* script_state, uint64_t position, @@ -331,8 +345,9 @@ DCHECK(data); if (!writer_remote_.is_bound() || pending_operation_) { - exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, - "Object reached an invalid state"); + ThrowDOMExceptionAndInvalidateSink(exception_state, + DOMExceptionCode::kInvalidStateError, + "Object reached an invalid state"); return ScriptPromise(); } @@ -380,6 +395,9 @@ mojo::ScopedDataPipeConsumerHandle consumer_handle; if (!CreateDataPipe(data_size, exception_state, producer_handle, consumer_handle)) { + ThrowDOMExceptionAndInvalidateSink(exception_state, + DOMExceptionCode::kInvalidStateError, + "Failed to create datapipe"); return ScriptPromise(); } @@ -422,8 +440,9 @@ uint64_t size, ExceptionState& exception_state) { if (!writer_remote_.is_bound() || pending_operation_) { - exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, - "Object reached an invalid state"); + ThrowDOMExceptionAndInvalidateSink(exception_state, + DOMExceptionCode::kInvalidStateError, + "Object reached an invalid state"); return ScriptPromise(); } pending_operation_ = @@ -439,8 +458,9 @@ uint64_t offset, ExceptionState& exception_state) { if (!writer_remote_.is_bound() || pending_operation_) { - exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, - "Object reached an invalid state"); + ThrowDOMExceptionAndInvalidateSink(exception_state, + DOMExceptionCode::kInvalidStateError, + "Object reached an invalid state"); return ScriptPromise(); } offset_ = offset;
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_underlying_sink.h b/third_party/blink/renderer/modules/file_system_access/file_system_underlying_sink.h index 303847c..cb6f3f6 100644 --- a/third_party/blink/renderer/modules/file_system_access/file_system_underlying_sink.h +++ b/third_party/blink/renderer/modules/file_system_access/file_system_underlying_sink.h
@@ -8,6 +8,7 @@ #include "third_party/blink/public/mojom/file_system_access/file_system_access_file_writer.mojom-blink.h" #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h" #include "third_party/blink/renderer/core/streams/underlying_sink_base.h" +#include "third_party/blink/renderer/platform/bindings/exception_code.h" #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" #include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h" @@ -40,6 +41,16 @@ void Trace(Visitor*) const override; private: + // Helpers which ensure `writer_remote_` is reset when there's an error. + // A WritableStream becomes unusable once there's been an error on the stream. + // Resetting the remote destroys the corresponding receiver, thereby releasing + // any locks tied to the writer. + void ThrowDOMExceptionAndInvalidateSink(ExceptionState& exception_state, + DOMExceptionCode error, + const char* message); + void ThrowTypeErrorAndInvalidateSink(ExceptionState& exception_state, + const char* message); + ScriptPromise HandleParams(ScriptState*, const WriteParams&, ExceptionState&); ScriptPromise WriteData( ScriptState*,
diff --git a/third_party/blink/renderer/modules/notifications/notification.cc b/third_party/blink/renderer/modules/notifications/notification.cc index 79e0c76..9f61a874 100644 --- a/third_party/blink/renderer/modules/notifications/notification.cc +++ b/third_party/blink/renderer/modules/notifications/notification.cc
@@ -395,6 +395,18 @@ return result; } +String Notification::scenario() const { + switch (data_->scenario) { + case mojom::blink::NotificationScenario::DEFAULT: + return "default"; + case mojom::blink::NotificationScenario::INCOMING_CALL: + return "incoming-call"; + } + + NOTREACHED(); + return String(); +} + String Notification::PermissionString( mojom::blink::PermissionStatus permission) { switch (permission) {
diff --git a/third_party/blink/renderer/modules/notifications/notification.h b/third_party/blink/renderer/modules/notifications/notification.h index 2d20d7c..c4c9f9a40 100644 --- a/third_party/blink/renderer/modules/notifications/notification.h +++ b/third_party/blink/renderer/modules/notifications/notification.h
@@ -118,6 +118,7 @@ ScriptValue data(ScriptState* script_state); Vector<v8::Local<v8::Value>> actions(ScriptState* script_state) const; TimestampTrigger* showTrigger() const { return show_trigger_; } + String scenario() const; static String PermissionString(mojom::blink::PermissionStatus permission); static String permission(ExecutionContext* context);
diff --git a/third_party/blink/renderer/modules/notifications/notification.idl b/third_party/blink/renderer/modules/notifications/notification.idl index d7604ec..96375619 100644 --- a/third_party/blink/renderer/modules/notifications/notification.idl +++ b/third_party/blink/renderer/modules/notifications/notification.idl
@@ -74,6 +74,7 @@ [CallWith=ScriptState, SameObject, SaveSameObject] readonly attribute any data; [CallWith=ScriptState, SameObject, SaveSameObject] readonly attribute FrozenArray<NotificationAction> actions; [RuntimeEnabled=NotificationTriggers, SameObject] readonly attribute TimestampTrigger showTrigger; + [RuntimeEnabled=IncomingCallNotifications] readonly attribute DOMString scenario; [MeasureAs=NotificationClosed] void close(); };
diff --git a/third_party/blink/renderer/modules/notifications/notification_data.cc b/third_party/blink/renderer/modules/notifications/notification_data.cc index 504ee05..271741b 100644 --- a/third_party/blink/renderer/modules/notifications/notification_data.cc +++ b/third_party/blink/renderer/modules/notifications/notification_data.cc
@@ -36,6 +36,15 @@ return mojom::blink::NotificationDirection::AUTO; } +mojom::blink::NotificationScenario ToScenarioEnumValue(const String& scenario) { + if (scenario == "default") + return mojom::blink::NotificationScenario::DEFAULT; + if (scenario == "incoming-call") + return mojom::blink::NotificationScenario::INCOMING_CALL; + NOTREACHED() << "Unknown scenario: " << scenario; + return mojom::blink::NotificationScenario::DEFAULT; +} + KURL CompleteURL(ExecutionContext* context, const String& string_url) { KURL url = context->CompleteURL(string_url); if (url.IsValid()) @@ -183,6 +192,8 @@ notification_data->show_trigger_timestamp = timestamp; } + notification_data->scenario = ToScenarioEnumValue(options->scenario()); + return notification_data; }
diff --git a/third_party/blink/renderer/modules/notifications/notification_options.idl b/third_party/blink/renderer/modules/notifications/notification_options.idl index f8a8ff7..84dd631 100644 --- a/third_party/blink/renderer/modules/notifications/notification_options.idl +++ b/third_party/blink/renderer/modules/notifications/notification_options.idl
@@ -10,6 +10,11 @@ "rtl" }; +enum NotificationScenario { + "default", + "incoming-call" +}; + dictionary NotificationOptions { NotificationDirection dir = "auto"; DOMString lang = ""; @@ -27,4 +32,5 @@ any data = null; sequence<NotificationAction> actions = []; [RuntimeEnabled=NotificationTriggers] TimestampTrigger showTrigger; + [RuntimeEnabled=IncomingCallNotifications] NotificationScenario scenario = "default"; };
diff --git a/third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler.cc b/third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler.cc index a4196e5..7d3de33 100644 --- a/third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler.cc +++ b/third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler.cc
@@ -23,8 +23,10 @@ * DAMAGE. */ -#include <memory> #include "third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler.h" + +#include <memory> + #include "third_party/blink/renderer/modules/webaudio/audio_node_input.h" #include "third_party/blink/renderer/modules/webaudio/audio_node_output.h" #include "third_party/blink/renderer/platform/audio/audio_bus.h"
diff --git a/third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler.h b/third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler.h index 6f4d60f..c17195a1 100644 --- a/third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler.h +++ b/third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler.h
@@ -27,6 +27,7 @@ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_AUDIO_BASIC_PROCESSOR_HANDLER_H_ #include <memory> + #include "third_party/blink/renderer/modules/modules_export.h" #include "third_party/blink/renderer/modules/webaudio/audio_node.h" #include "third_party/blink/renderer/platform/wtf/forward.h"
diff --git a/third_party/blink/renderer/modules/webaudio/audio_buffer.cc b/third_party/blink/renderer/modules/webaudio/audio_buffer.cc index 1d32341..5933e32 100644 --- a/third_party/blink/renderer/modules/webaudio/audio_buffer.cc +++ b/third_party/blink/renderer/modules/webaudio/audio_buffer.cc
@@ -29,6 +29,7 @@ #include "third_party/blink/renderer/modules/webaudio/audio_buffer.h" #include <memory> + #include "third_party/blink/renderer/bindings/modules/v8/v8_audio_buffer_options.h" #include "third_party/blink/renderer/modules/webaudio/base_audio_context.h" #include "third_party/blink/renderer/platform/audio/audio_bus.h"
diff --git a/third_party/blink/renderer/modules/webaudio/audio_node_input_test.cc b/third_party/blink/renderer/modules/webaudio/audio_node_input_test.cc index 7d9385f..ddf0abfc 100644 --- a/third_party/blink/renderer/modules/webaudio/audio_node_input_test.cc +++ b/third_party/blink/renderer/modules/webaudio/audio_node_input_test.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/modules/webaudio/audio_node.h" +#include "third_party/blink/renderer/modules/webaudio/audio_node_input.h" #include <memory> @@ -10,7 +10,7 @@ #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/testing/dummy_page_holder.h" -#include "third_party/blink/renderer/modules/webaudio/audio_node_input.h" +#include "third_party/blink/renderer/modules/webaudio/audio_node.h" #include "third_party/blink/renderer/modules/webaudio/audio_node_output.h" #include "third_party/blink/renderer/modules/webaudio/audio_node_wiring.h" #include "third_party/blink/renderer/modules/webaudio/delay_node.h"
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param.h b/third_party/blink/renderer/modules/webaudio/audio_param.h index 7a84fabd..e4b832d 100644 --- a/third_party/blink/renderer/modules/webaudio/audio_param.h +++ b/third_party/blink/renderer/modules/webaudio/audio_param.h
@@ -30,6 +30,7 @@ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_AUDIO_PARAM_H_ #include <sys/types.h> + #include <atomic> #include "base/memory/scoped_refptr.h"
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param_handler.h b/third_party/blink/renderer/modules/webaudio/audio_param_handler.h index 26c2537d..9258b65 100644 --- a/third_party/blink/renderer/modules/webaudio/audio_param_handler.h +++ b/third_party/blink/renderer/modules/webaudio/audio_param_handler.h
@@ -6,6 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_AUDIO_PARAM_HANDLER_H_ #include <sys/types.h> + #include <atomic> #include "base/memory/scoped_refptr.h"
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param_timeline.h b/third_party/blink/renderer/modules/webaudio/audio_param_timeline.h index 3e0ffa2..770738010 100644 --- a/third_party/blink/renderer/modules/webaudio/audio_param_timeline.h +++ b/third_party/blink/renderer/modules/webaudio/audio_param_timeline.h
@@ -29,6 +29,8 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_AUDIO_PARAM_TIMELINE_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_AUDIO_PARAM_TIMELINE_H_ +#include <tuple> + #include "base/synchronization/lock.h" #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h" #include "third_party/blink/renderer/modules/webaudio/audio_destination_node.h" @@ -37,8 +39,6 @@ #include "third_party/blink/renderer/platform/wtf/threading.h" #include "third_party/blink/renderer/platform/wtf/vector.h" -#include <tuple> - namespace blink { class AudioParamTimeline {
diff --git a/third_party/blink/renderer/modules/webaudio/audio_summing_junction.cc b/third_party/blink/renderer/modules/webaudio/audio_summing_junction.cc index 5d4e129c0..d1d7479 100644 --- a/third_party/blink/renderer/modules/webaudio/audio_summing_junction.cc +++ b/third_party/blink/renderer/modules/webaudio/audio_summing_junction.cc
@@ -23,10 +23,12 @@ * DAMAGE. */ -#include <algorithm> -#include "third_party/blink/renderer/modules/webaudio/audio_node_output.h" #include "third_party/blink/renderer/modules/webaudio/audio_summing_junction.h" +#include <algorithm> + +#include "third_party/blink/renderer/modules/webaudio/audio_node_output.h" + namespace blink { AudioSummingJunction::AudioSummingJunction(DeferredTaskHandler& handler)
diff --git a/third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.cc b/third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.cc index 5c4999f..ac8f548a 100644 --- a/third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.cc +++ b/third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.cc
@@ -23,8 +23,10 @@ * DAMAGE. */ -#include <limits.h> #include "third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.h" + +#include <limits.h> + #include "third_party/blink/renderer/platform/audio/audio_utilities.h" #include "third_party/blink/renderer/platform/wtf/math_extras.h" #include "third_party/blink/renderer/platform/wtf/vector.h"
diff --git a/third_party/blink/renderer/modules/webaudio/biquad_processor.cc b/third_party/blink/renderer/modules/webaudio/biquad_processor.cc index 8853a6c..2fc8252 100644 --- a/third_party/blink/renderer/modules/webaudio/biquad_processor.cc +++ b/third_party/blink/renderer/modules/webaudio/biquad_processor.cc
@@ -23,11 +23,12 @@ * DAMAGE. */ +#include "third_party/blink/renderer/modules/webaudio/biquad_processor.h" + #include <memory> #include "base/synchronization/lock.h" #include "third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.h" -#include "third_party/blink/renderer/modules/webaudio/biquad_processor.h" #include "third_party/blink/renderer/platform/audio/audio_utilities.h" namespace blink {
diff --git a/third_party/blink/renderer/modules/webaudio/biquad_processor.h b/third_party/blink/renderer/modules/webaudio/biquad_processor.h index 8dec01f..967c43bd 100644 --- a/third_party/blink/renderer/modules/webaudio/biquad_processor.h +++ b/third_party/blink/renderer/modules/webaudio/biquad_processor.h
@@ -27,6 +27,7 @@ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_BIQUAD_PROCESSOR_H_ #include <memory> + #include "base/memory/scoped_refptr.h" #include "third_party/blink/renderer/modules/webaudio/audio_node.h" #include "third_party/blink/renderer/modules/webaudio/audio_param.h"
diff --git a/third_party/blink/renderer/modules/webaudio/cpu/arm/oscillator_kernel_neon.cc b/third_party/blink/renderer/modules/webaudio/cpu/arm/oscillator_kernel_neon.cc index b0811bd..060f19ab 100644 --- a/third_party/blink/renderer/modules/webaudio/cpu/arm/oscillator_kernel_neon.cc +++ b/third_party/blink/renderer/modules/webaudio/cpu/arm/oscillator_kernel_neon.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/modules/webaudio/oscillator_node.h" +#include "third_party/blink/renderer/modules/webaudio/oscillator_handler.h" #include "third_party/blink/renderer/modules/webaudio/periodic_wave.h"
diff --git a/third_party/blink/renderer/modules/webaudio/cpu/x86/oscillator_kernel_sse2.cc b/third_party/blink/renderer/modules/webaudio/cpu/x86/oscillator_kernel_sse2.cc index bbb099b..b12df79 100644 --- a/third_party/blink/renderer/modules/webaudio/cpu/x86/oscillator_kernel_sse2.cc +++ b/third_party/blink/renderer/modules/webaudio/cpu/x86/oscillator_kernel_sse2.cc
@@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/modules/webaudio/oscillator_node.h" - -#include "third_party/blink/renderer/modules/webaudio/periodic_wave.h" +#include "third_party/blink/renderer/modules/webaudio/oscillator_handler.h" #include <xmmintrin.h> +#include "third_party/blink/renderer/modules/webaudio/periodic_wave.h" + namespace blink { namespace {
diff --git a/third_party/blink/renderer/modules/webaudio/deferred_task_handler.h b/third_party/blink/renderer/modules/webaudio/deferred_task_handler.h index 6ad98e8f..212613e 100644 --- a/third_party/blink/renderer/modules/webaudio/deferred_task_handler.h +++ b/third_party/blink/renderer/modules/webaudio/deferred_task_handler.h
@@ -27,6 +27,7 @@ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_DEFERRED_TASK_HANDLER_H_ #include <atomic> + #include "base/memory/scoped_refptr.h" #include "base/memory/weak_ptr.h" #include "base/synchronization/lock.h"
diff --git a/third_party/blink/renderer/modules/webaudio/delay_dsp_kernel.cc b/third_party/blink/renderer/modules/webaudio/delay_dsp_kernel.cc index e41ae1d..e479fbd 100644 --- a/third_party/blink/renderer/modules/webaudio/delay_dsp_kernel.cc +++ b/third_party/blink/renderer/modules/webaudio/delay_dsp_kernel.cc
@@ -23,8 +23,10 @@ * DAMAGE. */ -#include <algorithm> #include "third_party/blink/renderer/modules/webaudio/delay_dsp_kernel.h" + +#include <algorithm> + #include "third_party/blink/renderer/platform/audio/audio_utilities.h" #include "third_party/blink/renderer/platform/wtf/math_extras.h"
diff --git a/third_party/blink/renderer/modules/webaudio/delay_processor.cc b/third_party/blink/renderer/modules/webaudio/delay_processor.cc index 5b278bde..0426142 100644 --- a/third_party/blink/renderer/modules/webaudio/delay_processor.cc +++ b/third_party/blink/renderer/modules/webaudio/delay_processor.cc
@@ -23,9 +23,11 @@ * DAMAGE. */ -#include <memory> -#include "third_party/blink/renderer/modules/webaudio/delay_dsp_kernel.h" #include "third_party/blink/renderer/modules/webaudio/delay_processor.h" + +#include <memory> + +#include "third_party/blink/renderer/modules/webaudio/delay_dsp_kernel.h" #include "third_party/blink/renderer/platform/audio/audio_utilities.h" namespace blink {
diff --git a/third_party/blink/renderer/modules/webaudio/delay_processor.h b/third_party/blink/renderer/modules/webaudio/delay_processor.h index 59f58ae..197c307 100644 --- a/third_party/blink/renderer/modules/webaudio/delay_processor.h +++ b/third_party/blink/renderer/modules/webaudio/delay_processor.h
@@ -27,6 +27,7 @@ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_DELAY_PROCESSOR_H_ #include <memory> + #include "base/memory/scoped_refptr.h" #include "third_party/blink/renderer/modules/webaudio/audio_param.h" #include "third_party/blink/renderer/platform/audio/audio_dsp_kernel_processor.h"
diff --git a/third_party/blink/renderer/modules/webaudio/iir_processor.cc b/third_party/blink/renderer/modules/webaudio/iir_processor.cc index 755062fe..41ad752 100644 --- a/third_party/blink/renderer/modules/webaudio/iir_processor.cc +++ b/third_party/blink/renderer/modules/webaudio/iir_processor.cc
@@ -5,6 +5,7 @@ #include "third_party/blink/renderer/modules/webaudio/iir_processor.h" #include <memory> + #include "third_party/blink/renderer/modules/webaudio/iir_dsp_kernel.h" namespace blink {
diff --git a/third_party/blink/renderer/modules/webaudio/iir_processor.h b/third_party/blink/renderer/modules/webaudio/iir_processor.h index 0383b4c..868e60a3 100644 --- a/third_party/blink/renderer/modules/webaudio/iir_processor.h +++ b/third_party/blink/renderer/modules/webaudio/iir_processor.h
@@ -6,6 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_IIR_PROCESSOR_H_ #include <memory> + #include "third_party/blink/renderer/modules/webaudio/audio_node.h" #include "third_party/blink/renderer/platform/audio/audio_dsp_kernel.h" #include "third_party/blink/renderer/platform/audio/audio_dsp_kernel_processor.h"
diff --git a/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.cc b/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.cc index d1a0f8c..ce1eb7a2 100644 --- a/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.cc +++ b/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.cc
@@ -5,6 +5,7 @@ #include "third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.h" #include <memory> + #include "third_party/blink/renderer/core/page/page.h" #include "third_party/blink/renderer/modules/webaudio/base_audio_context.h" #include "third_party/blink/renderer/modules/webaudio/audio_context.h"
diff --git a/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.h b/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.h index 9945d039..136686b 100644 --- a/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.h +++ b/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.h
@@ -6,6 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_INSPECTOR_WEB_AUDIO_AGENT_H_ #include <memory> + #include "third_party/blink/renderer/core/inspector/inspector_base_agent.h" #include "third_party/blink/renderer/core/inspector/protocol/web_audio.h" #include "third_party/blink/renderer/modules/modules_export.h"
diff --git a/third_party/blink/renderer/modules/webaudio/periodic_wave.cc b/third_party/blink/renderer/modules/webaudio/periodic_wave.cc index d28b8f6..c39e832 100644 --- a/third_party/blink/renderer/modules/webaudio/periodic_wave.cc +++ b/third_party/blink/renderer/modules/webaudio/periodic_wave.cc
@@ -26,6 +26,8 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "third_party/blink/renderer/modules/webaudio/periodic_wave.h" + #include <algorithm> #include <memory> @@ -33,7 +35,6 @@ #include "third_party/blink/renderer/bindings/modules/v8/v8_periodic_wave_options.h" #include "third_party/blink/renderer/modules/webaudio/base_audio_context.h" #include "third_party/blink/renderer/modules/webaudio/oscillator_node.h" -#include "third_party/blink/renderer/modules/webaudio/periodic_wave.h" #include "third_party/blink/renderer/platform/audio/fft_frame.h" #include "third_party/blink/renderer/platform/audio/vector_math.h" #include "third_party/blink/renderer/platform/bindings/exception_messages.h"
diff --git a/third_party/blink/renderer/modules/webaudio/periodic_wave.h b/third_party/blink/renderer/modules/webaudio/periodic_wave.h index 5a5bbd9b..cafc329 100644 --- a/third_party/blink/renderer/modules/webaudio/periodic_wave.h +++ b/third_party/blink/renderer/modules/webaudio/periodic_wave.h
@@ -30,6 +30,7 @@ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_PERIODIC_WAVE_H_ #include <memory> + #include "third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h" #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h" #include "third_party/blink/renderer/platform/audio/audio_array.h"
diff --git a/third_party/blink/renderer/modules/webaudio/wave_shaper_dsp_kernel.h b/third_party/blink/renderer/modules/webaudio/wave_shaper_dsp_kernel.h index 35addcbd..0eaf8cf 100644 --- a/third_party/blink/renderer/modules/webaudio/wave_shaper_dsp_kernel.h +++ b/third_party/blink/renderer/modules/webaudio/wave_shaper_dsp_kernel.h
@@ -27,6 +27,7 @@ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_WAVE_SHAPER_DSP_KERNEL_H_ #include <memory> + #include "third_party/blink/renderer/modules/webaudio/wave_shaper_processor.h" #include "third_party/blink/renderer/platform/audio/audio_array.h" #include "third_party/blink/renderer/platform/audio/audio_dsp_kernel.h"
diff --git a/third_party/blink/renderer/modules/webaudio/wave_shaper_processor.cc b/third_party/blink/renderer/modules/webaudio/wave_shaper_processor.cc index 8f65095c..d4554fd 100644 --- a/third_party/blink/renderer/modules/webaudio/wave_shaper_processor.cc +++ b/third_party/blink/renderer/modules/webaudio/wave_shaper_processor.cc
@@ -23,11 +23,12 @@ * DAMAGE. */ +#include "third_party/blink/renderer/modules/webaudio/wave_shaper_processor.h" + #include <memory> #include "base/synchronization/lock.h" #include "third_party/blink/renderer/modules/webaudio/wave_shaper_dsp_kernel.h" -#include "third_party/blink/renderer/modules/webaudio/wave_shaper_processor.h" namespace blink {
diff --git a/third_party/blink/renderer/modules/webaudio/wave_shaper_processor.h b/third_party/blink/renderer/modules/webaudio/wave_shaper_processor.h index 939d780f..b8fc6ea53 100644 --- a/third_party/blink/renderer/modules/webaudio/wave_shaper_processor.h +++ b/third_party/blink/renderer/modules/webaudio/wave_shaper_processor.h
@@ -27,6 +27,7 @@ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_WAVE_SHAPER_PROCESSOR_H_ #include <memory> + #include "base/memory/scoped_refptr.h" #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h" #include "third_party/blink/renderer/modules/webaudio/audio_node.h"
diff --git a/third_party/blink/renderer/platform/p2p/ipc_network_manager.cc b/third_party/blink/renderer/platform/p2p/ipc_network_manager.cc index a2834d3..d33f0629 100644 --- a/third_party/blink/renderer/platform/p2p/ipc_network_manager.cc +++ b/third_party/blink/renderer/platform/p2p/ipc_network_manager.cc
@@ -130,8 +130,8 @@ underlying_adapter_type = adapter_type; adapter_type = rtc::ADAPTER_TYPE_VPN; } - auto network = std::make_unique<rtc::Network>( - it->name, it->name, prefix, it->prefix_length, adapter_type); + auto network = CreateNetwork(it->name, it->name, prefix, it->prefix_length, + adapter_type); if (adapter_type == rtc::ADAPTER_TYPE_VPN) { network->set_underlying_type_for_vpn(underlying_adapter_type); } @@ -176,8 +176,8 @@ if (Platform::Current()->AllowsLoopbackInPeerConnection()) { std::string name_v4("loopback_ipv4"); rtc::IPAddress ip_address_v4(INADDR_LOOPBACK); - auto network_v4 = std::make_unique<rtc::Network>( - name_v4, name_v4, ip_address_v4, 32, rtc::ADAPTER_TYPE_UNKNOWN); + auto network_v4 = CreateNetwork(name_v4, name_v4, ip_address_v4, 32, + rtc::ADAPTER_TYPE_UNKNOWN); network_v4->set_default_local_address_provider(this); network_v4->set_mdns_responder_provider(this); network_v4->AddIP(ip_address_v4); @@ -191,8 +191,8 @@ DCHECK(!ipv6_default_address.IsNil()); std::string name_v6("loopback_ipv6"); rtc::IPAddress ip_address_v6(in6addr_loopback); - auto network_v6 = std::make_unique<rtc::Network>( - name_v6, name_v6, ip_address_v6, 64, rtc::ADAPTER_TYPE_UNKNOWN); + auto network_v6 = CreateNetwork(name_v6, name_v6, ip_address_v6, 64, + rtc::ADAPTER_TYPE_UNKNOWN); network_v6->set_default_local_address_provider(this); network_v6->set_mdns_responder_provider(this); network_v6->AddIP(ip_address_v6);
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index 83d847c..1f705433 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1477,6 +1477,10 @@ status: {"Android": "stable"}, }, { + name: "IncomingCallNotifications", + base_feature: "IncomingCallNotifications", + }, + { name: "InertAttribute", status: "stable", },
diff --git a/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py b/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py index 80b1c98..aa490ec5 100644 --- a/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py +++ b/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py
@@ -618,7 +618,7 @@ return results def _worker_factory(self, worker_connection): - return Worker(worker_connection, self._tool.git().checkout_root) + return Worker(self._tool.git().checkout_root) def rebaseline(self, options, test_baseline_set): """Fetches new baselines and removes related test expectation lines. @@ -827,28 +827,27 @@ self.rebaseline(options, test_baseline_set) -class Worker(object): - def __init__(self, caller, cwd): +class Worker: + """A worker delegate for running Blink tool commands. - # Add the header here to avoid the circle import - from blinkpy.tool.blink_tool import BlinkTool + The purpose of this worker is to persist HTTP(S) connections to ResultDB + across command invocations. - self._caller = caller - self._tool = BlinkTool(cwd) + See Also: + crbug.com/1213998#c50 + """ - def __del__(self): - self.stop() + def __init__(self, cwd: str): + self._cwd = cwd def start(self): - """This method is called when the object is starting to be used and it is safe - for the object to create state that does not need to be pickled (usually this means - it is called in a child process). - """ - pass + # Dynamically import `BlinkTool` to avoid a circular import. + from blinkpy.tool.blink_tool import BlinkTool + # `BlinkTool` cannot be serialized, so construct one here in the worker + # process instead of in the constructor, which runs in the managing + # process. See crbug.com/1386267. + self._tool = BlinkTool(self._cwd) def handle(self, name, source, command): assert name == 'rebaseline' self._tool.main(command[1:]) - - def stop(self): - _log.debug('%s cleaning up', self._caller.name)
diff --git a/third_party/blink/tools/run_wpt_tests.py b/third_party/blink/tools/run_wpt_tests.py index a97f277..dd5bd50 100755 --- a/third_party/blink/tools/run_wpt_tests.py +++ b/third_party/blink/tools/run_wpt_tests.py
@@ -12,6 +12,7 @@ import shutil import sys +from blinkpy.common import exit_codes from blinkpy.common import path_finder from blinkpy.common.path_finder import PathFinder from blinkpy.web_tests.port.android import ( @@ -259,8 +260,10 @@ json.dump(run_info, file_handle) def do_post_test_run_tasks(self): - self.process_and_upload_results() - if self.options.show_results_in_browser: + process_return = self.process_and_upload_results() + + if (process_return != exit_codes.INTERRUPTED_EXIT_STATUS + and self.options.show_results_in_browser): self.show_results_in_browser() def show_results_in_browser(self): @@ -870,8 +873,12 @@ # This environment fix is needed on windows as codec is trying # to encode in cp1252 rather than utf-8 and throwing errors. os.environ['PYTHONUTF8'] = '1' - adapter = WPTAdapter() - return adapter.run_test() + + try: + adapter = WPTAdapter() + return adapter.run_test() + except KeyboardInterrupt: + return exit_codes.INTERRUPTED_EXIT_STATUS # This is not really a "script test" so does not need to manually add
diff --git a/third_party/blink/web_tests/LeakExpectations b/third_party/blink/web_tests/LeakExpectations index 801837537..8b3d678 100644 --- a/third_party/blink/web_tests/LeakExpectations +++ b/third_party/blink/web_tests/LeakExpectations
@@ -58,6 +58,9 @@ # Sheriff 2022-08-04 crbug.com/1350279 [ Linux ] external/wpt/accessibility/crashtests/svg-mouse-listener.html [ Failure Pass ] +# This has been marked as Skip in the TestExpectations file. +crbug.com/1385278 [ Linux ] external/wpt/resource-timing/iframe-failed-commit.html [ Skip ] + ########################################################################### # WARNING: Memory leaks must be fixed asap. Sheriff is expected to revert # # culprit CLs instead of suppressing the leaks. If you have any question, #
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 7e09e35..9460f63 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -3131,7 +3131,9 @@ crbug.com/626703 external/wpt/geolocation-API/enabled-by-feature-policy-attribute.https.sub.html [ Timeout ] crbug.com/626703 external/wpt/geolocation-API/enabled-by-feature-policy.https.sub.html [ Timeout ] crbug.com/626703 external/wpt/geolocation-API/enabled-on-self-origin-by-feature-policy.https.sub.html [ Timeout ] -crbug.com/626703 [ Linux ] external/wpt/resource-timing/object-not-found-adds-entry.html [ Timeout ] +crbug.com/626703 external/wpt/resource-timing/object-not-found-adds-entry.html [ Timeout Failure Pass ] +crbug.com/1385265 virtual/plz-dedicated-worker/external/wpt/resource-timing/object-not-found-adds-entry.html [ Skip Failure Pass ] +crbug.com/1385278 external/wpt/resource-timing/iframe-failed-commit.html [ Skip Failure Pass Timeout ] crbug.com/626703 external/wpt/webrtc/RTCConfiguration-iceTransportPolicy.html [ Skip Timeout ] crbug.com/626703 [ Mac12 ] external/wpt/mediacapture-record/MediaRecorder-mimetype.html [ Failure Timeout ] crbug.com/626703 [ Mac10.14 ] virtual/portals/external/wpt/portals/no-portal-in-sandboxed-popup.html [ Timeout ]
diff --git a/third_party/blink/web_tests/external/wpt/fs/FileSystemDirectoryHandle-removeEntry.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/fs/FileSystemDirectoryHandle-removeEntry.https.any-expected.txt deleted file mode 100644 index 4abcb23..0000000 --- a/third_party/blink/web_tests/external/wpt/fs/FileSystemDirectoryHandle-removeEntry.https.any-expected.txt +++ /dev/null
@@ -1,4 +0,0 @@ -This is a testharness.js-based test. -FAIL removeEntry() while the file has an open writable fails assert_unreached: Should have rejected: undefined Reached unreachable code -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/fs/FileSystemDirectoryHandle-removeEntry.https.any.js.ini b/third_party/blink/web_tests/external/wpt/fs/FileSystemDirectoryHandle-removeEntry.https.any.js.ini deleted file mode 100644 index d3bc3cb..0000000 --- a/third_party/blink/web_tests/external/wpt/fs/FileSystemDirectoryHandle-removeEntry.https.any.js.ini +++ /dev/null
@@ -1,8 +0,0 @@ -[FileSystemDirectoryHandle-removeEntry.https.any.worker.html] - [removeEntry() while the file has an open writable fails] - expected: FAIL - - -[FileSystemDirectoryHandle-removeEntry.https.any.html] - [removeEntry() while the file has an open writable fails] - expected: FAIL
diff --git a/third_party/blink/web_tests/external/wpt/fs/FileSystemDirectoryHandle-removeEntry.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/fs/FileSystemDirectoryHandle-removeEntry.https.any.worker-expected.txt deleted file mode 100644 index 4abcb23..0000000 --- a/third_party/blink/web_tests/external/wpt/fs/FileSystemDirectoryHandle-removeEntry.https.any.worker-expected.txt +++ /dev/null
@@ -1,4 +0,0 @@ -This is a testharness.js-based test. -FAIL removeEntry() while the file has an open writable fails assert_unreached: Should have rejected: undefined Reached unreachable code -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/fs/script-tests/FileSystemDirectoryHandle-removeEntry.js b/third_party/blink/web_tests/external/wpt/fs/script-tests/FileSystemDirectoryHandle-removeEntry.js index 108b9135..93b03ef 100644 --- a/third_party/blink/web_tests/external/wpt/fs/script-tests/FileSystemDirectoryHandle-removeEntry.js +++ b/third_party/blink/web_tests/external/wpt/fs/script-tests/FileSystemDirectoryHandle-removeEntry.js
@@ -4,15 +4,120 @@ const handle = await createFileWithContents(t, 'file-to-remove', '12345', root); await createFileWithContents(t, 'file-to-keep', 'abc', root); + await root.removeEntry('file-to-remove'); + + assert_array_equals(await getSortedDirectoryEntries(root), ['file-to-keep']); + await promise_rejects_dom(t, 'NotFoundError', getFileContents(handle)); +}, 'removeEntry() to remove a file'); + +directory_test(async (t, root) => { + const handle = + await createFileWithContents(t, 'file-to-remove', '12345', root); + await root.removeEntry('file-to-remove'); + + await promise_rejects_dom( + t, 'NotFoundError', root.removeEntry('file-to-remove')); +}, 'removeEntry() on an already removed file should fail'); + +directory_test(async (t, root) => { + const dir = await root.getDirectoryHandle('dir-to-remove', {create: true}); + await createFileWithContents(t, 'file-to-keep', 'abc', root); + await root.removeEntry('dir-to-remove'); + + assert_array_equals(await getSortedDirectoryEntries(root), ['file-to-keep']); +}, 'removeEntry() to remove an empty directory'); + +directory_test(async (t, root) => { + const dir = await createDirectory(t, 'dir-to-remove', root); + await createFileWithContents(t, 'file-in-dir', 'abc', dir); + + await promise_rejects_dom( + t, 'InvalidModificationError', root.removeEntry('dir-to-remove')); + assert_array_equals( + await getSortedDirectoryEntries(root), ['dir-to-remove/']); + assert_array_equals(await getSortedDirectoryEntries(dir), ['file-in-dir']); +}, 'removeEntry() on a non-empty directory should fail'); + +directory_test(async (t, root) => { + // root + // ├──file-to-keep + // ├──dir-to-remove + // ├── file0 + // ├── dir1-in-dir + // │ └── file1 + // └── dir2 + const dir = await root.getDirectoryHandle('dir-to-remove', {create: true}); + await createFileWithContents(t, 'file-to-keep', 'abc', root); + await createEmptyFile(t, 'file0', dir); + const dir1_in_dir = await createDirectory(t, 'dir1-in-dir', dir); + await createEmptyFile(t, 'file1', dir1_in_dir); + await createDirectory(t, 'dir2-in-dir', dir); + + await root.removeEntry('dir-to-remove', {recursive: true}); + assert_array_equals(await getSortedDirectoryEntries(root), ['file-to-keep']); +}, 'removeEntry() on a directory recursively should delete all sub-items'); + +directory_test(async (t, root) => { + const dir = await createDirectory(t, 'dir', root); + await promise_rejects_js(t, TypeError, dir.removeEntry('')); +}, 'removeEntry() with empty name should fail'); + +directory_test(async (t, root) => { + const dir = await createDirectory(t, 'dir', root); + await promise_rejects_js(t, TypeError, dir.removeEntry(kCurrentDirectory)); +}, `removeEntry() with "${kCurrentDirectory}" name should fail`); + +directory_test(async (t, root) => { + const dir = await createDirectory(t, 'dir', root); + await promise_rejects_js(t, TypeError, dir.removeEntry(kParentDirectory)); +}, `removeEntry() with "${kParentDirectory}" name should fail`); + +directory_test(async (t, root) => { + const dir_name = 'dir-name'; + const dir = await createDirectory(t, dir_name, root); + + const file_name = 'file-name'; + await createEmptyFile(t, file_name, dir); + + for (let i = 0; i < kPathSeparators.length; ++i) { + const path_with_separator = `${dir_name}${kPathSeparators[i]}${file_name}`; + await promise_rejects_js( + t, TypeError, root.removeEntry(path_with_separator), + `removeEntry() must reject names containing "${kPathSeparators[i]}"`); + } +}, 'removeEntry() with a path separator should fail.'); + +directory_test(async (t, root) => { + const handle = + await createFileWithContents(t, 'file-to-remove', '12345', root); + await createFileWithContents(t, 'file-to-keep', 'abc', root); const writable = await cleanup_writable(t, await handle.createWritable()); await promise_rejects_dom( - t, 'InvalidModificationError', root.removeEntry('file-to-remove')); + t, 'NoModificationAllowedError', root.removeEntry('file-to-remove')); await writable.close(); await root.removeEntry('file-to-remove'); - assert_array_equals( - await getSortedDirectoryEntries(root), - ['file-to-keep']); + assert_array_equals(await getSortedDirectoryEntries(root), ['file-to-keep']); }, 'removeEntry() while the file has an open writable fails'); + +directory_test(async (t, root) => { + const dir_name = 'dir-name'; + const dir = await createDirectory(t, dir_name, root); + + const handle = + await createFileWithContents(t, 'file-to-remove', '12345', dir); + await createFileWithContents(t, 'file-to-keep', 'abc', dir); + + const writable = await cleanup_writable(t, await handle.createWritable()); + await promise_rejects_dom( + t, 'NoModificationAllowedError', root.removeEntry(dir_name)); + + await writable.close(); + assert_array_equals( + await getSortedDirectoryEntries(dir), ['file-to-keep', 'file-to-remove']); + + await dir.removeEntry('file-to-remove'); + assert_array_equals(await getSortedDirectoryEntries(dir), ['file-to-keep']); +}, 'removeEntry() of a directory while a containing file has an open writable fails');
diff --git a/third_party/blink/web_tests/external/wpt/fs/script-tests/FileSystemWritableFileStream-write.js b/third_party/blink/web_tests/external/wpt/fs/script-tests/FileSystemWritableFileStream-write.js index 70ba72f..43c8ec7 100644 --- a/third_party/blink/web_tests/external/wpt/fs/script-tests/FileSystemWritableFileStream-write.js +++ b/third_party/blink/web_tests/external/wpt/fs/script-tests/FileSystemWritableFileStream-write.js
@@ -195,17 +195,6 @@ }, 'write() with a valid typed array buffer'); directory_test(async (t, root) => { - const dir = await createDirectory(t, 'parent_dir', root); - const file_name = 'close_fails_when_dir_removed.txt'; - const handle = await createEmptyFile(t, file_name, dir); - const stream = await handle.createWritable(); - await stream.write('foo'); - - await root.removeEntry('parent_dir', {recursive: true}); - await promise_rejects_dom(t, 'NotFoundError', stream.close()); -}, 'atomic writes: close() fails when parent directory is removed'); - -directory_test(async (t, root) => { const handle = await createEmptyFile(t, 'atomic_writes.txt', root); const stream = await handle.createWritable(); await stream.write('foox'); @@ -277,22 +266,6 @@ }, 'atomic writes: only one close() operation may succeed'); directory_test(async (t, root) => { - const dir = await createDirectory(t, 'parent_dir', root); - const file_name = 'atomic_writable_file_stream_persists_removed.txt'; - const handle = await createFileWithContents(t, file_name, 'foo', dir); - - const stream = await handle.createWritable(); - await stream.write('bar'); - - await dir.removeEntry(file_name); - await promise_rejects_dom(t, 'NotFoundError', getFileContents(handle)); - - await stream.close(); - assert_equals(await getFileContents(handle), 'bar'); - assert_equals(await getFileSize(handle), 3); -}, 'atomic writes: writable file stream persists file on close, even if file is removed'); - -directory_test(async (t, root) => { const handle = await createEmptyFile(t, 'writer_written', root); const stream = await handle.createWritable(); assert_false(stream.locked); @@ -315,8 +288,8 @@ const stream = await handle.createWritable(); await promise_rejects_dom( - t, "SyntaxError", stream.write({type: 'truncate'}), 'truncate without size'); - + t, 'SyntaxError', stream.write({type: 'truncate'}), + 'truncate without size'); }, 'WriteParams: truncate missing size param'); directory_test(async (t, root) => { @@ -324,8 +297,7 @@ const stream = await handle.createWritable(); await promise_rejects_dom( - t, "SyntaxError", stream.write({type: 'write'}), 'write without data'); - + t, 'SyntaxError', stream.write({type: 'write'}), 'write without data'); }, 'WriteParams: write missing data param'); directory_test(async (t, root) => { @@ -333,18 +305,17 @@ const stream = await handle.createWritable(); await promise_rejects_js( - t, TypeError, stream.write({type: 'write', data: null}), 'write with null data'); - + t, TypeError, stream.write({type: 'write', data: null}), + 'write with null data'); }, 'WriteParams: write null data param'); directory_test(async (t, root) => { - const handle = await createFileWithContents( - t, 'content.txt', 'seekable', root); + const handle = + await createFileWithContents(t, 'content.txt', 'seekable', root); const stream = await handle.createWritable(); await promise_rejects_dom( - t, "SyntaxError", stream.write({type: 'seek'}), 'seek without position'); - + t, 'SyntaxError', stream.write({type: 'seek'}), 'seek without position'); }, 'WriteParams: seek missing position param'); directory_test(async (t, root) => {
diff --git a/third_party/blink/web_tests/external/wpt/fs/script-tests/FileSystemWritableFileStream.js b/third_party/blink/web_tests/external/wpt/fs/script-tests/FileSystemWritableFileStream.js index d9c4f35..53e4fc1 100644 --- a/third_party/blink/web_tests/external/wpt/fs/script-tests/FileSystemWritableFileStream.js +++ b/third_party/blink/web_tests/external/wpt/fs/script-tests/FileSystemWritableFileStream.js
@@ -34,26 +34,6 @@ }, 'createWritable() fails when parent directory is removed'); directory_test(async (t, root) => { - const dir = await createDirectory(t, 'parent_dir', root); - const file_name = 'write_fails_when_dir_removed.txt'; - const handle = await createEmptyFile(t, file_name, dir); - const stream = await handle.createWritable(); - - await root.removeEntry('parent_dir', {recursive: true}); - await promise_rejects_dom(t, 'NotFoundError', stream.write('foo')); -}, 'write() fails when parent directory is removed'); - -directory_test(async (t, root) => { - const dir = await createDirectory(t, 'parent_dir', root); - const file_name = 'truncate_fails_when_dir_removed.txt'; - const handle = await createEmptyFile(t, file_name, dir); - const stream = await handle.createWritable(); - - await root.removeEntry('parent_dir', {recursive: true}); - await promise_rejects_dom(t, 'NotFoundError', stream.truncate(0)); -}, 'truncate() fails when parent directory is removed'); - -directory_test(async (t, root) => { const handle = await createFileWithContents( t, 'atomic_file_is_copied.txt', 'fooks', root); const stream = await handle.createWritable({keepExistingData: true});
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-events.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-events.tentative.html index 2f530d1..78d4a22 100644 --- a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-events.tentative.html +++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-events.tentative.html
@@ -17,21 +17,31 @@ assert_false(popover.matches(':open')); let showCount = 0; let hideCount = 0; + function showListener(e) { + assert_true(e.target.matches(':closed'),'The popover should be in the :closed state when the popovershow event fires.'); + assert_false(e.target.matches(':open'),'The popover should *not* be in the :open state when the popovershow event fires.'); + ++showCount; + }; + function hideListener(e) { + assert_true(e.target.matches(':open'),'The popover should be in the :open state when the popoverhide event fires.'); + assert_false(e.target.matches(':closed'),'The popover should *not* be in the :closed state when the popoverhide event fires.'); + ++hideCount; + }; switch (method) { case "listener": const controller = new AbortController(); const signal = controller.signal; t.add_cleanup(() => controller.abort()); - document.addEventListener('popovershow',() => ++showCount, {signal}); - document.addEventListener('popoverhide',() => ++hideCount, {signal}); + document.addEventListener('popovershow',showListener, {signal}); + document.addEventListener('popoverhide',hideListener, {signal}); break; case "attribute": assert_false(popover.hasAttribute('onpopovershow')); assert_false(popover.hasAttribute('onpopoverhide')); t.add_cleanup(() => popover.removeAttribute('onpopovershow')); t.add_cleanup(() => popover.removeAttribute('onpopoverhide')); - popover.onpopovershow = () => ++showCount; - popover.onpopoverhide = () => ++hideCount; + popover.onpopovershow = showListener; + popover.onpopoverhide = hideListener; break; default: assert_unreached(); }
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/commitStyles-svg-crash.html b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/commitStyles-svg-crash.html new file mode 100644 index 0000000..7fc1fef --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/commitStyles-svg-crash.html
@@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html class=test-wait> +<link rel=help href="https://crbug.com/1385691"> +<svg id=svg></svg> +<script> + let anim = svg.animate({'svg-viewBox': '1 1 1 1'}, 1); + anim.ready.then(() => { + anim.commitStyles(); + document.documentElement.classList.remove('test-wait'); + }); +</script> +</html>
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/resources/web-vitals.html b/third_party/blink/web_tests/http/tests/inspector-protocol/resources/web-vitals.html index b54346b..195d845 100644 --- a/third_party/blink/web_tests/http/tests/inspector-protocol/resources/web-vitals.html +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/resources/web-vitals.html
@@ -2,6 +2,7 @@ <html> <body> <script> + (new PerformanceObserver(console.log)).observe({ entryTypes: ["layout-shift"] }); window.setTimeout(() => { const img = document.querySelector('img'); img.setAttribute('src', './big-image.png');
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/tracing/frame-lifecycle-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/tracing/frame-lifecycle-expected.txt deleted file mode 100644 index 2fabc08..0000000 --- a/third_party/blink/web_tests/http/tests/inspector-protocol/tracing/frame-lifecycle-expected.txt +++ /dev/null
@@ -1,147 +0,0 @@ -Tests the data of page load and web vitals trace events -Recording started -Tracing complete -Got SetLayerTreeId event: -Object: { - args: { - data: { - frame: string - layerTreeId: number - } - } - cat: string - name: string - ph: string - pid: number - s: string - tid: number - ts: number - tts: number -} -Got BeginFrame event: -Array: [ - { - args: { - frameSeqId: number - layerTreeId: number - } - cat: string - name: string - ph: string - pid: number - s: string - tid: number - ts: number - } -] -Got DrawFrame event: -Object: { - args: { - frameSeqId: number - layerTreeId: number - } - cat: string - name: string - ph: string - pid: number - s: string - tid: number - ts: number - tts: number -} -Got ActivateLayerTree event: -Object: { - args: { - frameId: number - layerTreeId: number - } - cat: string - name: string - ph: string - pid: number - s: string - tid: number - ts: number - tts: number -} -Got NeedsBeginFrameChanged event: -Object: { - args: { - data: { - needsBeginFrame: number - } - layerTreeId: number - } - cat: string - name: string - ph: string - pid: number - s: string - tid: number - ts: number - tts: number -} -Got BeginMainThreadFrame event: -Object: { - args: { - data: { - frameId: number - } - layerTreeId: number - } - cat: string - name: string - ph: string - pid: number - s: string - tid: number - ts: number - tts: number -} -Got Paint event: -Object: { - args: { - data: { - clip: [ - number, - number, - number, - number, - number, - number, - number, - number, - ] - frame: string - layerId: number - nodeId: number - } - } - cat: string - dur: number - name: string - ph: string - pid: number - tdur: number - tid: number - ts: number - tts: number -} -Got CompositeLayers event: -Object: { - args: { - frameSeqId: number - layerTreeId: number - } - cat: string - dur: number - name: string - ph: string - pid: number - tdur: number - tid: number - ts: number - tts: number -} -All screenshots have image data. -
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/tracing/frame-lifecycle.js b/third_party/blink/web_tests/http/tests/inspector-protocol/tracing/frame-lifecycle.js deleted file mode 100644 index 5212c34b..0000000 --- a/third_party/blink/web_tests/http/tests/inspector-protocol/tracing/frame-lifecycle.js +++ /dev/null
@@ -1,94 +0,0 @@ -(async function(testRunner) { - const {session, dp} = await testRunner.startBlank( - 'Tests the data of page load and web vitals trace events'); - - const TracingHelper = - await testRunner.loadScript('../resources/tracing-test.js'); - const tracingHelper = new TracingHelper(testRunner, session); - await tracingHelper.startTracing( - 'disabled-by-default-devtools.timeline,disabled-by-default-devtools.timeline.frame,devtools.timeline,loading'); - await dp.Page.navigate({ - url: 'http://127.0.0.1:8000/inspector-protocol/resources/web-vitals.html' - }); - - // Wait for trace events. - const largestContentfulPaintPromise = await session.evaluateAsync(` - (function() { - let resolvePromiseCallback = () => ({}); - const observerCallBack = entryList => { - const entries = entryList.getEntries(); - for (const entry of entries) { - if (entry.element.tagName === 'IMG' && entry.element.getAttribute('src', './big-image.png')) { - resolvePromiseCallback(); - } - } - }; - const observer = new PerformanceObserver(observerCallBack); - const largestContentfulPaintPromise = new Promise((res) => {resolvePromiseCallback = res}) - observer.observe({entryTypes: ['largest-contentful-paint']}); - return largestContentfulPaintPromise; - })() - `); - await largestContentfulPaintPromise; - await tracingHelper.stopTracing( - /loading|(disabled-by-default)?devtools.timeline(.frame)?|rail/); - - const drawFrame = tracingHelper.findEvents('DrawFrame', 'I').at(-1); - - function hasExpectedFrameSeqId(event) { - return event.args.frameSeqId === drawFrame.args.frameSeqId; - } - function hasExpectedLayerTreeId(event) { - return event.args.layerTreeId === drawFrame.args.layerTreeId; - } - - // Given a DrawFrame event, find the events marking the other steps - // of that frame's life cycle. - - const beginFrame = - tracingHelper.findEvents('BeginFrame', 'I', hasExpectedFrameSeqId); - const compositeLayers = - tracingHelper.findEvent('CompositeLayers', 'X', hasExpectedFrameSeqId); - const activateLayerTree = - tracingHelper.findEvent('ActivateLayerTree', 'I', hasExpectedLayerTreeId); - const needsBeginFrameChanged = tracingHelper.findEvent( - 'NeedsBeginFrameChanged', 'I', hasExpectedLayerTreeId); - const beginMainThreadFrame = tracingHelper.findEvent( - 'BeginMainThreadFrame', 'I', hasExpectedLayerTreeId); - - // Other trace events in the frame domain that do not necessarily - // belong to the life cycle of the DrawFrame event used above. - const setLayerTreeId = tracingHelper.findEvent('SetLayerTreeId', 'I'); - const paint = tracingHelper.findEvent('Paint', 'X'); - const screenshots = tracingHelper.findEvents('Screenshot', 'O'); - - testRunner.log('Got SetLayerTreeId event:'); - tracingHelper.logEventShape(setLayerTreeId) - - testRunner.log('Got BeginFrame event:'); - tracingHelper.logEventShape(beginFrame) - - testRunner.log('Got DrawFrame event:'); - tracingHelper.logEventShape(drawFrame) - - testRunner.log('Got ActivateLayerTree event:'); - tracingHelper.logEventShape(activateLayerTree) - - testRunner.log('Got NeedsBeginFrameChanged event:'); - tracingHelper.logEventShape(needsBeginFrameChanged) - - testRunner.log('Got BeginMainThreadFrame event:'); - tracingHelper.logEventShape(beginMainThreadFrame) - - testRunner.log('Got Paint event:'); - tracingHelper.logEventShape(paint) - - testRunner.log('Got CompositeLayers event:'); - tracingHelper.logEventShape(compositeLayers); - - if (screenshots && screenshots.every(s => s.args.snapshot)) { - testRunner.log('All screenshots have image data.'); - } - - testRunner.completeTest(); -}) \ No newline at end of file
diff --git a/third_party/boringssl/BUILD.generated.gni b/third_party/boringssl/BUILD.generated.gni index 71fed77b..ce4eddb 100644 --- a/third_party/boringssl/BUILD.generated.gni +++ b/third_party/boringssl/BUILD.generated.gni
@@ -22,7 +22,6 @@ "src/crypto/asn1/a_time.c", "src/crypto/asn1/a_type.c", "src/crypto/asn1/a_utctm.c", - "src/crypto/asn1/a_utf8.c", "src/crypto/asn1/asn1_lib.c", "src/crypto/asn1/asn1_par.c", "src/crypto/asn1/asn_pack.c", @@ -76,10 +75,12 @@ "src/crypto/conf/conf_def.h", "src/crypto/conf/internal.h", "src/crypto/cpu_aarch64_apple.c", + "src/crypto/cpu_aarch64_freebsd.c", "src/crypto/cpu_aarch64_fuchsia.c", "src/crypto/cpu_aarch64_linux.c", "src/crypto/cpu_aarch64_win.c", "src/crypto/cpu_arm.c", + "src/crypto/cpu_arm_freebsd.c", "src/crypto/cpu_arm_linux.c", "src/crypto/cpu_arm_linux.h", "src/crypto/cpu_intel.c",
diff --git a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/p256-armv8-asm.S b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/p256-armv8-asm.S index 7a5202dd..75d2b93 100644 --- a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/p256-armv8-asm.S +++ b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/p256-armv8-asm.S
@@ -1402,7 +1402,7 @@ //////////////////////////////////////////////////////////////////////// // void ecp_nistz256_ord_sqr_mont(uint64_t res[4], uint64_t a[4], -// int rep); +// uint64_t rep); .globl _ecp_nistz256_ord_sqr_mont .private_extern _ecp_nistz256_ord_sqr_mont
diff --git a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/p256-armv8-asm.S b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/p256-armv8-asm.S index 3efcccb6..d255da2 100644 --- a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/p256-armv8-asm.S +++ b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/p256-armv8-asm.S
@@ -1403,7 +1403,7 @@ //////////////////////////////////////////////////////////////////////// // void ecp_nistz256_ord_sqr_mont(uint64_t res[4], uint64_t a[4], -// int rep); +// uint64_t rep); .globl ecp_nistz256_ord_sqr_mont .hidden ecp_nistz256_ord_sqr_mont .type ecp_nistz256_ord_sqr_mont,%function
diff --git a/third_party/boringssl/win-aarch64/crypto/fipsmodule/p256-armv8-asm.S b/third_party/boringssl/win-aarch64/crypto/fipsmodule/p256-armv8-asm.S index cfffc0d..d84e536c 100644 --- a/third_party/boringssl/win-aarch64/crypto/fipsmodule/p256-armv8-asm.S +++ b/third_party/boringssl/win-aarch64/crypto/fipsmodule/p256-armv8-asm.S
@@ -1437,7 +1437,7 @@ //////////////////////////////////////////////////////////////////////// // void ecp_nistz256_ord_sqr_mont(uint64_t res[4], uint64_t a[4], -// int rep); +// uint64_t rep); .globl ecp_nistz256_ord_sqr_mont .def ecp_nistz256_ord_sqr_mont
diff --git a/third_party/libgav1/BUILD.gn b/third_party/libgav1/BUILD.gn index 78f53e8..97f5affc 100644 --- a/third_party/libgav1/BUILD.gn +++ b/third_party/libgav1/BUILD.gn
@@ -3,7 +3,6 @@ # found in the LICENSE file. import("//build/config/arm.gni") -import("//third_party/libgav1/libgav1_srcs.gni") import("//third_party/libgav1/options.gni") config("public_libgav1_config") { @@ -39,69 +38,43 @@ } } -# TODO(b/259317395): Add source_set for parsing only. if (use_libgav1_parser) { - # Separate from libgav1 because utils/constants.cc and dsp/constants.cc - # generate the same object file, constants.o. - source_set("libgav1_utils") { + static_library("libgav1_parser") { configs -= [ "//build/config/compiler:chromium_code" ] configs += [ "//build/config/compiler:no_chromium_code" ] configs += [ ":private_libgav1_config" ] public_configs = [ ":public_libgav1_config" ] - sources = gav1_utils_sources - } - - # Separate from libgav1 because film_grain.cc and dsp/film_grain.cc - # generate the same object file, film_grain.o. - source_set("libgav1_dsp") { - configs -= [ "//build/config/compiler:chromium_code" ] - configs += [ "//build/config/compiler:no_chromium_code" ] - configs += [ ":private_libgav1_config" ] - - deps = [ - ":libgav1_dsp_sse4", - ":libgav1_utils", + sources = [ + "//third_party/libgav1/src/src/buffer_pool.cc", + "//third_party/libgav1/src/src/buffer_pool.h", + "//third_party/libgav1/src/src/frame_buffer.cc", + "//third_party/libgav1/src/src/internal_frame_buffer_list.cc", + "//third_party/libgav1/src/src/internal_frame_buffer_list.h", + "//third_party/libgav1/src/src/obu_parser.cc", + "//third_party/libgav1/src/src/obu_parser.h", + "//third_party/libgav1/src/src/quantizer.cc", + "//third_party/libgav1/src/src/quantizer.h", + "//third_party/libgav1/src/src/status_code.cc", + "//third_party/libgav1/src/src/symbol_decoder_context.cc", + "//third_party/libgav1/src/src/symbol_decoder_context.h", + "//third_party/libgav1/src/src/utils/bit_reader.cc", + "//third_party/libgav1/src/src/utils/bit_reader.h", + "//third_party/libgav1/src/src/utils/constants.cc", + "//third_party/libgav1/src/src/utils/constants.h", + "//third_party/libgav1/src/src/utils/logging.cc", + "//third_party/libgav1/src/src/utils/logging.h", + "//third_party/libgav1/src/src/utils/raw_bit_reader.cc", + "//third_party/libgav1/src/src/utils/raw_bit_reader.h", + "//third_party/libgav1/src/src/utils/segmentation.cc", + "//third_party/libgav1/src/src/utils/segmentation.h", + "//third_party/libgav1/src/src/utils/segmentation_map.cc", + "//third_party/libgav1/src/src/utils/segmentation_map.h", + "//third_party/libgav1/src/src/warp_prediction.cc", + "//third_party/libgav1/src/src/warp_prediction.h", + "//third_party/libgav1/src/src/yuv_buffer.cc", + "//third_party/libgav1/src/src/yuv_buffer.h", ] - public_configs = [ ":public_libgav1_config" ] - - sources = gav1_dsp_sources + gav1_dsp_headers_sources - sources += gav1_dsp_avx2_sources + gav1_dsp_avx2_headers_sources - } - - # SSE4 sources are split to their own target as Chrome is currently built - # with -msse3. - source_set("libgav1_dsp_sse4") { - configs -= [ "//build/config/compiler:chromium_code" ] - configs += [ "//build/config/compiler:no_chromium_code" ] - configs += [ ":private_libgav1_config" ] - - deps = [ ":libgav1_utils" ] - public_configs = [ ":public_libgav1_config" ] - - if (current_cpu == "x86" || current_cpu == "x64") { - cflags = [ "-msse4.1" ] - } - - sources = gav1_dsp_sse4_sources + gav1_dsp_sse4_headers_sources + - gav1_dsp_headers_sources + gav1_dsp_avx2_headers_sources - } - - static_library("libgav1") { - configs -= [ "//build/config/compiler:chromium_code" ] - configs += [ "//build/config/compiler:no_chromium_code" ] - configs += [ ":private_libgav1_config" ] - - public_configs = [ ":public_libgav1_config" ] - public_deps = [ - ":libgav1_dsp", - ":libgav1_utils", - ] - - sources = gav1_common_sources - sources += gav1_gav1_sources - sources += gav1_post_filter_sources - sources += gav1_tile_sources } }
diff --git a/third_party/libgav1/README.chromium b/third_party/libgav1/README.chromium index 733e246..2323a4b 100644 --- a/third_party/libgav1/README.chromium +++ b/third_party/libgav1/README.chromium
@@ -22,6 +22,5 @@ Use the generated commit message for the roll. -2. Generate .gni and update Date and Commit in README.chromium - cd third_party/libgav1 - go run generate_libgav1_src_gni.go +2. Update Date and Commit in README.chromium: + go run update_readme.go
diff --git a/third_party/libgav1/generate_libgav1_src_gni.go b/third_party/libgav1/generate_libgav1_src_gni.go deleted file mode 100644 index 1bdc5ff..0000000 --- a/third_party/libgav1/generate_libgav1_src_gni.go +++ /dev/null
@@ -1,177 +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. - -// How to run. -// `go run generate_libgav1_src_gni.go.` at //third_party/libgav1. -// libgav1_src.gni is generated. -package main - -import ( - "fmt" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "sort" - "strings" -) - -const ( - gniFile = "libgav1_srcs.gni" - commonPrefix = "//third_party/libgav1/" - srcDir = "./src/src" - header = `# This file is generated. Do not edit.` -) - -func getCppFiles(dir string) []string { - files, err := ioutil.ReadDir(dir) - if err != nil { - panic(err) - } - - var paths []string - for _, file := range files { - if file.IsDir() { - paths = append(paths, getCppFiles(filepath.Join(dir, file.Name()))...) - continue - } - ext := filepath.Ext(file.Name()) - if ext == ".cc" || ext == ".h" { - matches := []string{"*_test.*", "*_test_data.*"} - var isTestFile bool - for i := range matches { - isTestFile, err = filepath.Match(matches[i], file.Name()) - if err != nil { - panic(err) - } - if isTestFile { - break - } - } - if !isTestFile { - paths = append(paths, filepath.Join(dir, file.Name())) - } - } - } - return paths -} - -func getTopDirs(dir string) []string { - files, _ := ioutil.ReadDir(dir) - var paths []string - for _, file := range files { - if file.IsDir() { - paths = append(paths, filepath.Join(dir, file.Name())) - } - } - return paths -} - -func format(dir string, files []string, file *os.File) { - sourcesName := "gav1_" + dir + "_sources" - fmt.Fprintf(file, "\n%s = [\n", sourcesName) - for _, f := range files { - fmt.Fprintf(file, " \"%s%s\",\n", commonPrefix, f) - } - fmt.Fprintf(file, "]\n") -} - -func updateReadme() { - gitCmd := exec.Command("bash", "-c", "git --no-pager log -1 --format=\"%cd%n%H\" --date=format:\"%A %B %d %Y\"") - gitCmd.Dir = "src" - out, err := gitCmd.Output() - if err != nil { - panic(fmt.Sprintf("failed to execute git command: %v", err)) - } - - vals := strings.Split(string(out), "\n") - - if len(vals) < 2 { - panic(fmt.Sprintf("unexpected git log result: %v %v", vals)) - } - date := vals[0] - hash := vals[1] - - sedCmd := exec.Command("sed", "-E", "-i.back", "-e", - fmt.Sprintf("s/^(Date:)[[:space:]]+.*$/\\1 %s/", date), "-e", - fmt.Sprintf("s/^(Commit:)[[:space:]]+[a-f0-9]{40}/\\1 %s/", hash), - "README.chromium") - if err := sedCmd.Run(); err != nil { - panic(fmt.Sprintf("failed to execute sed command: %v %v", sedCmd, err)) - } - - rmCmd := exec.Command("rm", "README.chromium.back") - if rmCmd.Run() != nil { - panic(fmt.Sprintf("failed to execute rm command: %v", err)) - } -} - -func main() { - files := getCppFiles(srcDir) - topDirs := getTopDirs(srcDir) - m := make(map[string][]string) - for _, f := range files { - found := false - for _, d := range topDirs { - if strings.HasPrefix(f, d) { - var bd string - for _, asm := range []string{"sse4", "avx2"} { - pattern := "*_" + asm + "*" - if match, err := filepath.Match(pattern, filepath.Base(f)); err != nil { - panic(err) - } else if match { - bd = filepath.Base(d) + "_" + asm - break - } - } - if bd == "" { - bd = filepath.Base(d) - } - - // Split the dsp headers out to their own variables as the - // assembly may depend on both its headers and the top-level - // headers. - if strings.HasPrefix(bd, "dsp") && filepath.Ext(f) == ".h" { - m[bd+"_headers"] = append(m[bd+"_headers"], f) - } else { - m[bd] = append(m[bd], f) - } - found = true - break - } - } - if !found { - m["common"] = append(m["common"], f) - } - } - - if err := os.RemoveAll(gniFile); err != nil { - panic(err) - } - - file, err := os.OpenFile(gniFile, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - panic(err) - } - fmt.Fprintf(file, "%s\n", header) - - var keys []string - for k := range m { - keys = append(keys, k) - } - sort.Strings(keys) - - for _, k := range keys { - v := m[k] - format(k, v, file) - } - file.Close() - - gnCmd := exec.Command("gn", "format", gniFile) - if gnCmd.Run() != nil { - panic(fmt.Sprintf("failed to execute gn format command: %v", err)) - } - - updateReadme() -}
diff --git a/third_party/libgav1/libgav1_srcs.gni b/third_party/libgav1/libgav1_srcs.gni deleted file mode 100644 index c559a1fe..0000000 --- a/third_party/libgav1/libgav1_srcs.gni +++ /dev/null
@@ -1,277 +0,0 @@ -# This file is generated. Do not edit. - -gav1_common_sources = [ - "//third_party/libgav1/src/src/buffer_pool.cc", - "//third_party/libgav1/src/src/buffer_pool.h", - "//third_party/libgav1/src/src/decoder.cc", - "//third_party/libgav1/src/src/decoder_impl.cc", - "//third_party/libgav1/src/src/decoder_impl.h", - "//third_party/libgav1/src/src/decoder_settings.cc", - "//third_party/libgav1/src/src/decoder_state.h", - "//third_party/libgav1/src/src/film_grain.cc", - "//third_party/libgav1/src/src/film_grain.h", - "//third_party/libgav1/src/src/frame_buffer.cc", - "//third_party/libgav1/src/src/frame_buffer_utils.h", - "//third_party/libgav1/src/src/frame_scratch_buffer.h", - "//third_party/libgav1/src/src/internal_frame_buffer_list.cc", - "//third_party/libgav1/src/src/internal_frame_buffer_list.h", - "//third_party/libgav1/src/src/loop_restoration_info.cc", - "//third_party/libgav1/src/src/loop_restoration_info.h", - "//third_party/libgav1/src/src/motion_vector.cc", - "//third_party/libgav1/src/src/motion_vector.h", - "//third_party/libgav1/src/src/obu_parser.cc", - "//third_party/libgav1/src/src/obu_parser.h", - "//third_party/libgav1/src/src/prediction_mask.cc", - "//third_party/libgav1/src/src/prediction_mask.h", - "//third_party/libgav1/src/src/quantizer.cc", - "//third_party/libgav1/src/src/quantizer.h", - "//third_party/libgav1/src/src/reconstruction.cc", - "//third_party/libgav1/src/src/reconstruction.h", - "//third_party/libgav1/src/src/residual_buffer_pool.cc", - "//third_party/libgav1/src/src/residual_buffer_pool.h", - "//third_party/libgav1/src/src/status_code.cc", - "//third_party/libgav1/src/src/symbol_decoder_context.cc", - "//third_party/libgav1/src/src/symbol_decoder_context.h", - "//third_party/libgav1/src/src/threading_strategy.cc", - "//third_party/libgav1/src/src/threading_strategy.h", - "//third_party/libgav1/src/src/version.cc", - "//third_party/libgav1/src/src/warp_prediction.cc", - "//third_party/libgav1/src/src/warp_prediction.h", - "//third_party/libgav1/src/src/yuv_buffer.cc", - "//third_party/libgav1/src/src/yuv_buffer.h", -] - -gav1_dsp_sources = [ - "//third_party/libgav1/src/src/dsp/arm/average_blend_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/cdef_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/convolve_10bit_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/convolve_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/distance_weighted_blend_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/film_grain_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/intra_edge_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/intrapred_cfl_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/intrapred_directional_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/intrapred_filter_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/intrapred_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/intrapred_smooth_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/inverse_transform_10bit_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/inverse_transform_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/loop_filter_10bit_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/loop_filter_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/loop_restoration_10bit_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/loop_restoration_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/mask_blend_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/motion_field_projection_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/motion_vector_search_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/obmc_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/super_res_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/warp_neon.cc", - "//third_party/libgav1/src/src/dsp/arm/weight_mask_neon.cc", - "//third_party/libgav1/src/src/dsp/average_blend.cc", - "//third_party/libgav1/src/src/dsp/cdef.cc", - "//third_party/libgav1/src/src/dsp/constants.cc", - "//third_party/libgav1/src/src/dsp/convolve.cc", - "//third_party/libgav1/src/src/dsp/distance_weighted_blend.cc", - "//third_party/libgav1/src/src/dsp/dsp.cc", - "//third_party/libgav1/src/src/dsp/film_grain.cc", - "//third_party/libgav1/src/src/dsp/intra_edge.cc", - "//third_party/libgav1/src/src/dsp/intrapred.cc", - "//third_party/libgav1/src/src/dsp/intrapred_cfl.cc", - "//third_party/libgav1/src/src/dsp/intrapred_directional.cc", - "//third_party/libgav1/src/src/dsp/intrapred_filter.cc", - "//third_party/libgav1/src/src/dsp/intrapred_smooth.cc", - "//third_party/libgav1/src/src/dsp/inverse_transform.cc", - "//third_party/libgav1/src/src/dsp/loop_filter.cc", - "//third_party/libgav1/src/src/dsp/loop_restoration.cc", - "//third_party/libgav1/src/src/dsp/mask_blend.cc", - "//third_party/libgav1/src/src/dsp/motion_field_projection.cc", - "//third_party/libgav1/src/src/dsp/motion_vector_search.cc", - "//third_party/libgav1/src/src/dsp/obmc.cc", - "//third_party/libgav1/src/src/dsp/super_res.cc", - "//third_party/libgav1/src/src/dsp/warp.cc", - "//third_party/libgav1/src/src/dsp/weight_mask.cc", -] - -gav1_dsp_avx2_sources = [ - "//third_party/libgav1/src/src/dsp/x86/cdef_avx2.cc", - "//third_party/libgav1/src/src/dsp/x86/convolve_avx2.cc", - "//third_party/libgav1/src/src/dsp/x86/loop_restoration_10bit_avx2.cc", - "//third_party/libgav1/src/src/dsp/x86/loop_restoration_avx2.cc", -] - -gav1_dsp_avx2_headers_sources = [ - "//third_party/libgav1/src/src/dsp/x86/cdef_avx2.h", - "//third_party/libgav1/src/src/dsp/x86/common_avx2.h", - "//third_party/libgav1/src/src/dsp/x86/convolve_avx2.h", - "//third_party/libgav1/src/src/dsp/x86/loop_restoration_avx2.h", -] - -gav1_dsp_headers_sources = [ - "//third_party/libgav1/src/src/dsp/arm/average_blend_neon.h", - "//third_party/libgav1/src/src/dsp/arm/cdef_neon.h", - "//third_party/libgav1/src/src/dsp/arm/common_neon.h", - "//third_party/libgav1/src/src/dsp/arm/convolve_neon.h", - "//third_party/libgav1/src/src/dsp/arm/distance_weighted_blend_neon.h", - "//third_party/libgav1/src/src/dsp/arm/film_grain_neon.h", - "//third_party/libgav1/src/src/dsp/arm/intra_edge_neon.h", - "//third_party/libgav1/src/src/dsp/arm/intrapred_cfl_neon.h", - "//third_party/libgav1/src/src/dsp/arm/intrapred_directional_neon.h", - "//third_party/libgav1/src/src/dsp/arm/intrapred_filter_neon.h", - "//third_party/libgav1/src/src/dsp/arm/intrapred_neon.h", - "//third_party/libgav1/src/src/dsp/arm/intrapred_smooth_neon.h", - "//third_party/libgav1/src/src/dsp/arm/inverse_transform_neon.h", - "//third_party/libgav1/src/src/dsp/arm/loop_filter_neon.h", - "//third_party/libgav1/src/src/dsp/arm/loop_restoration_neon.h", - "//third_party/libgav1/src/src/dsp/arm/mask_blend_neon.h", - "//third_party/libgav1/src/src/dsp/arm/motion_field_projection_neon.h", - "//third_party/libgav1/src/src/dsp/arm/motion_vector_search_neon.h", - "//third_party/libgav1/src/src/dsp/arm/obmc_neon.h", - "//third_party/libgav1/src/src/dsp/arm/super_res_neon.h", - "//third_party/libgav1/src/src/dsp/arm/warp_neon.h", - "//third_party/libgav1/src/src/dsp/arm/weight_mask_neon.h", - "//third_party/libgav1/src/src/dsp/average_blend.h", - "//third_party/libgav1/src/src/dsp/cdef.h", - "//third_party/libgav1/src/src/dsp/common.h", - "//third_party/libgav1/src/src/dsp/constants.h", - "//third_party/libgav1/src/src/dsp/convolve.h", - "//third_party/libgav1/src/src/dsp/distance_weighted_blend.h", - "//third_party/libgav1/src/src/dsp/dsp.h", - "//third_party/libgav1/src/src/dsp/film_grain.h", - "//third_party/libgav1/src/src/dsp/film_grain_common.h", - "//third_party/libgav1/src/src/dsp/intra_edge.h", - "//third_party/libgav1/src/src/dsp/intrapred.h", - "//third_party/libgav1/src/src/dsp/intrapred_cfl.h", - "//third_party/libgav1/src/src/dsp/intrapred_directional.h", - "//third_party/libgav1/src/src/dsp/intrapred_filter.h", - "//third_party/libgav1/src/src/dsp/intrapred_smooth.h", - "//third_party/libgav1/src/src/dsp/inverse_transform.h", - "//third_party/libgav1/src/src/dsp/loop_filter.h", - "//third_party/libgav1/src/src/dsp/loop_restoration.h", - "//third_party/libgav1/src/src/dsp/mask_blend.h", - "//third_party/libgav1/src/src/dsp/motion_field_projection.h", - "//third_party/libgav1/src/src/dsp/motion_vector_search.h", - "//third_party/libgav1/src/src/dsp/obmc.h", - "//third_party/libgav1/src/src/dsp/super_res.h", - "//third_party/libgav1/src/src/dsp/warp.h", - "//third_party/libgav1/src/src/dsp/weight_mask.h", -] - -gav1_dsp_sse4_sources = [ - "//third_party/libgav1/src/src/dsp/x86/average_blend_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/cdef_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/convolve_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/distance_weighted_blend_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/film_grain_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/intra_edge_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/intrapred_cfl_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/intrapred_directional_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/intrapred_filter_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/intrapred_smooth_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/intrapred_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/inverse_transform_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/loop_filter_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/loop_restoration_10bit_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/loop_restoration_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/mask_blend_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/motion_field_projection_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/motion_vector_search_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/obmc_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/super_res_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/warp_sse4.cc", - "//third_party/libgav1/src/src/dsp/x86/weight_mask_sse4.cc", -] - -gav1_dsp_sse4_headers_sources = [ - "//third_party/libgav1/src/src/dsp/x86/average_blend_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/cdef_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/common_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/convolve_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/distance_weighted_blend_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/film_grain_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/intra_edge_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/intrapred_cfl_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/intrapred_directional_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/intrapred_filter_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/intrapred_smooth_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/intrapred_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/inverse_transform_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/loop_filter_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/loop_restoration_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/mask_blend_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/motion_field_projection_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/motion_vector_search_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/obmc_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/super_res_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/transpose_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/warp_sse4.h", - "//third_party/libgav1/src/src/dsp/x86/weight_mask_sse4.h", -] - -gav1_gav1_sources = [ - "//third_party/libgav1/src/src/gav1/decoder.h", - "//third_party/libgav1/src/src/gav1/decoder_buffer.h", - "//third_party/libgav1/src/src/gav1/decoder_settings.h", - "//third_party/libgav1/src/src/gav1/frame_buffer.h", - "//third_party/libgav1/src/src/gav1/status_code.h", - "//third_party/libgav1/src/src/gav1/symbol_visibility.h", - "//third_party/libgav1/src/src/gav1/version.h", -] - -gav1_post_filter_sources = [ - "//third_party/libgav1/src/src/post_filter.h", - "//third_party/libgav1/src/src/post_filter/cdef.cc", - "//third_party/libgav1/src/src/post_filter/deblock.cc", - "//third_party/libgav1/src/src/post_filter/loop_restoration.cc", - "//third_party/libgav1/src/src/post_filter/post_filter.cc", - "//third_party/libgav1/src/src/post_filter/super_res.cc", -] - -gav1_tile_sources = [ - "//third_party/libgav1/src/src/tile.h", - "//third_party/libgav1/src/src/tile/bitstream/mode_info.cc", - "//third_party/libgav1/src/src/tile/bitstream/palette.cc", - "//third_party/libgav1/src/src/tile/bitstream/partition.cc", - "//third_party/libgav1/src/src/tile/bitstream/transform_size.cc", - "//third_party/libgav1/src/src/tile/prediction.cc", - "//third_party/libgav1/src/src/tile/tile.cc", - "//third_party/libgav1/src/src/tile_scratch_buffer.cc", - "//third_party/libgav1/src/src/tile_scratch_buffer.h", -] - -gav1_utils_sources = [ - "//third_party/libgav1/src/src/utils/array_2d.h", - "//third_party/libgav1/src/src/utils/bit_mask_set.h", - "//third_party/libgav1/src/src/utils/bit_reader.cc", - "//third_party/libgav1/src/src/utils/bit_reader.h", - "//third_party/libgav1/src/src/utils/block_parameters_holder.cc", - "//third_party/libgav1/src/src/utils/block_parameters_holder.h", - "//third_party/libgav1/src/src/utils/blocking_counter.h", - "//third_party/libgav1/src/src/utils/common.h", - "//third_party/libgav1/src/src/utils/compiler_attributes.h", - "//third_party/libgav1/src/src/utils/constants.cc", - "//third_party/libgav1/src/src/utils/constants.h", - "//third_party/libgav1/src/src/utils/cpu.cc", - "//third_party/libgav1/src/src/utils/cpu.h", - "//third_party/libgav1/src/src/utils/dynamic_buffer.h", - "//third_party/libgav1/src/src/utils/entropy_decoder.cc", - "//third_party/libgav1/src/src/utils/entropy_decoder.h", - "//third_party/libgav1/src/src/utils/executor.cc", - "//third_party/libgav1/src/src/utils/executor.h", - "//third_party/libgav1/src/src/utils/logging.cc", - "//third_party/libgav1/src/src/utils/logging.h", - "//third_party/libgav1/src/src/utils/memory.h", - "//third_party/libgav1/src/src/utils/queue.h", - "//third_party/libgav1/src/src/utils/raw_bit_reader.cc", - "//third_party/libgav1/src/src/utils/raw_bit_reader.h", - "//third_party/libgav1/src/src/utils/reference_info.h", - "//third_party/libgav1/src/src/utils/segmentation.cc", - "//third_party/libgav1/src/src/utils/segmentation.h", - "//third_party/libgav1/src/src/utils/segmentation_map.cc", - "//third_party/libgav1/src/src/utils/segmentation_map.h", - "//third_party/libgav1/src/src/utils/stack.h", - "//third_party/libgav1/src/src/utils/threadpool.cc", - "//third_party/libgav1/src/src/utils/threadpool.h", - "//third_party/libgav1/src/src/utils/types.h", - "//third_party/libgav1/src/src/utils/unbounded_queue.h", - "//third_party/libgav1/src/src/utils/vector.h", -]
diff --git a/third_party/libgav1/update_readme.go b/third_party/libgav1/update_readme.go new file mode 100644 index 0000000..223f258 --- /dev/null +++ b/third_party/libgav1/update_readme.go
@@ -0,0 +1,48 @@ +// 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. + +// How to run. +// `go run update_readme.go.` at //third_party/libgav1. +// README.chromium is updated with the correct info. +package main + +import ( + "fmt" + "os/exec" + "strings" +) + +func updateReadme() { + gitCmd := exec.Command("bash", "-c", "git --no-pager log -1 --format=\"%cd%n%H\" --date=format:\"%A %B %d %Y\"") + gitCmd.Dir = "src" + out, err := gitCmd.Output() + if err != nil { + panic(fmt.Sprintf("failed to execute git command: %v", err)) + } + + vals := strings.Split(string(out), "\n") + + if len(vals) < 2 { + panic(fmt.Sprintf("unexpected git log result: %v %v", vals)) + } + date := vals[0] + hash := vals[1] + + sedCmd := exec.Command("sed", "-E", "-i.back", "-e", + fmt.Sprintf("s/^(Date:)[[:space:]]+.*$/\\1 %s/", date), "-e", + fmt.Sprintf("s/^(Commit:)[[:space:]]+[a-f0-9]{40}/\\1 %s/", hash), + "README.chromium") + if err := sedCmd.Run(); err != nil { + panic(fmt.Sprintf("failed to execute sed command: %v %v", sedCmd, err)) + } + + rmCmd := exec.Command("rm", "README.chromium.back") + if rmCmd.Run() != nil { + panic(fmt.Sprintf("failed to execute rm command: %v", err)) + } +} + +func main() { + updateReadme() +}
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py index b110f90..a991d4b 100755 --- a/tools/clang/scripts/update.py +++ b/tools/clang/scripts/update.py
@@ -35,8 +35,8 @@ # https://chromium.googlesource.com/chromium/src/+/main/docs/updating_clang.md # Reverting problematic clang rolls is safe, though. # This is the output of `git describe` and is usable as a commit-ish. -CLANG_REVISION = 'llvmorg-16-init-10467-g1239d37b' -CLANG_SUB_REVISION = 2 +CLANG_REVISION = 'llvmorg-16-init-10736-ged9638c4' +CLANG_SUB_REVISION = 1 PACKAGE_VERSION = '%s-%s' % (CLANG_REVISION, CLANG_SUB_REVISION) RELEASE_VERSION = '16'
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index 851f810..ee40f75 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -363,7 +363,6 @@ 'Linux Builder (reclient compare)': 'gpu_tests_release_bot_reclient', 'Linux CFI (reclient shadow)': 'cfi_full_cfi_icall_cfi_diag_thin_lto_release_static_dcheck_always_on_reclient', 'Linux ChromiumOS MSan Focal': 'chromeos_msan_focal_release_bot_reclient', - 'Linux MSan Focal': 'msan_focal_release_bot_reclient', 'Linux Viz': 'release_trybot_minimal_symbols_reclient', # TODO(crbug.com/1260232): remove this after the migration. 'Mac Builder (reclient compare)': 'gpu_tests_release_bot_minimal_symbols_reclient', @@ -434,7 +433,6 @@ 'linux-lacros-dbg-fyi': 'lacros_on_linux_debug_bot_reclient', 'linux-lacros-dbg-tests-fyi': 'lacros_on_linux_debug_bot', 'linux-lacros-tester-fyi-rel': 'lacros_on_linux_release_bot', - 'linux-lacros-tester-rel-reviver': 'lacros_on_linux_release_bot_reclient', 'linux-lacros-version-skew-fyi': 'lacros_on_linux_release_not_build_ash_bot_reclient', 'linux-perfetto-rel': 'perfetto_release_bot_reclient', 'linux-rel-no-external-ip': 'gpu_tests_release_bot_do_typecheck_reclient', @@ -621,7 +619,7 @@ 'Linux CFI': 'cfi_full_cfi_icall_cfi_diag_thin_lto_release_static_dcheck_always_on_reclient', 'Linux Chromium OS ASan LSan Builder': 'asan_lsan_chromeos_release_bot_dcheck_always_on_reclient', 'Linux ChromiumOS MSan Builder': 'chromeos_msan_release_bot_reclient', - 'Linux MSan Builder': 'msan_release_bot_reclient', + 'Linux MSan Builder': 'msan_focal_release_bot_reclient', 'Linux TSan Builder': 'tsan_disable_nacl_release_bot_reclient', 'Mac ASan 64 Builder': 'asan_minimal_symbols_disable_nacl_release_bot_dcheck_always_on_reclient', 'WebKit Linux ASAN': 'asan_lsan_release_bot_blink_reclient', @@ -911,6 +909,7 @@ 'linux-chrome-beta': 'official_goma', 'linux-chrome-stable': 'official_goma', 'linux-chromeos-chrome': 'official_goma_chromeos_include_unwind_tables', + 'linux-chromeos-compile-chrome': 'official_goma_chromeos_include_unwind_tables', 'linux-finch-smoke-chrome': 'official_goma', 'mac-arm64-finch-smoke-chrome': 'official_goma_mac_arm', 'mac-chrome': 'official_goma_mac', @@ -938,7 +937,7 @@ 'tryserver.chromium': { 'android-official': 'android_official_optimize_reclient_trybot', - 'fuchsia-official': 'fuchsia_official_optimize_goma_trybot', + 'fuchsia-official': 'fuchsia_official_optimize_reclient_trybot', 'linux-official': 'official_optimize_reclient_trybot', 'mac-official': 'official_optimize_goma_trybot', 'win-official': 'official_optimize_reclient_trybot', @@ -1069,7 +1068,6 @@ 'linux-lacros-dbg': 'lacros_on_linux_debug_bot', 'linux-lacros-rel': 'lacros_on_linux_release_trybot_reclient', 'linux-lacros-rel-code-coverage': 'lacros_on_linux_release_trybot_coverage', - 'linux-lacros-tester-rel-reviver': 'lacros_on_linux_release_trybot', }, 'tryserver.chromium.codesearch': { @@ -1094,22 +1092,22 @@ 'dawn-try-win10-x86-rel': 'dawn_tests_release_trybot_x86', 'dawn-win10-x64-deps-rel': 'dawn_tests_release_trybot', 'dawn-win10-x86-deps-rel': 'dawn_tests_release_trybot_x86_reclient', - 'linux-dawn-rel': 'dawn_tests_with_desktop_gl_release_trybot', + 'linux-dawn-rel': 'dawn_tests_with_desktop_gl_release_trybot_reclient', 'mac-dawn-rel': 'dawn_tests_release_trybot_alloc_none', 'win-dawn-rel': 'dawn_tests_release_trybot_reclient', }, 'tryserver.chromium.fuchsia': { - 'fuchsia-arm64-cast-receiver-rel': 'release_trybot_fuchsia_arm64_cast_receiver', + 'fuchsia-arm64-cast-receiver-rel': 'release_trybot_fuchsia_arm64_cast_receiver_reclient', 'fuchsia-arm64-chrome-rel': 'release_trybot_fuchsia_arm64', 'fuchsia-arm64-rel': 'release_trybot_fuchsia_arm64', 'fuchsia-arm64-rel-orchestrator': 'release_trybot_fuchsia_arm64', - 'fuchsia-binary-size': 'release_fuchsia_arm64_binary_size', - 'fuchsia-compile-x64-dbg': 'debug_bot_fuchsia_compile_only', - 'fuchsia-deterministic-dbg': 'debug_bot_fuchsia', - 'fuchsia-fyi-arm64-dbg': 'debug_bot_fuchsia_arm64', - 'fuchsia-fyi-x64-dbg': 'debug_bot_fuchsia', - 'fuchsia-x64-cast-receiver-rel': 'release_trybot_fuchsia_cast_receiver', + 'fuchsia-binary-size': 'release_fuchsia_arm64_binary_size_reclient', + 'fuchsia-compile-x64-dbg': 'debug_bot_fuchsia_compile_only_reclient', + 'fuchsia-deterministic-dbg': 'debug_bot_fuchsia_reclient', + 'fuchsia-fyi-arm64-dbg': 'debug_bot_fuchsia_arm64_reclient', + 'fuchsia-fyi-x64-dbg': 'debug_bot_fuchsia_reclient', + 'fuchsia-x64-cast-receiver-rel': 'release_trybot_fuchsia_cast_receiver_reclient', 'fuchsia-x64-chrome-rel': 'release_trybot_fuchsia_chrome', 'fuchsia-x64-rel': 'release_trybot_fuchsia', 'fuchsia-x64-workstation': 'release_trybot_fuchsia_chrome', @@ -1182,8 +1180,7 @@ # This is intentionally a release_bot and not a release_trybot; # enabling DCHECKs seems to cause flaky failures that don't show up # on the continuous builder. - 'linux_chromium_msan_focal': 'msan_focal_release_bot_reclient', - 'linux_chromium_msan_rel_ng': 'msan_release_bot_reclient', + 'linux_chromium_msan_rel_ng': 'msan_focal_release_bot_reclient', 'linux_chromium_tsan_rel_ng': 'tsan_disable_nacl_release_trybot_reclient', @@ -1297,7 +1294,7 @@ 'tryserver.chromium.tricium': { 'android-clang-tidy-rel': 'android_release_trybot_reclient', - 'fuchsia-clang-tidy-rel': 'release_trybot_fuchsia', + 'fuchsia-clang-tidy-rel': 'release_trybot_fuchsia_reclient', 'ios-clang-tidy-rel': 'ios_device_release_compile_only', 'linux-chromeos-clang-tidy-rel': 'chromeos_with_codecs_release_trybot', 'linux-clang-tidy-dbg': 'debug_bot_reclient', @@ -2470,10 +2467,6 @@ 'dawn_tests', 'release_trybot_reclient', 'x86', ], - 'dawn_tests_with_desktop_gl_release_trybot': [ - 'dawn_tests', 'dawn_enable_desktop_gl', 'release_trybot_minimal_symbols', - ], - 'dawn_tests_with_desktop_gl_release_trybot_reclient': [ 'dawn_tests', 'dawn_enable_desktop_gl', 'release_trybot_minimal_symbols_reclient', ], @@ -2494,22 +2487,10 @@ 'debug_bot_reclient', 'enable_blink_animation_use_time_delta', ], - 'debug_bot_fuchsia': [ - 'debug_bot', 'fuchsia', - ], - - 'debug_bot_fuchsia_arm64': [ - 'debug_bot', 'fuchsia', 'arm64', 'arm64_host', - ], - 'debug_bot_fuchsia_arm64_reclient': [ 'debug_bot_reclient', 'fuchsia', 'arm64', 'arm64_host', ], - 'debug_bot_fuchsia_compile_only': [ - 'debug_bot', 'fuchsia', 'compile_only', - ], - 'debug_bot_fuchsia_compile_only_reclient': [ 'debug_bot_reclient', 'fuchsia', 'compile_only', ], @@ -2556,14 +2537,14 @@ 'fuchsia', 'release', 'clang_tot', 'static', ], - 'fuchsia_official_optimize_goma_trybot': [ - 'official_optimize_goma_trybot', 'fuchsia', - ], - 'fuchsia_official_optimize_reclient': [ 'official_optimize_reclient', 'fuchsia', ], + 'fuchsia_official_optimize_reclient_trybot': [ + 'official_optimize_reclient_trybot', 'fuchsia', + ], + 'gn_linux_upload': [ 'gn_linux_upload', 'official', 'goma', ], @@ -3571,8 +3552,8 @@ 'release_bot_reclient', 'x86', 'minimal_symbols', ], - 'release_fuchsia_arm64_binary_size': [ - 'release', 'official_optimize_goma', 'fuchsia', 'arm64', 'cast_receiver_size_optimized', + 'release_fuchsia_arm64_binary_size_reclient': [ + 'release', 'official_optimize_reclient', 'fuchsia', 'arm64', 'cast_receiver_size_optimized', ], 'release_rust_android_arm_reclient': [ @@ -3635,18 +3616,22 @@ 'release_trybot', 'fuchsia', 'arm64', 'arm64_host', ], - 'release_trybot_fuchsia_arm64_cast_receiver': [ - 'release_trybot', 'fuchsia', 'arm64', 'arm64_host', 'cast_receiver_size_optimized', + 'release_trybot_fuchsia_arm64_cast_receiver_reclient': [ + 'release_trybot_reclient', 'fuchsia', 'arm64', 'arm64_host', 'cast_receiver_size_optimized', ], - 'release_trybot_fuchsia_cast_receiver': [ - 'release_trybot', 'fuchsia', 'cast_receiver_size_optimized', + 'release_trybot_fuchsia_cast_receiver_reclient': [ + 'release_trybot_reclient', 'fuchsia', 'cast_receiver_size_optimized', ], 'release_trybot_fuchsia_chrome': [ 'release_trybot', 'fuchsia', 'fuchsia_chrome' ], + 'release_trybot_fuchsia_reclient': [ + 'release_trybot_reclient', 'fuchsia', + ], + 'release_trybot_minimal_symbols_reclient': [ 'release_trybot_minimal_symbols_reclient', ],
diff --git a/tools/mb/mb_config_expectations/chromium.fyi.json b/tools/mb/mb_config_expectations/chromium.fyi.json index 2fdc89db..7314e6f 100644 --- a/tools/mb/mb_config_expectations/chromium.fyi.json +++ b/tools/mb/mb_config_expectations/chromium.fyi.json
@@ -438,17 +438,6 @@ "use_remoteexec": true } }, - "Linux MSan Focal": { - "gn_args": { - "dcheck_always_on": false, - "instrumented_libraries_release": "focal", - "is_component_build": false, - "is_debug": false, - "is_msan": true, - "msan_track_origins": 2, - "use_remoteexec": true - } - }, "Linux Viz": { "gn_args": { "dcheck_always_on": true, @@ -1278,17 +1267,6 @@ "use_goma": true } }, - "linux-lacros-tester-rel-reviver": { - "gn_args": { - "also_build_ash_chrome": true, - "chromeos_is_browser_only": true, - "dcheck_always_on": false, - "is_component_build": false, - "is_debug": false, - "target_os": "chromeos", - "use_remoteexec": true - } - }, "linux-lacros-version-skew-fyi": { "gn_args": { "chromeos_is_browser_only": true,
diff --git a/tools/mb/mb_config_expectations/chromium.memory.json b/tools/mb/mb_config_expectations/chromium.memory.json index b8ad634..f026f05 100644 --- a/tools/mb/mb_config_expectations/chromium.memory.json +++ b/tools/mb/mb_config_expectations/chromium.memory.json
@@ -50,7 +50,7 @@ "Linux MSan Builder": { "gn_args": { "dcheck_always_on": false, - "instrumented_libraries_release": "xenial", + "instrumented_libraries_release": "focal", "is_component_build": false, "is_debug": false, "is_msan": true,
diff --git a/tools/mb/mb_config_expectations/tryserver.chrome.json b/tools/mb/mb_config_expectations/tryserver.chrome.json index 129f641..eff0052 100644 --- a/tools/mb/mb_config_expectations/tryserver.chrome.json +++ b/tools/mb/mb_config_expectations/tryserver.chrome.json
@@ -359,6 +359,16 @@ "use_goma": true } }, + "linux-chromeos-compile-chrome": { + "gn_args": { + "exclude_unwind_tables": false, + "is_chrome_branded": true, + "is_official_build": true, + "symbol_level": 1, + "target_os": "chromeos", + "use_goma": true + } + }, "linux-finch-smoke-chrome": { "gn_args": { "is_chrome_branded": true,
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.chromiumos.json b/tools/mb/mb_config_expectations/tryserver.chromium.chromiumos.json index 426c800..c83e982 100644 --- a/tools/mb/mb_config_expectations/tryserver.chromium.chromiumos.json +++ b/tools/mb/mb_config_expectations/tryserver.chromium.chromiumos.json
@@ -377,17 +377,5 @@ "use_clang_coverage": true, "use_goma": true } - }, - "linux-lacros-tester-rel-reviver": { - "gn_args": { - "also_build_ash_chrome": true, - "chromeos_is_browser_only": true, - "dcheck_always_on": true, - "is_component_build": false, - "is_debug": false, - "symbol_level": 0, - "target_os": "chromeos", - "use_goma": true - } } } \ No newline at end of file
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.dawn.json b/tools/mb/mb_config_expectations/tryserver.chromium.dawn.json index 3db5191..4ad5ea67 100644 --- a/tools/mb/mb_config_expectations/tryserver.chromium.dawn.json +++ b/tools/mb/mb_config_expectations/tryserver.chromium.dawn.json
@@ -137,7 +137,7 @@ "is_debug": false, "symbol_level": 1, "use_dawn": true, - "use_goma": true + "use_remoteexec": true } }, "mac-dawn-rel": {
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.fuchsia.json b/tools/mb/mb_config_expectations/tryserver.chromium.fuchsia.json index d16f57b5..67d4e82 100644 --- a/tools/mb/mb_config_expectations/tryserver.chromium.fuchsia.json +++ b/tools/mb/mb_config_expectations/tryserver.chromium.fuchsia.json
@@ -9,7 +9,7 @@ "target_cpu": "arm64", "target_os": "fuchsia", "test_host_cpu": "arm64", - "use_goma": true + "use_remoteexec": true } }, "fuchsia-arm64-chrome-rel": { @@ -56,7 +56,7 @@ "is_official_build": true, "target_cpu": "arm64", "target_os": "fuchsia", - "use_goma": true + "use_remoteexec": true } }, "fuchsia-compile-x64-dbg": { @@ -65,7 +65,7 @@ "is_debug": true, "symbol_level": 0, "target_os": "fuchsia", - "use_goma": true + "use_remoteexec": true } }, "fuchsia-deterministic-dbg": { @@ -74,7 +74,7 @@ "is_debug": true, "symbol_level": 1, "target_os": "fuchsia", - "use_goma": true + "use_remoteexec": true } }, "fuchsia-fyi-arm64-dbg": { @@ -85,7 +85,7 @@ "target_cpu": "arm64", "target_os": "fuchsia", "test_host_cpu": "arm64", - "use_goma": true + "use_remoteexec": true } }, "fuchsia-fyi-x64-dbg": { @@ -94,7 +94,7 @@ "is_debug": true, "symbol_level": 1, "target_os": "fuchsia", - "use_goma": true + "use_remoteexec": true } }, "fuchsia-x64-cast-receiver-rel": { @@ -105,7 +105,7 @@ "is_debug": false, "symbol_level": 0, "target_os": "fuchsia", - "use_goma": true + "use_remoteexec": true } }, "fuchsia-x64-chrome-rel": {
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.json b/tools/mb/mb_config_expectations/tryserver.chromium.json index a0fe32aa..7822daf 100644 --- a/tools/mb/mb_config_expectations/tryserver.chromium.json +++ b/tools/mb/mb_config_expectations/tryserver.chromium.json
@@ -15,7 +15,7 @@ "is_official_build": true, "symbol_level": 1, "target_os": "fuchsia", - "use_goma": true + "use_remoteexec": true } }, "linux-official": {
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.linux.json b/tools/mb/mb_config_expectations/tryserver.chromium.linux.json index 62af472..9fbf21b0 100644 --- a/tools/mb/mb_config_expectations/tryserver.chromium.linux.json +++ b/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
@@ -682,21 +682,10 @@ "use_remoteexec": true } }, - "linux_chromium_msan_focal": { - "gn_args": { - "dcheck_always_on": false, - "instrumented_libraries_release": "focal", - "is_component_build": false, - "is_debug": false, - "is_msan": true, - "msan_track_origins": 2, - "use_remoteexec": true - } - }, "linux_chromium_msan_rel_ng": { "gn_args": { "dcheck_always_on": false, - "instrumented_libraries_release": "xenial", + "instrumented_libraries_release": "focal", "is_component_build": false, "is_debug": false, "is_msan": true,
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.tricium.json b/tools/mb/mb_config_expectations/tryserver.chromium.tricium.json index 34ccec6..8fc9dfca 100644 --- a/tools/mb/mb_config_expectations/tryserver.chromium.tricium.json +++ b/tools/mb/mb_config_expectations/tryserver.chromium.tricium.json
@@ -20,7 +20,7 @@ "is_debug": false, "symbol_level": 0, "target_os": "fuchsia", - "use_goma": true + "use_remoteexec": true } }, "ios-clang-tidy-rel": {
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index b395bec..b89c2737 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -18146,11 +18146,6 @@ <int value="5" label="Try to override the status when its valus is error."/> </enum> -<enum name="ConcerningHeaderPresent"> - <int value="0" label="Header not present"/> - <int value="1" label="Header present"/> -</enum> - <enum name="ConditionalFocusDecision"> <int value="0" label="kExplicitFocusCapturedSurface"/> <int value="1" label="kExplicitNoFocusChange"/> @@ -36682,6 +36677,7 @@ <int value="1734" label="OS_DIAGNOSTICS_RUNNVMESELFTESTROUTINE"/> <int value="1735" label="AUTOTESTPRIVATE_STARTFRAMECOUNTING"/> <int value="1736" label="AUTOTESTPRIVATE_STOPFRAMECOUNTING"/> + <int value="1737" label="FEEDBACKPRIVATE_OPENFEEDBACK"/> </enum> <enum name="ExtensionIconState"> @@ -64727,6 +64723,7 @@ label="ExperimentalAccessibilityDictationMoreCommands:disabled"/> <int value="1874050287" label="UploadOfficeToCloud:enabled"/> <int value="1874195462" label="ChromeHomeMenuItemsExpandSheet:disabled"/> + <int value="1874203133" label="SystemSounds:enabled"/> <int value="1874604540" label="UseSuggestionsEvenIfFew:disabled"/> <int value="1875156497" label="CaptureMode:enabled"/> <int value="1877769074" label="PhoneHubFeatureSetupErrorHandling:enabled"/> @@ -65103,6 +65100,7 @@ <int value="2095740699" label="OmniboxPedalsBatch3:disabled"/> <int value="2096736155" label="BrowsingDataLifetimeManager:enabled"/> <int value="2097048479" label="disable-auto-hiding-toolbar-threshold"/> + <int value="2097313481" label="SystemSounds:disabled"/> <int value="2097465503" label="RequestDesktopSiteZoom:disabled"/> <int value="2097585272" label="ProductivityLauncherImageSearch:enabled"/> <int value="2098059607" label="WifiSyncAllowDeletes:disabled"/> @@ -72850,20 +72848,6 @@ <int value="4" label="Not responding"/> </enum> -<enum name="NetworkServiceConcerningRequestHeaders"> - <int value="0" label="kConnection"/> - <int value="1" label="kCookie"/> - <int value="2" label="kCookie2"/> - <int value="3" label="kContentTransferEncoding"/> - <int value="4" label="kDate"/> - <int value="5" label="kExpect"/> - <int value="6" label="kKeepAlive"/> - <int value="7" label="kReferer"/> - <int value="8" label="kTe"/> - <int value="9" label="kTransferEncoding"/> - <int value="10" label="kVia"/> -</enum> - <enum name="NetworkServiceCorpResult"> <int value="0" label="success"/> <int value="1" label="same-origin violation"/> @@ -91262,6 +91246,20 @@ start)"/> </enum> +<enum name="SecagentdBpfAttachResult"> + <int value="0" label="BPF loaded and attached successfully"/> + <int value="1" label="BPF failed to Open"/> + <int value="2" label="BPF failed to Load"/> + <int value="3" label="BPF failed to Attach"/> + <int value="4" label="BPF failed Ring Buffer creation"/> +</enum> + +<enum name="SecagentdPolicy"> + <int value="0" + label="Device XDR reporting policy was checked at startup (baseline)"/> + <int value="1" label="XDR reporting was enabled by Device policy"/> +</enum> + <enum name="SecondaryAccountConsentLoggerResult"> <obsolete> Deprecated in M91 after v2 EduCoexistence.
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml index b4c8a4f..17bc2cb 100644 --- a/tools/metrics/histograms/metadata/android/histograms.xml +++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -903,7 +903,7 @@ <histogram name="Android.ChildProcessBinding.Percentage{ChildProcessConnectionMetricsBindingState}Connections_{TotalConnectionBucket}" - units="%" expires_after="2023-02-12"> + units="%" expires_after="2023-04-16"> <owner>ckitagawa@chromium.org</owner> <owner>yfriedman@chromium.org</owner> <summary> @@ -940,7 +940,7 @@ <histogram name="Android.ChildProcessBinding.{ChildProcessConnectionMetricsBindingState}Connections" - units="connections" expires_after="2022-12-01"> + units="connections" expires_after="2023-04-16"> <owner>ckitagawa@chromium.org</owner> <owner>yfriedman@chromium.org</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml index c5cb0ae..05565f65 100644 --- a/tools/metrics/histograms/metadata/autofill/histograms.xml +++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -3451,6 +3451,29 @@ <summary>Usage of the "Scan card" control item.</summary> </histogram> +<histogram name="Autofill.Sectioning.FieldsPerSection" units="Fields" + expires_after="2023-03-31"> + <owner>fleimgruber@google.com</owner> + <owner>schwering@google.com</owner> + <owner>chrome-autofill-alerts@google.com</owner> + <summary> + The number of fields per section after sectioning a form using + kAutofillUseParameterizedSectioning. Emitted once per section every time + sectioning is done. + </summary> +</histogram> + +<histogram name="Autofill.Sectioning.NumberOfSections" units="Sections" + expires_after="2023-03-31"> + <owner>fleimgruber@google.com</owner> + <owner>schwering@google.com</owner> + <owner>chrome-autofill-alerts@google.com</owner> + <summary> + The number of sections after sectioning a form using + kAutofillUseParameterizedSectioning. Emitted every time sectioning is done. + </summary> +</histogram> + <histogram name="Autofill.ServerCardLinkClicked" enum="AutofillSyncState" expires_after="2023-09-30"> <owner>jsaul@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml index 59420a14..3059a74 100644 --- a/tools/metrics/histograms/metadata/chromeos/histograms.xml +++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -1811,6 +1811,35 @@ </summary> </histogram> +<histogram name="ChromeOS.Secagentd.Bpf.{Bpf}.AttachResult" + enum="SecagentdBpfAttachResult" expires_after="2023-05-18"> + <owner>aashay@google.com</owner> + <owner>cros-enterprise-security@google.com</owner> + <summary> + Records the initialization status of the {Bpf} BPF used by the CrOS + secagentd daemon. Emits either a success or a specific error value. + </summary> + <token key="Bpf"> + <variant name="Process" summary="Process BPF"/> + </token> +</histogram> + +<histogram name="ChromeOS.Secagentd.Policy" enum="SecagentdPolicy" + expires_after="2023-05-18"> + <owner>aashay@google.com</owner> + <owner>cros-enterprise-security@google.com</owner> + <summary> + Records whether the XDR event reporting policy was found enabled for a + device. Also provides an on-check enum for baseline. All of the + functionality of the CrOS secagentd daemon is gated by this enterprise + policy. + + Note that even though the daemon regularly polls device policy for updates, + it will emit exactly one on-check value and at most one enabled value during + its lifetime (generally per device per boot). + </summary> +</histogram> + <histogram name="ChromeOS.SecurityAnomaly" enum="SecurityAnomaly" expires_after="2023-04-16"> <owner>jorgelo@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/cookie/histograms.xml b/tools/metrics/histograms/metadata/cookie/histograms.xml index e3dd5f87..4b9759b 100644 --- a/tools/metrics/histograms/metadata/cookie/histograms.xml +++ b/tools/metrics/histograms/metadata/cookie/histograms.xml
@@ -510,7 +510,7 @@ </histogram> <histogram name="Cookie.IncludedRequestEffectiveSameSite" - enum="CookieEffectiveSameSite" expires_after="2023-01-01"> + enum="CookieEffectiveSameSite" expires_after="2023-07-01"> <owner>bingler@chromium.org</owner> <owner>miketaylr@chromium.org</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml index fab4852..721f586 100644 --- a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml +++ b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
@@ -5852,44 +5852,6 @@ <affected-histogram name="ServiceWorker.StartWorker.Time"/> </histogram_suffixes> -<histogram_suffixes name="ServiceWorkerScheduler" separator="." - ordering="prefix"> - <suffix name="BackgroundSyncManager" - label="Collected from a BackgroundSyncManager instance"/> - <suffix name="Cache" label="Collected from a CacheStorageCache instance"/> - <suffix name="CacheStorage" label="Collected from a CacheStorage instance"/> - <affected-histogram name="ServiceWorkerCache.Scheduler.OperationDuration2"/> - <affected-histogram name="ServiceWorkerCache.Scheduler.QueueDuration2"/> - <affected-histogram name="ServiceWorkerCache.Scheduler.QueueLength"/> -</histogram_suffixes> - -<histogram_suffixes name="ServiceWorkerSchedulerOp" separator="."> - <suffix name="Close" label=""/> - <suffix name="Delete" label=""/> - <suffix name="GetAllMatched" label=""/> - <suffix name="Has" label=""/> - <suffix name="Init" label=""/> - <suffix name="Keys" label=""/> - <suffix name="Match" label=""/> - <suffix name="MatchAll" label=""/> - <suffix name="Open" label=""/> - <suffix name="Put" label=""/> - <suffix name="Size" label=""/> - <suffix name="SizeThenClose" label=""/> - <suffix name="WriteIndex" label=""/> - <suffix name="WriteSideData" label=""/> - <affected-histogram - name="ServiceWorkerCache.Cache.Scheduler.OperationDuration2"/> - <affected-histogram name="ServiceWorkerCache.Cache.Scheduler.QueueDuration2"/> - <affected-histogram name="ServiceWorkerCache.Cache.Scheduler.QueueLength"/> - <affected-histogram - name="ServiceWorkerCache.CacheStorage.Scheduler.OperationDuration2"/> - <affected-histogram - name="ServiceWorkerCache.CacheStorage.Scheduler.QueueDuration2"/> - <affected-histogram - name="ServiceWorkerCache.CacheStorage.Scheduler.QueueLength"/> -</histogram_suffixes> - <histogram_suffixes name="SessionRestoreTabCounts" separator="_"> <suffix name="1" label="1 tab present"/> <suffix name="2" label="2 tabs present"/>
diff --git a/tools/metrics/histograms/metadata/history/histograms.xml b/tools/metrics/histograms/metadata/history/histograms.xml index 0b2fe2f3..06509b8 100644 --- a/tools/metrics/histograms/metadata/history/histograms.xml +++ b/tools/metrics/histograms/metadata/history/histograms.xml
@@ -1077,6 +1077,39 @@ </token> </histogram> +<histogram name="History.Clusters.ContextClusterer.NumClusters.AtCleanUp" + units="num clusters" expires_after="2023-10-01"> + <owner>sophiechang@chromium.org</owner> + <owner>chrome-journeys@google.com</owner> + <component>UI>Browser>Journeys</component> + <summary> + Number of clusters that are in-progress. Recorded once an hour + (Finch-configurable) prior to the clean-up pass. + </summary> +</histogram> + +<histogram name="History.Clusters.ContextClusterer.NumClusters.CleanedUp" + units="num clusters" expires_after="2023-10-01"> + <owner>sophiechang@chromium.org</owner> + <owner>chrome-journeys@google.com</owner> + <component>UI>Browser>Journeys</component> + <summary> + Number of clusters that were cleaned up in the clean-up pass. Recorded once + an hour (Finch-configurable) during the clean-up pass. + </summary> +</histogram> + +<histogram name="History.Clusters.ContextClusterer.NumClusters.PostCleanUp" + units="num clusters" expires_after="2023-10-01"> + <owner>sophiechang@chromium.org</owner> + <owner>chrome-journeys@google.com</owner> + <component>UI>Browser>Journeys</component> + <summary> + Number of clusters that are in-progress. Recorded once an hour + (Finch-configurable) after the clean-up pass. + </summary> +</histogram> + <histogram name="History.Clusters.KeywordCache.Latency" units="ms" expires_after="2023-10-01"> <owner>tommycli@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml index fd3e75a..cf4dda04 100644 --- a/tools/metrics/histograms/metadata/media/histograms.xml +++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -2659,30 +2659,6 @@ <token key="EmeApi" variants="EmeApi"/> </histogram> -<histogram name="Media.Engagement.PreloadedList.CheckResult" - enum="PreloadedListCheckResult" expires_after="M82"> - <owner>beccahughes@chromium.org</owner> - <owner>media-dev@chromium.org</owner> - <summary> - Recorded when the Media Engagement Preloaded List is checked whether a - string is present on that list. If the check was successful then the result - of the check is recorded in this histogram. If the check was not successful - then the reason for the check failing is recorded. - </summary> -</histogram> - -<histogram name="Media.Engagement.PreloadedList.LoadResult" - enum="PreloadedListLoadResult" expires_after="M82"> - <owner>beccahughes@chromium.org</owner> - <owner>media-dev@chromium.org</owner> - <summary> - Recorded when data is loaded into the Media Engagement Preloaded List. If - the load is successful then "loaded" is recorded to this - histogram. If the load was not successful then the reason why is recorded to - this histogram. - </summary> -</histogram> - <histogram name="Media.Engagement.ScoreAtPlayback" units="%" expires_after="M82"> <owner>beccahughes@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/network/histograms.xml b/tools/metrics/histograms/metadata/network/histograms.xml index d150809..21baf2c 100644 --- a/tools/metrics/histograms/metadata/network/histograms.xml +++ b/tools/metrics/histograms/metadata/network/histograms.xml
@@ -3239,23 +3239,6 @@ </summary> </histogram> -<histogram name="Network.URLLoader.BodyReadFromNetBeforePaused" units="bytes" - expires_after="M77"> - <owner>yzshen@chromium.org</owner> - <summary> - How much, in bytes, of the response body has been read from network by a - URLLoader before it pauses reading, when it receives a - PauseReadingBodyFromNet() call. If there are multiple calls to - PauseReadingBodyFromNet(), only a single value is recorded for the last - call. This histogram is recorded by URLLoader implementations that fetch - from network. When SafeBrowsing indicates that a resource may be unsafe and - therefore a more time-consuming check is required to classify it, reading - response body from network is paused in order to reduce the chance of - writing unsafe contents into cache. This histogram is useful to evaluate how - much data is cached during this window. - </summary> -</histogram> - <histogram name="Network.Wifi.Channel" enum="NetworkChannelType" expires_after="M85"> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> @@ -3402,55 +3385,6 @@ </summary> </histogram> -<histogram name="NetworkService.ConcerningRequestHeader.AddedOnRedirect" - enum="ConcerningHeaderPresent" expires_after="2019-11-01"> - <owner>mmenke@chromium.org</owner> - <summary> - Whether a request going through the network service has one of a number of - concerning headers added by the caller when the request is redirected. - Entries are only recorded when headers are actually added during a redirect. - We want to figure out if we can ban any of these headers from being set - outside the network service. For comparison with - NetworkService.ConcerningRequestHeader.HeaderAddedOnRedirect. - </summary> -</histogram> - -<histogram name="NetworkService.ConcerningRequestHeader.HeaderAddedOnRedirect" - enum="NetworkServiceConcerningRequestHeaders" expires_after="2019-11-01"> - <owner>mmenke@chromium.org</owner> - <summary> - Tracks how often each of a number of concerning headers are added by the - caller when a network service request is redirected. We want to figure out - if we can ban any of these headers from being set outside the network - service. To get meaningful percentages of requests, need to compare with - NetworkService.ConcerningRequestHeader.AddedOnRedirect. - </summary> -</histogram> - -<histogram name="NetworkService.ConcerningRequestHeader.HeaderPresentOnStart" - enum="NetworkServiceConcerningRequestHeaders" expires_after="2019-11-01"> - <owner>mmenke@chromium.org</owner> - <summary> - Tracks how often each of a number of concerning headers are set by the - caller when a network service request is started. We want to figure out if - we can ban any of these headers from being set outside the network service. - To get meaningful percentages of requests, need to compare with - NetworkService.ConcerningRequestHeader.PresentOnStart. - </summary> -</histogram> - -<histogram name="NetworkService.ConcerningRequestHeader.PresentOnStart" - enum="ConcerningHeaderPresent" expires_after="2019-11-01"> - <owner>mmenke@chromium.org</owner> - <summary> - Whether a request going through the network service has one of a number of - concerning headers set by the caller when the request starts. We want to - figure out if we can ban any of these headers from being set outside the - network service. For comparison with - NetworkService.ConcerningRequestHeader.HeadersPresentOnStart. - </summary> -</histogram> - <histogram name="NetworkService.CorsPreflightMethodAllowed" enum="NetworkServiceCorsPreflightMethodAllowed" expires_after="2023-02-12"> <owner>hiroshige@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/quota/histograms.xml b/tools/metrics/histograms/metadata/quota/histograms.xml index 6fd458d..4716eb2 100644 --- a/tools/metrics/histograms/metadata/quota/histograms.xml +++ b/tools/metrics/histograms/metadata/quota/histograms.xml
@@ -62,6 +62,22 @@ </summary> </histogram> +<histogram name="Quota.DatabaseMigration{VersionUpgrades}" + enum="BooleanSuccess" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> + <summary> + Records whether the database migration was successful. Recorded after + attempting to migrate the database. + </summary> + <token key="VersionUpgrades"> + <variant name="FromV5ToV7"/> + <variant name="FromV6ToV7"/> + <variant name="FromV7ToV8"/> + <variant name="FromV8ToV9"/> + </token> +</histogram> + <histogram name="Quota.DiskspaceShortage" units="MB" expires_after="2023-04-30"> <owner>ayui@chromium.org</owner> <owner>chrome-owp-storage@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/service/histograms.xml b/tools/metrics/histograms/metadata/service/histograms.xml index 9e14178..42fc951 100644 --- a/tools/metrics/histograms/metadata/service/histograms.xml +++ b/tools/metrics/histograms/metadata/service/histograms.xml
@@ -22,6 +22,14 @@ <histograms> +<variants name="ServiceWorkerCacheClientType"> + <variant name="BackgroundSyncManager" + summary="Collected from a BackgroundSyncManager instance"/> + <variant name="Cache" summary="Collected from a CacheStorageCache instance"/> + <variant name="CacheStorage" + summary="Collected from a CacheStorage instance"/> +</variants> + <variants name="ServiceWorkerCacheProcessType"> <variant name="Browser" summary="Measurements taken in the browser process on the IO thread"/> @@ -30,6 +38,12 @@ thread"/> </variants> +<variants name="ServiceWorkerCacheSchedulerOpClientType"> + <variant name="Cache" summary="Collected from a CacheStorageCache instance"/> + <variant name="CacheStorage" + summary="Collected from a CacheStorage instance"/> +</variants> + <variants name="ServiceWorkerEventType"> <variant name="ACTIVATE"/> <variant name="BACKGROUND_FETCH_ABORT"/> @@ -58,6 +72,23 @@ <variant name="SHOULD_FALLBACK"/> </variants> +<variants name="ServiceWorkerSchedulerOp"> + <variant name="Close"/> + <variant name="Delete"/> + <variant name="GetAllMatched"/> + <variant name="Has"/> + <variant name="Init"/> + <variant name="Keys"/> + <variant name="Match"/> + <variant name="MatchAll"/> + <variant name="Open"/> + <variant name="Put"/> + <variant name="Size"/> + <variant name="SizeThenClose"/> + <variant name="WriteIndex"/> + <variant name="WriteSideData"/> +</variants> + <histogram name="ServiceWorker.AbortPaymentEvent.Time" units="ms" expires_after="2023-10-18"> <owner>rouslan@chromium.org</owner> @@ -1419,8 +1450,9 @@ </summary> </histogram> -<histogram name="ServiceWorkerCache.Scheduler.OperationDuration2" units="ms" - expires_after="2023-05-26"> +<histogram + name="ServiceWorkerCache.{ServiceWorkerCacheClientType}.Scheduler.OperationDuration2" + units="ms" expires_after="2023-05-26"> <owner>ayui@chromium.org</owner> <owner>chrome-owp-storage@google.com</owner> <summary> @@ -1428,27 +1460,77 @@ histogram differs from the old OperationDuration in that it uses a different bucket size to measure longer values. </summary> + <token key="ServiceWorkerCacheClientType" + variants="ServiceWorkerCacheClientType"/> </histogram> -<histogram name="ServiceWorkerCache.Scheduler.QueueDuration2" units="ms" - expires_after="2023-05-26"> +<histogram + name="ServiceWorkerCache.{ServiceWorkerCacheClientType}.Scheduler.QueueDuration2" + units="ms" expires_after="2023-05-26"> <owner>ayui@chromium.org</owner> <owner>chrome-owp-storage@google.com</owner> <summary> - The time in ms from when an operation was queued until its task is posted. - This histogram differs from the old QueueDuration in that it uses a - different bucket size to measure longer values. + The time in ms from when an operation is started until it completes. This + histogram differs from the old OperationDuration in that it uses a different + bucket size to measure longer values. </summary> + <token key="ServiceWorkerCacheClientType" + variants="ServiceWorkerCacheClientType"/> </histogram> -<histogram name="ServiceWorkerCache.Scheduler.QueueLength" units="operations" - expires_after="2023-05-26"> +<histogram + name="ServiceWorkerCache.{ServiceWorkerCacheClientType}.Scheduler.QueueLength" + units="operations" expires_after="2023-05-26"> <owner>ayui@chromium.org</owner> <owner>chrome-owp-storage@google.com</owner> <summary> The number of operations in the scheduling queue just before enqueuing a new operation. </summary> + <token key="ServiceWorkerCacheClientType" + variants="ServiceWorkerCacheClientType"/> +</histogram> + +<histogram + name="ServiceWorkerCache.{ServiceWorkerCacheSchedulerOpClientType}.Scheduler.OperationDuration2.{ServiceWorkerSchedulerOp}" + units="ms" expires_after="2023-05-26"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> + <summary> + The time in ms from when an operation is started until it completes for the + {ServiceWorkerCacheSchedulerOpClientType} instance. + </summary> + <token key="ServiceWorkerCacheSchedulerOpClientType" + variants="ServiceWorkerCacheSchedulerOpClientType"/> + <token key="ServiceWorkerSchedulerOp" variants="ServiceWorkerSchedulerOp"/> +</histogram> + +<histogram + name="ServiceWorkerCache.{ServiceWorkerCacheSchedulerOpClientType}.Scheduler.QueueDuration2.{ServiceWorkerSchedulerOp}" + units="ms" expires_after="2023-05-26"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> + <summary> + The time in ms from when an operation is started until it completes for the + {ServiceWorkerCacheSchedulerOpClientType} instance. + </summary> + <token key="ServiceWorkerCacheSchedulerOpClientType" + variants="ServiceWorkerCacheSchedulerOpClientType"/> + <token key="ServiceWorkerSchedulerOp" variants="ServiceWorkerSchedulerOp"/> +</histogram> + +<histogram + name="ServiceWorkerCache.{ServiceWorkerCacheSchedulerOpClientType}.Scheduler.QueueLength.{ServiceWorkerSchedulerOp}" + units="operations" expires_after="2023-05-26"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> + <summary> + The number of operations in the scheduling queue just before enqueuing a new + operation for the {ServiceWorkerCacheSchedulerOpClientType} instance. + </summary> + <token key="ServiceWorkerCacheSchedulerOpClientType" + variants="ServiceWorkerCacheSchedulerOpClientType"/> + <token key="ServiceWorkerSchedulerOp" variants="ServiceWorkerSchedulerOp"/> </histogram> </histograms>
diff --git a/tools/metrics/histograms/metadata/storage/histograms.xml b/tools/metrics/histograms/metadata/storage/histograms.xml index 8d3c1fb6..36e1114 100644 --- a/tools/metrics/histograms/metadata/storage/histograms.xml +++ b/tools/metrics/histograms/metadata/storage/histograms.xml
@@ -262,7 +262,7 @@ </histogram> <histogram name="Storage.FileSystemAccess.PersistedPermissions.Age.{Type}" - units="ms" expires_after="2023-01-01"> + units="ms" expires_after="2024-05-01"> <owner>asully@chromium.org</owner> <owner>src/content/browser/file_system_access/OWNERS</owner> <summary> @@ -276,7 +276,7 @@ </histogram> <histogram name="Storage.FileSystemAccess.PersistedPermissions.Count" - units="paths" expires_after="2023-01-01"> + units="paths" expires_after="2024-05-01"> <owner>asully@chromium.org</owner> <owner>src/content/browser/file_system_access/OWNERS</owner> <summary> @@ -291,7 +291,7 @@ <histogram name="Storage.FileSystemAccess.PersistedPermissions.SweepTime.{Type}" - units="ms" expires_after="2023-01-01"> + units="ms" expires_after="2024-05-01"> <owner>asully@chromium.org</owner> <owner>src/content/browser/file_system_access/OWNERS</owner> <summary> @@ -306,7 +306,7 @@ <histogram name="Storage.FileSystemAccess.{OpType}PermissionRequestOutcome.{EntryType}" - enum="FileSystemAccessPermissionRequestOutcome" expires_after="2023-05-01"> + enum="FileSystemAccessPermissionRequestOutcome" expires_after="2024-05-01"> <owner>asully@chromium.org</owner> <owner>src/content/browser/file_system_access/OWNERS</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/tab/histograms.xml b/tools/metrics/histograms/metadata/tab/histograms.xml index 1ffce4a..40718c7b 100644 --- a/tools/metrics/histograms/metadata/tab/histograms.xml +++ b/tools/metrics/histograms/metadata/tab/histograms.xml
@@ -1579,7 +1579,7 @@ </histogram> <histogram name="Tabs.PersistedTabData.Storage.Restore.File" - enum="BooleanSuccess" expires_after="2022-11-21"> + enum="BooleanSuccess" expires_after="2023-11-21"> <owner>yusufo@chromium.org</owner> <owner>nyquist@chromium.org</owner> <owner>dtrainor@chromium.org</owner>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml index 337cee4..9a08ab98 100644 --- a/tools/metrics/ukm/ukm.xml +++ b/tools/metrics/ukm/ukm.xml
@@ -2357,6 +2357,25 @@ </metric> </event> +<event name="Autofill.Sectioning"> + <owner>fleimgruber@google.com</owner> + <owner>schwering@google.com</owner> + <summary> + Recorded after sectioning is performed when the server classification + arrived, and only when `AutofillUseParameterizedSectioning` is enabled. + </summary> + <metric name="FormSignature"> + <summary> + A 10-bit hash of the form's signature. + </summary> + </metric> + <metric name="SectioningSignature"> + <summary> + A 10-bit hash of the form's computed sections. + </summary> + </metric> +</event> + <event name="Autofill.SelectedMaskedServerCard"> <obsolete> Deprecated 2/2019
diff --git a/tools/perf/benchmark.csv b/tools/perf/benchmark.csv index 771f5a2..5fa6706 100644 --- a/tools/perf/benchmark.csv +++ b/tools/perf/benchmark.csv
@@ -2,13 +2,13 @@ See the following link for directions for making changes to this data:,https://bit.ly/update-benchmarks-info Googlers can view additional information about internal perf infrastructure at,https://goto.google.com/chrome-benchmarking-sheet Benchmark name,Individual owners,Component,Documentation,Tags -UNSCHEDULED_ad_frames.fencedframe,lbrady@google.com,Blink>FencedFrames,https://tinyurl.com/fenced-frame-benchmark, UNSCHEDULED_ad_frames.iframe,lbrady@google.com,Blink>FencedFrames,https://tinyurl.com/fenced-frame-benchmark, UNSCHEDULED_blink_perf.performance_apis,yoavweiss@chromium.org,Blink>PerformanceAPIs,https://bit.ly/blink-perf-benchmarks,all UNSCHEDULED_blink_perf.service_worker,"shimazu@chromium.org, falken@chromium.org, ting.shao@intel.com",Blink>ServiceWorker,https://bit.ly/blink-perf-benchmarks, UNSCHEDULED_loading.mbi,blink-isolation-dev@chromium.org,Blink>Internals>Modularization,https://bit.ly/loading-benchmarks,many_agents UNSCHEDULED_v8.loading_desktop,"cbruni@chromium.org, tmrts@chromium.org, almuthanna@chromium.org",Blink>JavaScript,https://bit.ly/system-health-v8-benchmarks,"2016,2018,2019,2020,emerging_market,health_check,international,javascript_heavy" UNSCHEDULED_v8.loading_mobile,"cbruni@chromium.org, leszeks@chromium.org, tmrts@chromium.org",Blink>JavaScript,https://bit.ly/system-health-v8-benchmarks,"2016,2018,2019,2020,emerging_market,health_check,international,javascript_heavy" +ad_frames.fencedframe,lbrady@google.com,Blink>FencedFrames,https://tinyurl.com/fenced-frame-benchmark, base_perftests,"skyostil@chromium.org, gab@chromium.org",Internals>SequenceManager,https://chromium.googlesource.com/chromium/src/+/HEAD/base/README.md#performance-testing, blink_perf.accessibility,aleventhal@chromium.org,Blink>Accessibility,https://bit.ly/blink-perf-benchmarks,all blink_perf.bindings,"jbroman@chromium.org, yukishiino@chromium.org, haraken@chromium.org",Blink>Bindings,https://bit.ly/blink-perf-benchmarks,all
diff --git a/tools/perf/benchmarks/ad_frames.py b/tools/perf/benchmarks/ad_frames.py index 4733294..142465f 100644 --- a/tools/perf/benchmarks/ad_frames.py +++ b/tools/perf/benchmarks/ad_frames.py
@@ -49,7 +49,7 @@ @classmethod def Name(cls): - return 'UNSCHEDULED_ad_frames.fencedframe' + return 'ad_frames.fencedframe' @benchmark.Info(emails=['lbrady@google.com'],
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index 647ba8f..cbe6e1a 100644 --- a/tools/perf/core/perfetto_binary_roller/binary_deps.json +++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,24 +5,24 @@ "full_remote_path": "perfetto-luci-artifacts/v31.0/linux-arm64/trace_processor_shell" }, "win": { - "hash": "4b26afafb275d583436571f34abd081ffd127235", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/06921e5027e463675d71b2f6978cb3cda5ed5d14/trace_processor_shell.exe" + "hash": "2710fe3acd46370f2ff4d4634db0d783f44121a2", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/d8f0dc3d20c814236d97a042f06ffacd8f2d6b1f/trace_processor_shell.exe" }, "linux_arm": { "hash": "6373f26144aad58f230d11d6a91efda5a09c9873", "full_remote_path": "perfetto-luci-artifacts/v31.0/linux-arm/trace_processor_shell" }, "mac": { - "hash": "d86ef1c9c3bacf89132506cb29a024e9ad9e1d75", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/06921e5027e463675d71b2f6978cb3cda5ed5d14/trace_processor_shell" + "hash": "0692cf90a8a019c0fb2a77426517793a197313b7", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/d8f0dc3d20c814236d97a042f06ffacd8f2d6b1f/trace_processor_shell" }, "mac_arm64": { "hash": "5f47ee79e59d00bf3889d30ca52315522c158040", "full_remote_path": "perfetto-luci-artifacts/v31.0/mac-arm64/trace_processor_shell" }, "linux": { - "hash": "03d441f184e4f7fbd144de28cb92db2d90170b48", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/06921e5027e463675d71b2f6978cb3cda5ed5d14/trace_processor_shell" + "hash": "4bc08289d402692530fea7363630f5914fda63b5", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/f5b6c4ed868219aefe172ac2d5bd683814a5f001/trace_processor_shell" } }, "power_profile.sql": {
diff --git a/tools/perf/core/shard_maps/android-pixel2-perf-calibration_map.json b/tools/perf/core/shard_maps/android-pixel2-perf-calibration_map.json index 0ffc24b2..3cd538c 100644 --- a/tools/perf/core/shard_maps/android-pixel2-perf-calibration_map.json +++ b/tools/perf/core/shard_maps/android-pixel2-perf-calibration_map.json
@@ -1,11 +1,14 @@ { "0": { "benchmarks": { + "ad_frames.fencedframe": { + "abridged": false + }, "blink_perf.accessibility": { "abridged": false }, "blink_perf.bindings": { - "end": 19, + "end": 5, "abridged": false } } @@ -13,20 +16,20 @@ "1": { "benchmarks": { "blink_perf.bindings": { - "begin": 19, - "abridged": false - }, - "blink_perf.css": { - "end": 6, + "begin": 5, + "end": 34, "abridged": false } } }, "2": { "benchmarks": { + "blink_perf.bindings": { + "begin": 34, + "abridged": false + }, "blink_perf.css": { - "begin": 6, - "end": 44, + "end": 12, "abridged": false } } @@ -34,38 +37,38 @@ "3": { "benchmarks": { "blink_perf.css": { - "begin": 44, - "abridged": false - }, - "blink_perf.dom": { - "abridged": false - }, - "blink_perf.events": { - "abridged": false - }, - "blink_perf.image_decoder": { - "abridged": false - }, - "blink_perf.layout": { - "end": 3, + "begin": 12, + "end": 41, "abridged": false } } }, "4": { "benchmarks": { - "blink_perf.layout": { - "begin": 3, - "end": 41, + "blink_perf.css": { + "begin": 41, + "abridged": false + }, + "blink_perf.dom": { + "abridged": false + }, + "blink_perf.events": { + "end": 1, "abridged": false } } }, "5": { "benchmarks": { + "blink_perf.events": { + "begin": 1, + "abridged": false + }, + "blink_perf.image_decoder": { + "abridged": false + }, "blink_perf.layout": { - "begin": 41, - "end": 79, + "end": 13, "abridged": false } } @@ -73,69 +76,95 @@ "6": { "benchmarks": { "blink_perf.layout": { - "begin": 79, - "abridged": false - }, - "blink_perf.owp_storage": { - "abridged": false - }, - "blink_perf.paint": { - "end": 6, + "begin": 13, + "end": 42, "abridged": false } } }, "7": { "benchmarks": { - "blink_perf.paint": { - "begin": 6, - "abridged": false - }, - "blink_perf.parser": { - "end": 28, + "blink_perf.layout": { + "begin": 42, + "end": 71, "abridged": false } } }, "8": { "benchmarks": { - "blink_perf.parser": { - "begin": 28, - "abridged": false - }, - "blink_perf.shadow_dom": { - "end": 35, + "blink_perf.layout": { + "begin": 71, + "end": 100, "abridged": false } } }, "9": { "benchmarks": { - "blink_perf.shadow_dom": { - "begin": 35, + "blink_perf.layout": { + "begin": 100, "abridged": false }, - "blink_perf.svg": { + "blink_perf.owp_storage": { "abridged": false }, - "blink_perf.webaudio": { - "abridged": false - }, - "blink_perf.webcodecs": { - "end": 1, + "blink_perf.paint": { "abridged": false } } }, "10": { "benchmarks": { + "blink_perf.parser": { + "end": 29, + "abridged": false + } + } + }, + "11": { + "benchmarks": { + "blink_perf.parser": { + "begin": 29, + "abridged": false + }, + "blink_perf.shadow_dom": { + "end": 28, + "abridged": false + } + } + }, + "12": { + "benchmarks": { + "blink_perf.shadow_dom": { + "begin": 28, + "abridged": false + }, + "blink_perf.svg": { + "end": 18, + "abridged": false + } + } + }, + "13": { + "benchmarks": { + "blink_perf.svg": { + "begin": 18, + "abridged": false + }, + "blink_perf.webaudio": { + "abridged": false + }, "blink_perf.webcodecs": { - "begin": 1, "abridged": false }, "blink_perf.webgl": { "abridged": false - }, + } + } + }, + "14": { + "benchmarks": { "blink_perf.webgl_fast_call": { "abridged": false }, @@ -155,7 +184,7 @@ "abridged": false }, "loading.mobile": { - "end": 12, + "end": 14, "abridged": false } }, @@ -168,95 +197,59 @@ } } }, - "11": { - "benchmarks": { - "loading.mobile": { - "begin": 12, - "end": 50, - "abridged": false - } - } - }, - "12": { - "benchmarks": { - "loading.mobile": { - "begin": 50, - "end": 88, - "abridged": false - } - } - }, - "13": { - "benchmarks": { - "loading.mobile": { - "begin": 88, - "abridged": false - }, - "media.mobile": { - "abridged": false - }, - "octane": { - "abridged": false - }, - "rasterize_and_record_micro.top_25": { - "end": 13, - "abridged": false - } - } - }, - "14": { - "benchmarks": { - "rasterize_and_record_micro.top_25": { - "begin": 13, - "abridged": false - }, - "rendering.mobile": { - "end": 26, - "abridged": false - } - } - }, "15": { "benchmarks": { - "rendering.mobile": { - "begin": 26, - "end": 64, + "loading.mobile": { + "begin": 14, + "end": 43, "abridged": false } } }, "16": { "benchmarks": { - "rendering.mobile": { - "begin": 64, - "end": 102, + "loading.mobile": { + "begin": 43, + "end": 73, "abridged": false } } }, "17": { "benchmarks": { - "rendering.mobile": { - "begin": 102, - "end": 140, + "loading.mobile": { + "begin": 73, + "abridged": false + }, + "media.mobile": { + "end": 6, "abridged": false } } }, "18": { "benchmarks": { - "rendering.mobile": { - "begin": 140, - "end": 178, + "media.mobile": { + "begin": 6, + "abridged": false + }, + "octane": { + "abridged": false + }, + "rasterize_and_record_micro.top_25": { + "end": 19, "abridged": false } } }, "19": { "benchmarks": { + "rasterize_and_record_micro.top_25": { + "begin": 19, + "abridged": false + }, "rendering.mobile": { - "begin": 178, - "end": 216, + "end": 23, "abridged": false } } @@ -264,8 +257,8 @@ "20": { "benchmarks": { "rendering.mobile": { - "begin": 216, - "end": 254, + "begin": 23, + "end": 53, "abridged": false } } @@ -273,8 +266,8 @@ "21": { "benchmarks": { "rendering.mobile": { - "begin": 254, - "end": 292, + "begin": 53, + "end": 82, "abridged": false } } @@ -282,8 +275,8 @@ "22": { "benchmarks": { "rendering.mobile": { - "begin": 292, - "end": 331, + "begin": 82, + "end": 112, "abridged": false } } @@ -291,8 +284,8 @@ "23": { "benchmarks": { "rendering.mobile": { - "begin": 331, - "end": 369, + "begin": 112, + "end": 141, "abridged": false } } @@ -300,8 +293,8 @@ "24": { "benchmarks": { "rendering.mobile": { - "begin": 369, - "end": 408, + "begin": 141, + "end": 171, "abridged": false } } @@ -309,100 +302,82 @@ "25": { "benchmarks": { "rendering.mobile": { - "begin": 408, - "abridged": false - }, - "rendering.mobile.notracing": { - "end": 25, + "begin": 171, + "end": 200, "abridged": false } } }, "26": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 25, - "end": 64, + "rendering.mobile": { + "begin": 200, + "end": 230, "abridged": false } } }, "27": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 64, - "end": 102, + "rendering.mobile": { + "begin": 230, + "end": 259, "abridged": false } } }, "28": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 102, - "end": 141, + "rendering.mobile": { + "begin": 259, + "end": 289, "abridged": false } } }, "29": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 141, - "end": 179, + "rendering.mobile": { + "begin": 289, + "end": 318, "abridged": false } } }, "30": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 179, - "end": 218, + "rendering.mobile": { + "begin": 318, + "end": 348, "abridged": false } } }, "31": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 218, - "end": 256, + "rendering.mobile": { + "begin": 348, + "end": 377, "abridged": false } } }, "32": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 256, - "end": 295, + "rendering.mobile": { + "begin": 377, + "end": 407, "abridged": false } } }, "33": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 295, - "end": 333, + "rendering.mobile": { + "begin": 407, "abridged": false - } - } - }, - "34": { - "benchmarks": { + }, "rendering.mobile.notracing": { - "begin": 333, - "end": 372, - "abridged": false - } - } - }, - "35": { - "benchmarks": { - "rendering.mobile.notracing": { - "begin": 372, "abridged": false }, "speedometer": { @@ -424,7 +399,25 @@ "abridged": false }, "system_health.common_mobile": { - "end": 9, + "end": 4, + "abridged": false + } + } + }, + "34": { + "benchmarks": { + "system_health.common_mobile": { + "begin": 4, + "end": 34, + "abridged": false + } + } + }, + "35": { + "benchmarks": { + "system_health.common_mobile": { + "begin": 34, + "end": 63, "abridged": false } } @@ -432,20 +425,20 @@ "36": { "benchmarks": { "system_health.common_mobile": { - "begin": 9, - "end": 48, + "begin": 63, + "abridged": false + }, + "system_health.memory_mobile": { + "end": 18, "abridged": false } } }, "37": { "benchmarks": { - "system_health.common_mobile": { - "begin": 48, - "abridged": false - }, "system_health.memory_mobile": { - "end": 11, + "begin": 18, + "end": 47, "abridged": false } } @@ -453,16 +446,7 @@ "38": { "benchmarks": { "system_health.memory_mobile": { - "begin": 11, - "end": 50, - "abridged": false - } - } - }, - "39": { - "benchmarks": { - "system_health.memory_mobile": { - "begin": 50, + "begin": 47, "abridged": false }, "system_health.pcscan": { @@ -470,12 +454,16 @@ }, "system_health.webview_startup": { "abridged": false - }, + } + } + }, + "39": { + "benchmarks": { "tracing.tracing_with_background_memory_infra": { "abridged": false }, "v8.browsing_mobile": { - "end": 2, + "end": 20, "abridged": false } } @@ -483,11 +471,11 @@ "40": { "benchmarks": { "v8.browsing_mobile": { - "begin": 2, + "begin": 20, "abridged": false }, "v8.browsing_mobile-future": { - "end": 10, + "end": 19, "abridged": false } } @@ -495,7 +483,7 @@ "41": { "benchmarks": { "v8.browsing_mobile-future": { - "begin": 10, + "begin": 19, "abridged": false }, "wasmpspdfkit": { @@ -507,52 +495,52 @@ } }, "extra_infos": { - "num_stories": 1601, - "predicted_min_shard_time": 380, + "num_stories": 1226, + "predicted_min_shard_time": 290, "predicted_min_shard_index": 0, - "predicted_max_shard_time": 390, - "predicted_max_shard_index": 22, - "shard #0": 380, - "shard #1": 380, - "shard #2": 380, - "shard #3": 380, - "shard #4": 380, - "shard #5": 380, - "shard #6": 380, - "shard #7": 380, - "shard #8": 380, - "shard #9": 380, - "shard #10": 380.0, - "shard #11": 380, - "shard #12": 380, - "shard #13": 380, - "shard #14": 380, - "shard #15": 380, - "shard #16": 380, - "shard #17": 380, - "shard #18": 380, - "shard #19": 380, - "shard #20": 380, - "shard #21": 380, - "shard #22": 390, - "shard #23": 380, - "shard #24": 390, - "shard #25": 380, - "shard #26": 390, - "shard #27": 380, - "shard #28": 390, - "shard #29": 380, - "shard #30": 390, - "shard #31": 380, - "shard #32": 390, - "shard #33": 380, - "shard #34": 390, - "shard #35": 380, - "shard #36": 390, - "shard #37": 380, - "shard #38": 390, - "shard #39": 380, - "shard #40": 390, - "shard #41": 380 + "predicted_max_shard_time": 300, + "predicted_max_shard_index": 16, + "shard #0": 290, + "shard #1": 290, + "shard #2": 290, + "shard #3": 290, + "shard #4": 290, + "shard #5": 290, + "shard #6": 290, + "shard #7": 290, + "shard #8": 290, + "shard #9": 290, + "shard #10": 290, + "shard #11": 290, + "shard #12": 290, + "shard #13": 290, + "shard #14": 290.0, + "shard #15": 290, + "shard #16": 300, + "shard #17": 290, + "shard #18": 300, + "shard #19": 290, + "shard #20": 300, + "shard #21": 290, + "shard #22": 300, + "shard #23": 290, + "shard #24": 300, + "shard #25": 290, + "shard #26": 300, + "shard #27": 290, + "shard #28": 300, + "shard #29": 290, + "shard #30": 300, + "shard #31": 290, + "shard #32": 300, + "shard #33": 290, + "shard #34": 300, + "shard #35": 290, + "shard #36": 300, + "shard #37": 290, + "shard #38": 300, + "shard #39": 290, + "shard #40": 300, + "shard #41": 290 } } \ No newline at end of file
diff --git a/tools/perf/core/shard_maps/android-pixel2-perf-pgo_map.json b/tools/perf/core/shard_maps/android-pixel2-perf-pgo_map.json index 7fe34e55..80a8f5e 100644 --- a/tools/perf/core/shard_maps/android-pixel2-perf-pgo_map.json +++ b/tools/perf/core/shard_maps/android-pixel2-perf-pgo_map.json
@@ -1,11 +1,14 @@ { "0": { "benchmarks": { + "ad_frames.fencedframe": { + "abridged": false + }, "blink_perf.accessibility": { "abridged": false }, "blink_perf.bindings": { - "end": 38, + "end": 20, "abridged": false } } @@ -13,11 +16,11 @@ "1": { "benchmarks": { "blink_perf.bindings": { - "begin": 38, + "begin": 20, "abridged": false }, "blink_perf.css": { - "end": 44, + "end": 13, "abridged": false } } @@ -25,7 +28,16 @@ "2": { "benchmarks": { "blink_perf.css": { - "begin": 44, + "begin": 13, + "end": 57, + "abridged": false + } + } + }, + "3": { + "benchmarks": { + "blink_perf.css": { + "begin": 57, "abridged": false }, "blink_perf.dom": { @@ -38,16 +50,7 @@ "abridged": false }, "blink_perf.layout": { - "end": 22, - "abridged": false - } - } - }, - "3": { - "benchmarks": { - "blink_perf.layout": { - "begin": 22, - "end": 79, + "end": 15, "abridged": false } } @@ -55,7 +58,25 @@ "4": { "benchmarks": { "blink_perf.layout": { - "begin": 79, + "begin": 15, + "end": 59, + "abridged": false + } + } + }, + "5": { + "benchmarks": { + "blink_perf.layout": { + "begin": 59, + "end": 103, + "abridged": false + } + } + }, + "6": { + "benchmarks": { + "blink_perf.layout": { + "begin": 103, "abridged": false }, "blink_perf.owp_storage": { @@ -65,27 +86,27 @@ "abridged": false }, "blink_perf.parser": { - "end": 9, + "end": 18, "abridged": false } } }, - "5": { + "7": { "benchmarks": { "blink_perf.parser": { - "begin": 9, + "begin": 18, "abridged": false }, "blink_perf.shadow_dom": { - "end": 35, + "end": 32, "abridged": false } } }, - "6": { + "8": { "benchmarks": { "blink_perf.shadow_dom": { - "begin": 35, + "begin": 32, "abridged": false }, "blink_perf.svg": { @@ -95,6 +116,15 @@ "abridged": false }, "blink_perf.webcodecs": { + "end": 4, + "abridged": false + } + } + }, + "9": { + "benchmarks": { + "blink_perf.webcodecs": { + "begin": 4, "abridged": false }, "blink_perf.webgl": { @@ -102,19 +132,7 @@ }, "blink_perf.webgl_fast_call": { "abridged": false - } - }, - "executables": { - "components_perftests": { - "arguments": [ - "--xvfb" - ], - "path": "components_perftests" - } - } - }, - "7": { - "benchmarks": { + }, "dummy_benchmark.noisy_benchmark_1": { "abridged": false }, @@ -131,27 +149,44 @@ "abridged": false }, "loading.mobile": { - "end": 52, + "end": 19, + "abridged": false + } + }, + "executables": { + "components_perftests": { + "arguments": [ + "--xvfb" + ], + "path": "components_perftests" + } + } + }, + "10": { + "benchmarks": { + "loading.mobile": { + "begin": 19, + "end": 63, "abridged": false } } }, - "8": { + "11": { "benchmarks": { "loading.mobile": { - "begin": 52, + "begin": 63, "abridged": false }, "media.mobile": { - "end": 13, + "end": 11, "abridged": false } } }, - "9": { + "12": { "benchmarks": { "media.mobile": { - "begin": 13, + "begin": 11, "abridged": false }, "octane": { @@ -161,34 +196,7 @@ "abridged": false }, "rendering.mobile": { - "end": 28, - "abridged": false - } - } - }, - "10": { - "benchmarks": { - "rendering.mobile": { - "begin": 28, - "end": 85, - "abridged": false - } - } - }, - "11": { - "benchmarks": { - "rendering.mobile": { - "begin": 85, - "end": 142, - "abridged": false - } - } - }, - "12": { - "benchmarks": { - "rendering.mobile": { - "begin": 142, - "end": 200, + "end": 13, "abridged": false } } @@ -196,8 +204,8 @@ "13": { "benchmarks": { "rendering.mobile": { - "begin": 200, - "end": 257, + "begin": 13, + "end": 57, "abridged": false } } @@ -205,8 +213,8 @@ "14": { "benchmarks": { "rendering.mobile": { - "begin": 257, - "end": 315, + "begin": 57, + "end": 101, "abridged": false } } @@ -214,8 +222,8 @@ "15": { "benchmarks": { "rendering.mobile": { - "begin": 315, - "end": 372, + "begin": 101, + "end": 145, "abridged": false } } @@ -223,73 +231,64 @@ "16": { "benchmarks": { "rendering.mobile": { - "begin": 372, - "abridged": false - }, - "rendering.mobile.notracing": { - "end": 9, + "begin": 145, + "end": 189, "abridged": false } } }, "17": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 9, - "end": 66, + "rendering.mobile": { + "begin": 189, + "end": 233, "abridged": false } } }, "18": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 66, - "end": 124, + "rendering.mobile": { + "begin": 233, + "end": 277, "abridged": false } } }, "19": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 124, - "end": 181, + "rendering.mobile": { + "begin": 277, + "end": 321, "abridged": false } } }, "20": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 181, - "end": 239, + "rendering.mobile": { + "begin": 321, + "end": 365, "abridged": false } } }, "21": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 239, - "end": 296, + "rendering.mobile": { + "begin": 365, + "end": 409, "abridged": false } } }, "22": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 296, - "end": 354, + "rendering.mobile": { + "begin": 409, "abridged": false - } - } - }, - "23": { - "benchmarks": { + }, "rendering.mobile.notracing": { - "begin": 354, "abridged": false }, "speedometer": { @@ -311,7 +310,16 @@ "abridged": false }, "system_health.common_mobile": { - "end": 10, + "end": 21, + "abridged": false + } + } + }, + "23": { + "benchmarks": { + "system_health.common_mobile": { + "begin": 21, + "end": 65, "abridged": false } } @@ -319,28 +327,19 @@ "24": { "benchmarks": { "system_health.common_mobile": { - "begin": 10, - "end": 68, + "begin": 65, + "abridged": false + }, + "system_health.memory_mobile": { + "end": 34, "abridged": false } } }, "25": { "benchmarks": { - "system_health.common_mobile": { - "begin": 68, - "abridged": false - }, "system_health.memory_mobile": { - "end": 50, - "abridged": false - } - } - }, - "26": { - "benchmarks": { - "system_health.memory_mobile": { - "begin": 50, + "begin": 34, "abridged": false }, "system_health.pcscan": { @@ -350,21 +349,30 @@ "abridged": false }, "tracing.tracing_with_background_memory_infra": { + "end": 1, + "abridged": false + } + } + }, + "26": { + "benchmarks": { + "tracing.tracing_with_background_memory_infra": { + "begin": 1, "abridged": false }, "v8.browsing_mobile": { - "end": 22, + "abridged": false + }, + "v8.browsing_mobile-future": { + "end": 5, "abridged": false } } }, "27": { "benchmarks": { - "v8.browsing_mobile": { - "begin": 22, - "abridged": false - }, "v8.browsing_mobile-future": { + "begin": 5, "abridged": false }, "wasmpspdfkit": { @@ -376,38 +384,38 @@ } }, "extra_infos": { - "num_stories": 1601, - "predicted_min_shard_time": 570, - "predicted_min_shard_index": 0, - "predicted_max_shard_time": 590.0, - "predicted_max_shard_index": 6, - "shard #0": 570, - "shard #1": 570, - "shard #2": 570, - "shard #3": 570, - "shard #4": 570, - "shard #5": 570, - "shard #6": 590.0, - "shard #7": 570, - "shard #8": 570, - "shard #9": 570, - "shard #10": 570, - "shard #11": 570, - "shard #12": 580, - "shard #13": 570, - "shard #14": 580, - "shard #15": 570, - "shard #16": 580, - "shard #17": 570, - "shard #18": 580, - "shard #19": 570, - "shard #20": 580, - "shard #21": 570, - "shard #22": 580, - "shard #23": 570, - "shard #24": 580, - "shard #25": 570, - "shard #26": 580, - "shard #27": 570 + "num_stories": 1226, + "predicted_min_shard_time": 430, + "predicted_min_shard_index": 27, + "predicted_max_shard_time": 440, + "predicted_max_shard_index": 0, + "shard #0": 440, + "shard #1": 440, + "shard #2": 440, + "shard #3": 440, + "shard #4": 440, + "shard #5": 440, + "shard #6": 440, + "shard #7": 440, + "shard #8": 440, + "shard #9": 440.0, + "shard #10": 440, + "shard #11": 440, + "shard #12": 440, + "shard #13": 440, + "shard #14": 440, + "shard #15": 440, + "shard #16": 440, + "shard #17": 440, + "shard #18": 440, + "shard #19": 440, + "shard #20": 440, + "shard #21": 440, + "shard #22": 440, + "shard #23": 440, + "shard #24": 440, + "shard #25": 440, + "shard #26": 440, + "shard #27": 430 } } \ No newline at end of file
diff --git a/tools/perf/core/shard_maps/android-pixel2-perf_map.json b/tools/perf/core/shard_maps/android-pixel2-perf_map.json index d6291c4..a37b039 100644 --- a/tools/perf/core/shard_maps/android-pixel2-perf_map.json +++ b/tools/perf/core/shard_maps/android-pixel2-perf_map.json
@@ -85,8 +85,11 @@ "startup.mobile": { "abridged": false }, + "ad_frames.fencedframe": { + "abridged": false + }, "blink_perf.accessibility": { - "end": 17, + "end": 16, "abridged": false }, "speedometer2": { @@ -154,14 +157,14 @@ "abridged": false }, "blink_perf.accessibility": { - "begin": 17, + "begin": 16, "abridged": false }, "blink_perf.bindings": { "abridged": false }, "blink_perf.css": { - "end": 40, + "end": 37, "abridged": false } } @@ -209,7 +212,7 @@ "abridged": false }, "blink_perf.css": { - "begin": 40, + "begin": 37, "abridged": false }, "blink_perf.dom": { @@ -222,7 +225,7 @@ "abridged": false }, "blink_perf.layout": { - "end": 49, + "end": 46, "abridged": false } } @@ -233,7 +236,7 @@ "abridged": false }, "blink_perf.layout": { - "begin": 49, + "begin": 46, "abridged": false }, "blink_perf.owp_storage": { @@ -455,7 +458,7 @@ } }, "extra_infos": { - "num_stories": 1568, + "num_stories": 1575, "predicted_min_shard_time": 1927.0, "predicted_min_shard_index": 17, "predicted_max_shard_time": 2551.0, @@ -465,18 +468,18 @@ "shard #2": 1965.0, "shard #3": 2067.0, "shard #4": 2127.0, - "shard #5": 2066.0, + "shard #5": 2065.0, "shard #6": 2068.0, "shard #7": 2173.0, "shard #8": 2086.0, "shard #9": 2077.0, - "shard #10": 2070.0, + "shard #10": 2081.0, "shard #11": 2063.0, "shard #12": 2062.0, "shard #13": 2103.0, "shard #14": 2112.0, - "shard #15": 2075.0, - "shard #16": 2070.0, + "shard #15": 2080.0, + "shard #16": 2125.0, "shard #17": 1927.0, "shard #18": 2070.0, "shard #19": 2103.0,
diff --git a/tools/perf/core/shard_maps/android-pixel2_webview-perf-pgo_map.json b/tools/perf/core/shard_maps/android-pixel2_webview-perf-pgo_map.json index 5777258..8ac33a3 100644 --- a/tools/perf/core/shard_maps/android-pixel2_webview-perf-pgo_map.json +++ b/tools/perf/core/shard_maps/android-pixel2_webview-perf-pgo_map.json
@@ -1,22 +1,34 @@ { "0": { "benchmarks": { + "ad_frames.fencedframe": { + "abridged": false + }, "blink_perf.accessibility": { "abridged": false }, "blink_perf.bindings": { - "abridged": false - }, - "blink_perf.css": { - "end": 5, + "end": 33, "abridged": false } } }, "1": { "benchmarks": { + "blink_perf.bindings": { + "begin": 33, + "abridged": false + }, "blink_perf.css": { - "begin": 5, + "end": 39, + "abridged": false + } + } + }, + "2": { + "benchmarks": { + "blink_perf.css": { + "begin": 39, "abridged": false }, "blink_perf.dom": { @@ -29,16 +41,7 @@ "abridged": false }, "blink_perf.layout": { - "end": 1, - "abridged": false - } - } - }, - "2": { - "benchmarks": { - "blink_perf.layout": { - "begin": 1, - "end": 76, + "end": 10, "abridged": false } } @@ -46,46 +49,64 @@ "3": { "benchmarks": { "blink_perf.layout": { - "begin": 76, - "abridged": false - }, - "blink_perf.owp_storage": { - "abridged": false - }, - "blink_perf.paint": { - "abridged": false - }, - "blink_perf.parser": { - "end": 24, + "begin": 10, + "end": 67, "abridged": false } } }, "4": { "benchmarks": { - "blink_perf.parser": { - "begin": 24, + "blink_perf.layout": { + "begin": 67, "abridged": false }, - "blink_perf.shadow_dom": { + "blink_perf.owp_storage": { "abridged": false }, - "blink_perf.svg": { - "abridged": false - }, - "blink_perf.webaudio": { - "end": 5, + "blink_perf.paint": { + "end": 12, "abridged": false } } }, "5": { "benchmarks": { + "blink_perf.paint": { + "begin": 12, + "abridged": false + }, + "blink_perf.parser": { + "abridged": false + }, + "blink_perf.shadow_dom": { + "end": 22, + "abridged": false + } + } + }, + "6": { + "benchmarks": { + "blink_perf.shadow_dom": { + "begin": 22, + "abridged": false + }, + "blink_perf.svg": { + "abridged": false + }, "blink_perf.webaudio": { - "begin": 5, "abridged": false }, "blink_perf.webcodecs": { + "end": 7, + "abridged": false + } + } + }, + "7": { + "benchmarks": { + "blink_perf.webcodecs": { + "begin": 7, "abridged": false }, "blink_perf.webgl": { @@ -107,55 +128,37 @@ "abridged": false }, "loading.mobile": { - "end": 51, - "abridged": false - } - } - }, - "6": { - "benchmarks": { - "loading.mobile": { - "begin": 51, - "abridged": false - }, - "media.mobile": { - "abridged": false - }, - "octane": { - "abridged": false - }, - "rasterize_and_record_micro.top_25": { - "end": 13, - "abridged": false - } - } - }, - "7": { - "benchmarks": { - "rasterize_and_record_micro.top_25": { - "begin": 13, - "abridged": false - }, - "rendering.mobile": { - "end": 63, + "end": 42, "abridged": false } } }, "8": { "benchmarks": { - "rendering.mobile": { - "begin": 63, - "end": 137, + "loading.mobile": { + "begin": 42, + "abridged": false + }, + "media.mobile": { + "end": 3, "abridged": false } } }, "9": { "benchmarks": { + "media.mobile": { + "begin": 3, + "abridged": false + }, + "octane": { + "abridged": false + }, + "rasterize_and_record_micro.top_25": { + "abridged": false + }, "rendering.mobile": { - "begin": 137, - "end": 212, + "end": 18, "abridged": false } } @@ -163,8 +166,8 @@ "10": { "benchmarks": { "rendering.mobile": { - "begin": 212, - "end": 286, + "begin": 18, + "end": 75, "abridged": false } } @@ -172,8 +175,8 @@ "11": { "benchmarks": { "rendering.mobile": { - "begin": 286, - "end": 361, + "begin": 75, + "end": 132, "abridged": false } } @@ -181,64 +184,55 @@ "12": { "benchmarks": { "rendering.mobile": { - "begin": 361, - "abridged": false - }, - "rendering.mobile.notracing": { - "end": 14, + "begin": 132, + "end": 189, "abridged": false } } }, "13": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 14, - "end": 89, + "rendering.mobile": { + "begin": 189, + "end": 246, "abridged": false } } }, "14": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 89, - "end": 163, + "rendering.mobile": { + "begin": 246, + "end": 302, "abridged": false } } }, "15": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 163, - "end": 238, + "rendering.mobile": { + "begin": 302, + "end": 359, "abridged": false } } }, "16": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 238, - "end": 312, + "rendering.mobile": { + "begin": 359, + "end": 415, "abridged": false } } }, "17": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 312, - "end": 387, + "rendering.mobile": { + "begin": 415, "abridged": false - } - } - }, - "18": { - "benchmarks": { + }, "rendering.mobile.notracing": { - "begin": 387, "abridged": false }, "speedometer": { @@ -260,27 +254,27 @@ "abridged": false }, "system_health.common_mobile": { - "end": 60, + "end": 40, + "abridged": false + } + } + }, + "18": { + "benchmarks": { + "system_health.common_mobile": { + "begin": 40, + "abridged": false + }, + "system_health.memory_mobile": { + "end": 21, "abridged": false } } }, "19": { "benchmarks": { - "system_health.common_mobile": { - "begin": 60, - "abridged": false - }, "system_health.memory_mobile": { - "end": 60, - "abridged": false - } - } - }, - "20": { - "benchmarks": { - "system_health.memory_mobile": { - "begin": 60, + "begin": 21, "abridged": false }, "system_health.pcscan": { @@ -290,6 +284,15 @@ "abridged": false }, "tracing.tracing_with_background_memory_infra": { + "end": 1, + "abridged": false + } + } + }, + "20": { + "benchmarks": { + "tracing.tracing_with_background_memory_infra": { + "begin": 1, "abridged": false }, "v8.browsing_mobile": { @@ -304,31 +307,31 @@ } }, "extra_infos": { - "num_stories": 1568, - "predicted_min_shard_time": 740, - "predicted_min_shard_index": 8, - "predicted_max_shard_time": 750, + "num_stories": 1193, + "predicted_min_shard_time": 560, + "predicted_min_shard_index": 14, + "predicted_max_shard_time": 570, "predicted_max_shard_index": 0, - "shard #0": 750, - "shard #1": 750, - "shard #2": 750, - "shard #3": 750, - "shard #4": 750, - "shard #5": 750, - "shard #6": 750, - "shard #7": 750, - "shard #8": 740, - "shard #9": 750, - "shard #10": 740, - "shard #11": 750, - "shard #12": 740, - "shard #13": 750, - "shard #14": 740, - "shard #15": 750, - "shard #16": 740, - "shard #17": 750, - "shard #18": 740, - "shard #19": 750, - "shard #20": 740 + "shard #0": 570, + "shard #1": 570, + "shard #2": 570, + "shard #3": 570, + "shard #4": 570, + "shard #5": 570, + "shard #6": 570, + "shard #7": 570, + "shard #8": 570, + "shard #9": 570, + "shard #10": 570, + "shard #11": 570, + "shard #12": 570, + "shard #13": 570, + "shard #14": 560, + "shard #15": 570, + "shard #16": 560, + "shard #17": 570, + "shard #18": 560, + "shard #19": 570, + "shard #20": 560 } } \ No newline at end of file
diff --git a/tools/perf/core/shard_maps/android-pixel2_webview-perf_map.json b/tools/perf/core/shard_maps/android-pixel2_webview-perf_map.json index a8ff77d..9a1c9d0 100644 --- a/tools/perf/core/shard_maps/android-pixel2_webview-perf_map.json +++ b/tools/perf/core/shard_maps/android-pixel2_webview-perf_map.json
@@ -1,6 +1,9 @@ { "0": { "benchmarks": { + "ad_frames.fencedframe": { + "abridged": false + }, "blink_perf.accessibility": { "abridged": false }, @@ -8,7 +11,7 @@ "abridged": false }, "blink_perf.css": { - "end": 9, + "end": 4, "abridged": false } } @@ -16,7 +19,7 @@ "1": { "benchmarks": { "blink_perf.css": { - "begin": 9, + "begin": 4, "abridged": false }, "blink_perf.dom": { @@ -29,7 +32,7 @@ "abridged": false }, "blink_perf.layout": { - "end": 73, + "end": 67, "abridged": false } } @@ -37,7 +40,7 @@ "2": { "benchmarks": { "blink_perf.layout": { - "begin": 73, + "begin": 67, "abridged": false }, "blink_perf.owp_storage": { @@ -299,14 +302,14 @@ } }, "extra_infos": { - "num_stories": 1186, + "num_stories": 1193, "predicted_min_shard_time": 1465.0, "predicted_min_shard_index": 19, - "predicted_max_shard_time": 1560.0, - "predicted_max_shard_index": 18, - "shard #0": 1520.0, - "shard #1": 1535.0, - "shard #2": 1528.0, + "predicted_max_shard_time": 1590.0, + "predicted_max_shard_index": 2, + "shard #0": 1529.0, + "shard #1": 1534.0, + "shard #2": 1590.0, "shard #3": 1537.0, "shard #4": 1524.0, "shard #5": 1518.0,
diff --git a/tools/perf/core/shard_maps/android-pixel4-perf-pgo_map.json b/tools/perf/core/shard_maps/android-pixel4-perf-pgo_map.json index 7fe34e55..80a8f5e 100644 --- a/tools/perf/core/shard_maps/android-pixel4-perf-pgo_map.json +++ b/tools/perf/core/shard_maps/android-pixel4-perf-pgo_map.json
@@ -1,11 +1,14 @@ { "0": { "benchmarks": { + "ad_frames.fencedframe": { + "abridged": false + }, "blink_perf.accessibility": { "abridged": false }, "blink_perf.bindings": { - "end": 38, + "end": 20, "abridged": false } } @@ -13,11 +16,11 @@ "1": { "benchmarks": { "blink_perf.bindings": { - "begin": 38, + "begin": 20, "abridged": false }, "blink_perf.css": { - "end": 44, + "end": 13, "abridged": false } } @@ -25,7 +28,16 @@ "2": { "benchmarks": { "blink_perf.css": { - "begin": 44, + "begin": 13, + "end": 57, + "abridged": false + } + } + }, + "3": { + "benchmarks": { + "blink_perf.css": { + "begin": 57, "abridged": false }, "blink_perf.dom": { @@ -38,16 +50,7 @@ "abridged": false }, "blink_perf.layout": { - "end": 22, - "abridged": false - } - } - }, - "3": { - "benchmarks": { - "blink_perf.layout": { - "begin": 22, - "end": 79, + "end": 15, "abridged": false } } @@ -55,7 +58,25 @@ "4": { "benchmarks": { "blink_perf.layout": { - "begin": 79, + "begin": 15, + "end": 59, + "abridged": false + } + } + }, + "5": { + "benchmarks": { + "blink_perf.layout": { + "begin": 59, + "end": 103, + "abridged": false + } + } + }, + "6": { + "benchmarks": { + "blink_perf.layout": { + "begin": 103, "abridged": false }, "blink_perf.owp_storage": { @@ -65,27 +86,27 @@ "abridged": false }, "blink_perf.parser": { - "end": 9, + "end": 18, "abridged": false } } }, - "5": { + "7": { "benchmarks": { "blink_perf.parser": { - "begin": 9, + "begin": 18, "abridged": false }, "blink_perf.shadow_dom": { - "end": 35, + "end": 32, "abridged": false } } }, - "6": { + "8": { "benchmarks": { "blink_perf.shadow_dom": { - "begin": 35, + "begin": 32, "abridged": false }, "blink_perf.svg": { @@ -95,6 +116,15 @@ "abridged": false }, "blink_perf.webcodecs": { + "end": 4, + "abridged": false + } + } + }, + "9": { + "benchmarks": { + "blink_perf.webcodecs": { + "begin": 4, "abridged": false }, "blink_perf.webgl": { @@ -102,19 +132,7 @@ }, "blink_perf.webgl_fast_call": { "abridged": false - } - }, - "executables": { - "components_perftests": { - "arguments": [ - "--xvfb" - ], - "path": "components_perftests" - } - } - }, - "7": { - "benchmarks": { + }, "dummy_benchmark.noisy_benchmark_1": { "abridged": false }, @@ -131,27 +149,44 @@ "abridged": false }, "loading.mobile": { - "end": 52, + "end": 19, + "abridged": false + } + }, + "executables": { + "components_perftests": { + "arguments": [ + "--xvfb" + ], + "path": "components_perftests" + } + } + }, + "10": { + "benchmarks": { + "loading.mobile": { + "begin": 19, + "end": 63, "abridged": false } } }, - "8": { + "11": { "benchmarks": { "loading.mobile": { - "begin": 52, + "begin": 63, "abridged": false }, "media.mobile": { - "end": 13, + "end": 11, "abridged": false } } }, - "9": { + "12": { "benchmarks": { "media.mobile": { - "begin": 13, + "begin": 11, "abridged": false }, "octane": { @@ -161,34 +196,7 @@ "abridged": false }, "rendering.mobile": { - "end": 28, - "abridged": false - } - } - }, - "10": { - "benchmarks": { - "rendering.mobile": { - "begin": 28, - "end": 85, - "abridged": false - } - } - }, - "11": { - "benchmarks": { - "rendering.mobile": { - "begin": 85, - "end": 142, - "abridged": false - } - } - }, - "12": { - "benchmarks": { - "rendering.mobile": { - "begin": 142, - "end": 200, + "end": 13, "abridged": false } } @@ -196,8 +204,8 @@ "13": { "benchmarks": { "rendering.mobile": { - "begin": 200, - "end": 257, + "begin": 13, + "end": 57, "abridged": false } } @@ -205,8 +213,8 @@ "14": { "benchmarks": { "rendering.mobile": { - "begin": 257, - "end": 315, + "begin": 57, + "end": 101, "abridged": false } } @@ -214,8 +222,8 @@ "15": { "benchmarks": { "rendering.mobile": { - "begin": 315, - "end": 372, + "begin": 101, + "end": 145, "abridged": false } } @@ -223,73 +231,64 @@ "16": { "benchmarks": { "rendering.mobile": { - "begin": 372, - "abridged": false - }, - "rendering.mobile.notracing": { - "end": 9, + "begin": 145, + "end": 189, "abridged": false } } }, "17": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 9, - "end": 66, + "rendering.mobile": { + "begin": 189, + "end": 233, "abridged": false } } }, "18": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 66, - "end": 124, + "rendering.mobile": { + "begin": 233, + "end": 277, "abridged": false } } }, "19": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 124, - "end": 181, + "rendering.mobile": { + "begin": 277, + "end": 321, "abridged": false } } }, "20": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 181, - "end": 239, + "rendering.mobile": { + "begin": 321, + "end": 365, "abridged": false } } }, "21": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 239, - "end": 296, + "rendering.mobile": { + "begin": 365, + "end": 409, "abridged": false } } }, "22": { "benchmarks": { - "rendering.mobile.notracing": { - "begin": 296, - "end": 354, + "rendering.mobile": { + "begin": 409, "abridged": false - } - } - }, - "23": { - "benchmarks": { + }, "rendering.mobile.notracing": { - "begin": 354, "abridged": false }, "speedometer": { @@ -311,7 +310,16 @@ "abridged": false }, "system_health.common_mobile": { - "end": 10, + "end": 21, + "abridged": false + } + } + }, + "23": { + "benchmarks": { + "system_health.common_mobile": { + "begin": 21, + "end": 65, "abridged": false } } @@ -319,28 +327,19 @@ "24": { "benchmarks": { "system_health.common_mobile": { - "begin": 10, - "end": 68, + "begin": 65, + "abridged": false + }, + "system_health.memory_mobile": { + "end": 34, "abridged": false } } }, "25": { "benchmarks": { - "system_health.common_mobile": { - "begin": 68, - "abridged": false - }, "system_health.memory_mobile": { - "end": 50, - "abridged": false - } - } - }, - "26": { - "benchmarks": { - "system_health.memory_mobile": { - "begin": 50, + "begin": 34, "abridged": false }, "system_health.pcscan": { @@ -350,21 +349,30 @@ "abridged": false }, "tracing.tracing_with_background_memory_infra": { + "end": 1, + "abridged": false + } + } + }, + "26": { + "benchmarks": { + "tracing.tracing_with_background_memory_infra": { + "begin": 1, "abridged": false }, "v8.browsing_mobile": { - "end": 22, + "abridged": false + }, + "v8.browsing_mobile-future": { + "end": 5, "abridged": false } } }, "27": { "benchmarks": { - "v8.browsing_mobile": { - "begin": 22, - "abridged": false - }, "v8.browsing_mobile-future": { + "begin": 5, "abridged": false }, "wasmpspdfkit": { @@ -376,38 +384,38 @@ } }, "extra_infos": { - "num_stories": 1601, - "predicted_min_shard_time": 570, - "predicted_min_shard_index": 0, - "predicted_max_shard_time": 590.0, - "predicted_max_shard_index": 6, - "shard #0": 570, - "shard #1": 570, - "shard #2": 570, - "shard #3": 570, - "shard #4": 570, - "shard #5": 570, - "shard #6": 590.0, - "shard #7": 570, - "shard #8": 570, - "shard #9": 570, - "shard #10": 570, - "shard #11": 570, - "shard #12": 580, - "shard #13": 570, - "shard #14": 580, - "shard #15": 570, - "shard #16": 580, - "shard #17": 570, - "shard #18": 580, - "shard #19": 570, - "shard #20": 580, - "shard #21": 570, - "shard #22": 580, - "shard #23": 570, - "shard #24": 580, - "shard #25": 570, - "shard #26": 580, - "shard #27": 570 + "num_stories": 1226, + "predicted_min_shard_time": 430, + "predicted_min_shard_index": 27, + "predicted_max_shard_time": 440, + "predicted_max_shard_index": 0, + "shard #0": 440, + "shard #1": 440, + "shard #2": 440, + "shard #3": 440, + "shard #4": 440, + "shard #5": 440, + "shard #6": 440, + "shard #7": 440, + "shard #8": 440, + "shard #9": 440.0, + "shard #10": 440, + "shard #11": 440, + "shard #12": 440, + "shard #13": 440, + "shard #14": 440, + "shard #15": 440, + "shard #16": 440, + "shard #17": 440, + "shard #18": 440, + "shard #19": 440, + "shard #20": 440, + "shard #21": 440, + "shard #22": 440, + "shard #23": 440, + "shard #24": 440, + "shard #25": 440, + "shard #26": 440, + "shard #27": 430 } } \ No newline at end of file
diff --git a/tools/perf/core/shard_maps/android-pixel4-perf_map.json b/tools/perf/core/shard_maps/android-pixel4-perf_map.json index b103e22a..8da9b714 100644 --- a/tools/perf/core/shard_maps/android-pixel4-perf_map.json +++ b/tools/perf/core/shard_maps/android-pixel4-perf_map.json
@@ -1,11 +1,14 @@ { "0": { "benchmarks": { + "ad_frames.fencedframe": { + "abridged": false + }, "blink_perf.accessibility": { "abridged": false }, "blink_perf.bindings": { - "end": 31, + "end": 26, "abridged": false }, "jetstream2": { @@ -19,7 +22,7 @@ "1": { "benchmarks": { "blink_perf.bindings": { - "begin": 31, + "begin": 26, "abridged": false }, "blink_perf.css": { @@ -32,10 +35,7 @@ "abridged": false }, "blink_perf.image_decoder": { - "abridged": false - }, - "blink_perf.layout": { - "end": 4, + "end": 9, "abridged": false }, "jetstream2": { @@ -48,9 +48,12 @@ }, "2": { "benchmarks": { + "blink_perf.image_decoder": { + "begin": 9, + "abridged": false + }, "blink_perf.layout": { - "begin": 4, - "end": 102, + "end": 101, "abridged": false }, "jetstream2": { @@ -64,7 +67,7 @@ "3": { "benchmarks": { "blink_perf.layout": { - "begin": 102, + "begin": 101, "abridged": false }, "blink_perf.owp_storage": { @@ -453,15 +456,15 @@ } }, "extra_infos": { - "num_stories": 1242, - "predicted_min_shard_time": 1193.0, - "predicted_min_shard_index": 3, + "num_stories": 1249, + "predicted_min_shard_time": 1206.0, + "predicted_min_shard_index": 21, "predicted_max_shard_time": 1573.0, "predicted_max_shard_index": 27, - "shard #0": 1270.0, - "shard #1": 1268.0, - "shard #2": 1271.0, - "shard #3": 1193.0, + "shard #0": 1271.0, + "shard #1": 1273.0, + "shard #2": 1255.0, + "shard #3": 1273.0, "shard #4": 1312.0, "shard #5": 1271.0, "shard #6": 1252.0,
diff --git a/tools/perf/core/shard_maps/android-pixel4_webview-perf_map.json b/tools/perf/core/shard_maps/android-pixel4_webview-perf_map.json index 9a16aa1a..ae476e3 100644 --- a/tools/perf/core/shard_maps/android-pixel4_webview-perf_map.json +++ b/tools/perf/core/shard_maps/android-pixel4_webview-perf_map.json
@@ -1,6 +1,9 @@ { "0": { "benchmarks": { + "ad_frames.fencedframe": { + "abridged": false + }, "blink_perf.accessibility": { "abridged": false }, @@ -8,7 +11,7 @@ "abridged": false }, "blink_perf.css": { - "end": 15, + "end": 6, "abridged": false }, "speedometer2": { @@ -19,7 +22,7 @@ "1": { "benchmarks": { "blink_perf.css": { - "begin": 15, + "begin": 6, "abridged": false }, "blink_perf.dom": { @@ -32,7 +35,7 @@ "abridged": false }, "blink_perf.layout": { - "end": 84, + "end": 79, "abridged": false }, "speedometer2": { @@ -43,7 +46,7 @@ "2": { "benchmarks": { "blink_perf.layout": { - "begin": 84, + "begin": 79, "abridged": false }, "blink_perf.owp_storage": { @@ -56,7 +59,7 @@ "abridged": false }, "blink_perf.shadow_dom": { - "end": 20, + "end": 14, "abridged": false }, "speedometer2": { @@ -67,7 +70,7 @@ "3": { "benchmarks": { "blink_perf.shadow_dom": { - "begin": 20, + "begin": 14, "abridged": false }, "blink_perf.svg": { @@ -98,7 +101,7 @@ "abridged": false }, "loading.mobile": { - "end": 20, + "end": 18, "abridged": false }, "speedometer2": { @@ -109,8 +112,8 @@ "4": { "benchmarks": { "loading.mobile": { - "begin": 20, - "end": 59, + "begin": 18, + "end": 56, "abridged": false }, "speedometer2": { @@ -121,7 +124,7 @@ "5": { "benchmarks": { "loading.mobile": { - "begin": 59, + "begin": 56, "end": 95, "abridged": false }, @@ -356,17 +359,17 @@ } }, "extra_infos": { - "num_stories": 1205, + "num_stories": 1212, "predicted_min_shard_time": 1277.0, "predicted_min_shard_index": 15, "predicted_max_shard_time": 1391.0, "predicted_max_shard_index": 19, - "shard #0": 1330.0, - "shard #1": 1328.0, - "shard #2": 1334.0, - "shard #3": 1327.0, - "shard #4": 1330.0, - "shard #5": 1320.0, + "shard #0": 1339.0, + "shard #1": 1337.0, + "shard #2": 1345.0, + "shard #3": 1334.0, + "shard #4": 1346.0, + "shard #5": 1338.0, "shard #6": 1333.0, "shard #7": 1340.0, "shard #8": 1335.0,
diff --git a/tools/perf/core/shard_maps/lacros-eve-perf_map.json b/tools/perf/core/shard_maps/lacros-eve-perf_map.json index de2c614b..44030301 100644 --- a/tools/perf/core/shard_maps/lacros-eve-perf_map.json +++ b/tools/perf/core/shard_maps/lacros-eve-perf_map.json
@@ -1,6 +1,9 @@ { "0": { "benchmarks": { + "ad_frames.fencedframe": { + "abridged": false + }, "blink_perf.accessibility": { "abridged": false }, @@ -163,12 +166,12 @@ } }, "extra_infos": { - "num_stories": 1199, + "num_stories": 1204, "predicted_min_shard_time": 25857.0, "predicted_min_shard_index": 3, - "predicted_max_shard_time": 26028.0, + "predicted_max_shard_time": 26078.0, "predicted_max_shard_index": 0, - "shard #0": 26028.0, + "shard #0": 26078.0, "shard #1": 25922.0, "shard #2": 25909.0, "shard #3": 25857.0
diff --git a/tools/perf/core/shard_maps/lacros-x86-perf_map.json b/tools/perf/core/shard_maps/lacros-x86-perf_map.json index 25ee7cf..60ece26 100644 --- a/tools/perf/core/shard_maps/lacros-x86-perf_map.json +++ b/tools/perf/core/shard_maps/lacros-x86-perf_map.json
@@ -1,6 +1,9 @@ { "0": { "benchmarks": { + "ad_frames.fencedframe": { + "abridged": false + }, "blink_perf.accessibility": { "abridged": false }, @@ -20,7 +23,7 @@ "abridged": false }, "blink_perf.layout": { - "end": 44, + "end": 40, "abridged": false } } @@ -28,7 +31,7 @@ "1": { "benchmarks": { "blink_perf.layout": { - "begin": 44, + "begin": 40, "abridged": false }, "blink_perf.owp_storage": { @@ -51,17 +54,12 @@ }, "blink_perf.webcodecs": { "abridged": false - }, - "blink_perf.webgl": { - "end": 3, - "abridged": false } } }, "2": { "benchmarks": { "blink_perf.webgl": { - "begin": 3, "abridged": false }, "blink_perf.webgl_fast_call": { @@ -101,7 +99,7 @@ "abridged": false }, "rasterize_and_record_micro.top_25": { - "end": 9, + "end": 7, "abridged": false } } @@ -109,11 +107,11 @@ "3": { "benchmarks": { "rasterize_and_record_micro.top_25": { - "begin": 9, + "begin": 7, "abridged": false }, "rendering.desktop": { - "end": 184, + "end": 182, "abridged": false } } @@ -121,7 +119,7 @@ "4": { "benchmarks": { "rendering.desktop": { - "begin": 184, + "begin": 182, "abridged": false }, "rendering.desktop.notracing": { @@ -143,7 +141,7 @@ "abridged": false }, "system_health.common_desktop": { - "end": 49, + "end": 48, "abridged": false } } @@ -151,7 +149,7 @@ "5": { "benchmarks": { "system_health.common_desktop": { - "begin": 49, + "begin": 48, "abridged": false }, "system_health.memory_desktop": { @@ -181,16 +179,16 @@ } }, "extra_infos": { - "num_stories": 1199, - "predicted_min_shard_time": 1990, - "predicted_min_shard_index": 5, - "predicted_max_shard_time": 2000, + "num_stories": 1204, + "predicted_min_shard_time": 2000, + "predicted_min_shard_index": 3, + "predicted_max_shard_time": 2010, "predicted_max_shard_index": 0, - "shard #0": 2000, - "shard #1": 2000, - "shard #2": 2000, + "shard #0": 2010, + "shard #1": 2010, + "shard #2": 2010, "shard #3": 2000, - "shard #4": 2000, - "shard #5": 1990 + "shard #4": 2010, + "shard #5": 2000 } } \ No newline at end of file
diff --git a/tools/perf/core/shard_maps/linux-perf-calibration_map.json b/tools/perf/core/shard_maps/linux-perf-calibration_map.json index ef474c4..6e5cc06 100644 --- a/tools/perf/core/shard_maps/linux-perf-calibration_map.json +++ b/tools/perf/core/shard_maps/linux-perf-calibration_map.json
@@ -4,11 +4,11 @@ "blink_perf.shadow_dom": { "abridged": false }, - "blink_perf.accessibility": { + "ad_frames.fencedframe": { "abridged": false }, - "blink_perf.bindings": { - "end": 14, + "blink_perf.accessibility": { + "end": 17, "abridged": false }, "jetstream2": { @@ -33,12 +33,12 @@ "blink_perf.shadow_dom": { "abridged": false }, - "blink_perf.bindings": { - "begin": 14, + "blink_perf.accessibility": { + "begin": 17, "abridged": false }, - "blink_perf.css": { - "end": 16, + "blink_perf.bindings": { + "end": 40, "abridged": false }, "jetstream2": { @@ -54,18 +54,12 @@ "blink_perf.shadow_dom": { "abridged": false }, + "blink_perf.bindings": { + "begin": 40, + "abridged": false + }, "blink_perf.css": { - "begin": 16, - "abridged": false - }, - "blink_perf.dom": { - "abridged": false - }, - "blink_perf.events": { - "abridged": false - }, - "blink_perf.image_decoder": { - "end": 1, + "end": 31, "abridged": false }, "jetstream2": { @@ -81,12 +75,15 @@ "blink_perf.shadow_dom": { "abridged": false }, - "blink_perf.image_decoder": { - "begin": 1, + "blink_perf.css": { + "begin": 31, "abridged": false }, - "blink_perf.layout": { - "end": 44, + "blink_perf.dom": { + "abridged": false + }, + "blink_perf.events": { + "end": 4, "abridged": false }, "jetstream2": { @@ -102,9 +99,15 @@ "blink_perf.shadow_dom": { "abridged": false }, + "blink_perf.events": { + "begin": 4, + "abridged": false + }, + "blink_perf.image_decoder": { + "abridged": false + }, "blink_perf.layout": { - "begin": 44, - "end": 98, + "end": 29, "abridged": false }, "jetstream2": { @@ -121,17 +124,8 @@ "abridged": false }, "blink_perf.layout": { - "begin": 98, - "abridged": false - }, - "blink_perf.owp_storage": { - "abridged": false - }, - "blink_perf.paint": { - "abridged": false - }, - "blink_perf.parser": { - "end": 24, + "begin": 29, + "end": 71, "abridged": false }, "jetstream2": { @@ -147,26 +141,14 @@ "blink_perf.shadow_dom": { "abridged": false }, - "blink_perf.parser": { - "begin": 24, + "blink_perf.layout": { + "begin": 71, "abridged": false }, - "blink_perf.sanitizer-api": { + "blink_perf.owp_storage": { "abridged": false }, - "blink_perf.svg": { - "abridged": false - }, - "blink_perf.webaudio": { - "abridged": false - }, - "blink_perf.webcodecs": { - "abridged": false - }, - "blink_perf.webgl": { - "abridged": false - }, - "blink_perf.webgl_fast_call": { + "blink_perf.paint": { "end": 1, "abridged": false }, @@ -183,40 +165,20 @@ "blink_perf.shadow_dom": { "abridged": false }, - "blink_perf.webgl_fast_call": { + "blink_perf.paint": { "begin": 1, "abridged": false }, - "desktop_ui": { - "abridged": false - }, - "dummy_benchmark.noisy_benchmark_1": { - "abridged": false - }, - "dummy_benchmark.stable_benchmark_1": { - "abridged": false - }, - "jetstream": { + "blink_perf.parser": { + "end": 26, "abridged": false }, "jetstream2": { "abridged": false }, - "kraken": { - "abridged": false - }, - "loading.desktop": { - "end": 11, - "abridged": false - }, "speedometer2": { "abridged": false } - }, - "executables": { - "load_library_perf_tests": { - "path": "load_library_perf_tests" - } } }, "8": { @@ -224,9 +186,21 @@ "blink_perf.shadow_dom": { "abridged": false }, - "loading.desktop": { - "begin": 11, - "end": 65, + "blink_perf.parser": { + "begin": 26, + "abridged": false + }, + "blink_perf.sanitizer-api": { + "abridged": false + }, + "blink_perf.svg": { + "abridged": false + }, + "blink_perf.webaudio": { + "abridged": false + }, + "blink_perf.webcodecs": { + "end": 4, "abridged": false }, "jetstream2": { @@ -242,12 +216,20 @@ "blink_perf.shadow_dom": { "abridged": false }, - "loading.desktop": { - "begin": 65, + "blink_perf.webcodecs": { + "begin": 4, "abridged": false }, - "media.desktop": { - "end": 14, + "blink_perf.webgl": { + "abridged": false + }, + "blink_perf.webgl_fast_call": { + "abridged": false + }, + "desktop_ui": { + "abridged": false + }, + "dummy_benchmark.noisy_benchmark_1": { "abridged": false }, "jetstream2": { @@ -263,8 +245,72 @@ "blink_perf.shadow_dom": { "abridged": false }, + "dummy_benchmark.stable_benchmark_1": { + "abridged": false + }, + "jetstream": { + "abridged": false + }, + "jetstream2": { + "abridged": false + }, + "kraken": { + "abridged": false + }, + "loading.desktop": { + "end": 39, + "abridged": false + }, + "speedometer2": { + "abridged": false + } + }, + "executables": { + "load_library_perf_tests": { + "path": "load_library_perf_tests" + } + } + }, + "11": { + "benchmarks": { + "blink_perf.shadow_dom": { + "abridged": false + }, + "loading.desktop": { + "begin": 39, + "end": 82, + "abridged": false + }, + "speedometer2": { + "abridged": false + } + } + }, + "12": { + "benchmarks": { + "blink_perf.shadow_dom": { + "abridged": false + }, + "loading.desktop": { + "begin": 82, + "abridged": false + }, "media.desktop": { - "begin": 14, + "end": 21, + "abridged": false + }, + "speedometer2": { + "abridged": false + } + } + }, + "13": { + "benchmarks": { + "blink_perf.shadow_dom": { + "abridged": false + }, + "media.desktop": { + "begin": 21, "abridged": false }, "memory.desktop": { @@ -274,10 +320,7 @@ "abridged": false }, "power.desktop": { - "abridged": false - }, - "rasterize_and_record_micro.top_25": { - "end": 2, + "end": 13, "abridged": false }, "speedometer2": { @@ -299,62 +342,20 @@ } } }, - "11": { - "benchmarks": { - "blink_perf.shadow_dom": { - "abridged": false - }, - "rasterize_and_record_micro.top_25": { - "begin": 2, - "abridged": false - }, - "rendering.desktop": { - "end": 32, - "abridged": false - }, - "speedometer2": { - "abridged": false - } - } - }, - "12": { - "benchmarks": { - "blink_perf.shadow_dom": { - "abridged": false - }, - "rendering.desktop": { - "begin": 32, - "end": 86, - "abridged": false - }, - "speedometer2": { - "abridged": false - } - } - }, - "13": { - "benchmarks": { - "blink_perf.shadow_dom": { - "abridged": false - }, - "rendering.desktop": { - "begin": 86, - "end": 141, - "abridged": false - }, - "speedometer2": { - "abridged": false - } - } - }, "14": { "benchmarks": { "blink_perf.shadow_dom": { "abridged": false }, + "power.desktop": { + "begin": 13, + "abridged": false + }, + "rasterize_and_record_micro.top_25": { + "abridged": false + }, "rendering.desktop": { - "begin": 141, - "end": 195, + "end": 16, "abridged": false }, "speedometer2": { @@ -368,8 +369,8 @@ "abridged": false }, "rendering.desktop": { - "begin": 195, - "end": 250, + "begin": 16, + "end": 60, "abridged": false }, "speedometer2": { @@ -383,8 +384,8 @@ "abridged": false }, "rendering.desktop": { - "begin": 250, - "end": 304, + "begin": 60, + "end": 103, "abridged": false }, "speedometer2": { @@ -398,11 +399,8 @@ "abridged": false }, "rendering.desktop": { - "begin": 304, - "abridged": false - }, - "rendering.desktop.notracing": { - "end": 30, + "begin": 103, + "end": 147, "abridged": false }, "speedometer2": { @@ -415,9 +413,9 @@ "blink_perf.shadow_dom": { "abridged": false }, - "rendering.desktop.notracing": { - "begin": 30, - "end": 84, + "rendering.desktop": { + "begin": 147, + "end": 190, "abridged": false }, "speedometer2": { @@ -430,9 +428,9 @@ "blink_perf.shadow_dom": { "abridged": false }, - "rendering.desktop.notracing": { - "begin": 84, - "end": 139, + "rendering.desktop": { + "begin": 190, + "end": 234, "abridged": false }, "speedometer2": { @@ -445,9 +443,9 @@ "blink_perf.shadow_dom": { "abridged": false }, - "rendering.desktop.notracing": { - "begin": 139, - "end": 193, + "rendering.desktop": { + "begin": 234, + "end": 277, "abridged": false }, "speedometer2": { @@ -460,9 +458,9 @@ "blink_perf.shadow_dom": { "abridged": false }, - "rendering.desktop.notracing": { - "begin": 193, - "end": 248, + "rendering.desktop": { + "begin": 277, + "end": 321, "abridged": false }, "speedometer2": { @@ -475,23 +473,11 @@ "blink_perf.shadow_dom": { "abridged": false }, - "rendering.desktop.notracing": { - "begin": 248, - "end": 302, - "abridged": false - }, - "speedometer2": { - "abridged": false - } - } - }, - "23": { - "benchmarks": { - "blink_perf.shadow_dom": { + "rendering.desktop": { + "begin": 321, "abridged": false }, "rendering.desktop.notracing": { - "begin": 302, "abridged": false }, "speedometer": { @@ -510,7 +496,22 @@ "abridged": false }, "system_health.common_desktop": { - "end": 31, + "end": 30, + "abridged": false + } + } + }, + "23": { + "benchmarks": { + "blink_perf.shadow_dom": { + "abridged": false + }, + "system_health.common_desktop": { + "begin": 30, + "end": 74, + "abridged": false + }, + "speedometer2": { "abridged": false } } @@ -521,11 +522,11 @@ "abridged": false }, "system_health.common_desktop": { - "begin": 31, + "begin": 74, "abridged": false }, "system_health.memory_desktop": { - "end": 4, + "end": 36, "abridged": false }, "speedometer2": { @@ -539,8 +540,8 @@ "abridged": false }, "system_health.memory_desktop": { - "begin": 4, - "end": 59, + "begin": 36, + "end": 80, "abridged": false }, "speedometer2": { @@ -554,7 +555,7 @@ "abridged": false }, "system_health.memory_desktop": { - "begin": 59, + "begin": 80, "abridged": false }, "system_health.pcscan": { @@ -567,7 +568,10 @@ "abridged": false }, "v8.browsing_desktop": { - "end": 21, + "abridged": false + }, + "v8.browsing_desktop-future": { + "end": 2, "abridged": false }, "speedometer2": { @@ -585,11 +589,8 @@ "blink_perf.shadow_dom": { "abridged": false }, - "v8.browsing_desktop": { - "begin": 21, - "abridged": false - }, "v8.browsing_desktop-future": { + "begin": 2, "abridged": false }, "wasmpspdfkit": { @@ -604,38 +605,38 @@ } }, "extra_infos": { - "num_stories": 2612, - "predicted_min_shard_time": 940.0, + "num_stories": 2298, + "predicted_min_shard_time": 830.0, "predicted_min_shard_index": 0, - "predicted_max_shard_time": 950, - "predicted_max_shard_index": 2, - "shard #0": 940.0, - "shard #1": 940, - "shard #2": 950, - "shard #3": 940, - "shard #4": 950, - "shard #5": 940, - "shard #6": 950, - "shard #7": 943.0, - "shard #8": 950, - "shard #9": 940, - "shard #10": 945.0, - "shard #11": 950, - "shard #12": 940, - "shard #13": 950, - "shard #14": 940, - "shard #15": 950, - "shard #16": 940, - "shard #17": 950, - "shard #18": 940, - "shard #19": 950, - "shard #20": 940, - "shard #21": 950, - "shard #22": 940, - "shard #23": 950, - "shard #24": 940, - "shard #25": 950, - "shard #26": 945.0, - "shard #27": 940 + "predicted_max_shard_time": 840, + "predicted_max_shard_index": 15, + "shard #0": 830.0, + "shard #1": 830, + "shard #2": 830, + "shard #3": 830, + "shard #4": 830, + "shard #5": 830, + "shard #6": 830, + "shard #7": 830, + "shard #8": 830, + "shard #9": 830, + "shard #10": 833.0, + "shard #11": 830, + "shard #12": 830, + "shard #13": 835.0, + "shard #14": 830, + "shard #15": 840, + "shard #16": 830, + "shard #17": 840, + "shard #18": 830, + "shard #19": 840, + "shard #20": 830, + "shard #21": 840, + "shard #22": 830, + "shard #23": 840, + "shard #24": 830, + "shard #25": 840, + "shard #26": 835.0, + "shard #27": 840 } } \ No newline at end of file
diff --git a/tools/perf/core/shard_maps/linux-perf-pgo_map.json b/tools/perf/core/shard_maps/linux-perf-pgo_map.json index ee94237..07c77eaf 100644 --- a/tools/perf/core/shard_maps/linux-perf-pgo_map.json +++ b/tools/perf/core/shard_maps/linux-perf-pgo_map.json
@@ -1,11 +1,14 @@ { "0": { "benchmarks": { + "ad_frames.fencedframe": { + "abridged": false + }, "blink_perf.accessibility": { "abridged": false }, "blink_perf.bindings": { - "end": 21, + "end": 4, "abridged": false } }, @@ -22,11 +25,11 @@ "1": { "benchmarks": { "blink_perf.bindings": { - "begin": 21, + "begin": 4, "abridged": false }, "blink_perf.css": { - "end": 30, + "end": 1, "abridged": false } } @@ -34,7 +37,16 @@ "2": { "benchmarks": { "blink_perf.css": { - "begin": 30, + "begin": 1, + "end": 49, + "abridged": false + } + } + }, + "3": { + "benchmarks": { + "blink_perf.css": { + "begin": 49, "abridged": false }, "blink_perf.dom": { @@ -52,21 +64,25 @@ } } }, - "3": { + "4": { "benchmarks": { "blink_perf.layout": { "begin": 11, - "end": 71, + "end": 59, "abridged": false } } }, - "4": { + "5": { "benchmarks": { "blink_perf.layout": { - "begin": 71, + "begin": 59, "abridged": false - }, + } + } + }, + "6": { + "benchmarks": { "blink_perf.owp_storage": { "abridged": false }, @@ -74,33 +90,33 @@ "abridged": false }, "blink_perf.parser": { - "end": 4, + "end": 26, "abridged": false } } }, - "5": { + "7": { "benchmarks": { "blink_perf.parser": { - "begin": 4, + "begin": 26, "abridged": false }, "blink_perf.sanitizer-api": { "abridged": false }, "blink_perf.shadow_dom": { - "end": 32, + "abridged": false + }, + "blink_perf.svg": { + "end": 4, "abridged": false } } }, - "6": { + "8": { "benchmarks": { - "blink_perf.shadow_dom": { - "begin": 32, - "abridged": false - }, "blink_perf.svg": { + "begin": 4, "abridged": false }, "blink_perf.webaudio": { @@ -116,15 +132,15 @@ "abridged": false }, "desktop_ui": { - "end": 4, + "end": 1, "abridged": false } } }, - "7": { + "9": { "benchmarks": { "desktop_ui": { - "begin": 4, + "begin": 1, "abridged": false }, "dummy_benchmark.noisy_benchmark_1": { @@ -143,7 +159,7 @@ "abridged": false }, "loading.desktop": { - "end": 24, + "end": 16, "abridged": false } }, @@ -153,22 +169,31 @@ } } }, - "8": { + "10": { "benchmarks": { "loading.desktop": { - "begin": 24, - "end": 84, + "begin": 16, + "end": 64, "abridged": false } } }, - "9": { + "11": { "benchmarks": { "loading.desktop": { - "begin": 84, + "begin": 64, "abridged": false }, "media.desktop": { + "end": 8, + "abridged": false + } + } + }, + "12": { + "benchmarks": { + "media.desktop": { + "begin": 8, "abridged": false }, "memory.desktop": { @@ -176,18 +201,8 @@ }, "octane": { "abridged": false - } - } - }, - "10": { - "benchmarks": { + }, "power.desktop": { - "abridged": false - }, - "rasterize_and_record_micro.top_25": { - "abridged": false - }, - "rendering.desktop": { "end": 4, "abridged": false } @@ -207,29 +222,17 @@ } } }, - "11": { - "benchmarks": { - "rendering.desktop": { - "begin": 4, - "end": 64, - "abridged": false - } - } - }, - "12": { - "benchmarks": { - "rendering.desktop": { - "begin": 64, - "end": 124, - "abridged": false - } - } - }, "13": { "benchmarks": { + "power.desktop": { + "begin": 4, + "abridged": false + }, + "rasterize_and_record_micro.top_25": { + "abridged": false + }, "rendering.desktop": { - "begin": 124, - "end": 184, + "end": 12, "abridged": false } } @@ -237,8 +240,8 @@ "14": { "benchmarks": { "rendering.desktop": { - "begin": 184, - "end": 244, + "begin": 12, + "end": 60, "abridged": false } } @@ -246,8 +249,8 @@ "15": { "benchmarks": { "rendering.desktop": { - "begin": 244, - "end": 304, + "begin": 60, + "end": 108, "abridged": false } } @@ -255,55 +258,46 @@ "16": { "benchmarks": { "rendering.desktop": { - "begin": 304, - "abridged": false - }, - "rendering.desktop.notracing": { - "end": 35, + "begin": 108, + "end": 156, "abridged": false } } }, "17": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 35, - "end": 95, + "rendering.desktop": { + "begin": 156, + "end": 204, "abridged": false } } }, "18": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 95, - "end": 155, + "rendering.desktop": { + "begin": 204, + "end": 251, "abridged": false } } }, "19": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 155, - "end": 215, + "rendering.desktop": { + "begin": 251, + "end": 299, "abridged": false } } }, "20": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 215, - "end": 275, + "rendering.desktop": { + "begin": 299, "abridged": false - } - } - }, - "21": { - "benchmarks": { + }, "rendering.desktop.notracing": { - "begin": 275, "abridged": false }, "speedometer": { @@ -322,7 +316,16 @@ "abridged": false }, "system_health.common_desktop": { - "end": 8, + "end": 11, + "abridged": false + } + } + }, + "21": { + "benchmarks": { + "system_health.common_desktop": { + "begin": 11, + "end": 59, "abridged": false } } @@ -330,20 +333,20 @@ "22": { "benchmarks": { "system_health.common_desktop": { - "begin": 8, - "end": 68, + "begin": 59, + "abridged": false + }, + "system_health.memory_desktop": { + "end": 25, "abridged": false } } }, "23": { "benchmarks": { - "system_health.common_desktop": { - "begin": 68, - "abridged": false - }, "system_health.memory_desktop": { - "end": 47, + "begin": 25, + "end": 73, "abridged": false } } @@ -351,7 +354,7 @@ "24": { "benchmarks": { "system_health.memory_desktop": { - "begin": 47, + "begin": 73, "abridged": false }, "system_health.pcscan": { @@ -364,7 +367,7 @@ "abridged": false }, "v8.browsing_desktop": { - "end": 15, + "end": 28, "abridged": false } }, @@ -377,7 +380,7 @@ "25": { "benchmarks": { "v8.browsing_desktop": { - "begin": 15, + "begin": 28, "abridged": false }, "v8.browsing_desktop-future": { @@ -392,36 +395,36 @@ } }, "extra_infos": { - "num_stories": 1523, - "predicted_min_shard_time": 550, - "predicted_min_shard_index": 9, - "predicted_max_shard_time": 605.0, - "predicted_max_shard_index": 10, - "shard #0": 600.0, - "shard #1": 600, - "shard #2": 600, - "shard #3": 600, - "shard #4": 600, - "shard #5": 600, - "shard #6": 600, - "shard #7": 603.0, - "shard #8": 600, - "shard #9": 550, - "shard #10": 605.0, - "shard #11": 600, - "shard #12": 600, - "shard #13": 600, - "shard #14": 600, - "shard #15": 600, - "shard #16": 600, - "shard #17": 600, - "shard #18": 600, - "shard #19": 600, - "shard #20": 600, - "shard #21": 600, - "shard #22": 600, - "shard #23": 600, - "shard #24": 605.0, - "shard #25": 600 + "num_stories": 1209, + "predicted_min_shard_time": 470, + "predicted_min_shard_index": 18, + "predicted_max_shard_time": 480.0, + "predicted_max_shard_index": 0, + "shard #0": 480.0, + "shard #1": 480, + "shard #2": 480, + "shard #3": 480, + "shard #4": 480, + "shard #5": 480, + "shard #6": 480, + "shard #7": 480, + "shard #8": 480, + "shard #9": 473.0, + "shard #10": 480, + "shard #11": 480, + "shard #12": 475.0, + "shard #13": 480, + "shard #14": 480, + "shard #15": 480, + "shard #16": 480, + "shard #17": 480, + "shard #18": 470, + "shard #19": 480, + "shard #20": 470, + "shard #21": 480, + "shard #22": 470, + "shard #23": 480, + "shard #24": 475.0, + "shard #25": 470 } } \ No newline at end of file
diff --git a/tools/perf/core/shard_maps/linux-perf_map.json b/tools/perf/core/shard_maps/linux-perf_map.json index d4df7be..889a2ca8 100644 --- a/tools/perf/core/shard_maps/linux-perf_map.json +++ b/tools/perf/core/shard_maps/linux-perf_map.json
@@ -1,11 +1,14 @@ { "0": { "benchmarks": { + "ad_frames.fencedframe": { + "abridged": false + }, "blink_perf.accessibility": { "abridged": false }, "blink_perf.bindings": { - "end": 42, + "end": 35, "abridged": false }, "jetstream2": { @@ -45,7 +48,7 @@ "1": { "benchmarks": { "blink_perf.bindings": { - "begin": 42, + "begin": 35, "abridged": false }, "blink_perf.css": { @@ -61,7 +64,7 @@ "abridged": false }, "blink_perf.layout": { - "end": 42, + "end": 34, "abridged": false }, "jetstream2": { @@ -92,7 +95,7 @@ "2": { "benchmarks": { "blink_perf.layout": { - "begin": 42, + "begin": 34, "abridged": false }, "blink_perf.owp_storage": { @@ -102,13 +105,7 @@ "abridged": false }, "blink_perf.parser": { - "abridged": false - }, - "blink_perf.sanitizer-api": { - "abridged": false - }, - "blink_perf.shadow_dom": { - "end": 11, + "end": 29, "abridged": false }, "jetstream2": { @@ -138,8 +135,14 @@ }, "3": { "benchmarks": { + "blink_perf.parser": { + "begin": 29, + "abridged": false + }, + "blink_perf.sanitizer-api": { + "abridged": false + }, "blink_perf.shadow_dom": { - "begin": 11, "abridged": false }, "blink_perf.svg": { @@ -632,15 +635,15 @@ } }, "extra_infos": { - "num_stories": 1253, + "num_stories": 1260, "predicted_min_shard_time": 990.0, "predicted_min_shard_index": 20, "predicted_max_shard_time": 1319.0, "predicted_max_shard_index": 25, - "shard #0": 1072.0, - "shard #1": 1070.0, - "shard #2": 1034.0, - "shard #3": 1061.0, + "shard #0": 1075.0, + "shard #1": 1072.0, + "shard #2": 1074.0, + "shard #3": 1086.0, "shard #4": 996.0, "shard #5": 1096.0, "shard #6": 1084.0,
diff --git a/tools/perf/core/shard_maps/mac-laptop_high_end-perf-pgo_map.json b/tools/perf/core/shard_maps/mac-laptop_high_end-perf-pgo_map.json index 3612e92..cd89c3aa 100644 --- a/tools/perf/core/shard_maps/mac-laptop_high_end-perf-pgo_map.json +++ b/tools/perf/core/shard_maps/mac-laptop_high_end-perf-pgo_map.json
@@ -1,11 +1,11 @@ { "0": { "benchmarks": { - "blink_perf.accessibility": { + "ad_frames.fencedframe": { "abridged": false }, - "blink_perf.bindings": { - "end": 13, + "blink_perf.accessibility": { + "end": 15, "abridged": false } }, @@ -21,20 +21,32 @@ }, "1": { "benchmarks": { - "blink_perf.bindings": { - "begin": 13, + "blink_perf.accessibility": { + "begin": 15, "abridged": false }, - "blink_perf.css": { - "end": 24, + "blink_perf.bindings": { + "end": 46, "abridged": false } } }, "2": { "benchmarks": { + "blink_perf.bindings": { + "begin": 46, + "abridged": false + }, "blink_perf.css": { - "begin": 24, + "end": 45, + "abridged": false + } + } + }, + "3": { + "benchmarks": { + "blink_perf.css": { + "begin": 45, "abridged": false }, "blink_perf.dom": { @@ -47,16 +59,7 @@ "abridged": false }, "blink_perf.layout": { - "end": 7, - "abridged": false - } - } - }, - "3": { - "benchmarks": { - "blink_perf.layout": { - "begin": 7, - "end": 69, + "end": 9, "abridged": false } } @@ -64,40 +67,58 @@ "4": { "benchmarks": { "blink_perf.layout": { - "begin": 69, - "abridged": false - }, - "blink_perf.owp_storage": { - "abridged": false - }, - "blink_perf.paint": { - "abridged": false - }, - "blink_perf.parser": { - "end": 4, + "begin": 9, + "end": 58, "abridged": false } } }, "5": { "benchmarks": { - "blink_perf.parser": { - "begin": 4, + "blink_perf.layout": { + "begin": 58, "abridged": false }, - "blink_perf.shadow_dom": { - "end": 35, + "blink_perf.owp_storage": { + "end": 1, "abridged": false } } }, "6": { "benchmarks": { + "blink_perf.owp_storage": { + "begin": 1, + "abridged": false + }, + "blink_perf.paint": { + "abridged": false + }, + "blink_perf.parser": { + "end": 28, + "abridged": false + } + } + }, + "7": { + "benchmarks": { + "blink_perf.parser": { + "begin": 28, + "abridged": false + }, "blink_perf.shadow_dom": { - "begin": 35, "abridged": false }, "blink_perf.svg": { + "end": 9, + "abridged": false + } + } + }, + "8": { + "benchmarks": { + "blink_perf.svg": { + "begin": 9, "abridged": false }, "blink_perf.webaudio": { @@ -120,10 +141,10 @@ } } }, - "7": { + "9": { "benchmarks": { "desktop_ui": { - "end": 29, + "end": 17, "abridged": false } }, @@ -137,10 +158,10 @@ } } }, - "8": { + "10": { "benchmarks": { "desktop_ui": { - "begin": 29, + "begin": 17, "abridged": false }, "dummy_benchmark.noisy_benchmark_1": { @@ -159,30 +180,39 @@ "abridged": false }, "loading.desktop": { - "end": 51, + "end": 35, "abridged": false } } }, - "9": { + "11": { "benchmarks": { "loading.desktop": { - "begin": 51, + "begin": 35, + "end": 85, + "abridged": false + } + } + }, + "12": { + "benchmarks": { + "loading.desktop": { + "begin": 85, "abridged": false }, "media.desktop": { - "end": 9, - "abridged": false - } - } - }, - "10": { - "benchmarks": { - "media.desktop": { - "begin": 9, "abridged": false }, "memory.desktop": { + "end": 6, + "abridged": false + } + } + }, + "13": { + "benchmarks": { + "memory.desktop": { + "begin": 6, "abridged": false }, "octane": { @@ -192,7 +222,7 @@ "abridged": false }, "rasterize_and_record_micro.top_25": { - "end": 2, + "end": 12, "abridged": false } }, @@ -211,41 +241,14 @@ } } }, - "11": { + "14": { "benchmarks": { "rasterize_and_record_micro.top_25": { - "begin": 2, + "begin": 12, "abridged": false }, "rendering.desktop": { - "end": 39, - "abridged": false - } - } - }, - "12": { - "benchmarks": { - "rendering.desktop": { - "begin": 39, - "end": 101, - "abridged": false - } - } - }, - "13": { - "benchmarks": { - "rendering.desktop": { - "begin": 101, - "end": 163, - "abridged": false - } - } - }, - "14": { - "benchmarks": { - "rendering.desktop": { - "begin": 163, - "end": 225, + "end": 37, "abridged": false } } @@ -253,8 +256,8 @@ "15": { "benchmarks": { "rendering.desktop": { - "begin": 225, - "end": 287, + "begin": 37, + "end": 87, "abridged": false } } @@ -262,55 +265,46 @@ "16": { "benchmarks": { "rendering.desktop": { - "begin": 287, - "abridged": false - }, - "rendering.desktop.notracing": { - "end": 20, + "begin": 87, + "end": 137, "abridged": false } } }, "17": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 20, - "end": 82, + "rendering.desktop": { + "begin": 137, + "end": 187, "abridged": false } } }, "18": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 82, - "end": 144, + "rendering.desktop": { + "begin": 187, + "end": 237, "abridged": false } } }, "19": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 144, - "end": 206, + "rendering.desktop": { + "begin": 237, + "end": 287, "abridged": false } } }, "20": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 206, - "end": 268, + "rendering.desktop": { + "begin": 287, "abridged": false - } - } - }, - "21": { - "benchmarks": { + }, "rendering.desktop.notracing": { - "begin": 268, "abridged": false }, "speedometer": { @@ -329,7 +323,16 @@ "abridged": false }, "system_health.common_desktop": { - "end": 3, + "end": 1, + "abridged": false + } + } + }, + "21": { + "benchmarks": { + "system_health.common_desktop": { + "begin": 1, + "end": 51, "abridged": false } } @@ -337,20 +340,20 @@ "22": { "benchmarks": { "system_health.common_desktop": { - "begin": 3, - "end": 64, + "begin": 51, + "abridged": false + }, + "system_health.memory_desktop": { + "end": 19, "abridged": false } } }, "23": { "benchmarks": { - "system_health.common_desktop": { - "begin": 64, - "abridged": false - }, "system_health.memory_desktop": { - "end": 45, + "begin": 19, + "end": 69, "abridged": false } } @@ -358,7 +361,7 @@ "24": { "benchmarks": { "system_health.memory_desktop": { - "begin": 45, + "begin": 69, "abridged": false }, "system_health.pcscan": { @@ -371,7 +374,7 @@ "abridged": false }, "v8.browsing_desktop": { - "end": 14, + "end": 26, "abridged": false } } @@ -379,7 +382,7 @@ "25": { "benchmarks": { "v8.browsing_desktop": { - "begin": 14, + "begin": 26, "abridged": false }, "v8.browsing_desktop-future": { @@ -402,36 +405,36 @@ } }, "extra_infos": { - "num_stories": 1524, - "predicted_min_shard_time": 550, - "predicted_min_shard_index": 6, - "predicted_max_shard_time": 620.0, + "num_stories": 1210, + "predicted_min_shard_time": 440, + "predicted_min_shard_index": 8, + "predicted_max_shard_time": 500.0, "predicted_max_shard_index": 0, - "shard #0": 620.0, - "shard #1": 620, - "shard #2": 620, - "shard #3": 620, - "shard #4": 620, - "shard #5": 620, - "shard #6": 550, - "shard #7": 620.0, - "shard #8": 620, - "shard #9": 620, - "shard #10": 620.0, - "shard #11": 620, - "shard #12": 620, - "shard #13": 620, - "shard #14": 620, - "shard #15": 620, - "shard #16": 620, - "shard #17": 620, - "shard #18": 620, - "shard #19": 620, - "shard #20": 620, - "shard #21": 620, - "shard #22": 610, - "shard #23": 620, - "shard #24": 610, - "shard #25": 617.0 + "shard #0": 500.0, + "shard #1": 500, + "shard #2": 500, + "shard #3": 500, + "shard #4": 490, + "shard #5": 500, + "shard #6": 490, + "shard #7": 500, + "shard #8": 440, + "shard #9": 500.0, + "shard #10": 500, + "shard #11": 500, + "shard #12": 500, + "shard #13": 500.0, + "shard #14": 500, + "shard #15": 500, + "shard #16": 500, + "shard #17": 500, + "shard #18": 500, + "shard #19": 500, + "shard #20": 490, + "shard #21": 500, + "shard #22": 490, + "shard #23": 500, + "shard #24": 490, + "shard #25": 497.0 } } \ No newline at end of file
diff --git a/tools/perf/core/shard_maps/mac-laptop_high_end-perf_map.json b/tools/perf/core/shard_maps/mac-laptop_high_end-perf_map.json index e6343be4..8cb69283 100644 --- a/tools/perf/core/shard_maps/mac-laptop_high_end-perf_map.json +++ b/tools/perf/core/shard_maps/mac-laptop_high_end-perf_map.json
@@ -1,11 +1,14 @@ { "0": { "benchmarks": { + "ad_frames.fencedframe": { + "abridged": false + }, "blink_perf.accessibility": { "abridged": false }, "blink_perf.bindings": { - "end": 42, + "end": 16, "abridged": false }, "jetstream2": { @@ -28,7 +31,7 @@ "1": { "benchmarks": { "blink_perf.bindings": { - "begin": 42, + "begin": 16, "abridged": false }, "blink_perf.css": { @@ -44,7 +47,7 @@ "abridged": false }, "blink_perf.layout": { - "end": 67, + "end": 12, "abridged": false }, "jetstream2": { @@ -58,7 +61,7 @@ "2": { "benchmarks": { "blink_perf.layout": { - "begin": 67, + "begin": 12, "abridged": false }, "blink_perf.owp_storage": { @@ -68,10 +71,7 @@ "abridged": false }, "blink_perf.parser": { - "abridged": false - }, - "blink_perf.shadow_dom": { - "end": 13, + "end": 5, "abridged": false }, "jetstream2": { @@ -84,14 +84,32 @@ }, "3": { "benchmarks": { + "blink_perf.parser": { + "begin": 5, + "abridged": false + }, "blink_perf.shadow_dom": { - "begin": 13, "abridged": false }, "blink_perf.svg": { "abridged": false }, "blink_perf.webaudio": { + "end": 4, + "abridged": false + }, + "jetstream2": { + "abridged": false + }, + "speedometer2": { + "abridged": false + } + } + }, + "4": { + "benchmarks": { + "blink_perf.webaudio": { + "begin": 4, "abridged": false }, "blink_perf.webcodecs": { @@ -110,7 +128,7 @@ "abridged": false }, "desktop_ui": { - "end": 1, + "end": 5, "abridged": false }, "jetstream2": { @@ -130,25 +148,10 @@ } } }, - "4": { - "benchmarks": { - "desktop_ui": { - "begin": 1, - "end": 29, - "abridged": false - }, - "jetstream2": { - "abridged": false - }, - "speedometer2": { - "abridged": false - } - } - }, "5": { "benchmarks": { "desktop_ui": { - "begin": 29, + "begin": 5, "abridged": false }, "dummy_benchmark.noisy_benchmark_1": { @@ -167,7 +170,7 @@ "abridged": false }, "loading.desktop": { - "end": 18, + "end": 2, "abridged": false }, "speedometer2": { @@ -178,8 +181,8 @@ "6": { "benchmarks": { "loading.desktop": { - "begin": 18, - "end": 44, + "begin": 2, + "end": 27, "abridged": false }, "speedometer2": { @@ -190,8 +193,8 @@ "7": { "benchmarks": { "loading.desktop": { - "begin": 44, - "end": 70, + "begin": 27, + "end": 49, "abridged": false }, "speedometer2": { @@ -202,8 +205,8 @@ "8": { "benchmarks": { "loading.desktop": { - "begin": 70, - "end": 95, + "begin": 49, + "end": 73, "abridged": false }, "speedometer2": { @@ -214,14 +217,8 @@ "9": { "benchmarks": { "loading.desktop": { - "begin": 95, - "abridged": false - }, - "media.desktop": { - "abridged": false - }, - "memory.desktop": { - "end": 1, + "begin": 73, + "end": 96, "abridged": false }, "speedometer2": { @@ -231,15 +228,28 @@ }, "10": { "benchmarks": { + "loading.desktop": { + "begin": 96, + "abridged": false + }, + "media.desktop": { + "abridged": false + }, + "speedometer2": { + "abridged": false + } + } + }, + "11": { + "benchmarks": { "memory.desktop": { - "begin": 1, "abridged": false }, "octane": { "abridged": false }, "power.desktop": { - "end": 7, + "end": 2, "abridged": false }, "speedometer2": { @@ -261,29 +271,17 @@ } } }, - "11": { + "12": { "benchmarks": { "power.desktop": { - "begin": 7, + "begin": 2, "abridged": false }, "rasterize_and_record_micro.top_25": { "abridged": false }, "rendering.desktop": { - "end": 34, - "abridged": false - }, - "speedometer2": { - "abridged": false - } - } - }, - "12": { - "benchmarks": { - "rendering.desktop": { - "begin": 34, - "end": 98, + "end": 18, "abridged": false }, "speedometer2": { @@ -294,8 +292,8 @@ "13": { "benchmarks": { "rendering.desktop": { - "begin": 98, - "end": 162, + "begin": 18, + "end": 75, "abridged": false }, "speedometer2": { @@ -306,8 +304,8 @@ "14": { "benchmarks": { "rendering.desktop": { - "begin": 162, - "end": 216, + "begin": 75, + "end": 137, "abridged": false }, "speedometer2": { @@ -318,8 +316,8 @@ "15": { "benchmarks": { "rendering.desktop": { - "begin": 216, - "end": 277, + "begin": 137, + "end": 187, "abridged": false }, "speedometer2": { @@ -330,11 +328,8 @@ "16": { "benchmarks": { "rendering.desktop": { - "begin": 277, - "abridged": false - }, - "rendering.desktop.notracing": { - "end": 16, + "begin": 187, + "end": 242, "abridged": false }, "speedometer2": { @@ -344,9 +339,9 @@ }, "17": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 16, - "end": 140, + "rendering.desktop": { + "begin": 242, + "end": 287, "abridged": false }, "speedometer2": { @@ -356,20 +351,11 @@ }, "18": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 140, - "end": 264, + "rendering.desktop": { + "begin": 287, "abridged": false }, - "speedometer2": { - "abridged": false - } - } - }, - "19": { - "benchmarks": { "rendering.desktop.notracing": { - "begin": 264, "abridged": false }, "speedometer": { @@ -388,7 +374,19 @@ "abridged": false }, "system_health.common_desktop": { - "end": 19, + "end": 7, + "abridged": false + } + } + }, + "19": { + "benchmarks": { + "system_health.common_desktop": { + "begin": 7, + "end": 63, + "abridged": false + }, + "speedometer2": { "abridged": false } } @@ -396,7 +394,11 @@ "20": { "benchmarks": { "system_health.common_desktop": { - "begin": 19, + "begin": 63, + "abridged": false + }, + "system_health.memory_desktop": { + "end": 9, "abridged": false } } @@ -404,7 +406,8 @@ "21": { "benchmarks": { "system_health.memory_desktop": { - "end": 21, + "begin": 9, + "end": 28, "abridged": false } } @@ -412,8 +415,8 @@ "22": { "benchmarks": { "system_health.memory_desktop": { - "begin": 21, - "end": 45, + "begin": 28, + "end": 54, "abridged": false } } @@ -421,8 +424,8 @@ "23": { "benchmarks": { "system_health.memory_desktop": { - "begin": 45, - "end": 72, + "begin": 54, + "end": 75, "abridged": false } } @@ -430,7 +433,7 @@ "24": { "benchmarks": { "system_health.memory_desktop": { - "begin": 72, + "begin": 75, "abridged": false }, "system_health.pcscan": { @@ -443,7 +446,7 @@ "abridged": false }, "v8.browsing_desktop": { - "end": 2, + "end": 11, "abridged": false } } @@ -451,7 +454,7 @@ "25": { "benchmarks": { "v8.browsing_desktop": { - "begin": 2, + "begin": 11, "abridged": false }, "v8.browsing_desktop-future": { @@ -474,36 +477,36 @@ } }, "extra_infos": { - "num_stories": 1547, - "predicted_min_shard_time": 1242.0, - "predicted_min_shard_index": 23, - "predicted_max_shard_time": 1504.0, + "num_stories": 1233, + "predicted_min_shard_time": 1111.0, + "predicted_min_shard_index": 24, + "predicted_max_shard_time": 1274.0, "predicted_max_shard_index": 25, - "shard #0": 1306.0, - "shard #1": 1301.0, - "shard #2": 1306.0, - "shard #3": 1309.0, - "shard #4": 1302.0, - "shard #5": 1295.0, - "shard #6": 1320.0, - "shard #7": 1292.0, - "shard #8": 1278.0, - "shard #9": 1294.0, - "shard #10": 1309.0, - "shard #11": 1304.0, - "shard #12": 1295.0, - "shard #13": 1298.0, - "shard #14": 1304.0, - "shard #15": 1303.0, - "shard #16": 1298.0, - "shard #17": 1302.0, - "shard #18": 1302.0, - "shard #19": 1318.0, - "shard #20": 1291.0, - "shard #21": 1293.0, - "shard #22": 1311.0, - "shard #23": 1242.0, - "shard #24": 1319.0, - "shard #25": 1504.0 + "shard #0": 1178.0, + "shard #1": 1175.0, + "shard #2": 1168.0, + "shard #3": 1147.0, + "shard #4": 1196.0, + "shard #5": 1174.0, + "shard #6": 1164.0, + "shard #7": 1158.0, + "shard #8": 1192.0, + "shard #9": 1196.0, + "shard #10": 1152.0, + "shard #11": 1194.0, + "shard #12": 1183.0, + "shard #13": 1173.0, + "shard #14": 1163.0, + "shard #15": 1173.0, + "shard #16": 1180.0, + "shard #17": 1181.0, + "shard #18": 1171.0, + "shard #19": 1182.0, + "shard #20": 1243.0, + "shard #21": 1182.0, + "shard #22": 1164.0, + "shard #23": 1227.0, + "shard #24": 1111.0, + "shard #25": 1274.0 } } \ No newline at end of file
diff --git a/tools/perf/core/shard_maps/mac-laptop_low_end-perf-pgo_map.json b/tools/perf/core/shard_maps/mac-laptop_low_end-perf-pgo_map.json index 16e0b24c..05db244 100644 --- a/tools/perf/core/shard_maps/mac-laptop_low_end-perf-pgo_map.json +++ b/tools/perf/core/shard_maps/mac-laptop_low_end-perf-pgo_map.json
@@ -1,11 +1,14 @@ { "0": { "benchmarks": { + "ad_frames.fencedframe": { + "abridged": false + }, "blink_perf.accessibility": { "abridged": false }, "blink_perf.bindings": { - "end": 41, + "end": 24, "abridged": false } } @@ -13,11 +16,11 @@ "1": { "benchmarks": { "blink_perf.bindings": { - "begin": 41, + "begin": 24, "abridged": false }, "blink_perf.css": { - "end": 50, + "end": 21, "abridged": false } } @@ -25,13 +28,22 @@ "2": { "benchmarks": { "blink_perf.css": { - "begin": 50, + "begin": 21, "abridged": false }, "blink_perf.display_locking": { "abridged": false }, "blink_perf.dom": { + "end": 1, + "abridged": false + } + } + }, + "3": { + "benchmarks": { + "blink_perf.dom": { + "begin": 1, "abridged": false }, "blink_perf.events": { @@ -41,16 +53,7 @@ "abridged": false }, "blink_perf.layout": { - "end": 21, - "abridged": false - } - } - }, - "3": { - "benchmarks": { - "blink_perf.layout": { - "begin": 21, - "end": 81, + "end": 20, "abridged": false } } @@ -58,42 +61,60 @@ "4": { "benchmarks": { "blink_perf.layout": { - "begin": 81, - "abridged": false - }, - "blink_perf.owp_storage": { - "abridged": false - }, - "blink_perf.paint": { - "abridged": false - }, - "blink_perf.parser": { - "end": 14, + "begin": 20, + "end": 68, "abridged": false } } }, "5": { "benchmarks": { - "blink_perf.parser": { - "begin": 14, + "blink_perf.layout": { + "begin": 68, "abridged": false }, - "blink_perf.shadow_dom": { + "blink_perf.owp_storage": { "abridged": false }, - "blink_perf.svg": { - "end": 4, + "blink_perf.paint": { + "end": 3, "abridged": false } } }, "6": { "benchmarks": { - "blink_perf.svg": { + "blink_perf.paint": { + "begin": 3, + "abridged": false + }, + "blink_perf.parser": { + "abridged": false + }, + "blink_perf.shadow_dom": { + "end": 4, + "abridged": false + } + } + }, + "7": { + "benchmarks": { + "blink_perf.shadow_dom": { "begin": 4, "abridged": false }, + "blink_perf.svg": { + "end": 12, + "abridged": false + } + } + }, + "8": { + "benchmarks": { + "blink_perf.svg": { + "begin": 12, + "abridged": false + }, "blink_perf.webaudio": { "abridged": false }, @@ -113,15 +134,15 @@ "abridged": false }, "desktop_ui": { - "end": 13, + "end": 7, "abridged": false } } }, - "7": { + "9": { "benchmarks": { "desktop_ui": { - "begin": 13, + "begin": 7, "abridged": false }, "dummy_benchmark.noisy_benchmark_1": { @@ -137,7 +158,7 @@ "abridged": false }, "loading.desktop": { - "end": 33, + "end": 23, "abridged": false } }, @@ -147,22 +168,31 @@ } } }, - "8": { + "10": { "benchmarks": { "loading.desktop": { - "begin": 33, - "end": 93, + "begin": 23, + "end": 71, "abridged": false } } }, - "9": { + "11": { "benchmarks": { "loading.desktop": { - "begin": 93, + "begin": 71, "abridged": false }, "media.desktop": { + "end": 14, + "abridged": false + } + } + }, + "12": { + "benchmarks": { + "media.desktop": { + "begin": 14, "abridged": false }, "memory.desktop": { @@ -170,6 +200,10 @@ }, "octane": { "abridged": false + }, + "power.desktop": { + "end": 6, + "abridged": false } }, "executables": { @@ -187,43 +221,17 @@ } } }, - "10": { + "13": { "benchmarks": { "power.desktop": { + "begin": 6, "abridged": false }, "rasterize_and_record_micro.top_25": { "abridged": false }, "rendering.desktop": { - "end": 19, - "abridged": false - } - } - }, - "11": { - "benchmarks": { - "rendering.desktop": { - "begin": 19, - "end": 78, - "abridged": false - } - } - }, - "12": { - "benchmarks": { - "rendering.desktop": { - "begin": 78, - "end": 137, - "abridged": false - } - } - }, - "13": { - "benchmarks": { - "rendering.desktop": { - "begin": 137, - "end": 196, + "end": 13, "abridged": false } } @@ -231,8 +239,8 @@ "14": { "benchmarks": { "rendering.desktop": { - "begin": 196, - "end": 255, + "begin": 13, + "end": 61, "abridged": false } } @@ -240,8 +248,8 @@ "15": { "benchmarks": { "rendering.desktop": { - "begin": 255, - "end": 314, + "begin": 61, + "end": 108, "abridged": false } } @@ -249,55 +257,46 @@ "16": { "benchmarks": { "rendering.desktop": { - "begin": 314, - "abridged": false - }, - "rendering.desktop.notracing": { - "end": 44, + "begin": 108, + "end": 156, "abridged": false } } }, "17": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 44, - "end": 103, + "rendering.desktop": { + "begin": 156, + "end": 203, "abridged": false } } }, "18": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 103, - "end": 162, + "rendering.desktop": { + "begin": 203, + "end": 251, "abridged": false } } }, "19": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 162, - "end": 221, + "rendering.desktop": { + "begin": 251, + "end": 298, "abridged": false } } }, "20": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 221, - "end": 280, + "rendering.desktop": { + "begin": 298, "abridged": false - } - } - }, - "21": { - "benchmarks": { + }, "rendering.desktop.notracing": { - "begin": 280, "abridged": false }, "speedometer": { @@ -316,7 +315,16 @@ "abridged": false }, "system_health.common_desktop": { - "end": 12, + "end": 11, + "abridged": false + } + } + }, + "21": { + "benchmarks": { + "system_health.common_desktop": { + "begin": 11, + "end": 58, "abridged": false } } @@ -324,20 +332,20 @@ "22": { "benchmarks": { "system_health.common_desktop": { - "begin": 12, - "end": 71, + "begin": 58, + "abridged": false + }, + "system_health.memory_desktop": { + "end": 25, "abridged": false } } }, "23": { "benchmarks": { - "system_health.common_desktop": { - "begin": 71, - "abridged": false - }, "system_health.memory_desktop": { - "end": 49, + "begin": 25, + "end": 72, "abridged": false } } @@ -345,7 +353,7 @@ "24": { "benchmarks": { "system_health.memory_desktop": { - "begin": 49, + "begin": 72, "abridged": false }, "system_health.pcscan": { @@ -358,7 +366,7 @@ "abridged": false }, "v8.browsing_desktop": { - "end": 16, + "end": 28, "abridged": false } } @@ -366,7 +374,7 @@ "25": { "benchmarks": { "v8.browsing_desktop": { - "begin": 16, + "begin": 28, "abridged": false }, "v8.browsing_desktop-future": { @@ -381,36 +389,36 @@ } }, "extra_infos": { - "num_stories": 1531, - "predicted_min_shard_time": 590, - "predicted_min_shard_index": 10, - "predicted_max_shard_time": 670.0, - "predicted_max_shard_index": 9, - "shard #0": 600, - "shard #1": 600, - "shard #2": 600, - "shard #3": 600, - "shard #4": 600, - "shard #5": 600, - "shard #6": 600, - "shard #7": 593.0, - "shard #8": 600, - "shard #9": 670.0, - "shard #10": 590, - "shard #11": 590, - "shard #12": 590, - "shard #13": 590, - "shard #14": 590, - "shard #15": 590, - "shard #16": 590, - "shard #17": 590, - "shard #18": 590, - "shard #19": 590, - "shard #20": 590, - "shard #21": 590, - "shard #22": 590, - "shard #23": 590, - "shard #24": 590, - "shard #25": 590 + "num_stories": 1217, + "predicted_min_shard_time": 470, + "predicted_min_shard_index": 3, + "predicted_max_shard_time": 480, + "predicted_max_shard_index": 0, + "shard #0": 480, + "shard #1": 480, + "shard #2": 480, + "shard #3": 470, + "shard #4": 480, + "shard #5": 470, + "shard #6": 480, + "shard #7": 470, + "shard #8": 480, + "shard #9": 473.0, + "shard #10": 480, + "shard #11": 470, + "shard #12": 480.0, + "shard #13": 470, + "shard #14": 480, + "shard #15": 470, + "shard #16": 480, + "shard #17": 470, + "shard #18": 480, + "shard #19": 470, + "shard #20": 480, + "shard #21": 470, + "shard #22": 480, + "shard #23": 470, + "shard #24": 480, + "shard #25": 470 } } \ No newline at end of file
diff --git a/tools/perf/core/shard_maps/mac-laptop_low_end-perf_map.json b/tools/perf/core/shard_maps/mac-laptop_low_end-perf_map.json index 76e663b..3cbcef7 100644 --- a/tools/perf/core/shard_maps/mac-laptop_low_end-perf_map.json +++ b/tools/perf/core/shard_maps/mac-laptop_low_end-perf_map.json
@@ -1,6 +1,9 @@ { "0": { "benchmarks": { + "ad_frames.fencedframe": { + "abridged": false + }, "blink_perf.accessibility": { "abridged": false }, @@ -8,18 +11,18 @@ "abridged": false }, "blink_perf.css": { - "abridged": false - }, - "blink_perf.display_locking": { - "end": 1, + "end": 36, "abridged": false } } }, "1": { "benchmarks": { + "blink_perf.css": { + "begin": 36, + "abridged": false + }, "blink_perf.display_locking": { - "begin": 1, "abridged": false }, "blink_perf.dom": { @@ -32,21 +35,21 @@ "abridged": false }, "blink_perf.layout": { - "abridged": false - }, - "blink_perf.owp_storage": { - "abridged": false - }, - "blink_perf.paint": { - "end": 9, + "end": 88, "abridged": false } } }, "2": { "benchmarks": { + "blink_perf.layout": { + "begin": 88, + "abridged": false + }, + "blink_perf.owp_storage": { + "abridged": false + }, "blink_perf.paint": { - "begin": 9, "abridged": false }, "blink_perf.parser": { @@ -56,18 +59,18 @@ "abridged": false }, "blink_perf.svg": { - "abridged": false - }, - "blink_perf.webaudio": { - "end": 7, + "end": 11, "abridged": false } } }, "3": { "benchmarks": { + "blink_perf.svg": { + "begin": 11, + "abridged": false + }, "blink_perf.webaudio": { - "begin": 7, "abridged": false }, "blink_perf.webcodecs": { @@ -86,7 +89,7 @@ "abridged": false }, "desktop_ui": { - "end": 22, + "end": 6, "abridged": false } } @@ -94,7 +97,7 @@ "4": { "benchmarks": { "desktop_ui": { - "begin": 22, + "begin": 6, "abridged": false }, "dummy_benchmark.noisy_benchmark_1": { @@ -110,7 +113,7 @@ "abridged": false }, "loading.desktop": { - "end": 18, + "end": 10, "abridged": false } }, @@ -123,8 +126,8 @@ "5": { "benchmarks": { "loading.desktop": { - "begin": 18, - "end": 42, + "begin": 10, + "end": 32, "abridged": false } } @@ -132,8 +135,8 @@ "6": { "benchmarks": { "loading.desktop": { - "begin": 42, - "end": 66, + "begin": 32, + "end": 53, "abridged": false } } @@ -141,8 +144,8 @@ "7": { "benchmarks": { "loading.desktop": { - "begin": 66, - "end": 88, + "begin": 53, + "end": 75, "abridged": false } } @@ -150,39 +153,34 @@ "8": { "benchmarks": { "loading.desktop": { - "begin": 88, - "abridged": false - }, - "media.desktop": { - "end": 11, + "begin": 75, + "end": 96, "abridged": false } } }, "9": { "benchmarks": { + "loading.desktop": { + "begin": 96, + "abridged": false + }, "media.desktop": { - "begin": 11, - "abridged": false - }, - "memory.desktop": { - "abridged": false - }, - "octane": { + "end": 21, "abridged": false } } }, "10": { "benchmarks": { - "power.desktop": { + "media.desktop": { + "begin": 21, "abridged": false }, - "rasterize_and_record_micro.top_25": { + "memory.desktop": { "abridged": false }, - "rendering.desktop": { - "end": 13, + "octane": { "abridged": false } }, @@ -203,9 +201,14 @@ }, "11": { "benchmarks": { + "power.desktop": { + "abridged": false + }, + "rasterize_and_record_micro.top_25": { + "abridged": false + }, "rendering.desktop": { - "begin": 13, - "end": 68, + "end": 15, "abridged": false } } @@ -213,8 +216,8 @@ "12": { "benchmarks": { "rendering.desktop": { - "begin": 68, - "end": 127, + "begin": 15, + "end": 66, "abridged": false } } @@ -222,8 +225,8 @@ "13": { "benchmarks": { "rendering.desktop": { - "begin": 127, - "end": 178, + "begin": 66, + "end": 119, "abridged": false } } @@ -231,8 +234,8 @@ "14": { "benchmarks": { "rendering.desktop": { - "begin": 178, - "end": 236, + "begin": 119, + "end": 168, "abridged": false } } @@ -240,8 +243,8 @@ "15": { "benchmarks": { "rendering.desktop": { - "begin": 236, - "end": 284, + "begin": 168, + "end": 213, "abridged": false } } @@ -249,28 +252,28 @@ "16": { "benchmarks": { "rendering.desktop": { - "begin": 284, - "abridged": false - }, - "rendering.desktop.notracing": { - "end": 33, + "begin": 213, + "end": 264, "abridged": false } } }, "17": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 33, - "end": 184, + "rendering.desktop": { + "begin": 264, + "end": 315, "abridged": false } } }, "18": { "benchmarks": { + "rendering.desktop": { + "begin": 315, + "abridged": false + }, "rendering.desktop.notracing": { - "begin": 184, "abridged": false }, "speedometer": { @@ -278,11 +281,7 @@ }, "speedometer-future": { "abridged": false - } - } - }, - "19": { - "benchmarks": { + }, "speedometer2": { "abridged": false }, @@ -293,7 +292,16 @@ "abridged": false }, "system_health.common_desktop": { - "end": 49, + "end": 23, + "abridged": false + } + } + }, + "19": { + "benchmarks": { + "system_health.common_desktop": { + "begin": 23, + "end": 80, "abridged": false } } @@ -301,11 +309,11 @@ "20": { "benchmarks": { "system_health.common_desktop": { - "begin": 49, + "begin": 80, "abridged": false }, "system_health.memory_desktop": { - "end": 8, + "end": 16, "abridged": false } } @@ -313,8 +321,8 @@ "21": { "benchmarks": { "system_health.memory_desktop": { - "begin": 8, - "end": 26, + "begin": 16, + "end": 36, "abridged": false } } @@ -322,8 +330,8 @@ "22": { "benchmarks": { "system_health.memory_desktop": { - "begin": 26, - "end": 52, + "begin": 36, + "end": 57, "abridged": false } } @@ -331,8 +339,8 @@ "23": { "benchmarks": { "system_health.memory_desktop": { - "begin": 52, - "end": 74, + "begin": 57, + "end": 77, "abridged": false } } @@ -340,7 +348,7 @@ "24": { "benchmarks": { "system_health.memory_desktop": { - "begin": 74, + "begin": 77, "abridged": false }, "system_health.pcscan": { @@ -353,7 +361,7 @@ "abridged": false }, "v8.browsing_desktop": { - "end": 17, + "end": 24, "abridged": false } } @@ -361,7 +369,7 @@ "25": { "benchmarks": { "v8.browsing_desktop": { - "begin": 17, + "begin": 24, "abridged": false }, "v8.browsing_desktop-future": { @@ -376,36 +384,36 @@ } }, "extra_infos": { - "num_stories": 1531, - "predicted_min_shard_time": 1425.0, - "predicted_min_shard_index": 23, - "predicted_max_shard_time": 1577.0, - "predicted_max_shard_index": 20, - "shard #0": 1522.0, - "shard #1": 1517.0, - "shard #2": 1539.0, - "shard #3": 1528.0, - "shard #4": 1533.0, - "shard #5": 1542.0, - "shard #6": 1536.0, - "shard #7": 1530.0, - "shard #8": 1541.0, - "shard #9": 1511.0, - "shard #10": 1525.0, - "shard #11": 1503.0, - "shard #12": 1518.0, - "shard #13": 1520.0, - "shard #14": 1497.0, - "shard #15": 1480.0, - "shard #16": 1516.0, - "shard #17": 1510, - "shard #18": 1475.0, - "shard #19": 1509.0, - "shard #20": 1577.0, - "shard #21": 1488.0, - "shard #22": 1536.0, - "shard #23": 1425.0, - "shard #24": 1550.0, - "shard #25": 1549.0 + "num_stories": 1217, + "predicted_min_shard_time": 1353.0, + "predicted_min_shard_index": 22, + "predicted_max_shard_time": 1431.0, + "predicted_max_shard_index": 23, + "shard #0": 1387.0, + "shard #1": 1391.0, + "shard #2": 1388.0, + "shard #3": 1406.0, + "shard #4": 1405.0, + "shard #5": 1402.0, + "shard #6": 1380.0, + "shard #7": 1372.0, + "shard #8": 1414.0, + "shard #9": 1389.0, + "shard #10": 1415.0, + "shard #11": 1383.0, + "shard #12": 1380.0, + "shard #13": 1378.0, + "shard #14": 1372.0, + "shard #15": 1390.0, + "shard #16": 1398.0, + "shard #17": 1396.0, + "shard #18": 1397.0, + "shard #19": 1416.0, + "shard #20": 1357.0, + "shard #21": 1365.0, + "shard #22": 1353.0, + "shard #23": 1431.0, + "shard #24": 1372.0, + "shard #25": 1370.0 } } \ No newline at end of file
diff --git a/tools/perf/core/shard_maps/mac-m1_mini_2020-perf-pgo_map.json b/tools/perf/core/shard_maps/mac-m1_mini_2020-perf-pgo_map.json index 3612e92..cd89c3aa 100644 --- a/tools/perf/core/shard_maps/mac-m1_mini_2020-perf-pgo_map.json +++ b/tools/perf/core/shard_maps/mac-m1_mini_2020-perf-pgo_map.json
@@ -1,11 +1,11 @@ { "0": { "benchmarks": { - "blink_perf.accessibility": { + "ad_frames.fencedframe": { "abridged": false }, - "blink_perf.bindings": { - "end": 13, + "blink_perf.accessibility": { + "end": 15, "abridged": false } }, @@ -21,20 +21,32 @@ }, "1": { "benchmarks": { - "blink_perf.bindings": { - "begin": 13, + "blink_perf.accessibility": { + "begin": 15, "abridged": false }, - "blink_perf.css": { - "end": 24, + "blink_perf.bindings": { + "end": 46, "abridged": false } } }, "2": { "benchmarks": { + "blink_perf.bindings": { + "begin": 46, + "abridged": false + }, "blink_perf.css": { - "begin": 24, + "end": 45, + "abridged": false + } + } + }, + "3": { + "benchmarks": { + "blink_perf.css": { + "begin": 45, "abridged": false }, "blink_perf.dom": { @@ -47,16 +59,7 @@ "abridged": false }, "blink_perf.layout": { - "end": 7, - "abridged": false - } - } - }, - "3": { - "benchmarks": { - "blink_perf.layout": { - "begin": 7, - "end": 69, + "end": 9, "abridged": false } } @@ -64,40 +67,58 @@ "4": { "benchmarks": { "blink_perf.layout": { - "begin": 69, - "abridged": false - }, - "blink_perf.owp_storage": { - "abridged": false - }, - "blink_perf.paint": { - "abridged": false - }, - "blink_perf.parser": { - "end": 4, + "begin": 9, + "end": 58, "abridged": false } } }, "5": { "benchmarks": { - "blink_perf.parser": { - "begin": 4, + "blink_perf.layout": { + "begin": 58, "abridged": false }, - "blink_perf.shadow_dom": { - "end": 35, + "blink_perf.owp_storage": { + "end": 1, "abridged": false } } }, "6": { "benchmarks": { + "blink_perf.owp_storage": { + "begin": 1, + "abridged": false + }, + "blink_perf.paint": { + "abridged": false + }, + "blink_perf.parser": { + "end": 28, + "abridged": false + } + } + }, + "7": { + "benchmarks": { + "blink_perf.parser": { + "begin": 28, + "abridged": false + }, "blink_perf.shadow_dom": { - "begin": 35, "abridged": false }, "blink_perf.svg": { + "end": 9, + "abridged": false + } + } + }, + "8": { + "benchmarks": { + "blink_perf.svg": { + "begin": 9, "abridged": false }, "blink_perf.webaudio": { @@ -120,10 +141,10 @@ } } }, - "7": { + "9": { "benchmarks": { "desktop_ui": { - "end": 29, + "end": 17, "abridged": false } }, @@ -137,10 +158,10 @@ } } }, - "8": { + "10": { "benchmarks": { "desktop_ui": { - "begin": 29, + "begin": 17, "abridged": false }, "dummy_benchmark.noisy_benchmark_1": { @@ -159,30 +180,39 @@ "abridged": false }, "loading.desktop": { - "end": 51, + "end": 35, "abridged": false } } }, - "9": { + "11": { "benchmarks": { "loading.desktop": { - "begin": 51, + "begin": 35, + "end": 85, + "abridged": false + } + } + }, + "12": { + "benchmarks": { + "loading.desktop": { + "begin": 85, "abridged": false }, "media.desktop": { - "end": 9, - "abridged": false - } - } - }, - "10": { - "benchmarks": { - "media.desktop": { - "begin": 9, "abridged": false }, "memory.desktop": { + "end": 6, + "abridged": false + } + } + }, + "13": { + "benchmarks": { + "memory.desktop": { + "begin": 6, "abridged": false }, "octane": { @@ -192,7 +222,7 @@ "abridged": false }, "rasterize_and_record_micro.top_25": { - "end": 2, + "end": 12, "abridged": false } }, @@ -211,41 +241,14 @@ } } }, - "11": { + "14": { "benchmarks": { "rasterize_and_record_micro.top_25": { - "begin": 2, + "begin": 12, "abridged": false }, "rendering.desktop": { - "end": 39, - "abridged": false - } - } - }, - "12": { - "benchmarks": { - "rendering.desktop": { - "begin": 39, - "end": 101, - "abridged": false - } - } - }, - "13": { - "benchmarks": { - "rendering.desktop": { - "begin": 101, - "end": 163, - "abridged": false - } - } - }, - "14": { - "benchmarks": { - "rendering.desktop": { - "begin": 163, - "end": 225, + "end": 37, "abridged": false } } @@ -253,8 +256,8 @@ "15": { "benchmarks": { "rendering.desktop": { - "begin": 225, - "end": 287, + "begin": 37, + "end": 87, "abridged": false } } @@ -262,55 +265,46 @@ "16": { "benchmarks": { "rendering.desktop": { - "begin": 287, - "abridged": false - }, - "rendering.desktop.notracing": { - "end": 20, + "begin": 87, + "end": 137, "abridged": false } } }, "17": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 20, - "end": 82, + "rendering.desktop": { + "begin": 137, + "end": 187, "abridged": false } } }, "18": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 82, - "end": 144, + "rendering.desktop": { + "begin": 187, + "end": 237, "abridged": false } } }, "19": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 144, - "end": 206, + "rendering.desktop": { + "begin": 237, + "end": 287, "abridged": false } } }, "20": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 206, - "end": 268, + "rendering.desktop": { + "begin": 287, "abridged": false - } - } - }, - "21": { - "benchmarks": { + }, "rendering.desktop.notracing": { - "begin": 268, "abridged": false }, "speedometer": { @@ -329,7 +323,16 @@ "abridged": false }, "system_health.common_desktop": { - "end": 3, + "end": 1, + "abridged": false + } + } + }, + "21": { + "benchmarks": { + "system_health.common_desktop": { + "begin": 1, + "end": 51, "abridged": false } } @@ -337,20 +340,20 @@ "22": { "benchmarks": { "system_health.common_desktop": { - "begin": 3, - "end": 64, + "begin": 51, + "abridged": false + }, + "system_health.memory_desktop": { + "end": 19, "abridged": false } } }, "23": { "benchmarks": { - "system_health.common_desktop": { - "begin": 64, - "abridged": false - }, "system_health.memory_desktop": { - "end": 45, + "begin": 19, + "end": 69, "abridged": false } } @@ -358,7 +361,7 @@ "24": { "benchmarks": { "system_health.memory_desktop": { - "begin": 45, + "begin": 69, "abridged": false }, "system_health.pcscan": { @@ -371,7 +374,7 @@ "abridged": false }, "v8.browsing_desktop": { - "end": 14, + "end": 26, "abridged": false } } @@ -379,7 +382,7 @@ "25": { "benchmarks": { "v8.browsing_desktop": { - "begin": 14, + "begin": 26, "abridged": false }, "v8.browsing_desktop-future": { @@ -402,36 +405,36 @@ } }, "extra_infos": { - "num_stories": 1524, - "predicted_min_shard_time": 550, - "predicted_min_shard_index": 6, - "predicted_max_shard_time": 620.0, + "num_stories": 1210, + "predicted_min_shard_time": 440, + "predicted_min_shard_index": 8, + "predicted_max_shard_time": 500.0, "predicted_max_shard_index": 0, - "shard #0": 620.0, - "shard #1": 620, - "shard #2": 620, - "shard #3": 620, - "shard #4": 620, - "shard #5": 620, - "shard #6": 550, - "shard #7": 620.0, - "shard #8": 620, - "shard #9": 620, - "shard #10": 620.0, - "shard #11": 620, - "shard #12": 620, - "shard #13": 620, - "shard #14": 620, - "shard #15": 620, - "shard #16": 620, - "shard #17": 620, - "shard #18": 620, - "shard #19": 620, - "shard #20": 620, - "shard #21": 620, - "shard #22": 610, - "shard #23": 620, - "shard #24": 610, - "shard #25": 617.0 + "shard #0": 500.0, + "shard #1": 500, + "shard #2": 500, + "shard #3": 500, + "shard #4": 490, + "shard #5": 500, + "shard #6": 490, + "shard #7": 500, + "shard #8": 440, + "shard #9": 500.0, + "shard #10": 500, + "shard #11": 500, + "shard #12": 500, + "shard #13": 500.0, + "shard #14": 500, + "shard #15": 500, + "shard #16": 500, + "shard #17": 500, + "shard #18": 500, + "shard #19": 500, + "shard #20": 490, + "shard #21": 500, + "shard #22": 490, + "shard #23": 500, + "shard #24": 490, + "shard #25": 497.0 } } \ No newline at end of file
diff --git a/tools/perf/core/shard_maps/mac-m1_mini_2020-perf_map.json b/tools/perf/core/shard_maps/mac-m1_mini_2020-perf_map.json index 3d03f56..166e4e2 100644 --- a/tools/perf/core/shard_maps/mac-m1_mini_2020-perf_map.json +++ b/tools/perf/core/shard_maps/mac-m1_mini_2020-perf_map.json
@@ -1,14 +1,14 @@ { "0": { "benchmarks": { + "ad_frames.fencedframe": { + "abridged": false + }, "blink_perf.accessibility": { "abridged": false }, "blink_perf.bindings": { - "abridged": false - }, - "blink_perf.css": { - "end": 3, + "end": 20, "abridged": false }, "jetstream2": { @@ -30,8 +30,11 @@ }, "1": { "benchmarks": { + "blink_perf.bindings": { + "begin": 20, + "abridged": false + }, "blink_perf.css": { - "begin": 3, "abridged": false }, "blink_perf.dom": { @@ -44,7 +47,7 @@ "abridged": false }, "blink_perf.layout": { - "end": 89, + "end": 28, "abridged": false }, "jetstream2": { @@ -58,7 +61,7 @@ "2": { "benchmarks": { "blink_perf.layout": { - "begin": 89, + "begin": 28, "abridged": false }, "blink_perf.owp_storage": { @@ -68,19 +71,7 @@ "abridged": false }, "blink_perf.parser": { - "abridged": false - }, - "blink_perf.shadow_dom": { - "abridged": false - }, - "blink_perf.svg": { - "abridged": false - }, - "blink_perf.webaudio": { - "abridged": false - }, - "blink_perf.webcodecs": { - "end": 1, + "end": 28, "abridged": false }, "jetstream2": { @@ -93,8 +84,20 @@ }, "3": { "benchmarks": { + "blink_perf.parser": { + "begin": 28, + "abridged": false + }, + "blink_perf.shadow_dom": { + "abridged": false + }, + "blink_perf.svg": { + "abridged": false + }, + "blink_perf.webaudio": { + "abridged": false + }, "blink_perf.webcodecs": { - "begin": 1, "abridged": false }, "blink_perf.webgl": { @@ -109,10 +112,6 @@ "blink_perf.webgpu_fast_call": { "abridged": false }, - "desktop_ui": { - "end": 9, - "abridged": false - }, "jetstream2": { "abridged": false }, @@ -133,7 +132,6 @@ "4": { "benchmarks": { "desktop_ui": { - "begin": 9, "abridged": false }, "dummy_benchmark.noisy_benchmark_1": { @@ -142,6 +140,16 @@ "dummy_benchmark.stable_benchmark_1": { "abridged": false }, + "jetstream2": { + "abridged": false + }, + "speedometer2": { + "abridged": false + } + } + }, + "5": { + "benchmarks": { "jetstream": { "abridged": false }, @@ -152,19 +160,7 @@ "abridged": false }, "loading.desktop": { - "end": 11, - "abridged": false - }, - "speedometer2": { - "abridged": false - } - } - }, - "5": { - "benchmarks": { - "loading.desktop": { - "begin": 11, - "end": 38, + "end": 19, "abridged": false }, "speedometer2": { @@ -175,8 +171,8 @@ "6": { "benchmarks": { "loading.desktop": { - "begin": 38, - "end": 65, + "begin": 19, + "end": 43, "abridged": false }, "speedometer2": { @@ -187,8 +183,8 @@ "7": { "benchmarks": { "loading.desktop": { - "begin": 65, - "end": 90, + "begin": 43, + "end": 67, "abridged": false }, "speedometer2": { @@ -199,11 +195,8 @@ "8": { "benchmarks": { "loading.desktop": { - "begin": 90, - "abridged": false - }, - "media.desktop": { - "end": 17, + "begin": 67, + "end": 88, "abridged": false }, "speedometer2": { @@ -213,18 +206,46 @@ }, "9": { "benchmarks": { + "loading.desktop": { + "begin": 88, + "abridged": false + }, "media.desktop": { - "begin": 17, + "end": 11, + "abridged": false + }, + "speedometer2": { + "abridged": false + } + } + }, + "10": { + "benchmarks": { + "media.desktop": { + "begin": 11, "abridged": false }, "memory.desktop": { "abridged": false }, + "speedometer2": { + "abridged": false + } + } + }, + "11": { + "benchmarks": { "octane": { "abridged": false }, "power.desktop": { - "end": 2, + "abridged": false + }, + "rasterize_and_record_micro.top_25": { + "abridged": false + }, + "rendering.desktop": { + "end": 4, "abridged": false }, "speedometer2": { @@ -246,41 +267,11 @@ } } }, - "10": { - "benchmarks": { - "power.desktop": { - "begin": 2, - "abridged": false - }, - "rasterize_and_record_micro.top_25": { - "abridged": false - }, - "rendering.desktop": { - "end": 23, - "abridged": false - }, - "speedometer2": { - "abridged": false - } - } - }, - "11": { - "benchmarks": { - "rendering.desktop": { - "begin": 23, - "end": 89, - "abridged": false - }, - "speedometer2": { - "abridged": false - } - } - }, "12": { "benchmarks": { "rendering.desktop": { - "begin": 89, - "end": 157, + "begin": 4, + "end": 57, "abridged": false }, "speedometer2": { @@ -291,8 +282,8 @@ "13": { "benchmarks": { "rendering.desktop": { - "begin": 157, - "end": 209, + "begin": 57, + "end": 117, "abridged": false }, "speedometer2": { @@ -303,8 +294,8 @@ "14": { "benchmarks": { "rendering.desktop": { - "begin": 209, - "end": 274, + "begin": 117, + "end": 173, "abridged": false }, "speedometer2": { @@ -315,11 +306,8 @@ "15": { "benchmarks": { "rendering.desktop": { - "begin": 274, - "abridged": false - }, - "rendering.desktop.notracing": { - "end": 20, + "begin": 173, + "end": 219, "abridged": false }, "speedometer2": { @@ -329,9 +317,9 @@ }, "16": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 20, - "end": 128, + "rendering.desktop": { + "begin": 219, + "end": 275, "abridged": false }, "speedometer2": { @@ -341,20 +329,11 @@ }, "17": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 128, - "end": 237, + "rendering.desktop": { + "begin": 275, "abridged": false }, - "speedometer2": { - "abridged": false - } - } - }, - "18": { - "benchmarks": { "rendering.desktop.notracing": { - "begin": 237, "abridged": false }, "speedometer": { @@ -368,12 +347,19 @@ }, "speedometer2-future": { "abridged": false - }, + } + } + }, + "18": { + "benchmarks": { "speedometer2-pcscan": { "abridged": false }, "system_health.common_desktop": { - "end": 8, + "end": 44, + "abridged": false + }, + "speedometer2": { "abridged": false } } @@ -381,8 +367,11 @@ "19": { "benchmarks": { "system_health.common_desktop": { - "begin": 8, - "end": 61, + "begin": 44, + "abridged": false + }, + "system_health.memory_desktop": { + "end": 1, "abridged": false }, "speedometer2": { @@ -392,12 +381,9 @@ }, "20": { "benchmarks": { - "system_health.common_desktop": { - "begin": 61, - "abridged": false - }, "system_health.memory_desktop": { - "end": 8, + "begin": 1, + "end": 16, "abridged": false } } @@ -405,8 +391,8 @@ "21": { "benchmarks": { "system_health.memory_desktop": { - "begin": 8, - "end": 24, + "begin": 16, + "end": 36, "abridged": false } } @@ -414,8 +400,8 @@ "22": { "benchmarks": { "system_health.memory_desktop": { - "begin": 24, - "end": 53, + "begin": 36, + "end": 60, "abridged": false } } @@ -423,8 +409,8 @@ "23": { "benchmarks": { "system_health.memory_desktop": { - "begin": 53, - "end": 75, + "begin": 60, + "end": 77, "abridged": false } } @@ -432,7 +418,7 @@ "24": { "benchmarks": { "system_health.memory_desktop": { - "begin": 75, + "begin": 77, "abridged": false }, "system_health.pcscan": { @@ -445,7 +431,7 @@ "abridged": false }, "v8.browsing_desktop": { - "end": 22, + "end": 23, "abridged": false } } @@ -453,7 +439,7 @@ "25": { "benchmarks": { "v8.browsing_desktop": { - "begin": 22, + "begin": 23, "abridged": false }, "v8.browsing_desktop-future": { @@ -476,36 +462,36 @@ } }, "extra_infos": { - "num_stories": 1547, - "predicted_min_shard_time": 1081.0, - "predicted_min_shard_index": 20, - "predicted_max_shard_time": 1182.0, - "predicted_max_shard_index": 23, - "shard #0": 1115.0, - "shard #1": 1116.0, - "shard #2": 1118.0, - "shard #3": 1107.0, - "shard #4": 1104.0, - "shard #5": 1135.0, - "shard #6": 1123.0, - "shard #7": 1133.0, - "shard #8": 1117.0, - "shard #9": 1114.0, - "shard #10": 1111.0, - "shard #11": 1117.0, - "shard #12": 1113.0, - "shard #13": 1112.0, - "shard #14": 1124.0, - "shard #15": 1119.0, - "shard #16": 1113.0, - "shard #17": 1123.0, - "shard #18": 1143.0, - "shard #19": 1118.0, - "shard #20": 1081.0, - "shard #21": 1107.0, - "shard #22": 1104.0, - "shard #23": 1182.0, - "shard #24": 1096.0, - "shard #25": 1108.0 + "num_stories": 1233, + "predicted_min_shard_time": 923.0, + "predicted_min_shard_index": 4, + "predicted_max_shard_time": 1056.0, + "predicted_max_shard_index": 3, + "shard #0": 992.0, + "shard #1": 995.0, + "shard #2": 990.0, + "shard #3": 1056.0, + "shard #4": 923.0, + "shard #5": 992.0, + "shard #6": 1009.0, + "shard #7": 1001.0, + "shard #8": 977.0, + "shard #9": 1033.0, + "shard #10": 975.0, + "shard #11": 996.0, + "shard #12": 991.0, + "shard #13": 984.0, + "shard #14": 984.0, + "shard #15": 989.0, + "shard #16": 983.0, + "shard #17": 977.0, + "shard #18": 987.0, + "shard #19": 1006.0, + "shard #20": 990.0, + "shard #21": 984.0, + "shard #22": 981.0, + "shard #23": 1011.0, + "shard #24": 1013.0, + "shard #25": 1053.0 } } \ No newline at end of file
diff --git a/tools/perf/core/shard_maps/win-10-perf-pgo_map.json b/tools/perf/core/shard_maps/win-10-perf-pgo_map.json index 5f51994..d4f2aa17 100644 --- a/tools/perf/core/shard_maps/win-10-perf-pgo_map.json +++ b/tools/perf/core/shard_maps/win-10-perf-pgo_map.json
@@ -1,11 +1,14 @@ { "0": { "benchmarks": { + "ad_frames.fencedframe": { + "abridged": false + }, "blink_perf.accessibility": { "abridged": false }, "blink_perf.bindings": { - "end": 23, + "end": 6, "abridged": false } }, @@ -22,11 +25,11 @@ "1": { "benchmarks": { "blink_perf.bindings": { - "begin": 23, + "begin": 6, "abridged": false }, "blink_perf.css": { - "end": 34, + "end": 5, "abridged": false } } @@ -34,7 +37,16 @@ "2": { "benchmarks": { "blink_perf.css": { - "begin": 34, + "begin": 5, + "end": 55, + "abridged": false + } + } + }, + "3": { + "benchmarks": { + "blink_perf.css": { + "begin": 55, "abridged": false }, "blink_perf.dom": { @@ -47,16 +59,7 @@ "abridged": false }, "blink_perf.layout": { - "end": 17, - "abridged": false - } - } - }, - "3": { - "benchmarks": { - "blink_perf.layout": { - "begin": 17, - "end": 79, + "end": 19, "abridged": false } } @@ -64,40 +67,58 @@ "4": { "benchmarks": { "blink_perf.layout": { - "begin": 79, - "abridged": false - }, - "blink_perf.owp_storage": { - "abridged": false - }, - "blink_perf.paint": { - "abridged": false - }, - "blink_perf.parser": { - "end": 14, + "begin": 19, + "end": 69, "abridged": false } } }, "5": { "benchmarks": { - "blink_perf.parser": { - "begin": 14, + "blink_perf.layout": { + "begin": 69, "abridged": false }, - "blink_perf.shadow_dom": { + "blink_perf.owp_storage": { "abridged": false }, - "blink_perf.svg": { - "end": 6, + "blink_perf.paint": { + "end": 7, "abridged": false } } }, "6": { "benchmarks": { + "blink_perf.paint": { + "begin": 7, + "abridged": false + }, + "blink_perf.parser": { + "abridged": false + }, + "blink_perf.shadow_dom": { + "end": 10, + "abridged": false + } + } + }, + "7": { + "benchmarks": { + "blink_perf.shadow_dom": { + "begin": 10, + "abridged": false + }, "blink_perf.svg": { - "begin": 6, + "end": 21, + "abridged": false + } + } + }, + "8": { + "benchmarks": { + "blink_perf.svg": { + "begin": 21, "abridged": false }, "blink_perf.webaudio": { @@ -128,13 +149,7 @@ } } }, - "7": { - "benchmarks": { - "desktop_ui": { - "end": 2, - "abridged": false - } - }, + "9": { "executables": { "dawn_perf_tests": { "arguments": [ @@ -143,12 +158,12 @@ ], "path": "dawn_perf_tests" } - } + }, + "benchmarks": {} }, - "8": { + "10": { "benchmarks": { "desktop_ui": { - "begin": 2, "abridged": false }, "dummy_benchmark.noisy_benchmark_1": { @@ -167,27 +182,36 @@ "abridged": false }, "loading.desktop": { - "end": 24, + "end": 18, "abridged": false } } }, - "9": { + "11": { "benchmarks": { "loading.desktop": { - "begin": 24, - "end": 86, + "begin": 18, + "end": 68, "abridged": false } } }, - "10": { + "12": { "benchmarks": { "loading.desktop": { - "begin": 86, + "begin": 68, "abridged": false }, "media.desktop": { + "end": 14, + "abridged": false + } + } + }, + "13": { + "benchmarks": { + "media.desktop": { + "begin": 14, "abridged": false }, "memory.desktop": { @@ -197,49 +221,22 @@ "abridged": false }, "power.desktop": { - "end": 9, - "abridged": false - } - } - }, - "11": { - "benchmarks": { - "power.desktop": { - "begin": 9, "abridged": false }, "rasterize_and_record_micro.top_25": { - "abridged": false - }, - "rendering.desktop": { - "end": 31, - "abridged": false - } - } - }, - "12": { - "benchmarks": { - "rendering.desktop": { - "begin": 31, - "end": 93, - "abridged": false - } - } - }, - "13": { - "benchmarks": { - "rendering.desktop": { - "begin": 93, - "end": 155, + "end": 14, "abridged": false } } }, "14": { "benchmarks": { + "rasterize_and_record_micro.top_25": { + "begin": 14, + "abridged": false + }, "rendering.desktop": { - "begin": 155, - "end": 217, + "end": 39, "abridged": false } } @@ -247,8 +244,8 @@ "15": { "benchmarks": { "rendering.desktop": { - "begin": 217, - "end": 280, + "begin": 39, + "end": 89, "abridged": false } } @@ -256,55 +253,46 @@ "16": { "benchmarks": { "rendering.desktop": { - "begin": 280, - "abridged": false - }, - "rendering.desktop.notracing": { - "end": 13, + "begin": 89, + "end": 138, "abridged": false } } }, "17": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 13, - "end": 76, + "rendering.desktop": { + "begin": 138, + "end": 188, "abridged": false } } }, "18": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 76, - "end": 138, + "rendering.desktop": { + "begin": 188, + "end": 237, "abridged": false } } }, "19": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 138, - "end": 201, + "rendering.desktop": { + "begin": 237, + "end": 287, "abridged": false } } }, "20": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 201, - "end": 263, + "rendering.desktop": { + "begin": 287, "abridged": false - } - } - }, - "21": { - "benchmarks": { + }, "rendering.desktop.notracing": { - "begin": 263, "abridged": false }, "speedometer": { @@ -318,28 +306,42 @@ }, "speedometer2-future": { "abridged": false + }, + "speedometer2-pcscan": { + "abridged": false + }, + "system_health.common_desktop": { + "end": 1, + "abridged": false + } + } + }, + "21": { + "benchmarks": { + "system_health.common_desktop": { + "begin": 1, + "end": 51, + "abridged": false } } }, "22": { "benchmarks": { - "speedometer2-pcscan": { + "system_health.common_desktop": { + "begin": 51, "abridged": false }, - "system_health.common_desktop": { - "end": 61, + "system_health.memory_desktop": { + "end": 19, "abridged": false } } }, "23": { "benchmarks": { - "system_health.common_desktop": { - "begin": 61, - "abridged": false - }, "system_health.memory_desktop": { - "end": 43, + "begin": 19, + "end": 69, "abridged": false } } @@ -347,7 +349,7 @@ "24": { "benchmarks": { "system_health.memory_desktop": { - "begin": 43, + "begin": 69, "abridged": false }, "system_health.pcscan": { @@ -360,7 +362,7 @@ "abridged": false }, "v8.browsing_desktop": { - "end": 13, + "end": 26, "abridged": false } } @@ -368,7 +370,7 @@ "25": { "benchmarks": { "v8.browsing_desktop": { - "begin": 13, + "begin": 26, "abridged": false }, "v8.browsing_desktop-future": { @@ -391,36 +393,36 @@ } }, "extra_infos": { - "num_stories": 1524, - "predicted_min_shard_time": 575.0, - "predicted_min_shard_index": 6, - "predicted_max_shard_time": 630, - "predicted_max_shard_index": 15, - "shard #0": 620.0, - "shard #1": 620, - "shard #2": 620, - "shard #3": 620, - "shard #4": 620, - "shard #5": 620, - "shard #6": 575.0, - "shard #7": 620.0, - "shard #8": 620, - "shard #9": 620, - "shard #10": 620, - "shard #11": 620, - "shard #12": 620, - "shard #13": 620, - "shard #14": 620, - "shard #15": 630, - "shard #16": 620, - "shard #17": 630, - "shard #18": 620, - "shard #19": 630, - "shard #20": 620, - "shard #21": 630, - "shard #22": 620, - "shard #23": 630, - "shard #24": 620, - "shard #25": 627.0 + "num_stories": 1210, + "predicted_min_shard_time": 445.0, + "predicted_min_shard_index": 8, + "predicted_max_shard_time": 600.0, + "predicted_max_shard_index": 9, + "shard #0": 500.0, + "shard #1": 500, + "shard #2": 500, + "shard #3": 500, + "shard #4": 500, + "shard #5": 500, + "shard #6": 500, + "shard #7": 500, + "shard #8": 445.0, + "shard #9": 600.0, + "shard #10": 500, + "shard #11": 500, + "shard #12": 500, + "shard #13": 500, + "shard #14": 500, + "shard #15": 500, + "shard #16": 490, + "shard #17": 500, + "shard #18": 490, + "shard #19": 500, + "shard #20": 490, + "shard #21": 500, + "shard #22": 490, + "shard #23": 500, + "shard #24": 490, + "shard #25": 497.0 } } \ No newline at end of file
diff --git a/tools/perf/core/shard_maps/win-10-perf_map.json b/tools/perf/core/shard_maps/win-10-perf_map.json index 03a827aa..6c74d6f7 100644 --- a/tools/perf/core/shard_maps/win-10-perf_map.json +++ b/tools/perf/core/shard_maps/win-10-perf_map.json
@@ -1,6 +1,9 @@ { "0": { "benchmarks": { + "ad_frames.fencedframe": { + "abridged": false + }, "blink_perf.accessibility": { "abridged": false }, @@ -8,7 +11,7 @@ "abridged": false }, "blink_perf.css": { - "end": 17, + "end": 11, "abridged": false }, "jetstream2": { @@ -44,7 +47,7 @@ "1": { "benchmarks": { "blink_perf.css": { - "begin": 17, + "begin": 11, "abridged": false }, "blink_perf.dom": { @@ -57,7 +60,7 @@ "abridged": false }, "blink_perf.layout": { - "end": 95, + "end": 86, "abridged": false }, "jetstream2": { @@ -84,7 +87,7 @@ "2": { "benchmarks": { "blink_perf.layout": { - "begin": 95, + "begin": 86, "abridged": false }, "blink_perf.owp_storage": { @@ -103,7 +106,7 @@ "abridged": false }, "blink_perf.webaudio": { - "end": 6, + "end": 3, "abridged": false }, "jetstream2": { @@ -130,7 +133,7 @@ "3": { "benchmarks": { "blink_perf.webaudio": { - "begin": 6, + "begin": 3, "abridged": false }, "blink_perf.webcodecs": { @@ -206,6 +209,13 @@ "jetstream2": { "abridged": false }, + "kraken": { + "abridged": false + }, + "loading.desktop": { + "end": 4, + "abridged": false + }, "system_health.common_desktop": { "sections": [ { @@ -226,14 +236,9 @@ }, "5": { "benchmarks": { - "jetstream2": { - "abridged": false - }, - "kraken": { - "abridged": false - }, "loading.desktop": { - "end": 29, + "begin": 4, + "end": 38, "abridged": false }, "system_health.common_desktop": { @@ -257,8 +262,8 @@ "6": { "benchmarks": { "loading.desktop": { - "begin": 29, - "end": 66, + "begin": 38, + "end": 73, "abridged": false }, "system_health.common_desktop": { @@ -282,8 +287,11 @@ "7": { "benchmarks": { "loading.desktop": { - "begin": 66, - "end": 98, + "begin": 73, + "abridged": false + }, + "media.desktop": { + "end": 1, "abridged": false }, "system_health.common_desktop": { @@ -306,15 +314,12 @@ }, "8": { "benchmarks": { - "loading.desktop": { - "begin": 98, - "abridged": false - }, "media.desktop": { + "begin": 1, "abridged": false }, "memory.desktop": { - "end": 4, + "end": 6, "abridged": false }, "system_health.common_desktop": { @@ -338,7 +343,7 @@ "9": { "benchmarks": { "memory.desktop": { - "begin": 4, + "begin": 6, "abridged": false }, "octane": { @@ -348,7 +353,10 @@ "abridged": false }, "rasterize_and_record_micro.top_25": { - "end": 16, + "abridged": false + }, + "rendering.desktop": { + "end": 8, "abridged": false }, "system_health.common_desktop": { @@ -371,12 +379,9 @@ }, "10": { "benchmarks": { - "rasterize_and_record_micro.top_25": { - "begin": 16, - "abridged": false - }, "rendering.desktop": { - "end": 83, + "begin": 8, + "end": 104, "abridged": false }, "speedometer2": { @@ -387,8 +392,8 @@ "11": { "benchmarks": { "rendering.desktop": { - "begin": 83, - "end": 190, + "begin": 104, + "end": 198, "abridged": false }, "speedometer2": { @@ -399,8 +404,8 @@ "12": { "benchmarks": { "rendering.desktop": { - "begin": 190, - "end": 265, + "begin": 198, + "end": 276, "abridged": false }, "speedometer2": { @@ -411,7 +416,7 @@ "13": { "benchmarks": { "rendering.desktop": { - "begin": 265, + "begin": 276, "abridged": false }, "rendering.desktop.notracing": { @@ -433,7 +438,7 @@ "abridged": false }, "system_health.common_desktop": { - "end": 1, + "end": 9, "abridged": false } } @@ -441,8 +446,8 @@ "14": { "benchmarks": { "system_health.common_desktop": { - "begin": 1, - "end": 73, + "begin": 9, + "end": 80, "abridged": false }, "speedometer2": { @@ -453,11 +458,11 @@ "15": { "benchmarks": { "system_health.common_desktop": { - "begin": 73, + "begin": 80, "abridged": false }, "system_health.memory_desktop": { - "end": 21, + "end": 23, "abridged": false }, "speedometer2": { @@ -468,8 +473,8 @@ "16": { "benchmarks": { "system_health.memory_desktop": { - "begin": 21, - "end": 55, + "begin": 23, + "end": 61, "abridged": false }, "speedometer2": { @@ -480,8 +485,8 @@ "17": { "benchmarks": { "system_health.memory_desktop": { - "begin": 55, - "end": 74, + "begin": 61, + "end": 76, "abridged": false }, "speedometer2": { @@ -492,7 +497,7 @@ "18": { "benchmarks": { "system_health.memory_desktop": { - "begin": 74, + "begin": 76, "abridged": false }, "system_health.pcscan": { @@ -505,7 +510,7 @@ "abridged": false }, "v8.browsing_desktop": { - "end": 23, + "end": 26, "abridged": false }, "speedometer2": { @@ -516,7 +521,7 @@ "19": { "benchmarks": { "v8.browsing_desktop": { - "begin": 23, + "begin": 26, "abridged": false }, "v8.browsing_desktop-future": { @@ -542,30 +547,30 @@ } }, "extra_infos": { - "num_stories": 1251, - "predicted_min_shard_time": 1869.0, - "predicted_min_shard_index": 3, - "predicted_max_shard_time": 2234.0, + "num_stories": 1260, + "predicted_min_shard_time": 1886.0, + "predicted_min_shard_index": 17, + "predicted_max_shard_time": 2041.0, "predicted_max_shard_index": 19, - "shard #0": 1952.0, - "shard #1": 1955.0, - "shard #2": 1944.0, - "shard #3": 1869.0, - "shard #4": 1913.0, - "shard #5": 1973.0, - "shard #6": 1971.0, - "shard #7": 1965.0, - "shard #8": 1959.0, - "shard #9": 1951.0, - "shard #10": 1962.0, - "shard #11": 1963.0, - "shard #12": 1952.0, - "shard #13": 1950.0, - "shard #14": 1959.0, - "shard #15": 1891.0, - "shard #16": 1991.0, - "shard #17": 2024.0, - "shard #18": 1961.0, - "shard #19": 2234.0 + "shard #0": 1942.0, + "shard #1": 1950.0, + "shard #2": 1934.0, + "shard #3": 1984.0, + "shard #4": 1960.0, + "shard #5": 1961.0, + "shard #6": 1957.0, + "shard #7": 1941.0, + "shard #8": 1933.0, + "shard #9": 1933.0, + "shard #10": 1946.0, + "shard #11": 1930.0, + "shard #12": 1944.0, + "shard #13": 1976.0, + "shard #14": 1949.0, + "shard #15": 1935.0, + "shard #16": 1958.0, + "shard #17": 1886.0, + "shard #18": 1980.0, + "shard #19": 2041.0 } } \ No newline at end of file
diff --git a/tools/perf/core/shard_maps/win-10_laptop_low_end-perf-pgo_map.json b/tools/perf/core/shard_maps/win-10_laptop_low_end-perf-pgo_map.json index dcd4de04..8bfc70f 100644 --- a/tools/perf/core/shard_maps/win-10_laptop_low_end-perf-pgo_map.json +++ b/tools/perf/core/shard_maps/win-10_laptop_low_end-perf-pgo_map.json
@@ -1,11 +1,14 @@ { "0": { "benchmarks": { + "ad_frames.fencedframe": { + "abridged": false + }, "blink_perf.accessibility": { "abridged": false }, "blink_perf.bindings": { - "end": 22, + "end": 9, "abridged": false } } @@ -13,31 +16,40 @@ "1": { "benchmarks": { "blink_perf.bindings": { - "begin": 22, - "abridged": false - }, - "blink_perf.css": { - "end": 12, + "begin": 9, + "end": 42, "abridged": false } } }, "2": { "benchmarks": { - "blink_perf.css": { - "begin": 12, + "blink_perf.bindings": { + "begin": 42, "abridged": false }, - "blink_perf.dom": { - "end": 1, + "blink_perf.css": { + "end": 24, "abridged": false } } }, "3": { "benchmarks": { + "blink_perf.css": { + "begin": 24, + "end": 57, + "abridged": false + } + } + }, + "4": { + "benchmarks": { + "blink_perf.css": { + "begin": 57, + "abridged": false + }, "blink_perf.dom": { - "begin": 1, "abridged": false }, "blink_perf.events": { @@ -47,16 +59,7 @@ "abridged": false }, "blink_perf.layout": { - "end": 15, - "abridged": false - } - } - }, - "4": { - "benchmarks": { - "blink_perf.layout": { - "begin": 15, - "end": 56, + "end": 4, "abridged": false } } @@ -64,8 +67,8 @@ "5": { "benchmarks": { "blink_perf.layout": { - "begin": 56, - "end": 97, + "begin": 4, + "end": 37, "abridged": false } } @@ -73,7 +76,25 @@ "6": { "benchmarks": { "blink_perf.layout": { - "begin": 97, + "begin": 37, + "end": 70, + "abridged": false + } + } + }, + "7": { + "benchmarks": { + "blink_perf.layout": { + "begin": 70, + "end": 103, + "abridged": false + } + } + }, + "8": { + "benchmarks": { + "blink_perf.layout": { + "begin": 103, "abridged": false }, "blink_perf.owp_storage": { @@ -83,45 +104,54 @@ "abridged": false }, "blink_perf.parser": { - "end": 11, - "abridged": false - } - } - }, - "7": { - "benchmarks": { - "blink_perf.parser": { - "begin": 11, - "abridged": false - }, - "blink_perf.shadow_dom": { - "end": 21, - "abridged": false - } - } - }, - "8": { - "benchmarks": { - "blink_perf.shadow_dom": { - "begin": 21, - "abridged": false - }, - "blink_perf.svg": { - "end": 23, + "end": 7, "abridged": false } } }, "9": { "benchmarks": { + "blink_perf.parser": { + "begin": 7, + "abridged": false + }, + "blink_perf.shadow_dom": { + "end": 10, + "abridged": false + } + } + }, + "10": { + "benchmarks": { + "blink_perf.shadow_dom": { + "begin": 10, + "abridged": false + }, "blink_perf.svg": { - "begin": 23, + "end": 5, + "abridged": false + } + } + }, + "11": { + "benchmarks": { + "blink_perf.svg": { + "begin": 5, "abridged": false }, "blink_perf.webaudio": { "abridged": false }, "blink_perf.webcodecs": { + "end": 5, + "abridged": false + } + } + }, + "12": { + "benchmarks": { + "blink_perf.webcodecs": { + "begin": 5, "abridged": false }, "blink_perf.webgl": { @@ -137,15 +167,15 @@ "abridged": false }, "desktop_ui": { - "end": 13, + "end": 19, "abridged": false } } }, - "10": { + "13": { "benchmarks": { "desktop_ui": { - "begin": 13, + "begin": 19, "abridged": false }, "dummy_benchmark.noisy_benchmark_1": { @@ -164,48 +194,48 @@ "abridged": false }, "loading.desktop": { - "end": 14, - "abridged": false - } - } - }, - "11": { - "benchmarks": { - "loading.desktop": { - "begin": 14, - "end": 55, - "abridged": false - } - } - }, - "12": { - "benchmarks": { - "loading.desktop": { - "begin": 55, - "end": 96, - "abridged": false - } - } - }, - "13": { - "benchmarks": { - "loading.desktop": { - "begin": 96, - "abridged": false - }, - "media.desktop": { - "abridged": false - }, - "memory.desktop": { - "end": 8, + "end": 20, "abridged": false } } }, "14": { "benchmarks": { + "loading.desktop": { + "begin": 20, + "end": 54, + "abridged": false + } + } + }, + "15": { + "benchmarks": { + "loading.desktop": { + "begin": 54, + "end": 87, + "abridged": false + } + } + }, + "16": { + "benchmarks": { + "loading.desktop": { + "begin": 87, + "abridged": false + }, + "media.desktop": { + "end": 17, + "abridged": false + } + } + }, + "17": { + "benchmarks": { + "media.desktop": { + "begin": 17, + "abridged": false + }, "memory.desktop": { - "begin": 8, "abridged": false }, "octane": { @@ -213,48 +243,16 @@ }, "power.desktop": { "abridged": false - }, - "rasterize_and_record_micro.top_25": { - "end": 24, - "abridged": false - } - } - }, - "15": { - "benchmarks": { - "rasterize_and_record_micro.top_25": { - "begin": 24, - "abridged": false - }, - "rendering.desktop": { - "end": 40, - "abridged": false - } - } - }, - "16": { - "benchmarks": { - "rendering.desktop": { - "begin": 40, - "end": 81, - "abridged": false - } - } - }, - "17": { - "benchmarks": { - "rendering.desktop": { - "begin": 81, - "end": 122, - "abridged": false } } }, "18": { "benchmarks": { + "rasterize_and_record_micro.top_25": { + "abridged": false + }, "rendering.desktop": { - "begin": 122, - "end": 163, + "end": 9, "abridged": false } } @@ -262,8 +260,8 @@ "19": { "benchmarks": { "rendering.desktop": { - "begin": 163, - "end": 204, + "begin": 9, + "end": 42, "abridged": false } } @@ -271,8 +269,8 @@ "20": { "benchmarks": { "rendering.desktop": { - "begin": 204, - "end": 245, + "begin": 42, + "end": 76, "abridged": false } } @@ -280,8 +278,8 @@ "21": { "benchmarks": { "rendering.desktop": { - "begin": 245, - "end": 286, + "begin": 76, + "end": 109, "abridged": false } } @@ -289,8 +287,8 @@ "22": { "benchmarks": { "rendering.desktop": { - "begin": 286, - "end": 328, + "begin": 109, + "end": 143, "abridged": false } } @@ -298,73 +296,55 @@ "23": { "benchmarks": { "rendering.desktop": { - "begin": 328, - "abridged": false - }, - "rendering.desktop.notracing": { - "end": 40, + "begin": 143, + "end": 176, "abridged": false } } }, "24": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 40, - "end": 82, + "rendering.desktop": { + "begin": 176, + "end": 210, "abridged": false } } }, "25": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 82, - "end": 123, + "rendering.desktop": { + "begin": 210, + "end": 243, "abridged": false } } }, "26": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 123, - "end": 165, + "rendering.desktop": { + "begin": 243, + "end": 277, "abridged": false } } }, "27": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 165, - "end": 206, + "rendering.desktop": { + "begin": 277, + "end": 310, "abridged": false } } }, "28": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 206, - "end": 248, + "rendering.desktop": { + "begin": 310, "abridged": false - } - } - }, - "29": { - "benchmarks": { + }, "rendering.desktop.notracing": { - "begin": 248, - "end": 289, - "abridged": false - } - } - }, - "30": { - "benchmarks": { - "rendering.desktop.notracing": { - "begin": 289, "abridged": false }, "speedometer": { @@ -383,7 +363,25 @@ "abridged": false }, "system_health.common_desktop": { - "end": 4, + "end": 9, + "abridged": false + } + } + }, + "29": { + "benchmarks": { + "system_health.common_desktop": { + "begin": 9, + "end": 42, + "abridged": false + } + } + }, + "30": { + "benchmarks": { + "system_health.common_desktop": { + "begin": 42, + "end": 76, "abridged": false } } @@ -391,20 +389,20 @@ "31": { "benchmarks": { "system_health.common_desktop": { - "begin": 4, - "end": 45, + "begin": 76, + "abridged": false + }, + "system_health.memory_desktop": { + "end": 28, "abridged": false } } }, "32": { "benchmarks": { - "system_health.common_desktop": { - "begin": 45, - "abridged": false - }, "system_health.memory_desktop": { - "end": 6, + "begin": 28, + "end": 62, "abridged": false } } @@ -412,16 +410,7 @@ "33": { "benchmarks": { "system_health.memory_desktop": { - "begin": 6, - "end": 47, - "abridged": false - } - } - }, - "34": { - "benchmarks": { - "system_health.memory_desktop": { - "begin": 47, + "begin": 62, "abridged": false }, "system_health.pcscan": { @@ -431,34 +420,43 @@ "abridged": false }, "tracing.tracing_with_background_memory_infra": { - "end": 6, + "abridged": false + }, + "v8.browsing_desktop": { + "end": 3, + "abridged": false + } + } + }, + "34": { + "benchmarks": { + "v8.browsing_desktop": { + "begin": 3, + "abridged": false + }, + "v8.browsing_desktop-future": { + "end": 8, "abridged": false } } }, "35": { "benchmarks": { - "tracing.tracing_with_background_memory_infra": { - "begin": 6, - "abridged": false - }, - "v8.browsing_desktop": { - "abridged": false - }, "v8.browsing_desktop-future": { - "end": 9, + "begin": 8, + "abridged": false + }, + "v8.runtime_stats.top_25": { + "end": 12, "abridged": false } } }, "36": { "benchmarks": { - "v8.browsing_desktop-future": { - "begin": 9, - "abridged": false - }, "v8.runtime_stats.top_25": { - "end": 22, + "begin": 12, + "end": 46, "abridged": false } } @@ -466,8 +464,8 @@ "37": { "benchmarks": { "v8.runtime_stats.top_25": { - "begin": 22, - "end": 63, + "begin": 46, + "end": 79, "abridged": false } } @@ -475,8 +473,8 @@ "38": { "benchmarks": { "v8.runtime_stats.top_25": { - "begin": 63, - "end": 105, + "begin": 79, + "end": 113, "abridged": false } } @@ -484,7 +482,7 @@ "39": { "benchmarks": { "v8.runtime_stats.top_25": { - "begin": 105, + "begin": 113, "abridged": false }, "wasmpspdfkit": { @@ -496,50 +494,50 @@ } }, "extra_infos": { - "num_stories": 1649, - "predicted_min_shard_time": 410, + "num_stories": 1335, + "predicted_min_shard_time": 330, "predicted_min_shard_index": 0, - "predicted_max_shard_time": 420, - "predicted_max_shard_index": 22, - "shard #0": 410, - "shard #1": 410, - "shard #2": 410, - "shard #3": 410, - "shard #4": 410, - "shard #5": 410, - "shard #6": 410, - "shard #7": 410, - "shard #8": 410, - "shard #9": 410, - "shard #10": 410, - "shard #11": 410, - "shard #12": 410, - "shard #13": 410, - "shard #14": 410, - "shard #15": 410, - "shard #16": 410, - "shard #17": 410, - "shard #18": 410, - "shard #19": 410, - "shard #20": 410, - "shard #21": 410, - "shard #22": 420, - "shard #23": 410, - "shard #24": 420, - "shard #25": 410, - "shard #26": 420, - "shard #27": 410, - "shard #28": 420, - "shard #29": 410, - "shard #30": 420, - "shard #31": 410, - "shard #32": 420, - "shard #33": 410, - "shard #34": 420, - "shard #35": 410, - "shard #36": 420, - "shard #37": 410, - "shard #38": 420, - "shard #39": 410 + "predicted_max_shard_time": 340, + "predicted_max_shard_index": 10, + "shard #0": 330, + "shard #1": 330, + "shard #2": 330, + "shard #3": 330, + "shard #4": 330, + "shard #5": 330, + "shard #6": 330, + "shard #7": 330, + "shard #8": 330, + "shard #9": 330, + "shard #10": 340, + "shard #11": 330, + "shard #12": 340, + "shard #13": 330, + "shard #14": 340, + "shard #15": 330, + "shard #16": 340, + "shard #17": 330, + "shard #18": 340, + "shard #19": 330, + "shard #20": 340, + "shard #21": 330, + "shard #22": 340, + "shard #23": 330, + "shard #24": 340, + "shard #25": 330, + "shard #26": 340, + "shard #27": 330, + "shard #28": 340, + "shard #29": 330, + "shard #30": 340, + "shard #31": 330, + "shard #32": 340, + "shard #33": 330, + "shard #34": 340, + "shard #35": 330, + "shard #36": 340, + "shard #37": 330, + "shard #38": 340, + "shard #39": 330 } } \ No newline at end of file
diff --git a/tools/perf/core/shard_maps/win-10_laptop_low_end-perf_map.json b/tools/perf/core/shard_maps/win-10_laptop_low_end-perf_map.json index 716f856..8e2691d7 100644 --- a/tools/perf/core/shard_maps/win-10_laptop_low_end-perf_map.json +++ b/tools/perf/core/shard_maps/win-10_laptop_low_end-perf_map.json
@@ -1,10 +1,14 @@ { "0": { "benchmarks": { + "ad_frames.fencedframe": { + "abridged": false + }, "blink_perf.accessibility": { "abridged": false }, "blink_perf.bindings": { + "end": 46, "abridged": false }, "jetstream2": { @@ -17,14 +21,12 @@ }, "1": { "benchmarks": { + "blink_perf.bindings": { + "begin": 46, + "abridged": false + }, "blink_perf.css": { - "abridged": false - }, - "blink_perf.dom": { - "abridged": false - }, - "blink_perf.events": { - "end": 1, + "end": 56, "abridged": false }, "jetstream2": { @@ -37,15 +39,21 @@ }, "2": { "benchmarks": { + "blink_perf.css": { + "begin": 56, + "abridged": false + }, + "blink_perf.dom": { + "abridged": false + }, "blink_perf.events": { - "begin": 1, "abridged": false }, "blink_perf.image_decoder": { "abridged": false }, "blink_perf.layout": { - "end": 87, + "end": 46, "abridged": false }, "jetstream2": { @@ -59,7 +67,7 @@ "3": { "benchmarks": { "blink_perf.layout": { - "begin": 87, + "begin": 46, "abridged": false }, "blink_perf.owp_storage": { @@ -69,10 +77,7 @@ "abridged": false }, "blink_perf.parser": { - "abridged": false - }, - "blink_perf.shadow_dom": { - "end": 12, + "end": 8, "abridged": false }, "jetstream2": { @@ -85,17 +90,17 @@ }, "4": { "benchmarks": { + "blink_perf.parser": { + "begin": 8, + "abridged": false + }, "blink_perf.shadow_dom": { - "begin": 12, "abridged": false }, "blink_perf.svg": { "abridged": false }, "blink_perf.webaudio": { - "abridged": false - }, - "blink_perf.webcodecs": { "end": 1, "abridged": false }, @@ -109,10 +114,13 @@ }, "5": { "benchmarks": { - "blink_perf.webcodecs": { + "blink_perf.webaudio": { "begin": 1, "abridged": false }, + "blink_perf.webcodecs": { + "abridged": false + }, "blink_perf.webgl": { "abridged": false }, @@ -126,7 +134,7 @@ "abridged": false }, "desktop_ui": { - "end": 28, + "end": 7, "abridged": false }, "speedometer2": { @@ -137,7 +145,7 @@ "6": { "benchmarks": { "desktop_ui": { - "begin": 28, + "begin": 7, "abridged": false }, "dummy_benchmark.noisy_benchmark_1": { @@ -156,7 +164,7 @@ "abridged": false }, "loading.desktop": { - "end": 10, + "end": 4, "abridged": false }, "speedometer2": { @@ -167,8 +175,8 @@ "7": { "benchmarks": { "loading.desktop": { - "begin": 10, - "end": 28, + "begin": 4, + "end": 22, "abridged": false }, "speedometer2": { @@ -179,8 +187,8 @@ "8": { "benchmarks": { "loading.desktop": { - "begin": 28, - "end": 43, + "begin": 22, + "end": 37, "abridged": false }, "speedometer2": { @@ -191,8 +199,8 @@ "9": { "benchmarks": { "loading.desktop": { - "begin": 43, - "end": 57, + "begin": 37, + "end": 51, "abridged": false }, "speedometer2": { @@ -203,8 +211,8 @@ "10": { "benchmarks": { "loading.desktop": { - "begin": 57, - "end": 70, + "begin": 51, + "end": 64, "abridged": false }, "speedometer2": { @@ -215,8 +223,8 @@ "11": { "benchmarks": { "loading.desktop": { - "begin": 70, - "end": 82, + "begin": 64, + "end": 75, "abridged": false }, "speedometer2": { @@ -227,8 +235,8 @@ "12": { "benchmarks": { "loading.desktop": { - "begin": 82, - "end": 98, + "begin": 75, + "end": 88, "abridged": false }, "speedometer2": { @@ -239,11 +247,8 @@ "13": { "benchmarks": { "loading.desktop": { - "begin": 98, - "abridged": false - }, - "media.desktop": { - "end": 19, + "begin": 88, + "end": 102, "abridged": false }, "speedometer2": { @@ -253,17 +258,14 @@ }, "14": { "benchmarks": { + "loading.desktop": { + "begin": 102, + "abridged": false + }, "media.desktop": { - "begin": 19, "abridged": false }, "memory.desktop": { - "abridged": false - }, - "octane": { - "abridged": false - }, - "power.desktop": { "end": 2, "abridged": false }, @@ -274,15 +276,15 @@ }, "15": { "benchmarks": { - "power.desktop": { + "memory.desktop": { "begin": 2, "abridged": false }, - "rasterize_and_record_micro.top_25": { + "octane": { "abridged": false }, - "rendering.desktop": { - "end": 12, + "power.desktop": { + "end": 7, "abridged": false }, "speedometer2": { @@ -292,9 +294,15 @@ }, "16": { "benchmarks": { + "power.desktop": { + "begin": 7, + "abridged": false + }, + "rasterize_and_record_micro.top_25": { + "abridged": false + }, "rendering.desktop": { - "begin": 12, - "end": 53, + "end": 16, "abridged": false }, "speedometer2": { @@ -305,8 +313,8 @@ "17": { "benchmarks": { "rendering.desktop": { - "begin": 53, - "end": 89, + "begin": 16, + "end": 55, "abridged": false }, "speedometer2": { @@ -317,8 +325,8 @@ "18": { "benchmarks": { "rendering.desktop": { - "begin": 89, - "end": 113, + "begin": 55, + "end": 89, "abridged": false }, "speedometer2": { @@ -329,8 +337,8 @@ "19": { "benchmarks": { "rendering.desktop": { - "begin": 113, - "end": 150, + "begin": 89, + "end": 112, "abridged": false }, "speedometer2": { @@ -341,8 +349,8 @@ "20": { "benchmarks": { "rendering.desktop": { - "begin": 150, - "end": 174, + "begin": 112, + "end": 150, "abridged": false } } @@ -350,8 +358,8 @@ "21": { "benchmarks": { "rendering.desktop": { - "begin": 174, - "end": 212, + "begin": 150, + "end": 173, "abridged": false } } @@ -359,8 +367,8 @@ "22": { "benchmarks": { "rendering.desktop": { - "begin": 212, - "end": 250, + "begin": 173, + "end": 207, "abridged": false } } @@ -368,8 +376,8 @@ "23": { "benchmarks": { "rendering.desktop": { - "begin": 250, - "end": 285, + "begin": 207, + "end": 244, "abridged": false } } @@ -377,28 +385,28 @@ "24": { "benchmarks": { "rendering.desktop": { - "begin": 285, - "abridged": false - }, - "rendering.desktop.notracing": { - "end": 28, + "begin": 244, + "end": 277, "abridged": false } } }, "25": { "benchmarks": { - "rendering.desktop.notracing": { - "begin": 28, - "end": 193, + "rendering.desktop": { + "begin": 277, + "end": 320, "abridged": false } } }, "26": { "benchmarks": { + "rendering.desktop": { + "begin": 320, + "abridged": false + }, "rendering.desktop.notracing": { - "begin": 193, "abridged": false }, "speedometer": { @@ -412,16 +420,21 @@ }, "speedometer2-future": { "abridged": false + }, + "speedometer2-pcscan": { + "abridged": false + }, + "system_health.common_desktop": { + "end": 20, + "abridged": false } } }, "27": { "benchmarks": { - "speedometer2-pcscan": { - "abridged": false - }, "system_health.common_desktop": { - "end": 43, + "begin": 20, + "end": 70, "abridged": false } } @@ -429,7 +442,11 @@ "28": { "benchmarks": { "system_health.common_desktop": { - "begin": 43, + "begin": 70, + "abridged": false + }, + "system_health.memory_desktop": { + "end": 9, "abridged": false } } @@ -437,7 +454,8 @@ "29": { "benchmarks": { "system_health.memory_desktop": { - "end": 20, + "begin": 9, + "end": 27, "abridged": false } } @@ -445,8 +463,8 @@ "30": { "benchmarks": { "system_health.memory_desktop": { - "begin": 20, - "end": 45, + "begin": 27, + "end": 53, "abridged": false } } @@ -454,8 +472,8 @@ "31": { "benchmarks": { "system_health.memory_desktop": { - "begin": 45, - "end": 64, + "begin": 53, + "end": 65, "abridged": false } } @@ -463,8 +481,8 @@ "32": { "benchmarks": { "system_health.memory_desktop": { - "begin": 64, - "end": 73, + "begin": 65, + "end": 76, "abridged": false } } @@ -472,7 +490,7 @@ "33": { "benchmarks": { "system_health.memory_desktop": { - "begin": 73, + "begin": 76, "abridged": false }, "system_health.pcscan": { @@ -485,7 +503,7 @@ "abridged": false }, "v8.browsing_desktop": { - "end": 9, + "end": 13, "abridged": false } } @@ -493,8 +511,8 @@ "34": { "benchmarks": { "v8.browsing_desktop": { - "begin": 9, - "end": 26, + "begin": 13, + "end": 28, "abridged": false } } @@ -502,11 +520,11 @@ "35": { "benchmarks": { "v8.browsing_desktop": { - "begin": 26, + "begin": 28, "abridged": false }, "v8.browsing_desktop-future": { - "end": 19, + "end": 20, "abridged": false } } @@ -514,11 +532,11 @@ "36": { "benchmarks": { "v8.browsing_desktop-future": { - "begin": 19, + "begin": 20, "abridged": false }, "v8.runtime_stats.top_25": { - "end": 11, + "end": 14, "abridged": false } } @@ -526,8 +544,8 @@ "37": { "benchmarks": { "v8.runtime_stats.top_25": { - "begin": 11, - "end": 50, + "begin": 14, + "end": 52, "abridged": false } } @@ -535,8 +553,8 @@ "38": { "benchmarks": { "v8.runtime_stats.top_25": { - "begin": 50, - "end": 89, + "begin": 52, + "end": 90, "abridged": false } } @@ -544,7 +562,7 @@ "39": { "benchmarks": { "v8.runtime_stats.top_25": { - "begin": 89, + "begin": 90, "abridged": false }, "wasmpspdfkit": { @@ -556,50 +574,50 @@ } }, "extra_infos": { - "num_stories": 1672, - "predicted_min_shard_time": 1548.0, + "num_stories": 1358, + "predicted_min_shard_time": 1377.0, "predicted_min_shard_index": 31, - "predicted_max_shard_time": 2121.0, + "predicted_max_shard_time": 2080.0, "predicted_max_shard_index": 39, - "shard #0": 1645.0, - "shard #1": 1645.0, - "shard #2": 1648.0, - "shard #3": 1705.0, - "shard #4": 1646.0, - "shard #5": 1657.0, - "shard #6": 1666.0, - "shard #7": 1700.0, - "shard #8": 1600.0, - "shard #9": 1638.0, - "shard #10": 1624.0, - "shard #11": 1664.0, - "shard #12": 1702.0, - "shard #13": 1636.0, - "shard #14": 1663.0, - "shard #15": 1631.0, - "shard #16": 1649.0, - "shard #17": 1622.0, - "shard #18": 1632.0, - "shard #19": 1627.0, - "shard #20": 1657.0, - "shard #21": 1649.0, - "shard #22": 1651.0, - "shard #23": 1684.0, - "shard #24": 1646.0, - "shard #25": 1650, - "shard #26": 1711.0, - "shard #27": 1631.0, - "shard #28": 1627.0, - "shard #29": 1686.0, - "shard #30": 1608.0, - "shard #31": 1548.0, - "shard #32": 1674.0, - "shard #33": 1665.0, - "shard #34": 1634.0, - "shard #35": 1600.0, - "shard #36": 1671.0, - "shard #37": 1661.0, - "shard #38": 1652.0, - "shard #39": 2121.0 + "shard #0": 1569.0, + "shard #1": 1559.0, + "shard #2": 1558.0, + "shard #3": 1559.0, + "shard #4": 1537.0, + "shard #5": 1551.0, + "shard #6": 1573.0, + "shard #7": 1520.0, + "shard #8": 1598.0, + "shard #9": 1566.0, + "shard #10": 1566.0, + "shard #11": 1548.0, + "shard #12": 1608.0, + "shard #13": 1550.0, + "shard #14": 1530.0, + "shard #15": 1552.0, + "shard #16": 1564.0, + "shard #17": 1555.0, + "shard #18": 1552.0, + "shard #19": 1593.0, + "shard #20": 1524.0, + "shard #21": 1553.0, + "shard #22": 1557.0, + "shard #23": 1561.0, + "shard #24": 1556.0, + "shard #25": 1554.0, + "shard #26": 1585.0, + "shard #27": 1523.0, + "shard #28": 1602.0, + "shard #29": 1557.0, + "shard #30": 1557.0, + "shard #31": 1377.0, + "shard #32": 1575.0, + "shard #33": 1575.0, + "shard #34": 1558.0, + "shard #35": 1626.0, + "shard #36": 1592.0, + "shard #37": 1623.0, + "shard #38": 1605.0, + "shard #39": 2080.0 } } \ No newline at end of file
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn index 965e671e..d489a3d 100644 --- a/ui/android/BUILD.gn +++ b/ui/android/BUILD.gn
@@ -140,7 +140,7 @@ "java/res/color/default_text_color_secondary_list_baseline.xml", "java/res/color/filled_button_bg.xml", "java/res/color/filled_button_ripple_color.xml", - "java/res/color/text_button_ripple_color.xml", + "java/res/color/text_button_ripple_color_list_baseline.xml", "java/res/drawable-hdpi/btn_close.png", "java/res/drawable-hdpi/ic_expand_less_black_24dp.png", "java/res/drawable-hdpi/ic_expand_more_black_24dp.png",
diff --git a/ui/android/java/res/color/text_button_ripple_color.xml b/ui/android/java/res/color/text_button_ripple_color_list_baseline.xml similarity index 100% rename from ui/android/java/res/color/text_button_ripple_color.xml rename to ui/android/java/res/color/text_button_ripple_color_list_baseline.xml
diff --git a/ui/android/java/res/values-v17/styles.xml b/ui/android/java/res/values-v17/styles.xml index 018db0c..b4f57c3 100644 --- a/ui/android/java/res/values-v17/styles.xml +++ b/ui/android/java/res/values-v17/styles.xml
@@ -52,7 +52,12 @@ <item name="android:textAppearance">@style/TextAppearance.Button.Text.Blue</item> <item name="buttonTextColor">?attr/globalTextButtonTextColor</item> <item name="buttonColor">@android:color/transparent</item> - <item name="rippleColor">@color/text_button_ripple_color</item> + <!-- + If ?attr/globalTextButtonRippleColor isn't defined in the theme, ButtonCompat will fall + back to a blue ripple color for buttons with transparent background and a white one for + the buttons with a solid background. + --> + <item name="rippleColor">?attr/globalTextButtonRippleColor</item> <item name="buttonRaised">false</item> </style> <style name="OutlinedButton" parent="TextButton" tools:ignore="UnusedResources">
diff --git a/ui/android/java/res/values/attrs.xml b/ui/android/java/res/values/attrs.xml index a1f15e3..7f1f806 100644 --- a/ui/android/java/res/values/attrs.xml +++ b/ui/android/java/res/values/attrs.xml
@@ -33,13 +33,14 @@ </declare-styleable> <!-- The attributes prefixed with 'global' are used to control the button, link and URL colors - throughout the app. They are defined in ThemeOverlay.DynamicButtons and are applied - conditionally to the activity theme. This enables us to toggle dynamic colors for the - mentioned UI elements using a feature flag. These attributes may not be set in the themes, - so the code dealing with them should handle their absence. --> + throughout the app. They are defined in ThemeOverlay.DynamicButtons and are applied to the + activity theme. This enables us to apply dynamic colors to the mentioned UI elements. These + attributes may not be set in the themes, so the code dealing with them should handle their + absence. --> <attr name="globalFilledButtonBgColor" format="color"/> <attr name="globalFilledButtonTextColor" format="reference"/> <attr name="globalTextButtonTextColor" format="reference"/> + <attr name="globalTextButtonRippleColor" format="color"/> <attr name="globalOutlinedButtonBorderColor" format="color"/> <attr name="globalLinkTextColor" format="color"/> <attr name="globalClickableSpanColor" format="color"/>
diff --git a/ui/android/java/src/org/chromium/ui/widget/ButtonCompat.java b/ui/android/java/src/org/chromium/ui/widget/ButtonCompat.java index ab74448..2584f58 100644 --- a/ui/android/java/src/org/chromium/ui/widget/ButtonCompat.java +++ b/ui/android/java/src/org/chromium/ui/widget/ButtonCompat.java
@@ -9,6 +9,7 @@ import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; +import android.graphics.Color; import android.util.AttributeSet; import android.view.ContextThemeWrapper; @@ -67,8 +68,18 @@ attrs, R.styleable.ButtonCompat, android.R.attr.buttonStyle, 0); int buttonColorId = a.getResourceId( R.styleable.ButtonCompat_buttonColor, R.color.blue_when_enabled_list); - int rippleColorId = a.getResourceId( - R.styleable.ButtonCompat_rippleColor, R.color.filled_button_ripple_color); + + int rippleColorId = a.getResourceId(R.styleable.ButtonCompat_rippleColor, -1); + if (rippleColorId == -1) { + // If we can't resolve rippleColor, e.g. we're provided an attr that's not available in + // the theme, we'll use a fallback color based on the button color. A transparent color + // means a text button, which should have a blue ripple while a filled button should + // have a white ripple. + boolean isBgTransparent = getContext().getColor(buttonColorId) == Color.TRANSPARENT; + rippleColorId = isBgTransparent ? R.color.text_button_ripple_color_list_baseline + : R.color.filled_button_ripple_color; + } + int borderColorId = a.getResourceId(R.styleable.ButtonCompat_borderColor, android.R.color.transparent); int borderWidthId = a.getResourceId(R.styleable.ButtonCompat_borderWidth,
diff --git a/ui/android/resources/resource_manager.h b/ui/android/resources/resource_manager.h index 9dc4fa86..bff2565 100644 --- a/ui/android/resources/resource_manager.h +++ b/ui/android/resources/resource_manager.h
@@ -41,10 +41,16 @@ virtual Resource* GetResource(AndroidResourceType res_type, int res_id) = 0; // Return a handle to a static resource specified by |res_id| that has a tint - // of |tint_color| applied to it. + // of |tint_color| applied to it. Does not retain the alpha of the tint color. virtual Resource* GetStaticResourceWithTint(int res_id, SkColor tint_color) = 0; + // Return a handle to a static resource specified by |res_id| that has a tint + // of |tint_color| applied to it. + virtual Resource* GetStaticResourceWithTint(int res_id, + SkColor tint_color, + bool preserve_color_alpha) = 0; + // Trigger asynchronous loading of the resource specified by |res_type| and // |res_id|, if it has not yet been loaded. virtual void PreloadResource(AndroidResourceType res_type, int res_id) = 0;
diff --git a/ui/android/resources/resource_manager_impl.cc b/ui/android/resources/resource_manager_impl.cc index bab86ad..ac65593 100644 --- a/ui/android/resources/resource_manager_impl.cc +++ b/ui/android/resources/resource_manager_impl.cc
@@ -136,6 +136,13 @@ Resource* ResourceManagerImpl::GetStaticResourceWithTint(int res_id, SkColor tint_color) { + return GetStaticResourceWithTint(res_id, tint_color, false); +} + +Resource* ResourceManagerImpl::GetStaticResourceWithTint( + int res_id, + SkColor tint_color, + bool preserve_color_alpha) { if (tinted_resources_.find(tint_color) == tinted_resources_.end()) { tinted_resources_[tint_color] = std::make_unique<ResourceMap>(); } @@ -162,13 +169,16 @@ SkCanvas canvas(tinted_bitmap); canvas.clear(SK_ColorTRANSPARENT); - // Build a color filter to use on the base resource. This filter multiplies - // the RGB components by the components of the new color but retains the - // alpha of the original image. + // Build a color filter to use on the base resource. This filter ignores + // the original image's RGB components, instead using the components of the + // new color. The alpha of the original image will be conditionally preserved + // based on preserve_color_alpha. + float alpha_multiplier = + preserve_color_alpha ? SkColorGetA(tint_color) * (1.0f / 255) : 1; float color_matrix[20] = {0, 0, 0, 0, SkColorGetR(tint_color) * (1.0f / 255), 0, 0, 0, 0, SkColorGetG(tint_color) * (1.0f / 255), 0, 0, 0, 0, SkColorGetB(tint_color) * (1.0f / 255), - 0, 0, 0, 1, 0}; + 0, 0, 0, alpha_multiplier, 0}; SkPaint color_filter; color_filter.setColorFilter(SkColorFilters::Matrix(color_matrix));
diff --git a/ui/android/resources/resource_manager_impl.h b/ui/android/resources/resource_manager_impl.h index e933a03..d5a1bb8 100644 --- a/ui/android/resources/resource_manager_impl.h +++ b/ui/android/resources/resource_manager_impl.h
@@ -45,6 +45,9 @@ Resource* GetResource(AndroidResourceType res_type, int res_id) override; Resource* GetStaticResourceWithTint( int res_id, SkColor tint_color) override; + Resource* GetStaticResourceWithTint(int res_id, + SkColor tint_color, + bool preserve_color_alpha) override; void PreloadResource(AndroidResourceType res_type, int res_id) override; void OnFrameUpdatesFinished() override;
diff --git a/ui/display/fake/fake_display_snapshot.cc b/ui/display/fake/fake_display_snapshot.cc index 03d7b37..5b55a40 100644 --- a/ui/display/fake/fake_display_snapshot.cc +++ b/ui/display/fake/fake_display_snapshot.cc
@@ -172,7 +172,8 @@ has_color_correction_matrix_, color_correction_in_linear_space_, name_, std::move(modes_), current_mode_, native_mode_, product_code_, maximum_cursor_size_, color_space_, bits_per_channel_, - hdr_static_metadata_); + hdr_static_metadata_, variable_refresh_rate_state_, + vertical_display_range_limits_); } Builder& Builder::SetId(int64_t id) { @@ -314,6 +315,18 @@ return *this; } +Builder& Builder::SetVariableRefreshRateState( + VariableRefreshRateState variable_refresh_rate_state) { + variable_refresh_rate_state_ = variable_refresh_rate_state; + return *this; +} + +Builder& Builder::SetVerticalDisplayRangeLimits( + const absl::optional<gfx::Range>& vertical_display_range_limits) { + vertical_display_range_limits_ = vertical_display_range_limits; + return *this; +} + const DisplayMode* Builder::AddOrFindDisplayMode(const gfx::Size& size) { for (auto& mode : modes_) { if (mode->size() == size) @@ -363,7 +376,9 @@ const gfx::Size& maximum_cursor_size, const gfx::ColorSpace& color_space, uint32_t bits_per_channel, - const gfx::HDRStaticMetadata& hdr_static_metadata) + const gfx::HDRStaticMetadata& hdr_static_metadata, + VariableRefreshRateState variable_refresh_rate_state, + const absl::optional<gfx::Range>& vertical_display_range_limits) : DisplaySnapshot(display_id, port_display_id, edid_display_id, @@ -390,7 +405,9 @@ native_mode, product_code, 2018 /*year_of_manufacture */, - maximum_cursor_size) {} + maximum_cursor_size, + variable_refresh_rate_state, + vertical_display_range_limits) {} FakeDisplaySnapshot::~FakeDisplaySnapshot() {}
diff --git a/ui/display/fake/fake_display_snapshot.h b/ui/display/fake/fake_display_snapshot.h index d51640d..c8a0b08 100644 --- a/ui/display/fake/fake_display_snapshot.h +++ b/ui/display/fake/fake_display_snapshot.h
@@ -83,6 +83,10 @@ Builder& SetBitsPerChannel(uint32_t bits_per_channel); Builder& SetHDRStaticMetadata( const gfx::HDRStaticMetadata& hdr_static_metadata); + Builder& SetVariableRefreshRateState( + VariableRefreshRateState variable_refresh_rate_state); + Builder& SetVerticalDisplayRangeLimits( + const absl::optional<gfx::Range>& vertical_display_range_limits); private: // Returns a display mode with |size|. If there is no existing mode, insert @@ -115,31 +119,36 @@ gfx::ColorSpace color_space_; uint32_t bits_per_channel_ = 8u; gfx::HDRStaticMetadata hdr_static_metadata_; + VariableRefreshRateState variable_refresh_rate_state_ = kVrrNotCapable; + absl::optional<gfx::Range> vertical_display_range_limits_ = absl::nullopt; }; - FakeDisplaySnapshot(int64_t display_id, - int64_t port_display_id, - int64_t edid_display_id, - uint16_t connector_index, - const gfx::Point& origin, - const gfx::Size& physical_size, - DisplayConnectionType type, - uint64_t base_connector_id, - const std::vector<uint64_t>& path_topology, - bool is_aspect_preserving_scaling, - bool has_overscan, - PrivacyScreenState privacy_screen_state, - bool has_color_correction_matrix, - bool color_correction_in_linear_space, - std::string display_name, - DisplayModeList modes, - const DisplayMode* current_mode, - const DisplayMode* native_mode, - int64_t product_code, - const gfx::Size& maximum_cursor_size, - const gfx::ColorSpace& color_space, - uint32_t bits_per_channel, - const gfx::HDRStaticMetadata& hdr_static_metadata); + FakeDisplaySnapshot( + int64_t display_id, + int64_t port_display_id, + int64_t edid_display_id, + uint16_t connector_index, + const gfx::Point& origin, + const gfx::Size& physical_size, + DisplayConnectionType type, + uint64_t base_connector_id, + const std::vector<uint64_t>& path_topology, + bool is_aspect_preserving_scaling, + bool has_overscan, + PrivacyScreenState privacy_screen_state, + bool has_color_correction_matrix, + bool color_correction_in_linear_space, + std::string display_name, + DisplayModeList modes, + const DisplayMode* current_mode, + const DisplayMode* native_mode, + int64_t product_code, + const gfx::Size& maximum_cursor_size, + const gfx::ColorSpace& color_space, + uint32_t bits_per_channel, + const gfx::HDRStaticMetadata& hdr_static_metadata, + VariableRefreshRateState variable_refresh_rate_state, + const absl::optional<gfx::Range>& vertical_display_range_limits); FakeDisplaySnapshot(const FakeDisplaySnapshot&) = delete; FakeDisplaySnapshot& operator=(const FakeDisplaySnapshot&) = delete;
diff --git a/ui/display/manager/display_change_observer_unittest.cc b/ui/display/manager/display_change_observer_unittest.cc index ab7c8ba..249f4486 100644 --- a/ui/display/manager/display_change_observer_unittest.cc +++ b/ui/display/manager/display_change_observer_unittest.cc
@@ -202,7 +202,8 @@ /*base_connector_id=*/1u, /*path_topology=*/{}, false, false, PrivacyScreenState::kNotSupported, false, false, std::string(), {}, nullptr, nullptr, 0, gfx::Size(), gfx::ColorSpace(), - /*bits_per_channel=*/8u, /*hdr_static_metadata=*/{}); + /*bits_per_channel=*/8u, /*hdr_static_metadata=*/{}, kVrrNotCapable, + absl::nullopt); ManagedDisplayInfo::ManagedDisplayModeList display_modes = DisplayChangeObserver::GetExternalManagedDisplayModeList(
diff --git a/ui/display/mojom/BUILD.gn b/ui/display/mojom/BUILD.gn index 36e69eb..cc02460 100644 --- a/ui/display/mojom/BUILD.gn +++ b/ui/display/mojom/BUILD.gn
@@ -24,6 +24,7 @@ "//mojo/public/mojom/base", "//ui/gfx/geometry/mojom", "//ui/gfx/mojom", + "//ui/gfx/range/mojom", ] shared_cpp_typemaps = [ @@ -96,6 +97,10 @@ mojom = "display.mojom.PrivacyScreenState" cpp = "::display::PrivacyScreenState" }, + { + mojom = "display.mojom.VariableRefreshRateState" + cpp = "::display::VariableRefreshRateState" + }, ] traits_sources = [ "display_constants_mojom_traits.cc" ] traits_headers = [ "display_constants_mojom_traits.h" ]
diff --git a/ui/display/mojom/display_constants.mojom b/ui/display/mojom/display_constants.mojom index 23713a6..42c3cfc 100644 --- a/ui/display/mojom/display_constants.mojom +++ b/ui/display/mojom/display_constants.mojom
@@ -46,3 +46,10 @@ ENABLED_LOCKED = 3, NOT_SUPPORTED = 4 }; + +// Corresponds to display::VariableRefreshRateState +enum VariableRefreshRateState { + kVrrDisabled = 0, + kVrrEnabled = 1, + kVrrNotCapable = 2, +};
diff --git a/ui/display/mojom/display_constants_mojom_traits.cc b/ui/display/mojom/display_constants_mojom_traits.cc index 9c1f3d4c..4a62d91 100644 --- a/ui/display/mojom/display_constants_mojom_traits.cc +++ b/ui/display/mojom/display_constants_mojom_traits.cc
@@ -239,4 +239,40 @@ return false; } +// static +display::mojom::VariableRefreshRateState +EnumTraits<display::mojom::VariableRefreshRateState, + display::VariableRefreshRateState>:: + ToMojom(display::VariableRefreshRateState state) { + switch (state) { + case display::VariableRefreshRateState::kVrrDisabled: + return display::mojom::VariableRefreshRateState::kVrrDisabled; + case display::VariableRefreshRateState::kVrrEnabled: + return display::mojom::VariableRefreshRateState::kVrrEnabled; + case display::VariableRefreshRateState::kVrrNotCapable: + return display::mojom::VariableRefreshRateState::kVrrNotCapable; + } + NOTREACHED(); + return display::mojom::VariableRefreshRateState::kVrrNotCapable; +} + +// static +bool EnumTraits<display::mojom::VariableRefreshRateState, + display::VariableRefreshRateState>:: + FromMojom(display::mojom::VariableRefreshRateState state, + display::VariableRefreshRateState* out) { + switch (state) { + case display::mojom::VariableRefreshRateState::kVrrDisabled: + *out = display::VariableRefreshRateState::kVrrDisabled; + return true; + case display::mojom::VariableRefreshRateState::kVrrEnabled: + *out = display::VariableRefreshRateState::kVrrEnabled; + return true; + case display::mojom::VariableRefreshRateState::kVrrNotCapable: + *out = display::VariableRefreshRateState::kVrrNotCapable; + return true; + } + return false; +} + } // namespace mojo
diff --git a/ui/display/mojom/display_constants_mojom_traits.h b/ui/display/mojom/display_constants_mojom_traits.h index d77d804..6da91d21 100644 --- a/ui/display/mojom/display_constants_mojom_traits.h +++ b/ui/display/mojom/display_constants_mojom_traits.h
@@ -52,6 +52,15 @@ display::PrivacyScreenState* out); }; +template <> +struct EnumTraits<display::mojom::VariableRefreshRateState, + display::VariableRefreshRateState> { + static display::mojom::VariableRefreshRateState ToMojom( + display::VariableRefreshRateState type); + static bool FromMojom(display::mojom::VariableRefreshRateState type, + display::VariableRefreshRateState* out); +}; + } // namespace mojo #endif // UI_DISPLAY_MOJOM_DISPLAY_CONSTANTS_MOJOM_TRAITS_H_
diff --git a/ui/display/mojom/display_mojom_traits_unittest.cc b/ui/display/mojom/display_mojom_traits_unittest.cc index 6308d2a2..b6c4a024 100644 --- a/ui/display/mojom/display_mojom_traits_unittest.cc +++ b/ui/display/mojom/display_mojom_traits_unittest.cc
@@ -279,6 +279,8 @@ const base::FilePath sys_path = base::FilePath::FromUTF8Unsafe("a/cb"); const int64_t product_code = 19; const int32_t year_of_manufacture = 1776; + const VariableRefreshRateState variable_refresh_rate_state = kVrrEnabled; + const gfx::Range vertical_display_range_limits({48, 120}); const DisplayMode display_mode(gfx::Size(13, 11), true, 40.0f); @@ -296,7 +298,8 @@ has_color_correction_matrix, color_correction_in_linear_space, display_color_space, bits_per_channel, hdr_static_metadata, display_name, sys_path, std::move(modes), PanelOrientation::kNormal, edid, current_mode, - native_mode, product_code, year_of_manufacture, maximum_cursor_size); + native_mode, product_code, year_of_manufacture, maximum_cursor_size, + variable_refresh_rate_state, vertical_display_range_limits); std::unique_ptr<DisplaySnapshot> output; SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output); @@ -328,6 +331,8 @@ const base::FilePath sys_path = base::FilePath::FromUTF8Unsafe("z/b"); const int64_t product_code = 9; const int32_t year_of_manufacture = 1776; + const VariableRefreshRateState variable_refresh_rate_state = kVrrEnabled; + const gfx::Range vertical_display_range_limits({48, 120}); const DisplayMode display_mode(gfx::Size(13, 11), true, 50.0f); @@ -345,7 +350,8 @@ has_color_correction_matrix, color_correction_in_linear_space, display_color_space, bits_per_channel, hdr_static_metadata, display_name, sys_path, std::move(modes), PanelOrientation::kNormal, edid, current_mode, - native_mode, product_code, year_of_manufacture, maximum_cursor_size); + native_mode, product_code, year_of_manufacture, maximum_cursor_size, + variable_refresh_rate_state, vertical_display_range_limits); std::unique_ptr<DisplaySnapshot> output; SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output); @@ -377,6 +383,8 @@ const base::FilePath sys_path = base::FilePath::FromUTF8Unsafe("a/cb"); const int64_t product_code = 139; const int32_t year_of_manufacture = 2018; + const VariableRefreshRateState variable_refresh_rate_state = kVrrDisabled; + const gfx::Range vertical_display_range_limits({40, 144}); const DisplayMode display_mode(gfx::Size(1024, 768), false, 60.0f); const DisplayMode display_current_mode(gfx::Size(1440, 900), false, 59.89f); @@ -398,7 +406,8 @@ has_color_correction_matrix, color_correction_in_linear_space, display_color_space, bits_per_channel, hdr_static_metadata, display_name, sys_path, std::move(modes), PanelOrientation::kLeftUp, edid, current_mode, - native_mode, product_code, year_of_manufacture, maximum_cursor_size); + native_mode, product_code, year_of_manufacture, maximum_cursor_size, + variable_refresh_rate_state, vertical_display_range_limits); std::unique_ptr<DisplaySnapshot> output; SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output); @@ -430,6 +439,7 @@ const base::FilePath sys_path; const int64_t product_code = 139; const int32_t year_of_manufacture = 2018; + const VariableRefreshRateState variable_refresh_rate_state = kVrrNotCapable; const DisplayMode display_mode(gfx::Size(2560, 1700), false, 95.96f); @@ -448,7 +458,7 @@ display_color_space, bits_per_channel, hdr_static_metadata, display_name, sys_path, std::move(modes), PanelOrientation::kRightUp, edid, current_mode, native_mode, product_code, year_of_manufacture, - maximum_cursor_size); + maximum_cursor_size, variable_refresh_rate_state, absl::nullopt); std::unique_ptr<DisplaySnapshot> output; SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output);
diff --git a/ui/display/mojom/display_snapshot.mojom b/ui/display/mojom/display_snapshot.mojom index 37aac64..76eec0a 100644 --- a/ui/display/mojom/display_snapshot.mojom +++ b/ui/display/mojom/display_snapshot.mojom
@@ -10,6 +10,7 @@ import "ui/gfx/geometry/mojom/geometry.mojom"; import "ui/gfx/mojom/color_space.mojom"; import "ui/gfx/mojom/hdr_static_metadata.mojom"; +import "ui/gfx/range/mojom/range.mojom"; // Corresponds to display::DisplaySnapshot. struct DisplaySnapshot { @@ -43,4 +44,6 @@ int64 product_code; int32 year_of_manufacture; gfx.mojom.Size maximum_cursor_size; + VariableRefreshRateState variable_refresh_rate_state; + gfx.mojom.Range? vertical_display_range_limits; };
diff --git a/ui/display/mojom/display_snapshot_mojom_traits.cc b/ui/display/mojom/display_snapshot_mojom_traits.cc index fcb8e23..72cf0759 100644 --- a/ui/display/mojom/display_snapshot_mojom_traits.cc +++ b/ui/display/mojom/display_snapshot_mojom_traits.cc
@@ -147,6 +147,14 @@ if (!data.ReadMaximumCursorSize(&maximum_cursor_size)) return false; + display::VariableRefreshRateState variable_refresh_rate_state; + if (!data.ReadVariableRefreshRateState(&variable_refresh_rate_state)) + return false; + + absl::optional<gfx::Range> vertical_display_range_limits; + if (!data.ReadVerticalDisplayRangeLimits(&vertical_display_range_limits)) + return false; + *out = std::make_unique<display::DisplaySnapshot>( data.display_id(), data.port_display_id(), data.edid_display_id(), data.connector_index(), origin, physical_size, type, @@ -157,7 +165,8 @@ data.bits_per_channel(), hdr_static_metadata, display_name, file_path, std::move(modes), panel_orientation, std::move(edid), current_mode, native_mode, data.product_code(), data.year_of_manufacture(), - maximum_cursor_size); + maximum_cursor_size, variable_refresh_rate_state, + vertical_display_range_limits); return true; }
diff --git a/ui/display/mojom/display_snapshot_mojom_traits.h b/ui/display/mojom/display_snapshot_mojom_traits.h index 4bff463..2771de5 100644 --- a/ui/display/mojom/display_snapshot_mojom_traits.h +++ b/ui/display/mojom/display_snapshot_mojom_traits.h
@@ -157,6 +157,16 @@ return snapshot->maximum_cursor_size(); } + static display::VariableRefreshRateState variable_refresh_rate_state( + const std::unique_ptr<display::DisplaySnapshot>& snapshot) { + return snapshot->variable_refresh_rate_state(); + } + + static const absl::optional<gfx::Range>& vertical_display_range_limits( + const std::unique_ptr<display::DisplaySnapshot>& snapshot) { + return snapshot->vertical_display_range_limits(); + } + static bool Read(display::mojom::DisplaySnapshotDataView data, std::unique_ptr<display::DisplaySnapshot>* out); };
diff --git a/ui/display/types/BUILD.gn b/ui/display/types/BUILD.gn index 257fb985..d148b094 100644 --- a/ui/display/types/BUILD.gn +++ b/ui/display/types/BUILD.gn
@@ -28,5 +28,6 @@ "//ui/gfx:color_space", "//ui/gfx:memory_buffer", "//ui/gfx/geometry", + "//ui/gfx/range", ] }
diff --git a/ui/display/types/DEPS b/ui/display/types/DEPS index d58763e..e1edd22 100644 --- a/ui/display/types/DEPS +++ b/ui/display/types/DEPS
@@ -5,4 +5,5 @@ "+ui/gfx/color_space.h", "+ui/gfx/hdr_static_metadata.h", "+ui/gfx/geometry", + "+ui/gfx/range/range.h", ]
diff --git a/ui/display/types/display_constants.h b/ui/display/types/display_constants.h index 8a71d8e..0be24d69 100644 --- a/ui/display/types/display_constants.h +++ b/ui/display/types/display_constants.h
@@ -144,6 +144,13 @@ kSeamlessModeset = 1 << 2, }; +enum VariableRefreshRateState { + kVrrDisabled = 0, + kVrrEnabled = 1, + kVrrNotCapable = 2, + kVrrLast = kVrrNotCapable, +}; + // Defines the float values closest to repeating decimal scale factors. constexpr float kDsf_1_777 = 1.77777779102325439453125f; constexpr float kDsf_2_252 = 2.2522523403167724609375f;
diff --git a/ui/display/types/display_snapshot.cc b/ui/display/types/display_snapshot.cc index c906cb0..97bb96f6 100644 --- a/ui/display/types/display_snapshot.cc +++ b/ui/display/types/display_snapshot.cc
@@ -88,7 +88,9 @@ const DisplayMode* native_mode, int64_t product_code, int32_t year_of_manufacture, - const gfx::Size& maximum_cursor_size) + const gfx::Size& maximum_cursor_size, + VariableRefreshRateState variable_refresh_rate_state, + const absl::optional<gfx::Range>& vertical_display_range_limits) : display_id_(display_id), port_display_id_(port_display_id), edid_display_id_(edid_display_id), @@ -115,11 +117,12 @@ native_mode_(native_mode), product_code_(product_code), year_of_manufacture_(year_of_manufacture), - maximum_cursor_size_(maximum_cursor_size) { + maximum_cursor_size_(maximum_cursor_size), + variable_refresh_rate_state_(variable_refresh_rate_state), + vertical_display_range_limits_(vertical_display_range_limits) { // We must explicitly clear out the bytes that represent the serial number. - const size_t end = - std::min(kSerialNumberBeginingByte + kSerialNumberLengthInBytes, - edid_.size()); + const size_t end = std::min( + kSerialNumberBeginingByte + kSerialNumberLengthInBytes, edid_.size()); for (size_t i = kSerialNumberBeginingByte; i < end; ++i) edid_[i] = 0; } @@ -149,7 +152,8 @@ color_space_, bits_per_channel_, hdr_static_metadata_, display_name_, sys_path_, std::move(clone_modes), panel_orientation_, edid_, cloned_current_mode, cloned_native_mode, product_code_, - year_of_manufacture_, maximum_cursor_size_); + year_of_manufacture_, maximum_cursor_size_, variable_refresh_rate_state_, + vertical_display_range_limits_); } std::string DisplaySnapshot::ToString() const {
diff --git a/ui/display/types/display_snapshot.h b/ui/display/types/display_snapshot.h index fe342d6..4866edb3 100644 --- a/ui/display/types/display_snapshot.h +++ b/ui/display/types/display_snapshot.h
@@ -21,6 +21,7 @@ #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/hdr_static_metadata.h" +#include "ui/gfx/range/range.h" namespace display { @@ -58,7 +59,9 @@ const DisplayMode* native_mode, int64_t product_code, int32_t year_of_manufacture, - const gfx::Size& maximum_cursor_size); + const gfx::Size& maximum_cursor_size, + VariableRefreshRateState variable_refresh_rate_state, + const absl::optional<gfx::Range>& vertical_display_range_limits); DisplaySnapshot(const DisplaySnapshot&) = delete; DisplaySnapshot& operator=(const DisplaySnapshot&) = delete; @@ -109,6 +112,12 @@ int64_t product_code() const { return product_code_; } int32_t year_of_manufacture() const { return year_of_manufacture_; } const gfx::Size& maximum_cursor_size() const { return maximum_cursor_size_; } + VariableRefreshRateState variable_refresh_rate_state() const { + return variable_refresh_rate_state_; + } + const absl::optional<gfx::Range>& vertical_display_range_limits() const { + return vertical_display_range_limits_; + } void add_mode(const DisplayMode* mode) { modes_.push_back(mode->Clone()); } @@ -236,6 +245,12 @@ // Maximum supported cursor size on this display. const gfx::Size maximum_cursor_size_; + + // Whether VRR is enabled, disabled, or not capable on this display. + const VariableRefreshRateState variable_refresh_rate_state_; + // The supported vrefresh frequency range for this display. Omitted if this + // display is not VRR capable. + const absl::optional<gfx::Range> vertical_display_range_limits_; }; } // namespace display
diff --git a/ui/display/util/BUILD.gn b/ui/display/util/BUILD.gn index cf91a3c..33731b1 100644 --- a/ui/display/util/BUILD.gn +++ b/ui/display/util/BUILD.gn
@@ -26,6 +26,7 @@ "//ui/display/types", "//ui/gfx:color_space", "//ui/gfx/geometry", + "//ui/gfx/range", ] if (is_chromeos_ash) {
diff --git a/ui/display/util/edid_parser.cc b/ui/display/util/edid_parser.cc index 0433a9c..0f78c004 100644 --- a/ui/display/util/edid_parser.cc +++ b/ui/display/util/edid_parser.cc
@@ -468,26 +468,29 @@ if (edid[offset] == 0 && edid[offset + 1] == 0 && edid[offset + 2] == 0 && edid[offset + 3] == kDisplayRangeLimitsDescriptor) { // byte 4: Offsets for display range limits - const uint8_t kRateOffset = edid[offset + 4]; + const uint8_t rateOffset = edid[offset + 4]; // bits 7-4: Reserved \0 - if (kRateOffset & 0x10) + if (rateOffset & 0xf0) continue; // bit 3: Horizontal max rate offset (not used) // bit 2: Horizontal min rate offset (not used) // bit 1: Vertical max rate offset - const uint8_t verticalMaxRateOffset = kRateOffset & (1 << 1) ? 255 : 0; + const uint8_t verticalMaxRateOffset = rateOffset & (1 << 1) ? 255 : 0; // bit 0: Vertical min rate offset - const uint8_t verticalMinRateOffset = kRateOffset & (1 << 0) ? 255 : 0; + const uint8_t verticalMinRateOffset = rateOffset & (1 << 0) ? 255 : 0; // bytes 5-8: Rate limits // Each byte must be within [1, 255]. if (edid[offset + 5] == 0 || edid[offset + 6] == 0 || edid[offset + 7] == 0 || edid[offset + 8] == 0) continue; + vertical_display_range_limits_ = absl::make_optional<gfx::Range>(); // byte 5: Min vertical rate in Hz - min_vfreq_ = edid[offset + 5] + verticalMinRateOffset; + vertical_display_range_limits_->set_start(edid[offset + 5] + + verticalMinRateOffset); // byte 6: Max vertical rate in Hz - max_vfreq_ = edid[offset + 6] + verticalMaxRateOffset; + vertical_display_range_limits_->set_end(edid[offset + 6] + + verticalMaxRateOffset); // byte 7: Min horizontal rate in kHz (not used) // byte 8: Max horizontal rate in kHz (not used)
diff --git a/ui/display/util/edid_parser.h b/ui/display/util/edid_parser.h index 1b82695c..b4baa0a1 100644 --- a/ui/display/util/edid_parser.h +++ b/ui/display/util/edid_parser.h
@@ -18,6 +18,7 @@ #include "ui/gfx/color_space.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/hdr_static_metadata.h" +#include "ui/gfx/range/range.h" namespace display { @@ -67,8 +68,9 @@ const absl::optional<gfx::HDRStaticMetadata>& hdr_static_metadata() const { return hdr_static_metadata_; } - const absl::optional<uint16_t>& min_vfreq() const { return min_vfreq_; } - const absl::optional<uint16_t>& max_vfreq() const { return max_vfreq_; } + const absl::optional<gfx::Range>& vertical_display_range_limits() const { + return vertical_display_range_limits_; + } // Returns a 32-bit identifier for this display |manufacturer_id_| and // |product_id_|. uint32_t GetProductCode() const; @@ -138,8 +140,7 @@ base::flat_set<gfx::ColorSpace::PrimaryID> supported_color_primary_ids_; base::flat_set<gfx::ColorSpace::TransferID> supported_color_transfer_ids_; absl::optional<gfx::HDRStaticMetadata> hdr_static_metadata_; - absl::optional<uint16_t> min_vfreq_; - absl::optional<uint16_t> max_vfreq_; + absl::optional<gfx::Range> vertical_display_range_limits_; uint32_t audio_formats_; };
diff --git a/ui/display/util/edid_parser_unittest.cc b/ui/display/util/edid_parser_unittest.cc index e6d6305..720a842 100644 --- a/ui/display/util/edid_parser_unittest.cc +++ b/ui/display/util/edid_parser_unittest.cc
@@ -341,8 +341,7 @@ base::flat_set<gfx::ColorSpace::PrimaryID> supported_color_primary_ids_; base::flat_set<gfx::ColorSpace::TransferID> supported_color_transfer_ids_; absl::optional<gfx::HDRStaticMetadata> hdr_static_metadata_; - absl::optional<uint16_t> min_vfreq; - absl::optional<uint16_t> max_vfreq; + absl::optional<gfx::Range> vertical_display_range_limits_; const unsigned char* edid_blob; size_t edid_blob_length; @@ -369,7 +368,6 @@ {}, absl::nullopt, absl::nullopt, - absl::nullopt, kBadDisplayName, kBadDisplayNameLength}, {0x22f0u, @@ -394,7 +392,6 @@ {}, absl::nullopt, absl::nullopt, - absl::nullopt, kNormalDisplay, kNormalDisplayLength}, {0x22f0u, @@ -419,7 +416,6 @@ {}, absl::nullopt, absl::nullopt, - absl::nullopt, kNoMaxImageSizeDisplay, kNoMaxImageSizeDisplayLength}, {0x22f0u, @@ -444,7 +440,6 @@ {}, absl::nullopt, absl::nullopt, - absl::nullopt, kBlockZeroSerialNumberOnlyDisplay, kBlockZeroSerialNumberOnlyDisplayLength}, {0x22f0u, @@ -469,7 +464,6 @@ {}, absl::nullopt, absl::nullopt, - absl::nullopt, kNoSerialNumberDisplay, kNoSerialNumberDisplayLength}, {0x22f0u, @@ -494,7 +488,6 @@ {}, absl::nullopt, absl::nullopt, - absl::nullopt, kNoWeekOfManufactureDisplay, kNoWeekOfManufactureDisplayLength}, {0x22f0u, @@ -519,7 +512,6 @@ {}, absl::nullopt, absl::nullopt, - absl::nullopt, kModelYearDisplay, kModelYearDisplayLength}, {0x4ca3u, @@ -544,7 +536,6 @@ {}, absl::nullopt, absl::nullopt, - absl::nullopt, kInternalDisplay, kInternalDisplayLength}, {0x4c2du, @@ -568,8 +559,7 @@ {}, {}, absl::nullopt, - 24, - 75, + gfx::Range(24, 75), kOverscanDisplay, kOverscanDisplayLength}, {0x10ACu, @@ -593,8 +583,7 @@ {gfx::ColorSpace::PrimaryID::BT709, gfx::ColorSpace::PrimaryID::SMPTE170M}, {}, absl::nullopt, - 49, - 86, + gfx::Range(49, 86), kMisdetectedDisplay, kMisdetectedDisplayLength}, {0x22f0u, @@ -618,8 +607,7 @@ {}, {}, absl::nullopt, - 48, - 85, + gfx::Range(48, 85), kLP2565A, kLP2565ALength}, {0x22f0u, @@ -643,8 +631,7 @@ {}, {}, absl::nullopt, - 48, - 85, + gfx::Range(48, 85), kLP2565B, kLP2565BLength}, {0x22f0u, @@ -668,8 +655,7 @@ {}, {}, absl::nullopt, - 24, - 60, + gfx::Range(24, 60), kHPz32x, kHPz32xLength}, {0x30E4u, @@ -694,7 +680,6 @@ {}, absl::nullopt, absl::nullopt, - absl::nullopt, kSamus, kSamusLength}, {0x4D10u, @@ -719,7 +704,6 @@ {}, absl::nullopt, absl::nullopt, - absl::nullopt, kEve, kEveLength}, {19501u, @@ -745,8 +729,7 @@ {gfx::ColorSpace::TransferID::BT709, gfx::ColorSpace::TransferID::PQ, gfx::ColorSpace::TransferID::HLG}, absl::make_optional<gfx::HDRStaticMetadata>(603.666, 530.095, 0.00454), - 24, - 75, + gfx::Range(24, 75), kHDRMetadata, kHDRMetadataLength}, @@ -774,7 +757,6 @@ {}, absl::nullopt, absl::nullopt, - absl::nullopt, nullptr, 0u}, }; @@ -842,8 +824,17 @@ epsilon); } - EXPECT_EQ(parser_.min_vfreq(), GetParam().min_vfreq); - EXPECT_EQ(parser_.max_vfreq(), GetParam().max_vfreq); + const absl::optional<gfx::Range> vertical_display_range_limits = + parser_.vertical_display_range_limits(); + EXPECT_EQ(GetParam().vertical_display_range_limits_.has_value(), + vertical_display_range_limits.has_value()); + if (GetParam().vertical_display_range_limits_.has_value() && + vertical_display_range_limits.has_value()) { + EXPECT_EQ(vertical_display_range_limits->start(), + GetParam().vertical_display_range_limits_->start()); + EXPECT_EQ(vertical_display_range_limits->end(), + GetParam().vertical_display_range_limits_->end()); + } } INSTANTIATE_TEST_SUITE_P(All, EDIDParserTest, ValuesIn(kTestCases));
diff --git a/ui/events/x/x11_event_translation.cc b/ui/events/x/x11_event_translation.cc index 0f0ce74..dd8c160 100644 --- a/ui/events/x/x11_event_translation.cc +++ b/ui/events/x/x11_event_translation.cc
@@ -194,6 +194,15 @@ GetFlingDataFromXEvent(xev, &x_offset, &y_offset, &x_offset_ordinal, &y_offset_ordinal, nullptr); } + // When lifting up fingers x_offset and y_offset both have the value 0 + // If this is the case ET_SCROLL_FLING_START needs to be emitted, in order to + // trigger touchpad overscroll navigation gesture. + // x_offset and y_offset should not be manipulated, however, since some X11 + // drivers such as synaptics simulate the fling themselves + if (!x_offset && !y_offset) { + type = ET_SCROLL_FLING_START; + } + auto event = std::make_unique<ScrollEvent>( type, EventLocationFromXEvent(xev), EventTimeFromXEvent(xev), EventFlagsFromXEvent(xev), x_offset, y_offset, x_offset_ordinal, @@ -203,7 +212,8 @@ // We need to filter zero scroll offset here. Because MouseWheelEventQueue // assumes we'll never get a zero scroll offset event and we need delta to // determine which element to scroll on phaseBegan. - return (event->x_offset() != 0.0 || event->y_offset() != 0.0) + return (event->x_offset() != 0.0 || event->y_offset() != 0.0 || + event->type() == ET_SCROLL_FLING_START) ? std::move(event) : nullptr; }
diff --git a/ui/ozone/platform/drm/common/drm_util.cc b/ui/ozone/platform/drm/common/drm_util.cc index 127e5538..2dacd791 100644 --- a/ui/ozone/platform/drm/common/drm_util.cc +++ b/ui/ozone/platform/drm/common/drm_util.cc
@@ -344,10 +344,6 @@ } bool IsVrrCapable(int fd, drmModeConnector* connector) { - if (!features::IsVariableRefreshRateEnabled()) { - return false; - } - ScopedDrmPropertyPtr vrr_capable_property; const int vrr_capable_index = GetDrmProperty( fd, connector, kVrrCapablePropertyName, &vrr_capable_property); @@ -355,10 +351,6 @@ } bool IsVrrEnabled(int fd, drmModeCrtc* crtc) { - if (!features::IsVariableRefreshRateEnabled()) { - return false; - } - ScopedDrmObjectPropertyPtr crtc_props( drmModeObjectGetProperties(fd, crtc->crtc_id, DRM_MODE_OBJECT_CRTC)); ScopedDrmPropertyPtr vrr_enabled_property; @@ -367,6 +359,18 @@ return vrr_enabled_index >= 0 && crtc_props->prop_values[vrr_enabled_index]; } +display::VariableRefreshRateState GetVariableRefreshRateState( + int fd, + HardwareDisplayControllerInfo* info) { + if (!IsVrrCapable(fd, info->connector())) + return display::kVrrNotCapable; + + if (IsVrrEnabled(fd, info->crtc())) + return display::kVrrEnabled; + + return display::kVrrDisabled; +} + HardwareDisplayControllerInfo::HardwareDisplayControllerInfo( ScopedDrmConnectorPtr connector, ScopedDrmCrtcPtr crtc, @@ -562,6 +566,8 @@ const bool color_correction_in_linear_space = has_color_correction_matrix && GetDrmDriverNameFromFd(fd) == "rockchip"; const gfx::Size maximum_cursor_size = GetMaximumCursorSize(fd); + const display::VariableRefreshRateState variable_refresh_rate_state = + GetVariableRefreshRateState(fd, info); std::string display_name; // Make sure the ID contains non index part. @@ -575,6 +581,7 @@ absl::optional<gfx::HDRStaticMetadata> hdr_static_metadata{}; // Active pixels size from the first detailed timing descriptor in the EDID. gfx::Size active_pixel_size; + absl::optional<gfx::Range> vertical_display_range_limits; ScopedDrmPropertyBlobPtr edid_blob( GetDrmPropertyBlob(fd, info->connector(), "EDID")); @@ -602,6 +609,10 @@ base::UmaHistogramCounts100("DrmUtil.CreateDisplaySnapshot.BitsPerChannel", bits_per_channel); hdr_static_metadata = edid_parser.hdr_static_metadata(); + vertical_display_range_limits = + variable_refresh_rate_state == display::kVrrNotCapable + ? absl::nullopt + : edid_parser.vertical_display_range_limits(); } else { VLOG(1) << "Failed to get EDID blob for connector " << info->connector()->connector_id; @@ -619,7 +630,8 @@ has_color_correction_matrix, color_correction_in_linear_space, display_color_space, bits_per_channel, hdr_static_metadata, display_name, sys_path, std::move(modes), panel_orientation, edid, current_mode, - native_mode, product_code, year_of_manufacture, maximum_cursor_size); + native_mode, product_code, year_of_manufacture, maximum_cursor_size, + variable_refresh_rate_state, vertical_display_range_limits); } int GetFourCCFormatForOpaqueFramebuffer(gfx::BufferFormat format) {
diff --git a/ui/ozone/platform/drm/common/drm_util.h b/ui/ozone/platform/drm/common/drm_util.h index 10f7713..05ff0c4 100644 --- a/ui/ozone/platform/drm/common/drm_util.h +++ b/ui/ozone/platform/drm/common/drm_util.h
@@ -163,6 +163,10 @@ bool IsVrrEnabled(int fd, drmModeCrtc* crtc); +display::VariableRefreshRateState GetVariableRefreshRateState( + int fd, + HardwareDisplayControllerInfo* info); + uint64_t GetEnumValueForName(int fd, int property_id, const char* str); std::vector<uint64_t> ParsePathBlob(const drmModePropertyBlobRes& path_blob);
diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn index bf431e7..219c81da 100644 --- a/ui/ozone/platform/wayland/BUILD.gn +++ b/ui/ozone/platform/wayland/BUILD.gn
@@ -521,6 +521,7 @@ deps = [ ":wayland", "//base:base", + "//ui/base/wayland:wayland_display_util", "//ui/ozone:platform", "//ui/ozone:test_support", ] @@ -603,6 +604,7 @@ "//ui/base/dragdrop:types", "//ui/base/dragdrop/mojom", "//ui/base/ime/linux", + "//ui/base/wayland:wayland_display_util", "//ui/events/ozone/layout", "//ui/gfx/linux:test_support", "//ui/ozone:platform",
diff --git a/ui/ozone/platform/wayland/common/wayland_object.cc b/ui/ozone/platform/wayland/common/wayland_object.cc index 1c621e2..54edc6a 100644 --- a/ui/ozone/platform/wayland/common/wayland_object.cc +++ b/ui/ozone/platform/wayland/common/wayland_object.cc
@@ -156,14 +156,6 @@ return true; } -uint32_t CalculateBindVersion(uint32_t impl_version, - uint32_t compositor_version, - int libwayland_version) { - return std::min( - impl_version, - std::min(compositor_version, static_cast<uint32_t>(libwayland_version))); -} - void (*ObjectTraits<wl_cursor_theme>::deleter)(wl_cursor_theme*) = &wl_cursor_theme_destroy;
diff --git a/ui/ozone/platform/wayland/common/wayland_object.h b/ui/ozone/platform/wayland/common/wayland_object.h index 463320e..5f486509 100644 --- a/ui/ozone/platform/wayland/common/wayland_object.h +++ b/ui/ozone/platform/wayland/common/wayland_object.h
@@ -88,19 +88,6 @@ uint32_t min_version, uint32_t max_version); -// Calculates the version of an interface that we want to bind to, given: -// - |impl_version|, the maximum version that we have implemented support for; -// - |compositor_version|, the maximum version advertised by the compositor; and -// - |libwayland_version|, the maximum version that libwayland-client can handle -// (can be accessed using INTERFACE_NAME_interface.version). Binding to a -// version higher than this would lead to an "interface X has no event Y" -// runtime error. This is particularly relevant when building with -// use_system_libwayland = true. -// The result is the minimum of the three versions. -uint32_t CalculateBindVersion(uint32_t impl_version, - uint32_t compositor_version, - int libwayland_version); - } // namespace wl // Puts the forward declaration for struct TYPE and declares the template
diff --git a/ui/ozone/platform/wayland/host/gtk_shell1.cc b/ui/ozone/platform/wayland/host/gtk_shell1.cc index da909fe..041c71b 100644 --- a/ui/ozone/platform/wayland/host/gtk_shell1.cc +++ b/ui/ozone/platform/wayland/host/gtk_shell1.cc
@@ -38,10 +38,8 @@ return; } - auto gtk_shell1 = wl::Bind<::gtk_shell1>( - registry, name, - wl::CalculateBindVersion(version, kMaxVersion, - gtk_shell1_interface.version)); + auto gtk_shell1 = + wl::Bind<::gtk_shell1>(registry, name, std::min(version, kMaxVersion)); if (!gtk_shell1) { LOG(ERROR) << "Failed to bind gtk_shell1"; return;
diff --git a/ui/ozone/platform/wayland/host/surface_augmenter.cc b/ui/ozone/platform/wayland/host/surface_augmenter.cc index c573309..a7769414 100644 --- a/ui/ozone/platform/wayland/host/surface_augmenter.cc +++ b/ui/ozone/platform/wayland/host/surface_augmenter.cc
@@ -35,10 +35,8 @@ return; } - auto augmenter = wl::Bind<surface_augmenter>( - registry, name, - wl::CalculateBindVersion(version, kMaxVersion, - surface_augmenter_interface.version)); + auto augmenter = wl::Bind<surface_augmenter>(registry, name, + std::min(version, kMaxVersion)); if (!augmenter) { LOG(ERROR) << "Failed to bind surface_augmenter"; return;
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.cc b/ui/ozone/platform/wayland/host/wayland_connection.cc index a1e5850..241d80a 100644 --- a/ui/ozone/platform/wayland/host/wayland_connection.cc +++ b/ui/ozone/platform/wayland/host/wayland_connection.cc
@@ -4,19 +4,9 @@ #include "ui/ozone/platform/wayland/host/wayland_connection.h" -#include <alpha-compositing-unstable-v1-client-protocol.h> #include <content-type-v1-client-protocol.h> #include <extended-drag-unstable-v1-client-protocol.h> -#include <keyboard-extension-unstable-v1-client-protocol.h> -#include <keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h> -#include <linux-explicit-synchronization-unstable-v1-client-protocol.h> #include <presentation-time-client-protocol.h> -#include <stylus-unstable-v2-client-protocol.h> -#include <text-input-extension-unstable-v1-client-protocol.h> -#include <text-input-unstable-v1-client-protocol.h> -#include <viewporter-client-protocol.h> -#include <xdg-decoration-unstable-v1-client-protocol.h> -#include <xdg-output-unstable-v1-client-protocol.h> #include <xdg-shell-client-protocol.h> #include <algorithm> @@ -84,7 +74,8 @@ namespace { // The maximum supported versions for a given interface. -// The version bound will be calculated using wl::CalcBindVersion(). +// The version bound will be the minimum of the value and the version +// advertised by the server. constexpr uint32_t kMaxCompositorVersion = 4; constexpr uint32_t kMaxKeyboardExtensionVersion = 2; constexpr uint32_t kMaxXdgShellVersion = 5; @@ -443,9 +434,7 @@ } else if (!connection->compositor_ && strcmp(interface, "wl_compositor") == 0) { connection->compositor_ = wl::Bind<wl_compositor>( - registry, name, - wl::CalculateBindVersion(version, kMaxCompositorVersion, - wl_compositor_interface.version)); + registry, name, std::min(version, kMaxCompositorVersion)); connection->compositor_version_ = version; if (!connection->compositor_) { LOG(ERROR) << "Failed to bind to wl_compositor global"; @@ -460,9 +449,7 @@ } } else if (!connection->shell_ && strcmp(interface, "xdg_wm_base") == 0) { connection->shell_ = wl::Bind<xdg_wm_base>( - registry, name, - wl::CalculateBindVersion(version, kMaxXdgShellVersion, - xdg_wm_base_interface.version)); + registry, name, std::min(version, kMaxXdgShellVersion)); if (!connection->shell_) { LOG(ERROR) << "Failed to bind to xdg_wm_base global"; return; @@ -473,9 +460,7 @@ } else if (!connection->alpha_compositing_ && (strcmp(interface, "zcr_alpha_compositing_v1") == 0)) { connection->alpha_compositing_ = wl::Bind<zcr_alpha_compositing_v1>( - registry, name, - wl::CalculateBindVersion(version, kMaxAlphaCompositingVersion, - zcr_alpha_compositing_v1_interface.version)); + registry, name, std::min(version, kMaxAlphaCompositingVersion)); if (!connection->alpha_compositing_) { LOG(ERROR) << "Failed to bind zcr_alpha_compositing_v1"; return; @@ -485,10 +470,7 @@ 0)) { connection->linux_explicit_synchronization_ = wl::Bind<zwp_linux_explicit_synchronization_v1>( - registry, name, - wl::CalculateBindVersion( - version, kMaxExplicitSyncVersion, - zwp_linux_explicit_synchronization_v1_interface.version)); + registry, name, std::min(version, kMaxExplicitSyncVersion)); if (!connection->linux_explicit_synchronization_) { LOG(ERROR) << "Failed to bind zwp_linux_explicit_synchronization_v1"; return; @@ -496,9 +478,7 @@ } else if (!connection->content_type_manager_v1_ && (strcmp(interface, "wp_content_type_manager_v1") == 0)) { connection->content_type_manager_v1_ = wl::Bind<wp_content_type_manager_v1>( - registry, name, - wl::CalculateBindVersion(version, kMaxWpContentTypeVersion, - wp_content_type_manager_v1_interface.version)); + registry, name, std::min(version, kMaxWpContentTypeVersion)); if (!connection->content_type_manager_v1_) { LOG(ERROR) << "Failed to bind wp_content_type_v1"; return; @@ -506,9 +486,7 @@ } else if (!connection->presentation_ && (strcmp(interface, "wp_presentation") == 0)) { connection->presentation_ = wl::Bind<wp_presentation>( - registry, name, - wl::CalculateBindVersion(version, kMaxWpPresentationVersion, - wp_presentation_interface.version)); + registry, name, std::min(version, kMaxWpPresentationVersion)); if (!connection->presentation_) { LOG(ERROR) << "Failed to bind wp_presentation"; return; @@ -518,9 +496,7 @@ } else if (!connection->viewporter_ && (strcmp(interface, "wp_viewporter") == 0)) { connection->viewporter_ = wl::Bind<wp_viewporter>( - registry, name, - wl::CalculateBindVersion(version, kMaxWpViewporterVersion, - wp_viewporter_interface.version)); + registry, name, std::min(version, kMaxWpViewporterVersion)); if (!connection->viewporter_) { LOG(ERROR) << "Failed to bind wp_viewporter"; return; @@ -528,9 +504,7 @@ } else if (!connection->keyboard_extension_v1_ && strcmp(interface, "zcr_keyboard_extension_v1") == 0) { connection->keyboard_extension_v1_ = wl::Bind<zcr_keyboard_extension_v1>( - registry, name, - wl::CalculateBindVersion(version, kMaxKeyboardExtensionVersion, - zcr_keyboard_extension_v1_interface.version)); + registry, name, std::min(version, kMaxKeyboardExtensionVersion)); if (!connection->keyboard_extension_v1_) { LOG(ERROR) << "Failed to bind zcr_keyboard_extension_v1"; return; @@ -545,9 +519,7 @@ connection->keyboard_shortcuts_inhibit_manager_v1_ = wl::Bind<zwp_keyboard_shortcuts_inhibit_manager_v1>( registry, name, - wl::CalculateBindVersion( - version, kMaxKeyboardShortcutsInhibitManagerVersion, - zwp_keyboard_shortcuts_inhibit_manager_v1_interface.version)); + std::min(version, kMaxKeyboardShortcutsInhibitManagerVersion)); if (!connection->keyboard_shortcuts_inhibit_manager_v1_) { LOG(ERROR) << "Failed to bind zwp_keyboard_shortcuts_inhibit_manager_v1"; return; @@ -555,9 +527,7 @@ } else if (!connection->text_input_manager_v1_ && strcmp(interface, "zwp_text_input_manager_v1") == 0) { connection->text_input_manager_v1_ = wl::Bind<zwp_text_input_manager_v1>( - registry, name, - wl::CalculateBindVersion(version, kMaxTextInputManagerVersion, - zwp_text_input_manager_v1_interface.version)); + registry, name, std::min(version, kMaxTextInputManagerVersion)); if (!connection->text_input_manager_v1_) { LOG(ERROR) << "Failed to bind to zwp_text_input_manager_v1 global"; return; @@ -566,18 +536,12 @@ strcmp(interface, "zcr_text_input_extension_v1") == 0) { connection->text_input_extension_v1_ = wl::Bind<zcr_text_input_extension_v1>( - registry, name, - wl::CalculateBindVersion( - version, kMaxTextInputExtensionVersion, - zcr_text_input_extension_v1_interface.version)); + registry, name, std::min(version, kMaxTextInputExtensionVersion)); } else if (!connection->xdg_decoration_manager_ && strcmp(interface, "zxdg_decoration_manager_v1") == 0) { connection->xdg_decoration_manager_ = wl::Bind<struct zxdg_decoration_manager_v1>( - registry, name, - wl::CalculateBindVersion( - version, kMaxXdgDecorationVersion, - zxdg_decoration_manager_v1_interface.version)); + registry, name, std::min(version, kMaxXdgDecorationVersion)); if (!connection->xdg_decoration_manager_) { LOG(ERROR) << "Failed to bind zxdg_decoration_manager_v1"; return; @@ -585,9 +549,7 @@ } else if (!connection->extended_drag_v1_ && strcmp(interface, "zcr_extended_drag_v1") == 0) { connection->extended_drag_v1_ = wl::Bind<zcr_extended_drag_v1>( - registry, name, - wl::CalculateBindVersion(version, kMaxExtendedDragVersion, - zcr_extended_drag_v1_interface.version)); + registry, name, std::min(version, kMaxExtendedDragVersion)); if (!connection->extended_drag_v1_) { LOG(ERROR) << "Failed to bind to zcr_extended_drag_v1 global"; return; @@ -595,9 +557,7 @@ } else if (!connection->xdg_output_manager_ && strcmp(interface, "zxdg_output_manager_v1") == 0) { connection->xdg_output_manager_ = wl::Bind<struct zxdg_output_manager_v1>( - registry, name, - wl::CalculateBindVersion(version, kMaxXdgOutputManagerVersion, - zxdg_output_manager_v1_interface.version)); + registry, name, std::min(version, kMaxXdgOutputManagerVersion)); if (!connection->xdg_output_manager_) { LOG(ERROR) << "Failed to bind zxdg_output_manager_v1"; return; @@ -615,9 +575,7 @@ } else if (!connection->zcr_stylus_v2_ && strcmp(interface, "zcr_stylus_v2") == 0) { connection->zcr_stylus_v2_ = wl::Bind<zcr_stylus_v2>( - registry, name, - wl::CalculateBindVersion(version, kMaxStylusVersion, - zcr_stylus_v2_interface.version)); + registry, name, std::min(version, kMaxStylusVersion)); if (!connection->zcr_stylus_v2_) { LOG(ERROR) << "Failed to bind to zcr_stylus_v2"; return;
diff --git a/ui/ozone/platform/wayland/host/wayland_data_device_manager.cc b/ui/ozone/platform/wayland/host/wayland_data_device_manager.cc index 9c4a5d5..a4cbec2f 100644 --- a/ui/ozone/platform/wayland/host/wayland_data_device_manager.cc +++ b/ui/ozone/platform/wayland/host/wayland_data_device_manager.cc
@@ -37,9 +37,7 @@ } auto data_device_manager = wl::Bind<wl_data_device_manager>( - registry, name, - wl::CalculateBindVersion(version, kMaxVersion, - wl_data_device_manager_interface.version)); + registry, name, std::min(version, kMaxVersion)); if (!data_device_manager) { LOG(ERROR) << "Failed to bind to wl_data_device_manager global"; return;
diff --git a/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc b/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc index afb4371..e59d4ee 100644 --- a/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc +++ b/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc
@@ -1097,4 +1097,10 @@ WaylandDataDragControllerTest, Values(wl::ServerConfig{})); +INSTANTIATE_TEST_SUITE_P( + XdgVersionStableTestWithAuraShell, + WaylandDataDragControllerTest, + Values(wl::ServerConfig{ + .enable_aura_shell = wl::EnableAuraShellProtocol::kEnabled})); + } // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_output.cc b/ui/ozone/platform/wayland/host/wayland_output.cc index 715a1da7..274bb5fe 100644 --- a/ui/ozone/platform/wayland/host/wayland_output.cc +++ b/ui/ozone/platform/wayland/host/wayland_output.cc
@@ -42,10 +42,8 @@ return; } - auto output = wl::Bind<wl_output>( - registry, name, - wl::CalculateBindVersion(version, kMaxVersion, - wl_output_interface.version)); + auto output = + wl::Bind<wl_output>(registry, name, std::min(version, kMaxVersion)); if (!output) { LOG(ERROR) << "Failed to bind to wl_output global"; return; @@ -118,14 +116,8 @@ DCHECK(!delegate_); delegate_ = delegate; static constexpr wl_output_listener output_listener = { - &OutputHandleGeometry, &OutputHandleMode, - &OutputHandleDone, &OutputHandleScale, -#ifdef WL_OUTPUT_NAME_SINCE_VERSION - &OutputHandleName, -#endif -#ifdef WL_OUTPUT_DESCRIPTION_SINCE_VERSION - &OutputHandleDescription, -#endif + &OutputHandleGeometry, &OutputHandleMode, &OutputHandleDone, + &OutputHandleScale, &OutputHandleName, &OutputHandleDescription, }; wl_output_add_listener(output_.get(), &output_listener, this); @@ -266,7 +258,6 @@ wayland_output->scale_factor_ = factor; } -#ifdef WL_OUTPUT_NAME_SINCE_VERSION // static void WaylandOutput::OutputHandleName(void* data, struct wl_output* wl_output, @@ -274,9 +265,7 @@ if (WaylandOutput* wayland_output = static_cast<WaylandOutput*>(data)) wayland_output->name_ = name ? std::string(name) : std::string{}; } -#endif -#ifdef WL_OUTPUT_DESCRIPTION_SINCE_VERSION // static void WaylandOutput::OutputHandleDescription(void* data, struct wl_output* wl_output, @@ -286,6 +275,5 @@ description ? std::string(description) : std::string{}; } } -#endif } // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_output.h b/ui/ozone/platform/wayland/host/wayland_output.h index 8f052fa..e1b1bee 100644 --- a/ui/ozone/platform/wayland/host/wayland_output.h +++ b/ui/ozone/platform/wayland/host/wayland_output.h
@@ -154,16 +154,12 @@ static void OutputHandleScale(void* data, struct wl_output* wl_output, int32_t factor); -#ifdef WL_OUTPUT_NAME_SINCE_VERSION static void OutputHandleName(void* data, struct wl_output* wl_output, const char* name); -#endif -#ifdef WL_OUTPUT_DESCRIPTION_SINCE_VERSION static void OutputHandleDescription(void* data, struct wl_output* wl_output, const char* description); -#endif const Id output_id_ = 0; wl::Object<wl_output> output_;
diff --git a/ui/ozone/platform/wayland/host/wayland_output_manager.cc b/ui/ozone/platform/wayland/host/wayland_output_manager.cc index 88bf0241..843fa74 100644 --- a/ui/ozone/platform/wayland/host/wayland_output_manager.cc +++ b/ui/ozone/platform/wayland/host/wayland_output_manager.cc
@@ -156,7 +156,7 @@ // received for their root surface) and |output_id| is the primary output. const bool is_primary = wayland_screen_ && - metrics.output_id == wayland_screen_->GetPrimaryDisplay().id(); + metrics.display_id == wayland_screen_->GetPrimaryDisplay().id(); for (auto* window : connection_->wayland_window_manager()->GetAllWindows()) { auto entered_output = window->GetPreferredEnteredOutputId(); if (entered_output == metrics.output_id || (!entered_output && is_primary))
diff --git a/ui/ozone/platform/wayland/host/wayland_output_unittest.cc b/ui/ozone/platform/wayland/host/wayland_output_unittest.cc index 2c6506b8..74e7250 100644 --- a/ui/ozone/platform/wayland/host/wayland_output_unittest.cc +++ b/ui/ozone/platform/wayland/host/wayland_output_unittest.cc
@@ -59,4 +59,10 @@ WaylandOutputTest, Values(wl::ServerConfig{})); +INSTANTIATE_TEST_SUITE_P( + XdgVersionStableTestWithAuraShell, + WaylandOutputTest, + Values(wl::ServerConfig{ + .enable_aura_shell = wl::EnableAuraShellProtocol::kEnabled})); + } // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_pointer.cc b/ui/ozone/platform/wayland/host/wayland_pointer.cc index c827148..ba9832cd 100644 --- a/ui/ozone/platform/wayland/host/wayland_pointer.cc +++ b/ui/ozone/platform/wayland/host/wayland_pointer.cc
@@ -37,11 +37,8 @@ Delegate* delegate) : obj_(pointer), connection_(connection), delegate_(delegate) { static constexpr wl_pointer_listener listener = { - &Enter, &Leave, &Motion, &Button, &Axis, - &Frame, &AxisSource, &AxisStop, &AxisDiscrete, -#ifdef WL_POINTER_AXIS_VALUE120_SINCE_VERSION - &AxisValue120, -#endif + &Enter, &Leave, &Motion, &Button, &Axis, + &Frame, &AxisSource, &AxisStop, &AxisDiscrete, &AxisValue120, }; wl_pointer_add_listener(obj_.get(), &listener, this); @@ -225,7 +222,6 @@ NOTIMPLEMENTED_LOG_ONCE(); } -#ifdef WL_POINTER_AXIS_VALUE120_SINCE_VERSION // --- Version 8 --- // static @@ -237,7 +233,6 @@ // events. NOTIMPLEMENTED_LOG_ONCE(); } -#endif void WaylandPointer::SetupStylus() { auto* stylus_v2 = connection_->stylus_v2();
diff --git a/ui/ozone/platform/wayland/host/wayland_pointer.h b/ui/ozone/platform/wayland/host/wayland_pointer.h index ec80f29..9448d215 100644 --- a/ui/ozone/platform/wayland/host/wayland_pointer.h +++ b/ui/ozone/platform/wayland/host/wayland_pointer.h
@@ -82,12 +82,10 @@ wl_pointer* obj, uint32_t axis, int32_t discrete); -#ifdef WL_POINTER_AXIS_VALUE120_SINCE_VERSION static void AxisValue120(void* data, wl_pointer* obj, uint32_t axis, int32_t value120); -#endif void SetupStylus();
diff --git a/ui/ozone/platform/wayland/host/wayland_screen.cc b/ui/ozone/platform/wayland/host/wayland_screen.cc index ccb8aaf..43c9c54 100644 --- a/ui/ozone/platform/wayland/host/wayland_screen.cc +++ b/ui/ozone/platform/wayland/host/wayland_screen.cc
@@ -159,7 +159,7 @@ } } } - auto it = display_list_.FindDisplayById(output_id); + auto it = display_list_.FindDisplayById(display_id); if (it != display_list_.displays().end()) display_list_.RemoveDisplay(display_id); }
diff --git a/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc b/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc index 4bfbb77a5..0a1bb5a 100644 --- a/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc +++ b/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc
@@ -1312,9 +1312,17 @@ WaylandScreenTest, Values(wl::ServerConfig{})); -INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, - WaylandAuraShellScreenTest, - Values(wl::ServerConfig{})); +INSTANTIATE_TEST_SUITE_P( + XdgVersionStableTestWithAuraShell, + WaylandScreenTest, + Values(wl::ServerConfig{ + .enable_aura_shell = wl::EnableAuraShellProtocol::kEnabled})); + +INSTANTIATE_TEST_SUITE_P( + XdgVersionStableTest, + WaylandAuraShellScreenTest, + Values(wl::ServerConfig{ + .enable_aura_shell = wl::EnableAuraShellProtocol::kEnabled})); INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, LazilyConfiguredScreenTest,
diff --git a/ui/ozone/platform/wayland/host/wayland_seat.cc b/ui/ozone/platform/wayland/host/wayland_seat.cc index f4b7bcb..dd926ff 100644 --- a/ui/ozone/platform/wayland/host/wayland_seat.cc +++ b/ui/ozone/platform/wayland/host/wayland_seat.cc
@@ -38,10 +38,8 @@ return; } - auto seat = wl::Bind<struct wl_seat>( - registry, name, - wl::CalculateBindVersion(version, kMaxVersion, - wl_seat_interface.version)); + auto seat = + wl::Bind<struct wl_seat>(registry, name, std::min(version, kMaxVersion)); if (!seat) { LOG(ERROR) << "Failed to bind to wl_seat global"; return;
diff --git a/ui/ozone/platform/wayland/host/wayland_surface_unittest.cc b/ui/ozone/platform/wayland/host/wayland_surface_unittest.cc index 9577ab0..8c7f9c90 100644 --- a/ui/ozone/platform/wayland/host/wayland_surface_unittest.cc +++ b/ui/ozone/platform/wayland/host/wayland_surface_unittest.cc
@@ -62,5 +62,11 @@ WaylandSurfaceTest, ::testing::Values(wl::ServerConfig{})); +INSTANTIATE_TEST_SUITE_P( + XdgVersionStableTestWithAuraShell, + WaylandSurfaceTest, + ::testing::Values(wl::ServerConfig{ + .enable_aura_shell = wl::EnableAuraShellProtocol::kEnabled})); + } // namespace } // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_touch.cc b/ui/ozone/platform/wayland/host/wayland_touch.cc index b3b3664..503ef906 100644 --- a/ui/ozone/platform/wayland/host/wayland_touch.cc +++ b/ui/ozone/platform/wayland/host/wayland_touch.cc
@@ -42,13 +42,7 @@ Delegate* delegate) : obj_(touch), connection_(connection), delegate_(delegate) { static constexpr wl_touch_listener listener = { - &Down, &Up, &Motion, &Frame, &Cancel, -#ifdef WL_TOUCH_SHAPE_SINCE_VERSION - &Shape, -#endif -#ifdef WL_TOUCH_ORIENTATION_SINCE_VERSION - &Orientation, -#endif + &Down, &Up, &Motion, &Frame, &Cancel, &Shape, &Orientation, }; wl_touch_add_listener(obj_.get(), &listener, this); @@ -122,7 +116,6 @@ EventDispatchPolicyForPlatform()); } -#ifdef WL_TOUCH_SHAPE_SINCE_VERSION // static void WaylandTouch::Shape(void* data, wl_touch* obj, @@ -131,9 +124,7 @@ wl_fixed_t minor) { NOTIMPLEMENTED_LOG_ONCE(); } -#endif -#ifdef WL_TOUCH_ORIENTATION_SINCE_VERSION // static void WaylandTouch::Orientation(void* data, wl_touch* obj, @@ -141,7 +132,6 @@ wl_fixed_t orientation) { NOTIMPLEMENTED_LOG_ONCE(); } -#endif // static void WaylandTouch::Cancel(void* data, wl_touch* obj) {
diff --git a/ui/ozone/platform/wayland/host/wayland_touch.h b/ui/ozone/platform/wayland/host/wayland_touch.h index fda7076..4b8c4b0a 100644 --- a/ui/ozone/platform/wayland/host/wayland_touch.h +++ b/ui/ozone/platform/wayland/host/wayland_touch.h
@@ -59,19 +59,15 @@ int32_t id, wl_fixed_t x, wl_fixed_t y); -#ifdef WL_TOUCH_SHAPE_SINCE_VERSION static void Shape(void* data, wl_touch* obj, int32_t id, wl_fixed_t major, wl_fixed_t minor); -#endif -#ifdef WL_TOUCH_ORIENTATION_SINCE_VERSION static void Orientation(void* data, wl_touch* obj, int32_t id, wl_fixed_t orientation); -#endif static void Cancel(void* data, wl_touch* obj); static void Frame(void* data, wl_touch* obj);
diff --git a/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc b/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc index bdc1056..06223e2 100644 --- a/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc +++ b/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc
@@ -1395,4 +1395,10 @@ WaylandWindowDragControllerTest, Values(wl::ServerConfig{})); +INSTANTIATE_TEST_SUITE_P( + XdgVersionStableTestWithAuraShell, + WaylandWindowDragControllerTest, + Values(wl::ServerConfig{ + .enable_aura_shell = wl::EnableAuraShellProtocol::kEnabled})); + } // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc index 295dd1d..0d3dd33 100644 --- a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc +++ b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
@@ -4270,9 +4270,18 @@ INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, WaylandWindowTest, Values(wl::ServerConfig{})); - +INSTANTIATE_TEST_SUITE_P( + XdgVersionStableTestWithAuraShell, + WaylandWindowTest, + Values(wl::ServerConfig{ + .enable_aura_shell = wl::EnableAuraShellProtocol::kEnabled})); INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, WaylandSubsurfaceTest, Values(wl::ServerConfig{})); +INSTANTIATE_TEST_SUITE_P( + XdgVersionStableTestWithAuraShell, + WaylandSubsurfaceTest, + Values(wl::ServerConfig{ + .enable_aura_shell = wl::EnableAuraShellProtocol::kEnabled})); } // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_zaura_output_unittest.cc b/ui/ozone/platform/wayland/host/wayland_zaura_output_unittest.cc index 2c6afbf..7c2676d 100644 --- a/ui/ozone/platform/wayland/host/wayland_zaura_output_unittest.cc +++ b/ui/ozone/platform/wayland/host/wayland_zaura_output_unittest.cc
@@ -6,6 +6,7 @@ #include "base/memory/raw_ptr.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/wayland/wayland_display_util.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_output.h" #include "ui/ozone/platform/wayland/host/wayland_output_manager.h" @@ -105,7 +106,9 @@ static_cast<int64_t>(std::numeric_limits<int32_t>::min()) - 1, std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::min() + 1, + -1, 0, + 1, std::numeric_limits<int32_t>::max() - 1, std::numeric_limits<int32_t>::max(), static_cast<int64_t>(std::numeric_limits<int32_t>::max()) + 1, @@ -113,17 +116,18 @@ std::numeric_limits<int64_t>::max()}; for (int64_t id : kTestIds) { - uint32_t display_id_hi = static_cast<uint32_t>(id >> 32); - uint32_t display_id_lo = static_cast<uint32_t>(id); + auto display_id = ui::wayland::ToWaylandDisplayIdPair(id); WaylandZAuraOutput aura_output; - WaylandZAuraOutput::OnDisplayId(&aura_output, nullptr, display_id_hi, - display_id_lo); + WaylandZAuraOutput::OnDisplayId(&aura_output, nullptr, display_id.high, + display_id.low); EXPECT_EQ(id, aura_output.display_id().value()); } } -INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, - WaylandZAuraOutputTest, - Values(wl::ServerConfig{})); +INSTANTIATE_TEST_SUITE_P( + XdgVersionStableTest, + WaylandZAuraOutputTest, + Values(wl::ServerConfig{ + .enable_aura_shell = wl::EnableAuraShellProtocol::kEnabled})); } // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc b/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc index 6b8dafc..05481b1 100644 --- a/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc +++ b/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc
@@ -41,9 +41,7 @@ } auto zaura_shell = wl::Bind<struct zaura_shell>( - registry, name, - wl::CalculateBindVersion(version, kMaxVersion, - zaura_shell_interface.version)); + registry, name, std::min(version, kMaxVersion)); if (!zaura_shell) { LOG(ERROR) << "Failed to bind zaura_shell"; return;
diff --git a/ui/ozone/platform/wayland/host/wayland_zaura_shell_unittest.cc b/ui/ozone/platform/wayland/host/wayland_zaura_shell_unittest.cc index 625d94af..96498dfbc 100644 --- a/ui/ozone/platform/wayland/host/wayland_zaura_shell_unittest.cc +++ b/ui/ozone/platform/wayland/host/wayland_zaura_shell_unittest.cc
@@ -36,8 +36,10 @@ ASSERT_FALSE(connection_->zaura_shell()->HasBugFix(2)); } -INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, - WaylandZAuraShellTest, - Values(wl::ServerConfig{})); +INSTANTIATE_TEST_SUITE_P( + XdgVersionStableTest, + WaylandZAuraShellTest, + Values(wl::ServerConfig{ + .enable_aura_shell = wl::EnableAuraShellProtocol::kEnabled})); } // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_zcr_color_manager.cc b/ui/ozone/platform/wayland/host/wayland_zcr_color_manager.cc index 22e0e3be..9b30b7d9b 100644 --- a/ui/ozone/platform/wayland/host/wayland_zcr_color_manager.cc +++ b/ui/ozone/platform/wayland/host/wayland_zcr_color_manager.cc
@@ -40,9 +40,7 @@ return; auto color_manager = wl::Bind<struct zcr_color_manager_v1>( - registry, name, - wl::CalculateBindVersion(kMinVersion, kMaxVersion, - zcr_color_manager_v1_interface.version)); + registry, name, std::min(kMinVersion, kMaxVersion)); if (!color_manager) { LOG(ERROR) << "Failed to bind zcr_color_manager_v1"; return;
diff --git a/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.cc b/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.cc index 47f7939..3db8cb5 100644 --- a/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.cc +++ b/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.cc
@@ -38,9 +38,7 @@ } auto zwp_linux_dmabuf = wl::Bind<zwp_linux_dmabuf_v1>( - registry, name, - wl::CalculateBindVersion(version, kMaxVersion, - zwp_linux_dmabuf_v1_interface.version)); + registry, name, std::min(version, kMaxVersion)); if (!zwp_linux_dmabuf) { LOG(ERROR) << "Failed to bind zwp_linux_dmabuf_v1"; return;
diff --git a/ui/ozone/platform/wayland/host/xdg_activation.cc b/ui/ozone/platform/wayland/host/xdg_activation.cc index 765898a..3385758 100644 --- a/ui/ozone/platform/wayland/host/xdg_activation.cc +++ b/ui/ozone/platform/wayland/host/xdg_activation.cc
@@ -62,10 +62,8 @@ if (connection->xdg_activation_) return; - auto instance = wl::Bind<::xdg_activation_v1>( - registry, name, - wl::CalculateBindVersion(version, kMaxVersion, - xdg_activation_v1_interface.version)); + auto instance = wl::Bind<::xdg_activation_v1>(registry, name, + std::min(version, kMaxVersion)); if (!instance) { LOG(ERROR) << "Failed to bind " << kInterfaceName; return;
diff --git a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc index e604780..9116980a 100644 --- a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc +++ b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc
@@ -98,16 +98,12 @@ } static constexpr xdg_toplevel_listener xdg_toplevel_listener = { - &ConfigureTopLevel, - &CloseTopLevel, -#if defined(XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION) - // Since v4 - &ConfigureBounds, -#endif -#if defined(XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) - // Since v5 - &WmCapabilities, -#endif + &ConfigureTopLevel, + &CloseTopLevel, + // Since v4 + &ConfigureBounds, + // Since v5 + &WmCapabilities, }; if (!xdg_surface_wrapper_) @@ -324,7 +320,6 @@ surface->wayland_window_->OnCloseRequest(); } -#if defined(XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION) // static void XDGToplevelWrapperImpl::ConfigureBounds(void* data, struct xdg_toplevel* xdg_toplevel, @@ -332,16 +327,13 @@ int32_t height) { NOTIMPLEMENTED_LOG_ONCE(); } -#endif -#if defined(XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) // static void XDGToplevelWrapperImpl::WmCapabilities(void* data, struct xdg_toplevel* xdg_toplevel, struct wl_array* capabilities) { NOTIMPLEMENTED_LOG_ONCE(); } -#endif void XDGToplevelWrapperImpl::SetTopLevelDecorationMode( DecorationMode requested_mode) {
diff --git a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h index 03cbeba..a9a0bc1c 100644 --- a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h +++ b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h
@@ -69,19 +69,13 @@ int32_t height, struct wl_array* states); static void CloseTopLevel(void* data, struct xdg_toplevel* xdg_toplevel); - -#if defined(XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION) static void ConfigureBounds(void* data, struct xdg_toplevel* xdg_toplevel, int32_t width, int32_t height); -#endif - -#if defined(XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) static void WmCapabilities(void* data, struct xdg_toplevel* xdg_toplevel, struct wl_array* capabilities); -#endif // zxdg_decoration_listener static void ConfigureDecoration(
diff --git a/ui/ozone/platform/wayland/test/mock_zaura_shell.cc b/ui/ozone/platform/wayland/test/mock_zaura_shell.cc index 0ff1a73e..2f3bdc0 100644 --- a/ui/ozone/platform/wayland/test/mock_zaura_shell.cc +++ b/ui/ozone/platform/wayland/test/mock_zaura_shell.cc
@@ -16,8 +16,8 @@ namespace { -constexpr uint32_t kZAuraShellVersion = 42; -constexpr uint32_t kZAuraOutputVersion = 38; +constexpr uint32_t kZAuraShellVersion = 44; +constexpr uint32_t kZAuraOutputVersion = 43; void GetAuraSurface(wl_client* client, wl_resource* resource,
diff --git a/ui/ozone/platform/wayland/test/test_output.cc b/ui/ozone/platform/wayland/test/test_output.cc index 981f0fd..77c9982 100644 --- a/ui/ozone/platform/wayland/test/test_output.cc +++ b/ui/ozone/platform/wayland/test/test_output.cc
@@ -6,7 +6,9 @@ #include <wayland-server-protocol.h> +#include "base/check_op.h" #include "third_party/abseil-cpp/absl/types/optional.h" +#include "ui/display/types/display_constants.h" namespace wl { @@ -39,9 +41,10 @@ void TestOutput::Flush() { constexpr char kUnknownMake[] = "unknown_make"; constexpr char kUnknownModel[] = "unknown_model"; - - if (!pending_rect_ && !pending_scale_) + if ((!pending_rect_ && !pending_scale_) || + (aura_shell_enabled_ && !aura_output_)) { return; + } if (pending_rect_ || pending_transform_) { if (pending_rect_) @@ -82,6 +85,9 @@ void TestOutput::SetAuraOutput(TestZAuraOutput* aura_output) { aura_output_ = aura_output; + // Make sure to send the necessary information for a client that + // relies on the aura output information. + Flush(); } TestZAuraOutput* TestOutput::GetAuraOutput() {
diff --git a/ui/ozone/platform/wayland/test/test_output.h b/ui/ozone/platform/wayland/test/test_output.h index ff636c6f..0c138ec1 100644 --- a/ui/ozone/platform/wayland/test/test_output.h +++ b/ui/ozone/platform/wayland/test/test_output.h
@@ -28,6 +28,10 @@ static TestOutput* FromResource(wl_resource* resource); + // Useful only when zaura_shell is supported. + void set_aura_shell_enabled() { aura_shell_enabled_ = true; } + bool aura_shell_enabled() { return aura_shell_enabled_; } + const gfx::Rect GetRect() { return rect_; } void SetRect(const gfx::Rect& rect); int32_t GetScale() const { return scale_; } @@ -43,6 +47,7 @@ void OnBind() override; private: + bool aura_shell_enabled_ = false; gfx::Rect rect_; int32_t scale_; wl_output_transform transform_{WL_OUTPUT_TRANSFORM_NORMAL};
diff --git a/ui/ozone/platform/wayland/test/test_wayland_server_thread.cc b/ui/ozone/platform/wayland/test/test_wayland_server_thread.cc index 5a43400..6039565 100644 --- a/ui/ozone/platform/wayland/test/test_wayland_server_thread.cc +++ b/ui/ozone/platform/wayland/test/test_wayland_server_thread.cc
@@ -134,6 +134,7 @@ return false; if (!alpha_compositing_.Initialize(display_.get())) return false; + if (!output_.Initialize(display_.get())) return false; SetupOutputs(); @@ -148,8 +149,12 @@ if (!xdg_shell_.Initialize(display_.get())) return false; - if (!zaura_shell_.Initialize(display_.get())) - return false; + + if (config.enable_aura_shell == EnableAuraShellProtocol::kEnabled) { + output_.set_aura_shell_enabled(); + if (!zaura_shell_.Initialize(display_.get())) + return false; + } if (!zcr_stylus_.Initialize(display_.get())) return false;
diff --git a/ui/ozone/platform/wayland/test/test_wayland_server_thread.h b/ui/ozone/platform/wayland/test/test_wayland_server_thread.h index 02e9e8ee..4cd36ed 100644 --- a/ui/ozone/platform/wayland/test/test_wayland_server_thread.h +++ b/ui/ozone/platform/wayland/test/test_wayland_server_thread.h
@@ -16,6 +16,7 @@ #include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" #include "base/threading/thread_checker.h" +#include "ui/display/types/display_constants.h" #include "ui/ozone/platform/wayland/test/global_object.h" #include "ui/ozone/platform/wayland/test/mock_wp_presentation.h" #include "ui/ozone/platform/wayland/test/mock_xdg_shell.h" @@ -51,6 +52,7 @@ enum class PrimarySelectionProtocol { kNone, kGtk, kZwp }; enum class CompositorVersion { kV3, kV4 }; enum class ShouldUseExplicitSynchronizationProtocol { kNone, kUse }; +enum class EnableAuraShellProtocol { kEnabled, kDisabled }; struct ServerConfig { CompositorVersion compositor_version = CompositorVersion::kV4; @@ -58,6 +60,8 @@ PrimarySelectionProtocol::kNone; ShouldUseExplicitSynchronizationProtocol use_explicit_synchronization = ShouldUseExplicitSynchronizationProtocol::kUse; + EnableAuraShellProtocol enable_aura_shell = + EnableAuraShellProtocol::kDisabled; }; class TestWaylandServerThread; @@ -135,6 +139,8 @@ TestOutput* CreateAndInitializeOutput() { auto output = std::make_unique<TestOutput>(); + if (output_.aura_shell_enabled()) + output->set_aura_shell_enabled(); output->Initialize(display()); TestOutput* output_ptr = output.get();
diff --git a/ui/ozone/platform/wayland/test/test_zaura_output.cc b/ui/ozone/platform/wayland/test/test_zaura_output.cc index 7e9f364c..1195802 100644 --- a/ui/ozone/platform/wayland/test/test_zaura_output.cc +++ b/ui/ozone/platform/wayland/test/test_zaura_output.cc
@@ -6,13 +6,28 @@ #include <aura-shell-server-protocol.h> +#include "ui/base/wayland/wayland_display_util.h" + namespace wl { +namespace { +int64_t display_id_counter = 10; +} TestZAuraOutput::TestZAuraOutput(wl_resource* resource) - : ServerObject(resource) {} + : ServerObject(resource), display_id_(display_id_counter++) { + if (wl_resource_get_version(resource) >= + ZAURA_OUTPUT_DISPLAY_ID_SINCE_VERSION) { + auto display_id = ui::wayland::ToWaylandDisplayIdPair(display_id_); + zaura_output_send_display_id(resource, display_id.high, display_id.low); + } +} TestZAuraOutput::~TestZAuraOutput() = default; +void TestZAuraOutput::SendActivated() { + zaura_output_send_activated(resource()); +} + void TestZAuraOutput::Flush() { if (pending_insets_) { insets_ = std::move(*pending_insets_);
diff --git a/ui/ozone/platform/wayland/test/test_zaura_output.h b/ui/ozone/platform/wayland/test/test_zaura_output.h index 41ee51d..77a60d89 100644 --- a/ui/ozone/platform/wayland/test/test_zaura_output.h +++ b/ui/ozone/platform/wayland/test/test_zaura_output.h
@@ -23,6 +23,8 @@ ~TestZAuraOutput() override; + int64_t display_id() const { return display_id_; } + const gfx::Insets& GetInsets() const { return insets_; } void SetInsets(const gfx::Insets& insets) { pending_insets_ = insets; } @@ -31,9 +33,13 @@ pending_logical_transform_ = logical_transform; } + // Send display id, activated events, immediately. + void SendActivated(); + void Flush(); private: + int64_t display_id_; gfx::Insets insets_; absl::optional<gfx::Insets> pending_insets_;
diff --git a/ui/ozone/platform/x11/test/x11_event_translation_unittest.cc b/ui/ozone/platform/x11/test/x11_event_translation_unittest.cc index 5d133f45..f088919 100644 --- a/ui/ozone/platform/x11/test/x11_event_translation_unittest.cc +++ b/ui/ozone/platform/x11/test/x11_event_translation_unittest.cc
@@ -238,4 +238,44 @@ EXPECT_EQ(ET_KEY_RELEASED, keyev_shift_r_released->type()); } +// Verifies that scroll events remain ET_SCROLL type or are translated to +// ET_SCROLL_FLING_START depending on their X and Y offsets. +TEST(XEventTranslationTest, ScrollEventType) { + int device_id = 1; + ui::SetUpTouchPadForTest(device_id); + + struct ScrollEventTestData { + int x_offset_; + int y_offset_; + int x_offset_ordinal_; + int y_offset_ordinal_; + EventType expectedEventType_; + }; + const std::vector<ScrollEventTestData> test_data = { + // Ordinary horizontal scrolling remains ET_SCROLL. + {1, 0, 1, 0, EventType::ET_SCROLL}, + // Ordinary vertical scrolling remains ET_SCROLL. + {0, 10, 0, 10, EventType::ET_SCROLL}, + // Ordinary diagonal scrolling remains ET_SCROLL. + {47, -11, 47, -11, EventType::ET_SCROLL}, + // If x_offset and y_offset both are 0, expected event type is + // ET_SCROLL_FLING_START and not ET_SCROLL. + {0, 0, 0, 0, EventType::ET_SCROLL_FLING_START}}; + + for (const auto& data : test_data) { + ui::ScopedXI2Event xev; + xev.InitScrollEvent(device_id, data.x_offset_, data.y_offset_, + data.x_offset_ordinal_, data.y_offset_ordinal_, 2); + + const auto event = BuildEventFromXEvent(*xev); + EXPECT_TRUE(event); + EXPECT_EQ(event->type(), data.expectedEventType_); + + const ScrollEvent* scroll_event = static_cast<ScrollEvent*>(event.get()); + EXPECT_EQ(scroll_event->x_offset(), data.x_offset_); + EXPECT_EQ(scroll_event->y_offset(), data.y_offset_); + EXPECT_EQ(scroll_event->x_offset_ordinal(), data.x_offset_ordinal_); + EXPECT_EQ(scroll_event->y_offset_ordinal(), data.y_offset_ordinal_); + } +} } // namespace ui
diff --git a/ui/webui/resources/BUILD.gn b/ui/webui/resources/BUILD.gn index f7652b0..306423ef 100644 --- a/ui/webui/resources/BUILD.gn +++ b/ui/webui/resources/BUILD.gn
@@ -153,11 +153,14 @@ # Files for which .d.ts files will be auto-generated with ts_definitions(). generate_definitions_js_files = [ "js/assert.js", - "js/cr/event_target.js", "js/load_time_data.m.js", "js/util.js", ] +if (is_chromeos_ash) { + generate_definitions_js_files += [ "js/cr/event_target.js" ] +} + if (is_ios) { generate_definitions_js_files += [ "js/ios/web_ui.js" ] }
diff --git a/ui/webui/resources/cr_components/app_management/BUILD.gn b/ui/webui/resources/cr_components/app_management/BUILD.gn index 4b1d324c..59027bb 100644 --- a/ui/webui/resources/cr_components/app_management/BUILD.gn +++ b/ui/webui/resources/cr_components/app_management/BUILD.gn
@@ -108,7 +108,7 @@ copy("copy_mojo") { deps = [ - ":mojo_bindings_webui_js", + ":mojo_bindings_js__generator", "//components/services/app_service/public/mojom:types_js__generator", ] sources = [
diff --git a/ui/webui/resources/cr_components/color_change_listener/BUILD.gn b/ui/webui/resources/cr_components/color_change_listener/BUILD.gn index 6e07996..2b64012 100644 --- a/ui/webui/resources/cr_components/color_change_listener/BUILD.gn +++ b/ui/webui/resources/cr_components/color_change_listener/BUILD.gn
@@ -17,7 +17,7 @@ } copy("copy_mojom") { - deps = [ ":mojom_webui_js" ] + deps = [ ":mojom_js__generator" ] sources = [ "$root_gen_dir/mojom-webui/ui/webui/resources/cr_components/color_change_listener/color_change_listener.mojom-webui.js" ] outputs = [ "$target_gen_dir/{{source_file_part}}" ] }
diff --git a/ui/webui/resources/cr_components/customize_themes/BUILD.gn b/ui/webui/resources/cr_components/customize_themes/BUILD.gn index 4337173..085adeb 100644 --- a/ui/webui/resources/cr_components/customize_themes/BUILD.gn +++ b/ui/webui/resources/cr_components/customize_themes/BUILD.gn
@@ -44,7 +44,7 @@ } copy("copy_mojom") { - deps = [ ":mojom_webui_js" ] + deps = [ ":mojom_js__generator" ] sources = [ "$root_gen_dir/mojom-webui/ui/webui/resources/cr_components/customize_themes/customize_themes.mojom-webui.js" ] outputs = [ "$preprocess_folder_tmp/{{source_file_part}}" ] }
diff --git a/ui/webui/resources/cr_components/help_bubble/BUILD.gn b/ui/webui/resources/cr_components/help_bubble/BUILD.gn index ef902a9..229a679 100644 --- a/ui/webui/resources/cr_components/help_bubble/BUILD.gn +++ b/ui/webui/resources/cr_components/help_bubble/BUILD.gn
@@ -63,7 +63,7 @@ } copy("copy_mojom") { - deps = [ ":mojo_bindings_webui_js" ] + deps = [ ":mojo_bindings_js__generator" ] sources = [ "$root_gen_dir/mojom-webui/ui/webui/resources/cr_components/help_bubble/help_bubble.mojom-webui.js" ] outputs = [ "$preprocess_folder_tmp/{{source_file_part}}" ] }
diff --git a/ui/webui/resources/cr_components/history_clusters/BUILD.gn b/ui/webui/resources/cr_components/history_clusters/BUILD.gn index 42bf34e..9a820703 100644 --- a/ui/webui/resources/cr_components/history_clusters/BUILD.gn +++ b/ui/webui/resources/cr_components/history_clusters/BUILD.gn
@@ -55,7 +55,7 @@ copy("copy_history_clusters_mojom") { sources = [ "$root_gen_dir/mojom-webui/ui/webui/resources/cr_components/history_clusters/history_clusters.mojom-webui.js" ] outputs = [ "$preprocess_folder_tmp/{{source_file_part}}" ] - deps = [ ":mojo_bindings_webui_js" ] + deps = [ ":mojo_bindings_js__generator" ] } ts_library("build_ts") {
diff --git a/ui/webui/resources/cr_components/most_visited/BUILD.gn b/ui/webui/resources/cr_components/most_visited/BUILD.gn index 82ea292..aec4c74 100644 --- a/ui/webui/resources/cr_components/most_visited/BUILD.gn +++ b/ui/webui/resources/cr_components/most_visited/BUILD.gn
@@ -30,7 +30,7 @@ } copy("copy_mojom") { - deps = [ ":mojom_webui_js" ] + deps = [ ":mojom_js__generator" ] sources = [ "$root_gen_dir/mojom-webui/ui/webui/resources/cr_components/most_visited/most_visited.mojom-webui.js" ] outputs = [ "$target_gen_dir/{{source_file_part}}" ] }
diff --git a/ui/webui/resources/js/BUILD.gn b/ui/webui/resources/js/BUILD.gn index 8bdf393..53fd990 100644 --- a/ui/webui/resources/js/BUILD.gn +++ b/ui/webui/resources/js/BUILD.gn
@@ -81,13 +81,21 @@ out_manifest = "$target_gen_dir/$preprocess_src_manifest" in_files = [ "assert.js", - "cr.m.js", - "cr/event_target.js", "load_time_data.m.js", "load_time_data_deprecated.js", "util.js", ] + if (is_chromeos_ash) { + # Used by ChromeOS Closure UIs. + in_files += [ "cr/event_target.js" ] + } + + if (is_chromeos_ash || is_android) { + # Used by ChromeOS and Android Closure UIs. + in_files += [ "cr.m.js" ] + } + if (is_chromeos_ash || is_ios) { # Used by ChromeOS UIs and ios inspect and omaha UIs in_files += [ "util_deprecated.js" ] @@ -129,26 +137,32 @@ # Targets for type-checking JS Modules group("closure_compile_modules") { - deps = [ - ":js_resources_modules", - "cr:closure_compile_modules", - ] + deps = [ ":js_resources_modules" ] + + if (is_chromeos_ash) { + deps += [ "cr:closure_compile_modules" ] + } } js_type_check("js_resources_modules") { is_polymer3 = true deps = [ ":assert", - ":cr.m", ":load_time_data.m", ":util", ] + + if (is_chromeos_ash || is_android) { + deps += [ ":cr.m" ] + } } js_library("assert") { } -js_library("cr.m") { +if (is_chromeos_ash || is_android) { + js_library("cr.m") { + } } js_library("load_time_data.m") {
diff --git a/ui/webui/resources/js/browser_command/BUILD.gn b/ui/webui/resources/js/browser_command/BUILD.gn index 26de106f..016e0a2c 100644 --- a/ui/webui/resources/js/browser_command/BUILD.gn +++ b/ui/webui/resources/js/browser_command/BUILD.gn
@@ -37,7 +37,7 @@ } copy("copy_mojom") { - deps = [ ":mojo_bindings_webui_js" ] + deps = [ ":mojo_bindings_js__generator" ] sources = [ "$root_gen_dir/mojom-webui/ui/webui/resources/js/browser_command/browser_command.mojom-webui.js" ] outputs = [ "$tmp_folder/{{source_file_part}}" ] }
diff --git a/ui/webui/resources/js/cr/BUILD.gn b/ui/webui/resources/js/cr/BUILD.gn index 36515aa..083e9c6 100644 --- a/ui/webui/resources/js/cr/BUILD.gn +++ b/ui/webui/resources/js/cr/BUILD.gn
@@ -2,8 +2,11 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/chromeos/ui_mode.gni") import("//third_party/closure_compiler/compile_js.gni") +assert(is_chromeos_ash) + js_type_check("closure_compile_modules") { deps = [ ":event_target" ] }
diff --git a/ui/webui/resources/js/metrics_reporter/BUILD.gn b/ui/webui/resources/js/metrics_reporter/BUILD.gn index 775d617..6fcb00b 100644 --- a/ui/webui/resources/js/metrics_reporter/BUILD.gn +++ b/ui/webui/resources/js/metrics_reporter/BUILD.gn
@@ -35,7 +35,7 @@ } copy("copy_src_and_mojom") { - deps = [ ":mojo_bindings_webui_js" ] + deps = [ ":mojo_bindings_js__generator" ] sources = [ "$root_gen_dir/mojom-webui/ui/webui/resources/js/metrics_reporter/metrics_reporter.mojom-webui.js", "browser_proxy.ts",
diff --git a/weblayer/BUILD.gn b/weblayer/BUILD.gn index a60f19d..9d834b7 100644 --- a/weblayer/BUILD.gn +++ b/weblayer/BUILD.gn
@@ -946,7 +946,7 @@ "grit/weblayer_resources.h", "weblayer_resources.pak", ] - deps = [ "//weblayer/browser/webui:mojo_bindings_webui_js" ] + deps = [ "//weblayer/browser/webui:mojo_bindings_js__generator" ] } # TODO(jam): move weblayer_shell_resources_grit and copy_shell_resources here in # a way that's shareable?
diff --git a/weblayer/browser/android/javatests/BUILD.gn b/weblayer/browser/android/javatests/BUILD.gn index c2b910d..3dce88e 100644 --- a/weblayer/browser/android/javatests/BUILD.gn +++ b/weblayer/browser/android/javatests/BUILD.gn
@@ -8,10 +8,7 @@ android_library("webengine_java_tests") { testonly = true - sources = [ - "src/org/chromium/webengine/test/ExecuteScriptTest.java", - "src/org/chromium/webengine/test/WebFragmentTest.java", - ] + sources = [ "src/org/chromium/webengine/test/WebFragmentTest.java" ] deps = [ ":webengine_java_test_support", "//base:base_java", @@ -20,7 +17,6 @@ "//components/safe_browsing/android:safe_browsing_java", "//content/public/android:content_java", "//content/public/test/android:content_java_test_support", - "//net/android:net_java_test_support", "//third_party/android_deps:com_google_guava_listenablefuture_java", "//third_party/android_deps:guava_android_java", "//third_party/android_support_test_runner:rules_java", @@ -30,7 +26,6 @@ "//third_party/androidx:androidx_appcompat_appcompat_java", "//third_party/androidx:androidx_core_core_java", "//third_party/androidx:androidx_fragment_fragment_java", - "//third_party/androidx:androidx_test_core_java", "//third_party/androidx:androidx_test_runner_java", "//third_party/blink/public/common:common_java", "//third_party/junit:junit", @@ -52,10 +47,8 @@ "//base:base_java_test_support", "//content/public/test/android:content_java_test_support", "//net/android:net_java_test_support", - "//third_party/android_deps:com_google_guava_listenablefuture_java", "//third_party/android_support_test_runner:rules_java", "//third_party/android_support_test_runner:runner_java", - "//third_party/androidx:androidx_annotation_annotation_java", "//third_party/androidx:androidx_fragment_fragment_java", "//third_party/junit:junit", "//ui/android:ui_java_test_support",
diff --git a/weblayer/browser/android/javatests/src/org/chromium/webengine/test/ExecuteScriptTest.java b/weblayer/browser/android/javatests/src/org/chromium/webengine/test/ExecuteScriptTest.java deleted file mode 100644 index 574de93..0000000 --- a/weblayer/browser/android/javatests/src/org/chromium/webengine/test/ExecuteScriptTest.java +++ /dev/null
@@ -1,180 +0,0 @@ -// Copyright 2022 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.webengine.test; - -import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking; - -import android.content.pm.PackageManager; - -import androidx.test.filters.SmallTest; - -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.chromium.base.ContextUtils; -import org.chromium.base.PackageUtils; -import org.chromium.base.test.util.Batch; -import org.chromium.net.test.util.TestWebServer; -import org.chromium.webengine.RestrictedAPIException; -import org.chromium.webengine.Tab; - -import java.util.List; -import java.util.concurrent.CountDownLatch; - -/** - * Tests executing JavaScript in a Tab. - */ -@Batch(Batch.PER_CLASS) -@RunWith(WebEngineJUnit4ClassRunner.class) -public class ExecuteScriptTest { - @Rule - public InstrumentationActivityTestRule mActivityTestRule = - new InstrumentationActivityTestRule(); - - private static final String ASSETLINKS_PATH = "/.well-known/assetlinks.json"; - // TODO(crbug.com/1376522): Figure out how to not hardcode a port number. - private static final int PORT = 8888; - - private TestWebServer mServer; - private Tab mTab; - private String mDefaultUrl; - - @Before - public void setUp() throws Exception { - mServer = TestWebServer.start(PORT); - mActivityTestRule.launchShell(); - - mDefaultUrl = mServer.setResponse("/page.html", - "<html><head></head><body>contents!</body><script>window.foo = 42;</script></html>", - null); - // By default, the asset links are not set up. - mServer.setResponseWithNotFoundStatus(ASSETLINKS_PATH, null); - } - - @After - public void tearDown() { - mServer.shutdown(); - mActivityTestRule.finish(); - } - - private Tab navigate() throws Exception { - mActivityTestRule.attachNewFragmentThenNavigateAndWait(mDefaultUrl); - return mActivityTestRule.getFragment().getTabManager().get().getActiveTab().get(); - } - - private static String makeAssetFile(String packageName, String fingerprint) { - try { - return (new JSONArray().put( - new JSONObject() - .put("relation", - new JSONArray().put( - "delegate_permission/common.handle_all_urls")) - .put("target", - new JSONObject() - .put("namespace", "android_app") - .put("package_name", packageName) - .put("sha256_cert_fingerprints", - new JSONArray().put(fingerprint))))) - .toString(); - } catch (JSONException e) { - } - return ""; - } - - private void setUpDigitalAssetLinks() { - String packageName = ContextUtils.getApplicationContext().getPackageName(); - PackageManager packageManager = ContextUtils.getApplicationContext().getPackageManager(); - List<String> signatureFingerprints = - PackageUtils.getCertificateSHA256FingerprintForPackage(packageManager, packageName); - mServer.setResponse( - ASSETLINKS_PATH, makeAssetFile(packageName, signatureFingerprints.get(0)), null); - } - - @Test - @SmallTest - public void executeScriptFailsWithoutVerification() throws Exception { - Tab activeTab = navigate(); - - CountDownLatch executeLatch = new CountDownLatch(1); - - ListenableFuture<String> future = - runOnUiThreadBlocking(() -> activeTab.executeScript("1+1", true)); - - Futures.addCallback(future, new FutureCallback<String>() { - @Override - public void onSuccess(String result) { - Assert.fail("future resolved unexpectedly."); - } - - @Override - public void onFailure(Throwable thrown) { - if (!(thrown instanceof RestrictedAPIException)) { - Assert.fail( - "expected future to fail due to RestrictedAPIException, instead got: " - + thrown.getClass().getName()); - } - executeLatch.countDown(); - } - }, mActivityTestRule.getContext().getMainExecutor()); - - executeLatch.await(); - } - - @Test - @SmallTest - public void executeScriptSucceedsWithVerification() throws Exception { - setUpDigitalAssetLinks(); - Tab activeTab = navigate(); - - ListenableFuture<String> future = - runOnUiThreadBlocking(() -> activeTab.executeScript("1+1", true)); - Assert.assertEquals(future.get(), "2"); - } - - @Test - @SmallTest - public void useSeparateIsolate() throws Exception { - setUpDigitalAssetLinks(); - Tab activeTab = navigate(); - - ListenableFuture<String> futureIsolated = - runOnUiThreadBlocking(() -> activeTab.executeScript("window.foo", true)); - Assert.assertEquals(futureIsolated.get(), "null"); - - ListenableFuture<String> futureUnisolated = - runOnUiThreadBlocking(() -> activeTab.executeScript("window.foo", false)); - Assert.assertEquals(futureUnisolated.get(), "42"); - } - - @Test - @SmallTest - public void modifyDOMSucceeds() throws Exception { - setUpDigitalAssetLinks(); - Tab activeTab = navigate(); - - ListenableFuture<String> future1 = runOnUiThreadBlocking( - () -> activeTab.executeScript("document.body.innerText", false)); - Assert.assertEquals(future1.get(), "\"contents!\""); - - runOnUiThreadBlocking( - () -> activeTab.executeScript("document.body.innerText = 'foo'", false)) - .get(); - - ListenableFuture<String> future2 = runOnUiThreadBlocking( - () -> activeTab.executeScript("document.body.innerText", false)); - Assert.assertEquals(future2.get(), "\"foo\""); - } -}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/webengine/test/InstrumentationActivityTestRule.java b/weblayer/browser/android/javatests/src/org/chromium/webengine/test/InstrumentationActivityTestRule.java index c3898de6..7294e2a 100644 --- a/weblayer/browser/android/javatests/src/org/chromium/webengine/test/InstrumentationActivityTestRule.java +++ b/weblayer/browser/android/javatests/src/org/chromium/webengine/test/InstrumentationActivityTestRule.java
@@ -4,28 +4,16 @@ package org.chromium.webengine.test; -import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking; - import android.content.ComponentName; -import android.content.Context; import android.content.Intent; import android.support.test.InstrumentationRegistry; -import androidx.annotation.Nullable; +import org.junit.Rule; -import org.junit.Assert; - -import org.chromium.webengine.Navigation; -import org.chromium.webengine.NavigationObserver; -import org.chromium.webengine.Tab; -import org.chromium.webengine.TabManager; -import org.chromium.webengine.WebFragment; -import org.chromium.webengine.WebSandbox; +import org.chromium.net.test.EmbeddedTestServer; +import org.chromium.net.test.EmbeddedTestServerRule; import org.chromium.webengine.shell.InstrumentationActivity; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; - /** * ActivityTestRule for InstrumentationActivity. * @@ -33,8 +21,8 @@ */ public class InstrumentationActivityTestRule extends WebEngineActivityTestRule<InstrumentationActivity> { - @Nullable - private WebFragment mFragment; + @Rule + private EmbeddedTestServerRule mTestServerRule = new EmbeddedTestServerRule(); public InstrumentationActivityTestRule() { super(InstrumentationActivity.class); @@ -43,7 +31,7 @@ /** * Starts the WebEngine Instrumentation activity. */ - public void launchShell() { + public InstrumentationActivity launchShell() { Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); @@ -51,86 +39,14 @@ new ComponentName(InstrumentationRegistry.getInstrumentation().getTargetContext(), InstrumentationActivity.class)); launchActivity(intent); - } - - public void finish() { - Assert.assertNotNull(getActivity()); - getActivity().finish(); - } - - public WebSandbox getWebSandbox() throws Exception { - return getActivity().getWebSandboxFuture().get(); - } - - /** - * Attaches a fragment to the container in the activity. If a fragment is already present, it - * will detach it first. - */ - public void attachFragment(WebFragment fragment) { - if (mFragment != null) { - detachFragment(mFragment); - } - - getActivity().attachFragment(fragment); - mFragment = fragment; - } - - /** - * Return the current fragment attached to the fragment container in the activity. - */ - public WebFragment getFragment() { - Assert.assertNotNull(mFragment); - return mFragment; - } - - public void detachFragment(WebFragment fragment) { - getActivity().detachFragment(fragment); - } - - public Tab getActiveTab() throws Exception { - return getFragment().getTabManager().get().getActiveTab().get(); - } - - /** - * Creates and attaches a new WebFragment before navigating. - */ - public void attachNewFragmentThenNavigateAndWait(String path) throws Exception { - WebSandbox sandbox = getWebSandbox(); - WebFragment fragment = runOnUiThreadBlocking(() -> sandbox.createFragment()); - runOnUiThreadBlocking(() -> attachFragment(fragment)); - - TabManager tabManager = fragment.getTabManager().get(); - Tab activeTab = tabManager.getActiveTab().get(); - - navigateAndWait(activeTab, path); - } - - /** - * Navigates Tab to new path and waits for navigation to complete. - */ - public void navigateAndWait(Tab tab, String url) throws Exception { - CountDownLatch navigationCompleteLatch = new CountDownLatch(1); - AtomicBoolean failed = new AtomicBoolean(false); - runOnUiThreadBlocking(() -> { - tab.getNavigationController().registerNavigationObserver(new NavigationObserver() { - @Override - public void onNavigationCompleted(Navigation navigation) { - navigationCompleteLatch.countDown(); - } - @Override - public void onNavigationFailed(Navigation navigation) { - failed.set(true); - navigationCompleteLatch.countDown(); - } - }); - tab.getNavigationController().navigate(url); - }); - navigationCompleteLatch.await(); - - if (failed.get()) throw new RuntimeException("Navigation failed."); - } - - public Context getContext() { return getActivity(); } + + public EmbeddedTestServer getTestServer() { + return mTestServerRule.getServer(); + } + + public String getTestDataURL(String path) { + return getTestServer().getURL("/weblayer/test/data/" + path); + } }
diff --git a/weblayer/browser/android/javatests/src/org/chromium/webengine/test/WebFragmentTest.java b/weblayer/browser/android/javatests/src/org/chromium/webengine/test/WebFragmentTest.java index 37923bee..26fe950 100644 --- a/weblayer/browser/android/javatests/src/org/chromium/webengine/test/WebFragmentTest.java +++ b/weblayer/browser/android/javatests/src/org/chromium/webengine/test/WebFragmentTest.java
@@ -4,13 +4,10 @@ package org.chromium.webengine.test; -import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking; - -import android.net.Uri; +import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlockingNoException; import androidx.test.filters.SmallTest; -import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -18,106 +15,31 @@ import org.junit.runner.RunWith; import org.chromium.base.test.util.Batch; -import org.chromium.net.test.EmbeddedTestServer; -import org.chromium.net.test.EmbeddedTestServerRule; -import org.chromium.webengine.Tab; -import org.chromium.webengine.TabManager; -import org.chromium.webengine.WebFragment; import org.chromium.webengine.WebSandbox; +import org.chromium.webengine.shell.InstrumentationActivity; /** - * Tests that basic fragment operations work as intended. + * Tests that fragment lifecycle works as expected. */ -@Batch(Batch.PER_CLASS) @RunWith(WebEngineJUnit4ClassRunner.class) +@Batch(Batch.UNIT_TESTS) public class WebFragmentTest { @Rule - public EmbeddedTestServerRule mTestServerRule = new EmbeddedTestServerRule(); - - @Rule public InstrumentationActivityTestRule mActivityTestRule = new InstrumentationActivityTestRule(); - private EmbeddedTestServer mServer; private WebSandbox mWebSandbox; @Before public void setUp() throws Throwable { - mServer = mTestServerRule.getServer(); - mActivityTestRule.launchShell(); - mWebSandbox = mActivityTestRule.getWebSandbox(); - } - - @After - public void tearDown() { - mActivityTestRule.finish(); - runOnUiThreadBlocking(() -> mWebSandbox.shutdown()); - } - - private String getTestDataURL(String path) { - return mServer.getURL("/weblayer/test/data/" + path); + InstrumentationActivity activity = mActivityTestRule.launchShell(); + mWebSandbox = activity.getWebSandboxFuture().get(); } @Test @SmallTest - public void loadsPage() throws Exception { - WebFragment fragment = runOnUiThreadBlocking(() -> mWebSandbox.createFragment()); - runOnUiThreadBlocking(() -> mActivityTestRule.attachFragment(fragment)); - - TabManager tabManager = fragment.getTabManager().get(); - Tab activeTab = tabManager.getActiveTab().get(); - - Assert.assertEquals(activeTab.getDisplayUri(), Uri.EMPTY); - - String url = getTestDataURL("simple_page.html"); - mActivityTestRule.navigateAndWait(activeTab, url); - - Assert.assertEquals(activeTab.getDisplayUri(), Uri.parse(url)); - } - - /** - * This test is similar to the previous one and just ensures that these unit tests can be - * batched. - */ - @Test - @SmallTest - public void successfullyLoadDifferentPage() throws Exception { - mActivityTestRule.attachNewFragmentThenNavigateAndWait(getTestDataURL("simple_page2.html")); - } - - @Test - @SmallTest - public void fragmentTabCanLoadMultiplePages() throws Exception { - mActivityTestRule.attachNewFragmentThenNavigateAndWait(getTestDataURL("simple_page.html")); - - Tab tab = mActivityTestRule.getActiveTab(); - mActivityTestRule.navigateAndWait(tab, getTestDataURL("simple_page2.html")); - - Assert.assertTrue(tab.getDisplayUri().toString().endsWith("simple_page2.html")); - } - - @Test - @SmallTest - public void fragmentsCanBeReplaced() throws Exception { - mActivityTestRule.attachNewFragmentThenNavigateAndWait(getTestDataURL("simple_page.html")); - // New fragment - mActivityTestRule.attachNewFragmentThenNavigateAndWait(getTestDataURL("simple_page2.html")); - - Tab tab = mActivityTestRule.getActiveTab(); - Assert.assertTrue(tab.getDisplayUri().toString().endsWith("simple_page2.html")); - } - - @Test - @SmallTest - public void navigationFailure() { - try { - mActivityTestRule.attachNewFragmentThenNavigateAndWait( - getTestDataURL("missingpage.html")); - Assert.fail("exception not thrown"); - } catch (RuntimeException e) { - Assert.assertEquals(e.getMessage(), "Navigation failed."); - } catch (Exception e) { - Assert.fail("RuntimeException not thrown, instead got: " + e); - } + public void successfullyCreateFragment() { + Assert.assertNotNull(mWebSandbox); + Assert.assertNotNull(runOnUiThreadBlockingNoException(() -> mWebSandbox.createFragment())); } }
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/AccessibilityTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/AccessibilityTest.java new file mode 100644 index 0000000..f7b914a6 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/AccessibilityTest.java
@@ -0,0 +1,65 @@ +// Copyright 2022 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.weblayer.test; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.Profile; +import org.chromium.weblayer.TestWebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Tests that accessibility works as expected. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class AccessibilityTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + @Test + @SmallTest + public void testSetTextScaling() throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl( + mActivityTestRule.getTestDataURL("shakespeare.html")); + int originalHeight = getTextHeight(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + getTestWebLayer().setTextScaling(activity.getBrowser().getProfile(), 2.0f); + return null; + }); + assertThat(originalHeight).isLessThan(getTextHeight()); + } + + @Test + @SmallTest + public void testTextScalingSetsForceEnableZoom() throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Profile profile = activity.getBrowser().getProfile(); + Assert.assertFalse(getTestWebLayer().getForceEnableZoom(profile)); + getTestWebLayer().setTextScaling(profile, 2.0f); + Assert.assertTrue(getTestWebLayer().getForceEnableZoom(profile)); + return null; + }); + } + + private int getTextHeight() { + return mActivityTestRule.executeScriptAndExtractInt( + "document.querySelector('p').clientHeight"); + } + + private TestWebLayer getTestWebLayer() { + return TestWebLayer.getTestWebLayer( + mActivityTestRule.getActivity().getApplicationContext()); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/AutofillTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/AutofillTest.java new file mode 100644 index 0000000..a3ce9e7 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/AutofillTest.java
@@ -0,0 +1,131 @@ +// 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. + +package org.chromium.weblayer.test; + +import android.os.Build; +import android.os.Bundle; +import android.os.SystemClock; +import android.view.KeyEvent; + +import androidx.test.filters.SmallTest; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.MinAndroidSdkLevel; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.net.test.util.TestWebServer; +import org.chromium.weblayer.TestWebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; +import org.chromium.weblayer_private.test_interfaces.AutofillEventType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * Verifies Autofill works in WebLayer. The feature itself has AwAutofillTest.java for testing its + * functionality. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +@MinAndroidSdkLevel(Build.VERSION_CODES.O) +public class AutofillTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private static final String MAIN_FRAME_FILE = "/main_frame.html"; + + private TestWebServer mWebServer; + private InstrumentationActivity mActivity; + + private ArrayList<Integer> mEventsObserved; + private CallbackHelper mHelper; + + @Before + public void setUp() throws Exception { + mEventsObserved = new ArrayList<>(); + mHelper = new CallbackHelper(); + mActivity = mActivityTestRule.launchShell(new Bundle()); + // There is no way to talk to TestWebLayer before the WebLayer is created. + // TestAutofillManagerWrapper can only replace AutofillProvider's AutofillMangerWrapper + // after initialization is done. So this test can't be used to test AutofillProvider's + // initialization. As WebLayer doesn't have specific code in AutofillProvider + // initialization, the AutofillProvider initialization is sufficiently tested via + // AwAutofillTest. + TestWebLayer.getTestWebLayer(mActivity.getApplicationContext()) + .notifyOfAutofillEvents( + mActivity.getBrowser(), () -> mHelper.notifyCalled(), mEventsObserved); + mWebServer = TestWebServer.start(); + } + + @After + public void tearDown() throws Exception { + mWebServer.shutdown(); + } + + /** + * Verifies that Autofill is working for WebLayer as the TestAutofillManagerWrapper is + * receiving data from the renderer side. + */ + @Test + @SmallTest + public void testBasicAutofill() throws Throwable { + final String data = "<html><head></head><body><form action='a.html' name='formname'>" + + "<label>User Name:</label>" + + "<input type='text' id='text1' name='name' maxlength='30'" + + " placeholder='placeholder@placeholder.com' autocomplete='name given-name'>" + + "<input type='checkbox' id='checkbox1' name='showpassword'>" + + "<select id='select1' name='month'>" + + "<option value='1'>Jan</option>" + + "<option value='2'>Feb</option>" + + "</select><textarea id='textarea1'></textarea>" + + "<div contenteditable id='div1'>hello</div>" + + "<input type='submit'>" + + "<input type='reset' id='reset1'>" + + "<input type='color' id='color1'><input type='file' id='file1'>" + + "<input type='image' id='image1'>" + + "</form></body></html>"; + final String url = mWebServer.setResponse(MAIN_FRAME_FILE, data, null); + + // Load the test page. + mActivityTestRule.navigateAndWait(url); + int callCount = mHelper.getCallCount(); + // Select the "text1" element. + mActivityTestRule.executeScriptSync("document.getElementById('text1').select();", false); + // Press "a" to trigger Autofill. + dispatchDownAndUpKeyEvents(KeyEvent.KEYCODE_A); + + List<Integer> expected = new ArrayList(Arrays.asList(AutofillEventType.VIEW_ENTERED, + AutofillEventType.SESSION_STARTED, AutofillEventType.VALUE_CHANGED)); + // We don't have the cancel event on P+, but we will see them on O and OMR1. + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + expected.add(0, AutofillEventType.CANCEL); + } + + // Wait for Autofill events. + mHelper.waitForCallback( + /* currentCallCount */ callCount, /* numberOfCallsToWaitFor */ expected.size(), + CallbackHelper.WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS); + + Assert.assertEquals(expected, mEventsObserved); + } + + private void dispatchDownAndUpKeyEvents(final int code) throws Throwable { + long eventTime = SystemClock.uptimeMillis(); + dispatchKeyEvent(new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, code, 0)); + dispatchKeyEvent(new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP, code, 0)); + } + + private boolean dispatchKeyEvent(final KeyEvent event) throws Throwable { + return TestThreadUtils.runOnUiThreadBlocking(() -> mActivity.dispatchKeyEvent(event)); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BackgroundFetchTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BackgroundFetchTest.java new file mode 100644 index 0000000..263049c --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BackgroundFetchTest.java
@@ -0,0 +1,145 @@ +// 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. + +package org.chromium.weblayer.test; + +import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking; + +import android.app.Notification; +import android.app.NotificationManager; +import android.content.Context; +import android.os.Build; +import android.service.notification.StatusBarNotification; + +import androidx.annotation.RequiresApi; +import androidx.test.filters.LargeTest; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.base.test.util.MinAndroidSdkLevel; +import org.chromium.weblayer.Browser; +import org.chromium.weblayer.OpenUrlCallback; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.TestWebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Tests Background Fetch and the OpenUrlCallback API. + */ +@MinWebLayerVersion(91) +@RunWith(WebLayerJUnit4ClassRunner.class) +public class BackgroundFetchTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + + private TestWebLayer getTestWebLayer() { + return TestWebLayer.getTestWebLayer(mActivity.getApplicationContext()); + } + + @Before + public void setUp() throws Exception { + mActivity = mActivityTestRule.launchShellWithUrl(mActivityTestRule.getTestServer().getURL( + "/weblayer/test/data/background_fetch/index.html")); + getTestWebLayer().expediteDownloadService(); + } + + @Test + @LargeTest + @DisabledTest(message = "https://crbug.com/1272010") + @MinAndroidSdkLevel(Build.VERSION_CODES.M) + public void basic() throws Exception { + Browser browser = mActivity.getBrowser(); + + int numTabs = runOnUiThreadBlocking(() -> { return browser.getTabs().size(); }); + Assert.assertEquals(1, numTabs); + + CallbackHelper tabAddedCallback = new CallbackHelper(); + OpenUrlCallback openUrlCallback = new OpenUrlCallback() { + @Override + public Browser getBrowserForNewTab() { + return browser; + } + + @Override + public void onTabAdded(Tab tab) { + tabAddedCallback.notifyCalled(); + } + }; + + runOnUiThreadBlocking( + () -> { browser.getProfile().setTablessOpenUrlCallback(openUrlCallback); }); + + Assert.assertNull(getBackgroundFetchNotification(null)); + + EventUtils.simulateTouchCenterOfView( + mActivityTestRule.getActivity().getWindow().getDecorView()); + + // Wait for the notification to appear. + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(getBackgroundFetchNotification(null), Matchers.notNullValue()); + }, CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL * 2, CriteriaHelper.DEFAULT_POLLING_INTERVAL); + + // This part of the test passes locally but fails on some bots because the download never + // completes. TODO(estade): this also fails now that permissions are extra strict, so + // enabling this would require side-stepping permissions for this test. See + // crbug.com/1189247 + // + // Wait for the notification to indicate completion (also testing the page receives the + // success message). + // CriteriaHelper.pollInstrumentationThread(() -> { + // Criteria.checkThat( + // getBackgroundFetchNotification("New Fetched Title!"), + // Matchers.notNullValue()); + // }, 30000, 100); + + // -1 should be the ID of the first notification. + getTestWebLayer().activateBackgroundFetchNotification(-1); + + // Tapping should add a new tab. + tabAddedCallback.waitForFirst(); + + numTabs = runOnUiThreadBlocking(() -> { return browser.getTabs().size(); }); + Assert.assertEquals(2, numTabs); + } + + /** + * Retrieves the first active background fetch notification it finds, or null if none exists. + * + * + * {@link NotificationManager#getActiveNotifications()} is only available from M. + * + * @param expectedTitle The title of the notification in question, or null if any notification + * will do. + * @return The matched notification, or null. + */ + @RequiresApi(Build.VERSION_CODES.M) + private Notification getBackgroundFetchNotification(String expectedTitle) { + StatusBarNotification notifications[] = + ((NotificationManager) mActivity.getApplicationContext().getSystemService( + Context.NOTIFICATION_SERVICE)) + .getActiveNotifications(); + for (StatusBarNotification statusBarNotification : notifications) { + if (statusBarNotification.getTag().equals("org.chromium.weblayer.downloads")) { + Notification notification = statusBarNotification.getNotification(); + if (expectedTitle == null) return notification; + + String title = notification.extras.getString(Notification.EXTRA_TITLE); + if (title != null && title.contains(expectedTitle)) return notification; + } + } + return null; + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BoundedCountDownLatch.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BoundedCountDownLatch.java new file mode 100644 index 0000000..d7a8aa7 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BoundedCountDownLatch.java
@@ -0,0 +1,34 @@ +// Copyright 2020 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.weblayer.test; + +import org.junit.Assert; + +import org.chromium.base.test.util.CallbackHelper; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * A CountDownLatch with a default timeout. + */ +public class BoundedCountDownLatch extends CountDownLatch { + BoundedCountDownLatch(int count) { + super(count); + } + + /** + * This fails more quickly and gracefully than {@link CountDownLatch#await()}, which has no + * timeout. It gives useful error output, whereas a test that times out in {@link await()} may + * leave no stack. + */ + public void timedAwait() { + try { + Assert.assertTrue(super.await(CallbackHelper.WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS)); + } catch (InterruptedException e) { + Assert.fail(e.toString()); + } + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserControlsHelper.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserControlsHelper.java new file mode 100644 index 0000000..428cec4 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserControlsHelper.java
@@ -0,0 +1,99 @@ +// Copyright 2020 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.weblayer.test; + +import android.os.RemoteException; +import android.view.View; + +import org.hamcrest.Matchers; +import org.junit.Assert; + +import org.chromium.base.CommandLine; +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.TestWebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Helper class for tests that interact with browser controls. Such tests should add the following + * above "public class FooTest": + * @CommandLineFlags.Add("enable-features=ImmediatelyHideBrowserControlsForTest") + */ +public final class BrowserControlsHelper { + private InstrumentationActivity mActivity; + + // Height of the top-view. Set in waitForBrowserControlsInitialization(). + private int mTopViewHeight; + + // Blocks until browser controls are fully initialized. Should only be created in a test's + // setUp() method; see BrowserControlsHelper#createInSetUp(). + private BrowserControlsHelper(InstrumentationActivity activity) throws Exception { + Assert.assertTrue(CommandLine.isInitialized()); + Assert.assertTrue(CommandLine.getInstance().hasSwitch("enable-features")); + String enabledFeatures = CommandLine.getInstance().getSwitchValue("enable-features"); + Assert.assertTrue(enabledFeatures.contains("ImmediatelyHideBrowserControlsForTest")); + + mActivity = activity; + + waitForBrowserControlsInitialization(); + } + + private Tab getActiveTab() { + return mActivity.getBrowser().getActiveTab(); + } + + private TestWebLayer getTestWebLayer() { + return TestWebLayer.getTestWebLayer(mActivity.getApplicationContext()); + } + + void waitForBrowserControlsViewToBeVisible(View v) { + CriteriaHelper.pollUiThread(() -> { + Criteria.checkThat(v.getHeight(), Matchers.greaterThan(0)); + Criteria.checkThat(v.getVisibility(), Matchers.is(View.VISIBLE)); + }); + } + + // See TestWebLayer.waitForBrowserControlsMetadataState() for details on this. + void waitForBrowserControlsMetadataState(int top, int bottom) throws Exception { + CallbackHelper helper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + try { + getTestWebLayer().waitForBrowserControlsMetadataState( + getActiveTab(), top, bottom, () -> { helper.notifyCalled(); }); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + }); + helper.waitForFirst(); + } + + // Ensures that browser controls are fully initialized and ready for scrolls to be processed. + private void waitForBrowserControlsInitialization() throws Exception { + // Poll until the top view becomes visible. + waitForBrowserControlsViewToBeVisible(mActivity.getTopContentsContainer()); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mTopViewHeight = mActivity.getTopContentsContainer().getHeight(); + Assert.assertTrue(mTopViewHeight > 0); + }); + + // Wait for cc to see the top-controls height. + waitForBrowserControlsMetadataState(mTopViewHeight, 0); + } + + // Creates a BrowserControlsHelper instance and blocks until browser controls are fully + // initialized. Should be called from a test's setUp() method. + static BrowserControlsHelper createAndBlockUntilBrowserControlsInitializedInSetUp( + InstrumentationActivity activity) throws Exception { + return new BrowserControlsHelper(activity); + } + + // Returns the height of the top view. + int getTopViewHeight() { + return mTopViewHeight; + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserControlsOffsetCallbackTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserControlsOffsetCallbackTest.java new file mode 100644 index 0000000..1f8a482 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserControlsOffsetCallbackTest.java
@@ -0,0 +1,208 @@ +// Copyright 2020 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.weblayer.test; + +import android.graphics.Point; +import android.os.SystemClock; +import android.util.Range; +import android.view.MotionEvent; +import android.view.View; +import android.widget.TextView; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.BrowserControlsOffsetCallback; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.util.ArrayList; +import java.util.List; + +/** + * Test for ScrollOffsetCallback. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +@CommandLineFlags.Add("enable-features=ImmediatelyHideBrowserControlsForTest") +@DisabledTest(message = "https://crbug.com/1315399") +public class BrowserControlsOffsetCallbackTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private static final class BrowserControlsOffsetCallbackImpl + extends BrowserControlsOffsetCallback { + // All offsets are added to either |mTopOffsets| or |mBottomOffsets|. + public List<Integer> mTopOffsets = new ArrayList<>(); + public List<Integer> mBottomOffsets = new ArrayList<>(); + public CallbackHelper mCallbackHelper = new CallbackHelper(); + // If non-null an offset and an offset lies in the specified range + // mCallbackHelper.notifyCalled() is invoked. + public Range<Integer> mTopTriggerRange; + public Range<Integer> mBottomTriggerRange; + + @Override + public void onTopViewOffsetChanged(int offset) { + mTopOffsets.add(offset); + if (mTopTriggerRange != null && mTopTriggerRange.contains(offset)) { + mTopTriggerRange = null; + mCallbackHelper.notifyCalled(); + } + } + + @Override + public void onBottomViewOffsetChanged(int offset) { + mBottomOffsets.add(offset); + if (mBottomTriggerRange != null && mBottomTriggerRange.contains(offset)) { + mBottomTriggerRange = null; + mCallbackHelper.notifyCalled(); + } + } + } + + private BrowserControlsOffsetCallbackImpl mBrowserControlsOffsetCallback = + new BrowserControlsOffsetCallbackImpl(); + + private BrowserControlsHelper mBrowserControlsHelper; + private Point mCurrentDragPoint; + private boolean mInDrag; + + @Before + public void setUp() throws Throwable { + final String url = mActivityTestRule.getTestDataURL("tall_page.html"); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(url); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + activity.getBrowser().registerBrowserControlsOffsetCallback( + mBrowserControlsOffsetCallback); + }); + + mBrowserControlsHelper = + BrowserControlsHelper.createAndBlockUntilBrowserControlsInitializedInSetUp( + activity); + } + + private void startDrag() { + View view = mActivityTestRule.getActivity().getWindow().getDecorView(); + Assert.assertFalse(mInDrag); + mInDrag = true; + view.post(() -> { + mCurrentDragPoint = new Point(view.getWidth() / 2, view.getHeight() / 2); + long eventTime = SystemClock.uptimeMillis(); + view.dispatchTouchEvent(MotionEvent.obtain(eventTime, eventTime, + MotionEvent.ACTION_DOWN, mCurrentDragPoint.x, mCurrentDragPoint.y, 0)); + }); + } + + private void dragBy(final int deltaY) { + View view = mActivityTestRule.getActivity().getWindow().getDecorView(); + Assert.assertTrue(mInDrag); + view.post(() -> { + long eventTime = SystemClock.uptimeMillis(); + mCurrentDragPoint.y += deltaY; + view.dispatchTouchEvent(MotionEvent.obtain(eventTime, eventTime, + MotionEvent.ACTION_MOVE, mCurrentDragPoint.x, mCurrentDragPoint.y, 0)); + }); + } + + @Test + @SmallTest + public void testTopScroll() throws Exception { + int topViewHeight = mBrowserControlsHelper.getTopViewHeight(); + CallbackHelper callbackHelper = mBrowserControlsOffsetCallback.mCallbackHelper; + + // Scroll half the height. + mBrowserControlsOffsetCallback.mTopTriggerRange = new Range(-topViewHeight + 2, -2); + startDrag(); + dragBy(-topViewHeight / 2); + int callCount = 0; + callbackHelper.waitForCallback(callCount++); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mBrowserControlsOffsetCallback.mTopOffsets.clear(); + mBrowserControlsOffsetCallback.mTopTriggerRange = + new Range(-topViewHeight, -topViewHeight); + }); + + // Scroll a lot to ensure top view completely hides. + dragBy(-topViewHeight); + callbackHelper.waitForCallback(callCount++); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mBrowserControlsOffsetCallback.mTopOffsets.clear(); + mBrowserControlsOffsetCallback.mTopTriggerRange = new Range(-topViewHeight + 2, -2); + }); + + // Scroll up half the height to trigger showing again. + dragBy(topViewHeight / 2); + callbackHelper.waitForCallback(callCount++); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mBrowserControlsOffsetCallback.mTopOffsets.clear(); + mBrowserControlsOffsetCallback.mTopTriggerRange = new Range(0, 0); + }); + + // And enough to be fully visible. + dragBy(topViewHeight); + callbackHelper.waitForCallback(callCount++); + } + + @Test + @SmallTest + public void testBottomScroll() throws Exception { + CallbackHelper callbackHelper = mBrowserControlsOffsetCallback.mCallbackHelper; + int topViewHeight = mBrowserControlsHelper.getTopViewHeight(); + InstrumentationActivity activity = mActivityTestRule.getActivity(); + View bottomView = TestThreadUtils.runOnUiThreadBlocking(() -> { + TextView view = new TextView(activity); + view.setText("BOTTOM"); + activity.getBrowser().setBottomView(view); + return view; + }); + + mBrowserControlsHelper.waitForBrowserControlsViewToBeVisible(bottomView); + + int bottomViewHeight = + TestThreadUtils.runOnUiThreadBlocking(() -> { return bottomView.getHeight(); }); + Assert.assertTrue(bottomViewHeight > 0); + // The amount necessary to scroll is the sum of the two views. This is because the page + // height is reduced by the sum of these two. + int maxViewsHeight = topViewHeight + bottomViewHeight; + + // Wait for cc to see the bottom height. This is very important, as scrolling is gated by + // cc getting the bottom height. + mBrowserControlsHelper.waitForBrowserControlsMetadataState(topViewHeight, bottomViewHeight); + + // Move by the size of the controls, which should hide both. + TestThreadUtils.runOnUiThreadBlocking(() -> { + mBrowserControlsOffsetCallback.mTopTriggerRange = + new Range(-topViewHeight, -topViewHeight); + mBrowserControlsOffsetCallback.mBottomTriggerRange = + new Range(bottomViewHeight, bottomViewHeight); + }); + EventUtils.simulateDragFromCenterOfView( + activity.getWindow().getDecorView(), 0, -maxViewsHeight); + int callCount = 0; + // 2 is for the top and bottom. + callbackHelper.waitForCallback(callCount, 2); + callCount += 2; + + // Move so top and bottom controls are shown again. + TestThreadUtils.runOnUiThreadBlocking(() -> { + mBrowserControlsOffsetCallback.mTopTriggerRange = new Range(0, 0); + mBrowserControlsOffsetCallback.mBottomTriggerRange = new Range(0, 0); + }); + EventUtils.simulateDragFromCenterOfView( + activity.getWindow().getDecorView(), 0, maxViewsHeight); + // 2 is for the top and bottom. + callbackHelper.waitForCallback(callCount, 2); + callCount += 2; + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserControlsTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserControlsTest.java new file mode 100644 index 0000000..1301925c2 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserControlsTest.java
@@ -0,0 +1,636 @@ +// Copyright 2020 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.weblayer.test; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +import android.app.Instrumentation; +import android.os.Build; +import android.os.Bundle; +import android.os.RemoteException; +import android.support.test.InstrumentationRegistry; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.TextView; + +import androidx.test.filters.SmallTest; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.base.test.util.MinAndroidSdkLevel; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.content_public.browser.test.util.TestTouchUtils; +import org.chromium.weblayer.BrowserControlsOffsetCallback; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.TestWebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Test for bottom-controls. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +@CommandLineFlags.Add("enable-features=ImmediatelyHideBrowserControlsForTest") +public class BrowserControlsTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private BrowserControlsHelper mBrowserControlsHelper; + + // Height of the top-view. Set in setUp(). + private int mTopViewHeight; + // Height from the page (obtained using getVisiblePageHeight()) with the top-controls. + private int mPageHeightWithTopView; + + /** + * Returns the visible height of the page as determined by JS. The returned value is in CSS + * pixels (which are most likely not the same as device pixels). + */ + private int getVisiblePageHeight() { + return mActivityTestRule.executeScriptAndExtractInt("window.innerHeight"); + } + + private Tab getActiveTab() { + return mActivityTestRule.getActivity().getBrowser().getActiveTab(); + } + + private TestWebLayer getTestWebLayer() { + return TestWebLayer.getTestWebLayer( + mActivityTestRule.getActivity().getApplicationContext()); + } + + private void setAccessibilityEnabled(boolean value) { + TestThreadUtils.runOnUiThreadBlocking(() -> { + try { + getTestWebLayer().setAccessibilityEnabled(value); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + }); + } + + private boolean canBrowserControlsScroll() { + return TestThreadUtils.runOnUiThreadBlockingNoException(() -> { + try { + return getTestWebLayer().canBrowserControlsScroll(getActiveTab()); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + }); + } + + private void createActivityWithTopView() throws Exception { + final String url = mActivityTestRule.getTestDataURL("tall_page.html"); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(url); + + mBrowserControlsHelper = + BrowserControlsHelper.createAndBlockUntilBrowserControlsInitializedInSetUp( + activity); + + mTopViewHeight = mBrowserControlsHelper.getTopViewHeight(); + mPageHeightWithTopView = getVisiblePageHeight(); + } + + private View createBottomView() throws Exception { + InstrumentationActivity activity = mActivityTestRule.getActivity(); + View bottomView = TestThreadUtils.runOnUiThreadBlocking(() -> { + TextView view = new TextView(activity); + view.setText("BOTTOM"); + activity.getBrowser().setBottomView(view); + return view; + }); + mBrowserControlsHelper.waitForBrowserControlsViewToBeVisible(bottomView); + int bottomViewHeight = + TestThreadUtils.runOnUiThreadBlocking(() -> { return bottomView.getHeight(); }); + mBrowserControlsHelper.waitForBrowserControlsMetadataState( + mTopViewHeight, bottomViewHeight); + return bottomView; + } + + // Disabled on L bots due to unexplained flakes. See crbug.com/1035894. + @MinAndroidSdkLevel(Build.VERSION_CODES.M) + @Test + @SmallTest + public void testTopAndBottom() throws Exception { + createActivityWithTopView(); + InstrumentationActivity activity = mActivityTestRule.getActivity(); + View bottomView = TestThreadUtils.runOnUiThreadBlocking(() -> { + TextView view = new TextView(activity); + view.setText("BOTTOM"); + activity.getBrowser().setBottomView(view); + return view; + }); + + mBrowserControlsHelper.waitForBrowserControlsViewToBeVisible(bottomView); + + int bottomViewHeight = + TestThreadUtils.runOnUiThreadBlocking(() -> { return bottomView.getHeight(); }); + Assert.assertTrue(bottomViewHeight > 0); + // The amount necessary to scroll is the sum of the two views. This is because the page + // height is reduced by the sum of these two. + int maxViewsHeight = mTopViewHeight + bottomViewHeight; + + // Wait for cc to see the bottom height. This is very important, as scrolling is gated by + // cc getting the bottom height. + mBrowserControlsHelper.waitForBrowserControlsMetadataState( + mTopViewHeight, bottomViewHeight); + + // Adding a bottom view should change the page height. + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(getVisiblePageHeight(), Matchers.not(mPageHeightWithTopView)); + }); + int pageHeightWithTopAndBottomViews = getVisiblePageHeight(); + Assert.assertTrue(pageHeightWithTopAndBottomViews < mPageHeightWithTopView); + + // Move by the size of the controls. + EventUtils.simulateDragFromCenterOfView( + activity.getWindow().getDecorView(), 0, -maxViewsHeight); + + // Moving should hide the bottom View. + CriteriaHelper.pollUiThread(() -> { + Criteria.checkThat(bottomView.getVisibility(), Matchers.is(View.INVISIBLE)); + }); + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat( + getVisiblePageHeight(), Matchers.greaterThan(mPageHeightWithTopView)); + }); + + // Move so top and bottom-controls are shown again. + EventUtils.simulateDragFromCenterOfView( + activity.getWindow().getDecorView(), 0, maxViewsHeight); + + mBrowserControlsHelper.waitForBrowserControlsViewToBeVisible(bottomView); + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat( + getVisiblePageHeight(), Matchers.is(pageHeightWithTopAndBottomViews)); + }); + } + + // Disabled on L bots due to unexplained flakes. See crbug.com/1035894. + @MinAndroidSdkLevel(Build.VERSION_CODES.M) + @Test + @SmallTest + public void testBottomOnly() throws Exception { + createActivityWithTopView(); + InstrumentationActivity activity = mActivityTestRule.getActivity(); + // Remove the top-view. + TestThreadUtils.runOnUiThreadBlocking(() -> { activity.getBrowser().setTopView(null); }); + + // Wait for cc to see the top-controls height change. + mBrowserControlsHelper.waitForBrowserControlsMetadataState(0, 0); + + int pageHeightWithNoTopView = getVisiblePageHeight(); + Assert.assertNotEquals(pageHeightWithNoTopView, mPageHeightWithTopView); + + // Add in the bottom-view. + View bottomView = TestThreadUtils.runOnUiThreadBlocking(() -> { + TextView view = new TextView(activity); + view.setText("BOTTOM"); + activity.getBrowser().setBottomView(view); + return view; + }); + + mBrowserControlsHelper.waitForBrowserControlsViewToBeVisible(bottomView); + int bottomViewHeight = + TestThreadUtils.runOnUiThreadBlocking(() -> { return bottomView.getHeight(); }); + Assert.assertTrue(bottomViewHeight > 0); + // Wait for cc to see the bottom-controls height change. + mBrowserControlsHelper.waitForBrowserControlsMetadataState(0, bottomViewHeight); + + int pageHeightWithBottomView = getVisiblePageHeight(); + Assert.assertNotEquals(pageHeightWithNoTopView, pageHeightWithBottomView); + + // Move by the size of the bottom-controls. + EventUtils.simulateDragFromCenterOfView( + activity.getWindow().getDecorView(), 0, -bottomViewHeight); + + // Moving should hide the bottom-controls View. + CriteriaHelper.pollUiThread(() -> { + Criteria.checkThat(bottomView.getVisibility(), Matchers.is(View.INVISIBLE)); + }); + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(getVisiblePageHeight(), Matchers.is(pageHeightWithNoTopView)); + }); + + // Move so bottom-controls are shown again. + EventUtils.simulateDragFromCenterOfView( + activity.getWindow().getDecorView(), 0, bottomViewHeight); + + mBrowserControlsHelper.waitForBrowserControlsViewToBeVisible(bottomView); + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(getVisiblePageHeight(), Matchers.is(pageHeightWithBottomView)); + }); + } + + // Disabled on L bots due to unexplained flakes. See crbug.com/1035894. + @MinAndroidSdkLevel(Build.VERSION_CODES.M) + @Test + @SmallTest + public void testTopOnly() throws Exception { + createActivityWithTopView(); + InstrumentationActivity activity = mActivityTestRule.getActivity(); + View topView = activity.getTopContentsContainer(); + + // Move by the size of the top-controls. + EventUtils.simulateDragFromCenterOfView( + activity.getWindow().getDecorView(), 0, -mTopViewHeight); + + // Moving should hide the top-controls and change the page height. + CriteriaHelper.pollUiThread( + () -> Criteria.checkThat(topView.getVisibility(), Matchers.is(View.INVISIBLE))); + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(getVisiblePageHeight(), Matchers.not(mPageHeightWithTopView)); + }); + + // Move so top-controls are shown again. + EventUtils.simulateDragFromCenterOfView( + activity.getWindow().getDecorView(), 0, mTopViewHeight); + + // Wait for the page height to match initial height. + mBrowserControlsHelper.waitForBrowserControlsViewToBeVisible(topView); + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(getVisiblePageHeight(), Matchers.is(mPageHeightWithTopView)); + }); + } + + @MinWebLayerVersion(94) + @Test + @SmallTest + public void testEvents() throws Exception { + createActivityWithTopView(); + InstrumentationActivity activity = mActivityTestRule.getActivity(); + Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + View topView = activity.getTopContentsContainer(); + View bottomView = createBottomView(); + + int fragmentHeight = TestThreadUtils.runOnUiThreadBlocking( + () -> { return mActivityTestRule.getFragment().getView().getHeight(); }); + int pageHeight = getVisiblePageHeight(); + + // Record the maximum y position of clicks to detect if we clicked at the top or bottom. + mActivityTestRule.executeScriptSync( + "var max = 0; document.onclick = (e) => { max = Math.max(max, e.pageY); };", + true /* useSeparateIsolate */); + + // Clicks on the bottom view should not propagate to the content layer. + TestTouchUtils.singleClickView(instrumentation, bottomView, 0, 0); + TestTouchUtils.sleepForDoubleTapTimeout(instrumentation); + + // Click below the top view inside the content layer in the middle of the page. + TestTouchUtils.singleClickView( + instrumentation, topView, 0, topView.getHeight() + fragmentHeight / 2); + TestTouchUtils.sleepForDoubleTapTimeout(instrumentation); + + // Wait until we see the click from the middle of the page. + CriteriaHelper.pollInstrumentationThread(() -> { + int max = mActivityTestRule.executeScriptAndExtractInt("max"); + Criteria.checkThat(max, Matchers.greaterThan(pageHeight * 1 / 4)); + Criteria.checkThat(max, Matchers.lessThan(pageHeight * 3 / 4)); + }); + } + + @MinWebLayerVersion(100) + @Test + @SmallTest + public void testEventBelowView() throws Exception { + createActivityWithTopView(); + InstrumentationActivity activity = mActivityTestRule.getActivity(); + Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + View topContents = activity.getTopContentsContainer(); + + View fragmentView = TestThreadUtils.runOnUiThreadBlocking( + () -> { return mActivityTestRule.getFragment().getView(); }); + + // Move by the size of the top-controls. + EventUtils.simulateDragFromCenterOfView( + activity.getWindow().getDecorView(), 0, -mTopViewHeight); + + // Moving should collapse the top-controls to their min height and change the page height. + CriteriaHelper.pollUiThread(() -> { + Criteria.checkThat(topContents.getVisibility(), Matchers.is(View.INVISIBLE)); + }); + // Click to stop any flings. + TestTouchUtils.singleClickView(instrumentation, activity.getWindow().getDecorView()); + TestTouchUtils.sleepForDoubleTapTimeout(instrumentation); + + // Record when page is clicked. + mActivityTestRule.executeScriptSync( + "var didClick = false; document.onclick = (e) => { didClick = true; };", + true /* useSeparateIsolate */); + + // Click the area inside where top control would be if it's not hidden. + TestTouchUtils.singleClickView(instrumentation, fragmentView, 0, mTopViewHeight / 2); + TestTouchUtils.sleepForDoubleTapTimeout(instrumentation); + + // Wait until page sees click. + CriteriaHelper.pollInstrumentationThread(() -> { + boolean didClick = mActivityTestRule.executeScriptAndExtractBoolean("didClick"); + Criteria.checkThat(didClick, Matchers.is(true)); + }); + } + + // Disabled on L bots due to unexplained flakes. See crbug.com/1035894. + @MinAndroidSdkLevel(Build.VERSION_CODES.M) + @Test + @SmallTest + public void testTopMinHeight() throws Exception { + createActivityWithTopView(); + final int minHeight = 20; + InstrumentationActivity activity = mActivityTestRule.getActivity(); + View topContents = activity.getTopContentsContainer(); + TestThreadUtils.runOnUiThreadBlocking( + () -> activity.getBrowser().setTopView(topContents, minHeight, false, false)); + int expectedCollapseAmount = topContents.getHeight() - minHeight; + + // Make sure the top controls start out taller than the min height. + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(topContents.getHeight(), Matchers.greaterThan(minHeight)); + }); + + // Move by the size of the top-controls. + EventUtils.simulateDragFromCenterOfView( + activity.getWindow().getDecorView(), 0, -mTopViewHeight); + + // Moving should collapse the top-controls to their min height and change the page height. + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat( + getVisiblePageHeight(), Matchers.greaterThan(mPageHeightWithTopView)); + Criteria.checkThat( + topContents.getTranslationY(), Matchers.is((float) -expectedCollapseAmount)); + }); + + // Move so top-controls are shown again. + EventUtils.simulateDragFromCenterOfView( + activity.getWindow().getDecorView(), 0, mTopViewHeight); + + // Wait for the page height to match initial height. + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(getVisiblePageHeight(), Matchers.is(mPageHeightWithTopView)); + }); + } + + // Disabled on L bots due to unexplained flakes. See crbug.com/1035894. + @MinAndroidSdkLevel(Build.VERSION_CODES.M) + @DisabledTest(message = "https://crbug.com/1256861") + @Test + @SmallTest + public void testOnlyExpandTopControlsAtPageTop() throws Exception { + createActivityWithTopView(); + InstrumentationActivity activity = mActivityTestRule.getActivity(); + View topContents = activity.getTopContentsContainer(); + TestThreadUtils.runOnUiThreadBlocking( + () + -> activity.getBrowser().setTopView( + topContents, 0, /*onlyExpandControlsAtPageTop=*/true, false)); + + // Scroll down past the top-controls, which should collapse the top-controls and change the + // page height. + EventUtils.simulateDragFromCenterOfView( + activity.getWindow().getDecorView(), 0, -2 * mTopViewHeight); + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat( + getVisiblePageHeight(), Matchers.greaterThan(mPageHeightWithTopView)); + Criteria.checkThat(activity.getTopContentsContainer().getVisibility(), + Matchers.is(View.INVISIBLE)); + }); + + // Scroll part of the way up again, which should not show the top controls. + int scrolledPageHeight = getVisiblePageHeight(); + EventUtils.simulateDragFromCenterOfView( + activity.getWindow().getDecorView(), 0, mTopViewHeight); + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(getVisiblePageHeight(), Matchers.is(scrolledPageHeight)); + }); + + // Scroll to the top to show the top controls. + EventUtils.simulateDragFromCenterOfView( + activity.getWindow().getDecorView(), 0, 2 * mTopViewHeight); + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(getVisiblePageHeight(), Matchers.is(mPageHeightWithTopView)); + Criteria.checkThat( + activity.getTopContentsContainer().getVisibility(), Matchers.is(View.VISIBLE)); + Criteria.checkThat(topContents.getTranslationY(), Matchers.is(0.f)); + }); + } + + // Makes sure that the top controls are not shown when a js dialog is shown when + // onlyExpandTopControlsAtPageTop is true and the page is scrolled down. + // + // Disabled on L bots due to unexplained flakes. See crbug.com/1035894. + @MinAndroidSdkLevel(Build.VERSION_CODES.M) + @DisabledTest(message = "https://crbug.com/1319870") + @Test + @SmallTest + public void testAlertDoesntShowTopControlsIfOnlyExpandTopControlsAtPageTop() throws Exception { + createActivityWithTopView(); + InstrumentationActivity activity = mActivityTestRule.getActivity(); + View topContents = activity.getTopContentsContainer(); + TestThreadUtils.runOnUiThreadBlocking( + () + -> activity.getBrowser().setTopView( + topContents, 0, /*onlyExpandControlsAtPageTop=*/true, false)); + + // Scroll past the top-controls and wait until they're not visible. + EventUtils.simulateDragFromCenterOfView( + activity.getWindow().getDecorView(), 0, -2 * mTopViewHeight); + CriteriaHelper.pollUiThread(() -> { + Criteria.checkThat(activity.getTopContentsContainer().getVisibility(), + Matchers.is(View.INVISIBLE)); + }); + + // Trigger an alert dialog. + mActivityTestRule.executeScriptSync( + "window.setTimeout(function() { alert('alert dialog'); }, 1);", false); + onView(withText("alert dialog")).check(matches(isDisplayed())); + + // Top controls shouldn't be shown. + CriteriaHelper.pollUiThread(() -> { + Criteria.checkThat(activity.getTopContentsContainer().getVisibility(), + Matchers.is(View.INVISIBLE)); + }); + } + + /** + * Makes sure that the top controls are shown when a js dialog is shown. + * + * Regression test for https://crbug.com/1078181. + * + * Disabled on L bots due to unexplained flakes. See crbug.com/1035894. + */ + @MinAndroidSdkLevel(Build.VERSION_CODES.M) + @Test + @SmallTest + public void testAlertShowsTopControls() throws Exception { + createActivityWithTopView(); + InstrumentationActivity activity = mActivityTestRule.getActivity(); + + // Move by the size of the top-controls. + EventUtils.simulateDragFromCenterOfView( + activity.getWindow().getDecorView(), 0, -mTopViewHeight); + + // Wait till top controls are invisible. + CriteriaHelper.pollUiThread(() -> { + Criteria.checkThat(activity.getTopContentsContainer().getVisibility(), + Matchers.is(View.INVISIBLE)); + }); + + // Trigger an alert dialog. + mActivityTestRule.executeScriptSync( + "window.setTimeout(function() { alert('alert'); }, 1);", false); + + // Top controls are shown. + CriteriaHelper.pollUiThread(() -> { + Criteria.checkThat( + activity.getTopContentsContainer().getVisibility(), Matchers.is(View.VISIBLE)); + }); + } + + // Tests various assertions when accessibility is enabled. + // Disabled on L bots due to unexplained flakes. See crbug.com/1035894. + @MinAndroidSdkLevel(Build.VERSION_CODES.M) + @Test + @SmallTest + public void testAccessibility() throws Exception { + createActivityWithTopView(); + InstrumentationActivity activity = mActivityTestRule.getActivity(); + + // Scroll such that top-controls are hidden. + EventUtils.simulateDragFromCenterOfView( + activity.getWindow().getDecorView(), 0, -mTopViewHeight); + View topView = activity.getTopContentsContainer(); + CriteriaHelper.pollUiThread( + () -> Criteria.checkThat(topView.getVisibility(), Matchers.is(View.INVISIBLE))); + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(getVisiblePageHeight(), Matchers.not(mPageHeightWithTopView)); + }); + + // Turn on accessibility, which should force the controls to show. + setAccessibilityEnabled(true); + mBrowserControlsHelper.waitForBrowserControlsViewToBeVisible( + activity.getTopContentsContainer()); + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(getVisiblePageHeight(), Matchers.is(mPageHeightWithTopView)); + }); + + // When accessibility is enabled, the controls are not allowed to scroll. + Assert.assertFalse(canBrowserControlsScroll()); + + // Turn accessibility off, and verify the controls can scroll. This polls as + // setAccessibilityEnabled() is async. + setAccessibilityEnabled(false); + CriteriaHelper.pollInstrumentationThread( + () -> Criteria.checkThat(canBrowserControlsScroll(), Matchers.is(true))); + } + + @MinAndroidSdkLevel(Build.VERSION_CODES.M) + @Test + @SmallTest + public void testRemoveAllFromTopView() throws Exception { + createActivityWithTopView(); + InstrumentationActivity activity = mActivityTestRule.getActivity(); + + // Install a different top-view. + FrameLayout newTopView = TestThreadUtils.runOnUiThreadBlocking(() -> { + FrameLayout frameLayout = new FrameLayout(activity); + TextView textView = new TextView(activity); + textView.setText("new top"); + frameLayout.addView(textView, + new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT, + FrameLayout.LayoutParams.UNSPECIFIED_GRAVITY)); + activity.getBrowser().setTopView(frameLayout); + return frameLayout; + }); + + // Wait till new view is visible. + CriteriaHelper.pollUiThread( + () -> Criteria.checkThat(newTopView.getVisibility(), Matchers.is(View.VISIBLE))); + int newTopViewHeight = + TestThreadUtils.runOnUiThreadBlocking(() -> { return newTopView.getHeight(); }); + Assert.assertNotEquals(newTopViewHeight, mTopViewHeight); + Assert.assertTrue(newTopViewHeight > 0); + mBrowserControlsHelper.waitForBrowserControlsMetadataState(newTopViewHeight, 0); + + // Remove all, and ensure metadata and page-height change. + TestThreadUtils.runOnUiThreadBlocking(() -> { newTopView.removeAllViews(); }); + mBrowserControlsHelper.waitForBrowserControlsMetadataState(0, 0); + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(getVisiblePageHeight(), Matchers.not(mPageHeightWithTopView)); + }); + } + + private void registerBrowserControlsOffsetCallbackForOffset( + CallbackHelper helper, int targetOffset) { + InstrumentationActivity activity = mActivityTestRule.getActivity(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + activity.getBrowser().registerBrowserControlsOffsetCallback( + new BrowserControlsOffsetCallback() { + @Override + public void onTopViewOffsetChanged(int offset) { + if (offset == targetOffset) { + activity.getBrowser().unregisterBrowserControlsOffsetCallback(this); + helper.notifyCalled(); + } + } + }); + }); + } + + @MinAndroidSdkLevel(Build.VERSION_CODES.M) + @Test + @SmallTest + public void testTopExpandedWhenOnlyExpandAtTop() throws Exception { + Bundle extras = new Bundle(); + extras.putBoolean(InstrumentationActivity.EXTRA_ONLY_EXPAND_CONTROLS_AT_TOP, true); + final String url = mActivityTestRule.getTestDataURL("tall_page.html"); + InstrumentationActivity activity = mActivityTestRule.launchShell(extras); + CallbackHelper helper = new CallbackHelper(); + registerBrowserControlsOffsetCallbackForOffset(helper, 0); + mActivityTestRule.navigateAndWait(url); + helper.waitForFirst(); + } + + @MinAndroidSdkLevel(Build.VERSION_CODES.M) + @Test + @SmallTest + public void testTopExpandedOnLoadWhenOnlyExpandAtTop() throws Exception { + Bundle extras = new Bundle(); + extras.putBoolean(InstrumentationActivity.EXTRA_ONLY_EXPAND_CONTROLS_AT_TOP, true); + InstrumentationActivity activity = mActivityTestRule.launchShell(extras); + CallbackHelper helper = new CallbackHelper(); + registerBrowserControlsOffsetCallbackForOffset(helper, 0); + mActivityTestRule.navigateAndWait(mActivityTestRule.getTestDataURL("tall_page.html")); + int callCount = 0; + helper.waitForCallback(callCount++); + + mTopViewHeight = TestThreadUtils.runOnUiThreadBlocking( + () -> { return activity.getTopContentsContainer().getHeight(); }); + Assert.assertNotEquals(mTopViewHeight, 0); + + // Scroll such that top-controls are hidden. + registerBrowserControlsOffsetCallbackForOffset(helper, -mTopViewHeight); + EventUtils.simulateDragFromCenterOfView( + activity.getWindow().getDecorView(), 0, -mTopViewHeight); + helper.waitForCallback(callCount++); + + // Load a new page. The top-controls should be shown again. + registerBrowserControlsOffsetCallbackForOffset(helper, 0); + mActivityTestRule.navigateAndWait(mActivityTestRule.getTestDataURL("simple_page.html")); + helper.waitForCallback(callCount++); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserFragmentLifecycleTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserFragmentLifecycleTest.java new file mode 100644 index 0000000..645641e --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserFragmentLifecycleTest.java
@@ -0,0 +1,733 @@ +// 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. + +package org.chromium.weblayer.test; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.test.runner.lifecycle.ActivityLifecycleCallback; +import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry; +import android.support.test.runner.lifecycle.Stage; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.Browser; +import org.chromium.weblayer.BrowserRestoreCallback; +import org.chromium.weblayer.Navigation; +import org.chromium.weblayer.NavigationCallback; +import org.chromium.weblayer.NavigationController; +import org.chromium.weblayer.Profile; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.TabListCallback; +import org.chromium.weblayer.WebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Tests that fragment lifecycle works as expected. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class BrowserFragmentLifecycleTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private Tab getTab() { + return TestThreadUtils.runOnUiThreadBlockingNoException( + () -> mActivityTestRule.getActivity().getTab()); + } + + private Browser getBrowser() { + return TestThreadUtils.runOnUiThreadBlockingNoException( + () -> mActivityTestRule.getActivity().getBrowser()); + } + + private Fragment getFragment() { + return TestThreadUtils.runOnUiThreadBlockingNoException( + () -> mActivityTestRule.getActivity().getFragment()); + } + + private boolean isRestoringPreviousState() { + return TestThreadUtils.runOnUiThreadBlockingNoException( + () -> mActivityTestRule.getActivity().getBrowser().isRestoringPreviousState()); + } + + private int getSupportedMajorVersion() { + return TestThreadUtils.runOnUiThreadBlockingNoException( + () -> WebLayer.getSupportedMajorVersion(mActivityTestRule.getActivity())); + } + + @Test + @SmallTest + public void successfullyLoadsUrlAfterRecreation() { + mActivityTestRule.launchShellWithUrl("about:blank"); + String url = "data:text,foo"; + mActivityTestRule.navigateAndWait(getTab(), url, false); + + mActivityTestRule.recreateActivity(); + + url = "data:text,bar"; + mActivityTestRule.navigateAndWait(getTab(), url, false); + } + + @Test + @SmallTest + public void restoreAfterRecreate() throws Throwable { + mActivityTestRule.launchShellWithUrl("about:blank"); + String url = "data:text,foo"; + mActivityTestRule.navigateAndWait(getTab(), url, false); + + mActivityTestRule.recreateActivity(); + + waitForTabToFinishRestore(getTab(), url); + } + + /** + * Helper class used to ensure that during recreation the following are called: + * . TabListCallback#onTabAdded(). + * . NavigationCallback#onNavigationStarted(). + */ + private static final class RecreateCallbackTracker { + private final ActivityLifecycleCallback mStateListener; + private boolean mGotCreate; + private boolean mGotOnTabAdded; + private boolean mGotOnNavigationStarted; + + RecreateCallbackTracker(Browser browser) { + mStateListener = (Activity newActivity, Stage newStage) -> { + if (newStage == Stage.CREATED && !mGotCreate) { + mGotCreate = true; + Browser newBrowser = ((InstrumentationActivity) newActivity).getBrowser(); + Assert.assertTrue(newBrowser.getTabs().isEmpty()); + Assert.assertNotEquals(newBrowser, browser); + newBrowser.registerTabListCallback(new TabListCallback() { + @Override + public void onTabAdded(@NonNull Tab tab) { + mGotOnTabAdded = true; + tab.getNavigationController().registerNavigationCallback( + new NavigationCallback() { + @Override + public void onNavigationStarted( + @NonNull Navigation navigation) { + mGotOnNavigationStarted = true; + } + }); + } + }); + // This is needed to ensure the callback added above runs first. Without it the + // tab is made active onTabAdded(), resulting in onNavigationStarted() never + // being called (because the navigation starts from setActiveTab(), before the + // callback is added). + ((InstrumentationActivity) newActivity).reregisterTabListCallback(); + } + }; + ActivityLifecycleMonitorRegistry.getInstance().addLifecycleCallback(mStateListener); + } + + public void runAssertsAfterRecreate() { + Assert.assertTrue(mGotCreate); + Assert.assertTrue(mGotOnTabAdded); + Assert.assertTrue(mGotOnNavigationStarted); + ActivityLifecycleMonitorRegistry.getInstance().removeLifecycleCallback(mStateListener); + } + } + + @Test + @SmallTest + @MinWebLayerVersion(99) + public void restoreAfterRecreateCallsTabAndNavigationCallbacks() throws Throwable { + mActivityTestRule.launchShellWithUrl("about:blank"); + String url = "data:text,foo"; + mActivityTestRule.navigateAndWait(getTab(), url, false); + final RecreateCallbackTracker tracker = new RecreateCallbackTracker(getBrowser()); + + mActivityTestRule.recreateActivity(); + + waitForTabToFinishRestore(getTab(), url); + tracker.runAssertsAfterRecreate(); + } + + @Test + @SmallTest + @MinWebLayerVersion(98) + public void setMaxNavigationsPerTabForInstanceState() throws Throwable { + TestThreadUtils.runOnUiThreadBlocking( + () -> Browser.setMaxNavigationsPerTabForInstanceState(4)); + + // Navigate to 5 urls. + mActivityTestRule.launchShellWithUrl("about:blank"); + final String url1 = "data:text,foo"; + mActivityTestRule.navigateAndWait(getTab(), url1, false); + final String url2 = mActivityTestRule.getTestDataURL("simple_page.html"); + mActivityTestRule.navigateAndWait(getTab(), url2, false); + final String url3 = mActivityTestRule.getTestDataURL("simple_page2.html"); + mActivityTestRule.navigateAndWait(getTab(), url3, false); + final String url4 = mActivityTestRule.getTestDataURL("simple_page3.html"); + mActivityTestRule.navigateAndWait(getTab(), url4, false); + final String url5 = mActivityTestRule.getTestDataURL("simple_page4.html"); + mActivityTestRule.navigateAndWait(getTab(), url5, false); + + mActivityTestRule.recreateActivity(); + + // The max set to 4, so only 4 navigation entries should be persisted. + waitForTabToFinishRestore(getTab(), url5); + TestThreadUtils.runOnUiThreadBlocking(() -> { + final NavigationController navigationController = + mActivityTestRule.getActivity().getTab().getNavigationController(); + Assert.assertEquals(4, navigationController.getNavigationListSize()); + Assert.assertEquals( + Uri.parse(url5), navigationController.getNavigationEntryDisplayUri(3)); + }); + } + + private void destroyFragment(CallbackHelper helper) { + FragmentManager fm = mActivityTestRule.getActivity().getSupportFragmentManager(); + fm.beginTransaction() + .remove(fm.getFragments().get(0)) + .runOnCommit(helper::notifyCalled) + .commit(); + } + + // https://crbug.com/1021041 + @Test + @SmallTest + public void handlesFragmentDestroyWhileNavigating() throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + CallbackHelper helper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + NavigationController navigationController = activity.getTab().getNavigationController(); + navigationController.registerNavigationCallback(new NavigationCallback() { + @Override + public void onNavigationStarted(@NonNull Navigation navigation) { + destroyFragment(helper); + } + }); + navigationController.navigate(Uri.parse("data:text,foo")); + }); + helper.waitForFirst(); + } + + // Waits for |tab| to finish loadding |url. This is intended to be called after restore. + private void waitForTabToFinishRestore(Tab tab, String url) { + BoundedCountDownLatch latch = new BoundedCountDownLatch(1); + TestThreadUtils.runOnUiThreadBlocking(() -> { + // It's possible the NavigationController hasn't loaded yet, handle either scenario. + NavigationController navigationController = tab.getNavigationController(); + for (int i = 0; i < navigationController.getNavigationListSize(); ++i) { + if (navigationController.getNavigationEntryDisplayUri(i).equals(Uri.parse(url))) { + latch.countDown(); + return; + } + } + navigationController.registerNavigationCallback(new NavigationCallback() { + @Override + public void onNavigationCompleted(@NonNull Navigation navigation) { + if (navigation.getUri().equals(Uri.parse(url))) { + latch.countDown(); + } + } + }); + }); + latch.timedAwait(); + } + + // Recreates the activity and waits for the first tab to be restored. |extras| is the Bundle + // used to launch the shell. + private void restoresPreviousSession(Bundle extras) { + extras.putString(InstrumentationActivity.EXTRA_PERSISTENCE_ID, "x"); + final String url = mActivityTestRule.getTestDataURL("simple_page.html"); + mActivityTestRule.launchShellWithUrl(url, extras); + if (getSupportedMajorVersion() >= 88) { + Assert.assertFalse(isRestoringPreviousState()); + } + + mActivityTestRule.recreateActivity(); + + Tab tab = getTab(); + Assert.assertNotNull(tab); + waitForTabToFinishRestore(tab, url); + if (getSupportedMajorVersion() >= 88) { + Assert.assertFalse(isRestoringPreviousState()); + } + } + + @Test + @SmallTest + @MinWebLayerVersion(99) + public void restoresPreviousSessionAndNotifiesCallbacks() throws Throwable { + Bundle extras = new Bundle(); + extras.putString(InstrumentationActivity.EXTRA_PERSISTENCE_ID, "x"); + final String url = mActivityTestRule.getTestDataURL("simple_page.html"); + mActivityTestRule.launchShellWithUrl(url, extras); + final RecreateCallbackTracker tracker = new RecreateCallbackTracker(getBrowser()); + + mActivityTestRule.recreateActivity(); + + Tab tab = getTab(); + Assert.assertNotNull(tab); + waitForTabToFinishRestore(tab, url); + tracker.runAssertsAfterRecreate(); + } + + @Test + @SmallTest + public void restoresPreviousSession() throws Throwable { + restoresPreviousSession(new Bundle()); + } + + @Test + @SmallTest + public void restoresPreviousSessionIncognito() throws Throwable { + Bundle extras = new Bundle(); + // This forces incognito. + extras.putString(InstrumentationActivity.EXTRA_PROFILE_NAME, null); + restoresPreviousSession(extras); + } + + @Test + @SmallTest + public void restoresTabGuid() throws Throwable { + Bundle extras = new Bundle(); + extras.putString(InstrumentationActivity.EXTRA_PERSISTENCE_ID, "x"); + final String url = mActivityTestRule.getTestDataURL("simple_page.html"); + mActivityTestRule.launchShellWithUrl(url, extras); + final String initialTabId = TestThreadUtils.runOnUiThreadBlocking( + () -> { return mActivityTestRule.getActivity().getTab().getGuid(); }); + Assert.assertNotNull(initialTabId); + Assert.assertFalse(initialTabId.isEmpty()); + + mActivityTestRule.recreateActivity(); + + Tab tab = getTab(); + Assert.assertNotNull(tab); + waitForTabToFinishRestore(tab, url); + final String restoredTabId = + TestThreadUtils.runOnUiThreadBlockingNoException(() -> { return tab.getGuid(); }); + Assert.assertEquals(initialTabId, restoredTabId); + } + + @Test + @SmallTest + public void restoreTabGuidAfterRecreate() throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + final Tab tab = getTab(); + final String initialTabId = + TestThreadUtils.runOnUiThreadBlocking(() -> { return tab.getGuid(); }); + String url = "data:text,foo"; + mActivityTestRule.navigateAndWait(tab, url, false); + + mActivityTestRule.recreateActivity(); + + final Tab restoredTab = getTab(); + Assert.assertNotEquals(tab, restoredTab); + waitForTabToFinishRestore(restoredTab, url); + final String restoredTabId = TestThreadUtils.runOnUiThreadBlockingNoException( + () -> { return restoredTab.getGuid(); }); + Assert.assertEquals(initialTabId, restoredTabId); + } + + @Test + @SmallTest + public void useViewModelDoesntRecreateBrowser() throws Throwable { + Bundle extras = new Bundle(); + extras.putBoolean(InstrumentationActivity.EXTRA_USE_VIEW_MODEL, true); + InstrumentationActivity activity = + mActivityTestRule.launchShellWithUrl("about:blank", extras); + final Browser browser = getBrowser(); + final Tab tab = getTab(); + final Fragment fragment = getFragment(); + + mActivityTestRule.recreateActivity(); + + // The tab and browser should not have changed. + Assert.assertEquals(tab, getTab()); + Assert.assertEquals(browser, getBrowser()); + Assert.assertFalse( + TestThreadUtils.runOnUiThreadBlocking(() -> { return browser.isDestroyed(); })); + // But the fragment should have. + Assert.assertNotEquals(fragment, getFragment()); + } + + @Test + @SmallTest + public void useViewModelDestroysBrowserWhenActivityDestroyed() throws Throwable { + Bundle extras = new Bundle(); + extras.putBoolean(InstrumentationActivity.EXTRA_USE_VIEW_MODEL, true); + InstrumentationActivity activity = + mActivityTestRule.launchShellWithUrl("about:blank", extras); + final Browser browser = getBrowser(); + final CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + activity.getBrowser().registerTabListCallback(new TabListCallback() { + @Override + public void onWillDestroyBrowserAndAllTabs() { + callbackHelper.notifyCalled(); + } + }); + activity.finish(); + }); + callbackHelper.waitForFirst(); + Assert.assertTrue( + TestThreadUtils.runOnUiThreadBlocking(() -> { return browser.isDestroyed(); })); + } + + @Test + @SmallTest + public void useViewModelDestroysBrowserWhenFragmentDestroyed() throws Throwable { + Bundle extras = new Bundle(); + extras.putBoolean(InstrumentationActivity.EXTRA_USE_VIEW_MODEL, true); + InstrumentationActivity activity = + mActivityTestRule.launchShellWithUrl("about:blank", extras); + final Browser browser = getBrowser(); + final CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + activity.getBrowser().registerTabListCallback(new TabListCallback() { + @Override + public void onWillDestroyBrowserAndAllTabs() { + callbackHelper.notifyCalled(); + } + }); + destroyFragment(callbackHelper); + }); + // There are two callbacks, one from destroying the fragment, and the second from + // onWillDestroyBrowserAndAllTabs(). + callbackHelper.waitForCallback(0, 2); + Assert.assertTrue( + TestThreadUtils.runOnUiThreadBlocking(() -> { return browser.isDestroyed(); })); + } + + @Test + @SmallTest + public void restoresTabData() throws Throwable { + Bundle extras = new Bundle(); + extras.putString(InstrumentationActivity.EXTRA_PERSISTENCE_ID, "x"); + + Map<String, String> initialData = new HashMap<>(); + initialData.put("foo", "bar"); + restoreTabData(extras, initialData); + } + + @Test + @SmallTest + public void restoreTabDataAfterRecreate() throws Throwable { + Map<String, String> initialData = new HashMap<>(); + initialData.put("foo", "bar"); + restoreTabData(new Bundle(), initialData); + } + + private void restoreTabData(Bundle extras, Map<String, String> initialData) { + String url = mActivityTestRule.getTestDataURL("simple_page.html"); + mActivityTestRule.launchShellWithUrl(url, extras); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + Tab tab = mActivityTestRule.getActivity().getTab(); + Assert.assertTrue(tab.getData().isEmpty()); + tab.setData(initialData); + }); + + mActivityTestRule.recreateActivity(); + + Tab tab = getTab(); + Assert.assertNotNull(tab); + waitForTabToFinishRestore(tab, url); + TestThreadUtils.runOnUiThreadBlocking( + () -> Assert.assertEquals(initialData, tab.getData())); + } + + @Test + @SmallTest + public void getAndRemoveBrowserPersistenceIds() throws Throwable { + // Creates a browser with the persistence id 'x'. + final String persistenceId = "x"; + Bundle extras = new Bundle(); + extras.putString(InstrumentationActivity.EXTRA_PERSISTENCE_ID, persistenceId); + final String url = mActivityTestRule.getTestDataURL("simple_page.html"); + mActivityTestRule.launchShellWithUrl(url, extras); + + // Destroy the frament, which ensures the persistence file was written to. + CallbackHelper helper = new CallbackHelper(); + Profile profile = TestThreadUtils.runOnUiThreadBlocking( + () -> { return mActivityTestRule.getActivity().getBrowser().getProfile(); }); + TestThreadUtils.runOnUiThreadBlocking(() -> destroyFragment(helper)); + helper.waitForCallback(0, 1); + int callCount = helper.getCallCount(); + + // Verify the id can be fetched. + TestThreadUtils.runOnUiThreadBlocking(() -> { + profile.getBrowserPersistenceIds((Set<String> ids) -> { + Assert.assertEquals(1, ids.size()); + Assert.assertTrue(ids.contains(persistenceId)); + helper.notifyCalled(); + }); + }); + helper.waitForCallback(callCount, 1); + callCount = helper.getCallCount(); + + // Remove the storage. + HashSet<String> ids = new HashSet<String>(); + ids.add("x"); + TestThreadUtils.runOnUiThreadBlocking(() -> { + profile.removeBrowserPersistenceStorage(ids, (Boolean result) -> { + Assert.assertTrue(result); + helper.notifyCalled(); + }); + }); + helper.waitForCallback(callCount, 1); + callCount = helper.getCallCount(); + + // Verify it was actually removed. + TestThreadUtils.runOnUiThreadBlocking(() -> { + profile.getBrowserPersistenceIds((Set<String> actualIds) -> { + Assert.assertTrue(actualIds.isEmpty()); + helper.notifyCalled(); + }); + }); + helper.waitForCallback(callCount, 1); + } + + @Test + @SmallTest + public void browserAndTabIsDestroyedWhenFragmentDestroyed() throws Throwable { + mActivityTestRule.launchShellWithUrl(mActivityTestRule.getTestDataURL("simple_page.html")); + + CallbackHelper helper = new CallbackHelper(); + Browser browser = TestThreadUtils.runOnUiThreadBlocking( + () -> { return mActivityTestRule.getActivity().getBrowser(); }); + Tab tab = TestThreadUtils.runOnUiThreadBlocking(() -> { return browser.getActiveTab(); }); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Assert.assertFalse(browser.isDestroyed()); + Assert.assertFalse(tab.isDestroyed()); + destroyFragment(helper); + }); + helper.waitForFirst(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Assert.assertTrue(browser.isDestroyed()); + Assert.assertTrue(tab.isDestroyed()); + }); + } + + // Used to track new Browsers finish restoring. + private static final class BrowserRestoreHelper + implements InstrumentationActivity.OnCreatedCallback { + private final CallbackHelper mCallbackHelper; + public List<Browser> mBrowsers; + + // |helper| is notified once restore is complete (or if a browser is created already + // restored). + BrowserRestoreHelper(CallbackHelper helper) { + mCallbackHelper = helper; + mBrowsers = new ArrayList<Browser>(); + InstrumentationActivity.registerOnCreatedCallback(this); + } + + @Override + public void onCreated(Browser browser, InstrumentationActivity activity) { + mBrowsers.add(browser); + if (!browser.isRestoringPreviousState()) { + mCallbackHelper.notifyCalled(); + return; + } + browser.registerBrowserRestoreCallback(new BrowserRestoreCallback() { + @Override + public void onRestoreCompleted() { + Assert.assertFalse(browser.isRestoringPreviousState()); + browser.unregisterBrowserRestoreCallback(this); + mCallbackHelper.notifyCalled(); + } + }); + } + } + @Test + @SmallTest + @MinWebLayerVersion(88) + public void restoreUsingOnRestoreCompleted() throws Throwable { + final String persistenceId = "x"; + Bundle extras = new Bundle(); + extras.putString(InstrumentationActivity.EXTRA_PERSISTENCE_ID, persistenceId); + CallbackHelper callbackHelper = new CallbackHelper(); + BrowserRestoreHelper restoreHelper = new BrowserRestoreHelper(callbackHelper); + + final String url = mActivityTestRule.getTestDataURL("simple_page.html"); + mActivityTestRule.launchShellWithUrl(url, extras); + // Wait for the restore to complete. + callbackHelper.waitForCallback(0, 1); + + // Recreate and wait for restore. + mActivityTestRule.recreateActivity(); + callbackHelper.waitForCallback(1, 1); + } + + private String getCurrentDisplayUri(Browser browser) { + NavigationController navigationController = + browser.getActiveTab().getNavigationController(); + return navigationController + .getNavigationEntryDisplayUri(navigationController.getNavigationListCurrentIndex()) + .toString(); + } + + @Test + @SmallTest + public void twoFragmentsDifferentIncognitoProfiles() throws Throwable { + // This test creates two browsers with different profile names and persistence ids. + final String persistenceId1 = "x"; + final String persistenceId2 = "y"; + Bundle extras = new Bundle(); + extras.putString(InstrumentationActivity.EXTRA_PERSISTENCE_ID, persistenceId1); + extras.putString(InstrumentationActivity.EXTRA_PROFILE_NAME, persistenceId1); + extras.putBoolean(InstrumentationActivity.EXTRA_IS_INCOGNITO, true); + CallbackHelper callbackHelper = new CallbackHelper(); + BrowserRestoreHelper restoreHelper = new BrowserRestoreHelper(callbackHelper); + final String url1 = mActivityTestRule.getTestDataURL("simple_page.html"); + mActivityTestRule.launchShellWithUrl(url1, extras); + + // Wait for the restore to complete. + int currentCallCount = 0; + callbackHelper.waitForCallback(currentCallCount++, 1); + + // Create another fragment + Browser newBrowser = TestThreadUtils.runOnUiThreadBlocking(() -> { + InstrumentationActivity activity = mActivityTestRule.getActivity(); + Intent intent = new Intent(activity.getIntent()); + intent.putExtra(InstrumentationActivity.EXTRA_PERSISTENCE_ID, persistenceId2); + intent.putExtra(InstrumentationActivity.EXTRA_PROFILE_NAME, persistenceId2); + + // The newly created browser should have a different Profile, but be incognito. + Browser browser = Browser.fromFragment( + activity.createBrowserFragment(android.R.id.content, intent)); + Assert.assertNotEquals(browser, activity.getBrowser()); + Assert.assertNotEquals(browser.getProfile(), activity.getBrowser().getProfile()); + Assert.assertTrue(activity.getBrowser().getProfile().isIncognito()); + Assert.assertTrue(browser.getProfile().isIncognito()); + return browser; + }); + + // Wait for restore. + callbackHelper.waitForCallback(currentCallCount++, 1); + + // Navigate to url2. + final String url2 = mActivityTestRule.getTestDataURL("simple_page2.html"); + Tab newTab = TestThreadUtils.runOnUiThreadBlocking(() -> newBrowser.getActiveTab()); + mActivityTestRule.navigateAndWait(newTab, url2, true); + + Profile[] profiles = new Profile[2]; + TestThreadUtils.runOnUiThreadBlocking(() -> { + profiles[0] = mActivityTestRule.getActivity().getBrowser().getProfile(); + profiles[1] = newBrowser.getProfile(); + }); + + // Recreate the activity and wait for two restores (for the two fragments). + InstrumentationActivity.sAllowMultipleFragments = true; + restoreHelper.mBrowsers.clear(); + mActivityTestRule.recreateActivity(); + callbackHelper.waitForCallback(currentCallCount++, 1); + callbackHelper.waitForCallback(currentCallCount++, 1); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + // Two new Browsers should be created, but have the profiles created earlier. + Assert.assertEquals(2, restoreHelper.mBrowsers.size()); + Browser restoredBrowser1 = restoreHelper.mBrowsers.get(0); + Browser restoredBrowser2 = restoreHelper.mBrowsers.get(1); + if (restoredBrowser2.getProfile().getName().equals(persistenceId1)) { + restoredBrowser1 = restoreHelper.mBrowsers.get(1); + restoredBrowser2 = restoreHelper.mBrowsers.get(0); + } + Assert.assertEquals(restoredBrowser1.getProfile().getName(), persistenceId1); + Assert.assertTrue(restoredBrowser1.getProfile().isIncognito()); + Assert.assertEquals(profiles[0], restoredBrowser1.getProfile()); + Assert.assertEquals(url1, getCurrentDisplayUri(restoredBrowser1)); + + Assert.assertEquals(restoredBrowser2.getProfile().getName(), persistenceId2); + Assert.assertTrue(restoredBrowser2.getProfile().isIncognito()); + Assert.assertEquals(profiles[1], restoredBrowser2.getProfile()); + Assert.assertEquals(url2, getCurrentDisplayUri(restoredBrowser2)); + Assert.assertNotEquals(restoredBrowser2, newBrowser); + }); + } + + @Test + @SmallTest + public void twoFragmentsSameIncognitoProfile() throws Throwable { + // This test creates two browsers with the same profile, but different persistence ids. + final String persistenceId1 = "x"; + final String persistenceId2 = "y"; + Bundle extras = new Bundle(); + extras.putString(InstrumentationActivity.EXTRA_PERSISTENCE_ID, persistenceId1); + extras.putString(InstrumentationActivity.EXTRA_PROFILE_NAME, persistenceId1); + extras.putBoolean(InstrumentationActivity.EXTRA_IS_INCOGNITO, true); + CallbackHelper callbackHelper = new CallbackHelper(); + BrowserRestoreHelper restoreHelper = new BrowserRestoreHelper(callbackHelper); + + final String url1 = mActivityTestRule.getTestDataURL("simple_page.html"); + mActivityTestRule.launchShellWithUrl(url1, extras); + // Wait for the restore to complete. + int currentCallCount = 0; + callbackHelper.waitForCallback(currentCallCount++, 1); + + // Create another fragment + Browser newBrowser = TestThreadUtils.runOnUiThreadBlocking(() -> { + InstrumentationActivity activity = mActivityTestRule.getActivity(); + Intent intent = new Intent(activity.getIntent()); + intent.putExtra(InstrumentationActivity.EXTRA_PERSISTENCE_ID, persistenceId2); + + // The newly created browser should have a different Profile, but be incognito. + Browser browser = Browser.fromFragment( + activity.createBrowserFragment(android.R.id.content, intent)); + Assert.assertNotEquals(browser, activity.getBrowser()); + Assert.assertEquals(browser.getProfile(), activity.getBrowser().getProfile()); + Assert.assertTrue(activity.getBrowser().getProfile().isIncognito()); + return browser; + }); + Profile profile = TestThreadUtils.runOnUiThreadBlocking(() -> newBrowser.getProfile()); + // Wait for restore. + callbackHelper.waitForCallback(currentCallCount++, 1); + + // Navigate to url2. + final String url2 = mActivityTestRule.getTestDataURL("simple_page2.html"); + Tab newTab = TestThreadUtils.runOnUiThreadBlocking(() -> newBrowser.getActiveTab()); + mActivityTestRule.navigateAndWait(newTab, url2, true); + + // Recreate the activity and wait for two restores (for the two fragments). + InstrumentationActivity.sAllowMultipleFragments = true; + restoreHelper.mBrowsers.clear(); + mActivityTestRule.recreateActivity(); + callbackHelper.waitForCallback(currentCallCount++, 1); + callbackHelper.waitForCallback(currentCallCount++, 1); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + // Two new Browsers should be created. + Assert.assertEquals(2, restoreHelper.mBrowsers.size()); + Browser restoredBrowser1 = restoreHelper.mBrowsers.get(0); + Browser restoredBrowser2 = restoreHelper.mBrowsers.get(1); + Assert.assertEquals(profile, restoredBrowser1.getProfile()); + + Assert.assertEquals(profile, restoredBrowser2.getProfile()); + Assert.assertNotEquals(restoredBrowser2, newBrowser); + + if (getCurrentDisplayUri(restoredBrowser1).equals(url1)) { + Assert.assertEquals(url2, getCurrentDisplayUri(restoredBrowser2)); + } else { + Assert.assertEquals(url1, getCurrentDisplayUri(restoredBrowser2)); + Assert.assertEquals(url2, getCurrentDisplayUri(restoredBrowser1)); + } + }); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserTest.java new file mode 100644 index 0000000..99eb13a02 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserTest.java
@@ -0,0 +1,88 @@ +// Copyright 2020 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.weblayer.test; + +import androidx.fragment.app.FragmentManager; +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.Browser; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Tests for Browser. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class BrowserTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + + @Test + @SmallTest + public void testDestroyTab() { + String url = mActivityTestRule.getTestDataURL("before_unload.html"); + mActivity = mActivityTestRule.launchShellWithUrl(url); + Assert.assertNotNull(mActivity); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Browser browser = mActivity.getBrowser(); + Tab tab = browser.getActiveTab(); + Assert.assertFalse(tab.isDestroyed()); + browser.destroyTab(tab); + Assert.assertTrue(tab.isDestroyed()); + }); + } + + private boolean isPageVisible() { + return mActivityTestRule.executeScriptAndExtractBoolean( + "document.visibilityState === 'visible'"); + } + + @Test + @SmallTest + @MinWebLayerVersion(91) + public void testSetChangeVisibilityOnNextDetach() { + String url = mActivityTestRule.getTestDataURL("visibility.html"); + mActivity = mActivityTestRule.launchShellWithUrl(url); + Assert.assertNotNull(mActivity); + // Force 'gotHide' to initially be false. + mActivityTestRule.executeScriptSync("gotHide = false;", false); + + // Force the page to be visible during detach, detach the Fragment, and ensure the page is + // still visible. + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.getBrowser().setChangeVisibilityOnNextDetach(false); + FragmentManager fm = mActivity.getSupportFragmentManager(); + fm.beginTransaction().detach(mActivityTestRule.getFragment()).commitNow(); + }); + Assert.assertFalse(mActivityTestRule.executeScriptAndExtractBoolean("gotHide", false)); + Assert.assertTrue(isPageVisible()); + + // Attach the Fragment, the page should still be visible. + TestThreadUtils.runOnUiThreadBlocking(() -> { + FragmentManager fm = mActivity.getSupportFragmentManager(); + fm.beginTransaction().attach(mActivityTestRule.getFragment()).commitNow(); + }); + Assert.assertFalse(mActivityTestRule.executeScriptAndExtractBoolean("gotHide", false)); + Assert.assertTrue(isPageVisible()); + + // Detach the Fragment. Because setChangeVisibilityOnNextDetach() was reset as part of + // attach, the page should no longer be visible and 'gotHide' should be true. + TestThreadUtils.runOnUiThreadBlocking(() -> { + FragmentManager fm = mActivity.getSupportFragmentManager(); + fm.beginTransaction().detach(mActivityTestRule.getFragment()).commitNow(); + }); + Assert.assertTrue(mActivityTestRule.executeScriptAndExtractBoolean("gotHide", false)); + Assert.assertFalse(isPageVisible()); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BundleLanguageTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BundleLanguageTest.java new file mode 100644 index 0000000..e1101e64 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BundleLanguageTest.java
@@ -0,0 +1,137 @@ +// Copyright 2020 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.weblayer.test; + +import android.content.Context; +import android.content.res.AssetManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.util.SparseArray; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.DisabledTest; +import org.chromium.weblayer.TestWebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.Locale; + +/** Tests that translations work correctly for Java strings inside bundles. */ +@RunWith(WebLayerJUnit4ClassRunner.class) +@DisabledTest(message = "crbug.com/1226712") +public class BundleLanguageTest { + private static final String WEBLAYER_SPECIFIC_STRING = + "string/infobar_missing_location_permission_text"; + private static final String SHARED_STRING = "string/color_picker_dialog_title"; + + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private Context mRemoteContext; + private Context mWebLayerContext; + + @Before + public void setUp() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + mRemoteContext = TestWebLayer.getRemoteContext(activity.getApplicationContext()); + mWebLayerContext = TestWebLayer.getWebLayerContext(activity.getApplicationContext()); + } + + @Test + @SmallTest + public void testWebLayerString() throws Exception { + // The bundle tests have both "es" and "fr" splits installed, so each of these should have a + // separate translation. + HashSet<String> translations = new HashSet<>(); + for (String locale : new String[] {"en", "es", "fr"}) { + translations.add(getStringForLocale(WEBLAYER_SPECIFIC_STRING, locale)); + } + Assert.assertEquals(3, translations.size()); + + // The "ko" language split is not installed, so should fall back to english. + Assert.assertEquals(getStringForLocale(WEBLAYER_SPECIFIC_STRING, "en"), + getStringForLocale(WEBLAYER_SPECIFIC_STRING, "ko")); + } + + @Test + @SmallTest + public void testSharedString() throws Exception { + // This string is shared with WebView, so should have a separate translation for all + // locales, even locales without splits installed. + HashSet<String> translations = new HashSet<>(); + for (String locale : new String[] {"en", "es", "fr", "ko"}) { + translations.add(getStringForLocale(SHARED_STRING, locale)); + } + Assert.assertEquals(4, translations.size()); + } + + /** + * Tests that all locale resources have been moved into splits, so the only package ID left in + * the base APK has a dynamic ID. + */ + @Test + @SmallTest + public void testBasePackageIdCorrect() throws Exception { + AssetManager assetManager = createEmptyAssetManager(); + addAssetPath(assetManager, mWebLayerContext.getApplicationInfo().sourceDir); + SparseArray<String> packageIds = getPackageIds(assetManager); + Assert.assertEquals(2, packageIds.size()); + Assert.assertEquals(packageIds.get(1), "android"); + Assert.assertEquals(packageIds.get(2), mWebLayerContext.getPackageName()); + } + + /** Tests that locale splits only have resources from the hardcoded locale package ID. */ + @Test + @SmallTest + public void testLocalePackageIdCorrect() throws Exception { + AssetManager assetManager = createEmptyAssetManager(); + for (String path : mWebLayerContext.getApplicationInfo().splitSourceDirs) { + addAssetPath(assetManager, path); + } + SparseArray<String> packageIds = getPackageIds(assetManager); + Assert.assertEquals(2, packageIds.size()); + Assert.assertEquals(packageIds.get(1), "android"); + Assert.assertEquals(packageIds.get(ResourceUtil.REQUIRED_PACKAGE_IDENTIFIER), + mWebLayerContext.getPackageName() + "_translations"); + } + + private String getStringForLocale(String name, String locale) { + Resources resources = mRemoteContext.getResources(); + Configuration config = resources.getConfiguration(); + config.setLocale(new Locale(locale)); + resources.updateConfiguration(config, resources.getDisplayMetrics()); + return resources.getString(ResourceUtil.getIdentifier( + mRemoteContext, name, mWebLayerContext.getPackageName())); + } + + private static AssetManager createEmptyAssetManager() throws ReflectiveOperationException { + Constructor<AssetManager> constructor = AssetManager.class.getDeclaredConstructor(); + constructor.setAccessible(true); + return constructor.newInstance(); + } + + private static void addAssetPath(AssetManager assetManager, String path) + throws ReflectiveOperationException { + Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class); + addAssetPath.invoke(assetManager, path); + } + + private static SparseArray<String> getPackageIds(AssetManager assetManager) + throws ReflectiveOperationException { + Method getAssignedPackageIdentifiers = + AssetManager.class.getMethod("getAssignedPackageIdentifiers"); + return (SparseArray) getAssignedPackageIdentifiers.invoke(assetManager); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ContentCaptureTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ContentCaptureTest.java new file mode 100644 index 0000000..d9e13c08 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ContentCaptureTest.java
@@ -0,0 +1,116 @@ +// 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. + +package org.chromium.weblayer.test; + +import android.os.Bundle; + +import androidx.test.filters.SmallTest; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.net.test.util.TestWebServer; +import org.chromium.weblayer.TestWebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.util.ArrayList; +import java.util.concurrent.TimeoutException; + +/** + * Verifies Content Capture works in WebLayer. The feature itself has AwContentCaptureTest.java for + * testing its functionality. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class ContentCaptureTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private static final String MAIN_FRAME_FILE = "/main_frame.html"; + + private TestWebServer mWebServer; + private InstrumentationActivity mActivity; + + @Before + public void setUp() throws Exception { + mWebServer = TestWebServer.start(); + } + + @After + public void tearDown() throws Exception { + mWebServer.shutdown(); + } + + private TestWebLayer getTestWebLayer() { + return TestWebLayer.getTestWebLayer(mActivity.getApplicationContext()); + } + + /** + * Verifies that ContentCapture is working for WebLayer as the TestContentCaptureConsumer is + * receiving data from the renderer side. + */ + @Test + @SmallTest + public void testContentCapture() throws Exception { + final String response = "<html><head></head><body>" + + "<div id='place_holder'>" + + "<p style=\"height: 100vh\">Hello</p>" + + "<p>world</p>" + + "</body></html>"; + final String url = mWebServer.setResponse(MAIN_FRAME_FILE, response, null); + + ArrayList<Integer> eventsObserved = new ArrayList<>(); + CallbackHelper helper = new CallbackHelper(); + + mActivity = mActivityTestRule.launchShellWithUrl("about:blank"); + TestWebLayer testWebLayer = getTestWebLayer(); + testWebLayer.addContentCaptureConsumer( + mActivity.getBrowser(), () -> helper.notifyCalled(), eventsObserved); + + mActivityTestRule.navigateAndWait(url); + helper.waitForFirst(); + + Assert.assertEquals(1, eventsObserved.size()); + Assert.assertEquals(/* CONTENT_CAPTURED*/ Integer.valueOf(1), eventsObserved.get(0)); + } + + /** + * Verifies that ContentCapture doesn't report data for incognito mode. + */ + @Test + @SmallTest + public void testContentCaptureIncognito() throws Exception { + final String response = "<html><head></head><body>" + + "<div id='place_holder'>" + + "<p style=\"height: 100vh\">Hello</p>" + + "<p>world</p>" + + "</body></html>"; + final String url = mWebServer.setResponse(MAIN_FRAME_FILE, response, null); + + ArrayList<Integer> eventsObserved = new ArrayList<>(); + CallbackHelper helper = new CallbackHelper(); + + Bundle extras = new Bundle(); + extras.putBoolean(InstrumentationActivity.EXTRA_IS_INCOGNITO, true); + mActivity = mActivityTestRule.launchShellWithUrl("about:blank", extras); + TestWebLayer testWebLayer = getTestWebLayer(); + testWebLayer.addContentCaptureConsumer( + mActivity.getBrowser(), () -> helper.notifyCalled(), eventsObserved); + + mActivityTestRule.navigateAndWait(url); + try { + helper.waitForFirst(); + } catch (TimeoutException e) { + // Expecting TimeoutException. + return; + } + Assert.assertTrue("There should be a TimeoutException", false); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/CookieManagerTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/CookieManagerTest.java new file mode 100644 index 0000000..e7fc09e --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/CookieManagerTest.java
@@ -0,0 +1,264 @@ +// Copyright 2020 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.weblayer.test; + +import android.net.Uri; + +import androidx.fragment.app.FragmentManager; +import androidx.test.filters.SmallTest; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.CookieChangeCause; +import org.chromium.weblayer.CookieChangedCallback; +import org.chromium.weblayer.CookieManager; +import org.chromium.weblayer.Profile; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.util.List; +import java.util.concurrent.TimeoutException; + +/** + * Tests that CookieManager works as expected. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class CookieManagerTest { + private CookieManager mCookieManager; + private Uri mBaseUri; + private Uri mBaseUriWithPath; + + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + @Before + public void setUp() { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + mCookieManager = TestThreadUtils.runOnUiThreadBlockingNoException( + () -> { return activity.getBrowser().getProfile().getCookieManager(); }); + mBaseUri = Uri.parse(mActivityTestRule.getTestServer().getURL("/")); + mBaseUriWithPath = Uri.parse(mActivityTestRule.getTestServer().getURL("/path")); + } + + @Test + @SmallTest + public void testSetCookie() throws Exception { + Assert.assertTrue(setCookie("foo=bar")); + + mActivityTestRule.navigateAndWait( + mActivityTestRule.getTestServer().getURL("/echoheader?Cookie")); + Assert.assertEquals("foo=bar", + mActivityTestRule.executeScriptAndExtractString("document.body.textContent")); + } + + @Test + @SmallTest + public void testSetCookieInvalid() throws Exception { + TestThreadUtils.runOnUiThreadBlocking(() -> { + try { + mCookieManager.setCookie(mBaseUri, "", null); + Assert.fail("Exception not thrown."); + } catch (IllegalArgumentException e) { + Assert.assertEquals(e.getMessage(), "Invalid cookie: "); + } + }); + } + + @Test + @SmallTest + public void testSetCookieNotSet() throws Exception { + // Attempting to set a Secure cookie from an insecure origin is rejected. + // A different hostname must be used because non-cryptographic localhost origins such as + // http://127.0.0.1 are considered trustworthy and are allowed to set Secure cookies. + Assert.assertFalse(mActivityTestRule.setCookie( + mCookieManager, Uri.parse("http://a.test/path"), "foo=bar; Secure")); + } + + @Test + @SmallTest + public void testSetCookieNullCallback() throws Exception { + TestThreadUtils.runOnUiThreadBlocking( + () -> { mCookieManager.setCookie(mBaseUri, "foo=bar", null); }); + + // Do a navigation to make sure the cookie gets set. + mActivityTestRule.navigateAndWait(mActivityTestRule.getTestServer().getURL("/echo")); + + mActivityTestRule.navigateAndWait( + mActivityTestRule.getTestServer().getURL("/echoheader?Cookie")); + Assert.assertEquals("foo=bar", + mActivityTestRule.executeScriptAndExtractString("document.body.textContent")); + } + + @Test + @SmallTest + public void testGetCookie() throws Exception { + Assert.assertEquals(getCookie(), ""); + Assert.assertTrue(setCookie("foo=")); + Assert.assertEquals(getCookie(), "foo="); + Assert.assertTrue(setCookie("foo=bar")); + Assert.assertEquals(getCookie(), "foo=bar"); + } + + @Test + @SmallTest + @MinWebLayerVersion(101) + public void testGetResponseCookiesSimple() throws Exception { + Assert.assertTrue(getResponseCookies().isEmpty()); + Assert.assertTrue(setCookie("foo=")); + Assert.assertThat(getResponseCookies(), + Matchers.containsInAnyOrder("foo=; path=/; domain=127.0.0.1; priority=medium")); + Assert.assertTrue(setCookie("foo=bar")); + Assert.assertThat(getResponseCookies(), + Matchers.containsInAnyOrder("foo=bar; path=/; domain=127.0.0.1; priority=medium")); + + Assert.assertTrue(setCookie("baz=blah")); + Assert.assertThat(getResponseCookies(), + Matchers.containsInAnyOrder("foo=bar; path=/; domain=127.0.0.1; priority=medium", + "baz=blah; path=/; domain=127.0.0.1; priority=medium")); + } + + @Test + @SmallTest + @MinWebLayerVersion(101) + public void testGetResponseCookiesAllAttributes() throws Exception { + Assert.assertTrue(getResponseCookies().isEmpty()); + + // Setting a cookie with all attributes should return the same cookie. + String cookieStart = "foo=bar; path=/; domain=127.0.0.1; expires="; + String cookieExpires = "Thu, 15 Jul 2032 00:00:01 GMT"; + String cookieEnd = "; secure; httponly; samesite=lax; priority=high; sameparty"; + Assert.assertTrue(setCookie(cookieStart + cookieExpires + cookieEnd)); + List<String> cookiesSet = getResponseCookies(); + Assert.assertEquals(cookiesSet.size(), 1); + // Expiration is clamped to 400 days, so for now we just test that some date was sent back. + Assert.assertTrue(cookiesSet.get(0).matches(cookieStart + "[A-Za-z0-9 ,:]*" + cookieEnd)); + } + + @Test + @SmallTest + @MinWebLayerVersion(101) + public void testGetResponseCookiesWithPath() throws Exception { + Assert.assertTrue(setCookie(mBaseUriWithPath, "foo=bar; path=/path")); + Assert.assertThat(getResponseCookies(mBaseUriWithPath), + Matchers.containsInAnyOrder( + "foo=bar; path=/path; domain=127.0.0.1; priority=medium")); + } + + @Test + @SmallTest + @DisabledTest(message = "Flaky - https://crbug.com/1133891") + public void testCookieChanged() throws Exception { + CookieChangedCallbackHelper helper = new CookieChangedCallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking( + () -> { mCookieManager.addCookieChangedCallback(mBaseUri, null, helper); }); + Assert.assertTrue(setCookie("foo=bar")); + helper.waitForChange(); + Assert.assertEquals(helper.getCause(), CookieChangeCause.INSERTED); + Assert.assertEquals(helper.getCookie(), "foo=bar"); + + Assert.assertTrue(setCookie("foo=baz")); + helper.waitForChange(); + Assert.assertEquals(helper.getCause(), CookieChangeCause.OVERWRITE); + Assert.assertEquals(helper.getCookie(), "foo=bar"); + helper.waitForChange(); + Assert.assertEquals(helper.getCause(), CookieChangeCause.INSERTED); + Assert.assertEquals(helper.getCookie(), "foo=baz"); + } + + @Test + @SmallTest + public void testCookieChangedRemoveCallback() throws Exception { + CookieChangedCallbackHelper helper = new CookieChangedCallbackHelper(); + Runnable remove = TestThreadUtils.runOnUiThreadBlocking(() -> { + mCookieManager.addCookieChangedCallback(mBaseUri, "cookie2", helper); + return mCookieManager.addCookieChangedCallback(mBaseUri, "cookie1", helper); + }); + Assert.assertTrue(setCookie("cookie1=something")); + helper.waitForChange(); + Assert.assertEquals(helper.getCookie(), "cookie1=something"); + + TestThreadUtils.runOnUiThreadBlocking(remove); + + // Set cookie1 first and then cookie2. We should only receive a cookie change event for + // cookie2. + Assert.assertTrue(setCookie("cookie1=other")); + Assert.assertTrue(setCookie("cookie2=something")); + helper.waitForChange(); + Assert.assertEquals(helper.getCookie(), "cookie2=something"); + } + + @Test + @SmallTest + public void testCookieChangedRemoveCallbackAfterProfileDestroyed() throws Exception { + // Removing change callback should be a no-op after the profile is destroyed. + TestThreadUtils.runOnUiThreadBlocking(() -> { + Profile profile = mActivityTestRule.getActivity().getBrowser().getProfile(); + Runnable remove = mCookieManager.addCookieChangedCallback( + mBaseUri, null, new CookieChangedCallbackHelper()); + // We need to remove the fragment before calling Profile#destroy(). + FragmentManager fm = mActivityTestRule.getActivity().getSupportFragmentManager(); + fm.beginTransaction().remove(fm.getFragments().get(0)).commitNow(); + + profile.destroy(); + remove.run(); + }); + } + + private boolean setCookie(Uri uri, String value) throws Exception { + return mActivityTestRule.setCookie(mCookieManager, uri, value); + } + + private boolean setCookie(String value) throws Exception { + return setCookie(mBaseUri, value); + } + + private String getCookie() throws Exception { + return mActivityTestRule.getCookie(mCookieManager, mBaseUri); + } + + private List<String> getResponseCookies(Uri uri) throws Exception { + return mActivityTestRule.getResponseCookies(mCookieManager, uri); + } + + private List<String> getResponseCookies() throws Exception { + return getResponseCookies(mBaseUri); + } + + private static class CookieChangedCallbackHelper extends CookieChangedCallback { + private CallbackHelper mCallbackHelper = new CallbackHelper(); + private int mCallCount; + private int mCause; + private String mCookie; + + public void waitForChange() throws TimeoutException { + mCallbackHelper.waitForCallback(mCallCount); + mCallCount++; + } + + @CookieChangeCause + public int getCause() { + return mCause; + } + + public String getCookie() { + return mCookie; + } + + @Override + public void onCookieChanged(String cookie, @CookieChangeCause int cause) { + mCookie = cookie; + mCause = cause; + mCallbackHelper.notifyCalled(); + } + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/CrashReporterTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/CrashReporterTest.java new file mode 100644 index 0000000..b5dab58 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/CrashReporterTest.java
@@ -0,0 +1,144 @@ +// 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. + +package org.chromium.weblayer.test; + +import android.os.Bundle; +import android.support.test.InstrumentationRegistry; + +import androidx.test.filters.SmallTest; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.CrashReporterCallback; +import org.chromium.weblayer.CrashReporterController; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Arrays; + +/** + * Tests for crash reporting in WebLayer. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class CrashReporterTest { + private static final String UUID = "032b90a6-836c-49bc-a9f4-aa210458eaf3"; + private static final String LOCAL_ID = "aa210458eaf3"; + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + private File mCrashReport; + private File mCrashSidecar; + + @Before + public void setUp() throws IOException { + File cacheDir = + InstrumentationRegistry.getInstrumentation().getTargetContext().getCacheDir(); + File crashReportDir = new File(cacheDir, "weblayer/Crash Reports"); + crashReportDir.mkdirs(); + mCrashReport = new File(crashReportDir, UUID + ".dmp0.try0"); + mCrashSidecar = new File(crashReportDir, UUID + ".json"); + mCrashReport.createNewFile(); + try (FileOutputStream out = new FileOutputStream(mCrashSidecar)) { + out.write("{\"foo\":\"bar\"}".getBytes()); + } + } + + @After + public void tearDown() throws IOException { + if (mCrashReport.exists()) mCrashReport.delete(); + if (mCrashSidecar.exists()) mCrashSidecar.delete(); + } + + private static final class BundleCallbackHelper extends CallbackHelper { + private Bundle mResult; + + public Bundle getResult() { + return mResult; + } + + public void notifyCalled(Bundle result) { + mResult = result; + notifyCalled(); + } + } + + @Test + @SmallTest + public void testCrashReporterLoading() throws Exception { + BundleCallbackHelper callbackHelper = new BundleCallbackHelper(); + CallbackHelper deleteHelper = new CallbackHelper(); + + Bundle extras = new Bundle(); + extras.putBoolean(InstrumentationActivity.EXTRA_CREATE_WEBLAYER, false); + InstrumentationActivity activity = mActivityTestRule.launchShell(extras); + TestThreadUtils.runOnUiThreadBlocking(() -> { + CrashReporterController crashReporterController = + CrashReporterController.getInstance(activity); + // Set up a callback object that will fetch the crash keys for the crash with id + // LOCAL_ID and then delete it. + crashReporterController.registerCallback(new CrashReporterCallback() { + @Override + public void onPendingCrashReports(String[] localIds) { + if (!Arrays.asList(localIds).contains(LOCAL_ID)) { + callbackHelper.notifyFailed("localIds does not contain " + LOCAL_ID); + return; + } + Bundle crashKeys = crashReporterController.getCrashKeys(localIds[0]); + callbackHelper.notifyCalled(crashKeys); + crashReporterController.deleteCrash(localIds[0]); + } + + @Override + public void onCrashDeleted(String localId) { + deleteHelper.notifyCalled(); + } + }); + + // Check for crash reports ready to upload + crashReporterController.checkForPendingCrashReports(); + }); + // Expect that a Bundle containing { "foo": "bar" } is returned. + callbackHelper.waitForFirst(); + Bundle crashKeys = callbackHelper.getResult(); + Assert.assertArrayEquals(crashKeys.keySet().toArray(new String[0]), new String[] {"foo"}); + Assert.assertEquals(crashKeys.getString("foo"), "bar"); + + // Expect that the crash report and its sidecar are deleted. + deleteHelper.waitForFirst(); + Assert.assertFalse(mCrashReport.exists()); + Assert.assertFalse(mCrashSidecar.exists()); + } + + @MinWebLayerVersion(88) // Fix first appeared in 88. + @Test + @SmallTest + @DisabledTest(message = "https://crbug.com/1355817") + public void testBogusCrashId() throws Exception { + CallbackHelper callbackHelper = new CallbackHelper(); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + TestThreadUtils.runOnUiThreadBlocking(() -> { + CrashReporterController crashReporterController = + CrashReporterController.getInstance(activity); + crashReporterController.registerCallback(new CrashReporterCallback() { + @Override + public void onCrashUploadFailed(String localId, String message) { + callbackHelper.notifyCalled(); + } + }); + crashReporterController.uploadCrash("bogus-crash-id"); + }); + callbackHelper.waitForFirst(); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DarkModeTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DarkModeTest.java new file mode 100644 index 0000000..1bae512 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DarkModeTest.java
@@ -0,0 +1,110 @@ +// 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. + +package org.chromium.weblayer.test; + +import android.os.Bundle; + +import androidx.appcompat.app.AppCompatDelegate; +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.DarkModeStrategy; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Tests that dark mode is handled correctly. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class DarkModeTest { + private InstrumentationActivity mActivity; + + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private void setDarkModeStrategy(@DarkModeStrategy int darkModeStrategy) { + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.loadWebLayerSync(mActivityTestRule.getContextForWebLayer()); + mActivity.getBrowser().setDarkModeStrategy(darkModeStrategy); + }); + } + + private boolean loadPageAndGetPrefersDark() { + mActivityTestRule.navigateAndWait(mActivityTestRule.getTestDataURL("dark_mode.html")); + return mActivityTestRule.executeScriptAndExtractBoolean( + "window.matchMedia('(prefers-color-scheme: dark)').matches"); + } + + @Test + @SmallTest + @MinWebLayerVersion(90) + public void testDarkModeWithWebThemeDarkening() throws Exception { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); + mActivity = mActivityTestRule.launchShell(new Bundle()); + setDarkModeStrategy(DarkModeStrategy.WEB_THEME_DARKENING_ONLY); + boolean prefersDark = loadPageAndGetPrefersDark(); + Assert.assertTrue(prefersDark); + } + + @Test + @SmallTest + @MinWebLayerVersion(90) + public void testDarkModeWithUserAgentDarkening() throws Exception { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); + mActivity = mActivityTestRule.launchShell(new Bundle()); + setDarkModeStrategy(DarkModeStrategy.USER_AGENT_DARKENING_ONLY); + boolean prefersDark = loadPageAndGetPrefersDark(); + Assert.assertFalse(prefersDark); + } + + @Test + @SmallTest + @MinWebLayerVersion(90) + public void testDarkModeWithPreferWebThemeDarkening() throws Exception { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); + mActivity = mActivityTestRule.launchShell(new Bundle()); + setDarkModeStrategy(DarkModeStrategy.PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING); + boolean prefersDark = loadPageAndGetPrefersDark(); + Assert.assertTrue(prefersDark); + } + + @Test + @SmallTest + @MinWebLayerVersion(90) + public void testLightModeWithWebThemeDarkening() throws Exception { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); + mActivity = mActivityTestRule.launchShell(new Bundle()); + setDarkModeStrategy(DarkModeStrategy.WEB_THEME_DARKENING_ONLY); + boolean prefersDark = loadPageAndGetPrefersDark(); + Assert.assertFalse(prefersDark); + } + + @Test + @SmallTest + @MinWebLayerVersion(90) + public void testLightModeWithUserAgentDarkening() throws Exception { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); + mActivity = mActivityTestRule.launchShell(new Bundle()); + setDarkModeStrategy(DarkModeStrategy.USER_AGENT_DARKENING_ONLY); + boolean prefersDark = loadPageAndGetPrefersDark(); + Assert.assertFalse(prefersDark); + } + + @Test + @SmallTest + @MinWebLayerVersion(90) + public void testLightModeWithPreferWebThemeDarkening() throws Exception { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); + mActivity = mActivityTestRule.launchShell(new Bundle()); + setDarkModeStrategy(DarkModeStrategy.PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING); + boolean prefersDark = loadPageAndGetPrefersDark(); + Assert.assertFalse(prefersDark); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DataClearingTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DataClearingTest.java new file mode 100644 index 0000000..1519090 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DataClearingTest.java
@@ -0,0 +1,124 @@ +// 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. + +package org.chromium.weblayer.test; + +import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking; +import static org.chromium.weblayer.BrowsingDataType.CACHE; +import static org.chromium.weblayer.BrowsingDataType.COOKIES_AND_SITE_DATA; + +import androidx.fragment.app.FragmentManager; +import androidx.test.filters.SmallTest; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.weblayer.Profile; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Example test that just starts the weblayer shell. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class DataClearingTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + @Test + @SmallTest + public void clearDataWithPersistedProfile_TriggersCallback() { + checkTriggersCallbackOnClearData(new int[] {COOKIES_AND_SITE_DATA}, "Profile"); + } + + @Test + @SmallTest + public void clearDataWithInMemoryProfile_TriggersCallback() { + checkTriggersCallbackOnClearData(new int[] {COOKIES_AND_SITE_DATA}, null); + } + + @Test + @SmallTest + public void clearCacheWithPersistedProfile_TriggersCallback() { + checkTriggersCallbackOnClearData(new int[] {CACHE}, "Profile"); + } + + @Test + @SmallTest + public void clearCacheWithInMemoryProfile_TriggersCallback() { + checkTriggersCallbackOnClearData(new int[] {CACHE}, null); + } + + @Test + @SmallTest + public void clearMultipleTypes_TriggersCallback() { + checkTriggersCallbackOnClearData(new int[] {COOKIES_AND_SITE_DATA, CACHE}, "Profile"); + } + + @Test + @SmallTest + public void clearUnknownType_TriggersCallback() { + // This is a forward compatibility test: the older versions of Chrome that don't yet + // implement clearing some data type should just ignore it and call the callback. + checkTriggersCallbackOnClearData(new int[] {9999}, "Profile"); + } + + @Test + @SmallTest + public void twoSuccesiveRequestsTriggerCallbacks() { + InstrumentationActivity activity = mActivityTestRule.launchWithProfile("profile"); + + BoundedCountDownLatch latch = new BoundedCountDownLatch(2); + runOnUiThreadBlocking(() -> { + Profile profile = activity.getBrowser().getProfile(); + profile.clearBrowsingData(new int[] {COOKIES_AND_SITE_DATA}, latch::countDown); + profile.clearBrowsingData(new int[] {CACHE}, latch::countDown); + }); + latch.timedAwait(); + } + + @Test + @SmallTest + public void clearingAgainAfterClearFinished_TriggersCallback() { + InstrumentationActivity activity = mActivityTestRule.launchWithProfile("profile"); + + BoundedCountDownLatch latch = new BoundedCountDownLatch(1); + runOnUiThreadBlocking(() -> { + Profile profile = activity.getBrowser().getProfile(); + profile.clearBrowsingData(new int[] {COOKIES_AND_SITE_DATA}, + () -> { profile.clearBrowsingData(new int[] {CACHE}, latch::countDown); }); + }); + latch.timedAwait(); + } + + @Test + @SmallTest + public void destroyingProfileDuringDataClear_DoesntCrash() { + InstrumentationActivity activity = mActivityTestRule.launchWithProfile("profile"); + + BoundedCountDownLatch latch = new BoundedCountDownLatch(1); + runOnUiThreadBlocking(() -> { + Profile profile = activity.getBrowser().getProfile(); + profile.clearBrowsingData(new int[] {COOKIES_AND_SITE_DATA}, () -> {}); + + // We need to remove the fragment before calling Profile#destroy(). + FragmentManager fm = activity.getSupportFragmentManager(); + fm.beginTransaction().remove(fm.getFragments().get(0)).commitNow(); + + profile.destroy(); + latch.countDown(); + }); + latch.timedAwait(); + } + + private void checkTriggersCallbackOnClearData(int[] dataTypes, String profileName) { + InstrumentationActivity activity = mActivityTestRule.launchWithProfile(profileName); + BoundedCountDownLatch latch = new BoundedCountDownLatch(1); + runOnUiThreadBlocking(() -> { + activity.getBrowser().getProfile().clearBrowsingData(dataTypes, latch::countDown); + }); + latch.timedAwait(); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DisplayCutoutTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DisplayCutoutTest.java new file mode 100644 index 0000000..5eb8e86 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DisplayCutoutTest.java
@@ -0,0 +1,86 @@ +// Copyright 2020 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.weblayer.test; + +import android.os.Build; +import android.view.WindowManager.LayoutParams; + +import androidx.test.filters.SmallTest; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.MinAndroidSdkLevel; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Tests that viewport-fit is respected. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class DisplayCutoutTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + + @Before + public void setUp() { + String url = mActivityTestRule.getTestDataURL("display_cutout.html"); + mActivity = mActivityTestRule.launchShellWithUrl(url); + Assert.assertNotNull(mActivity); + } + + @Test + @SmallTest + @MinAndroidSdkLevel(Build.VERSION_CODES.P) + public void testWithFullscreen() { + Assert.assertEquals(mActivity.getWindow().getAttributes().layoutInDisplayCutoutMode, + LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT); + + // First touch enters fullscreen. + EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView()); + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat( + mActivityTestRule.executeScriptAndExtractBoolean("document.webkitIsFullScreen"), + Matchers.is(true)); + }); + + mActivityTestRule.executeScriptSync("setViewportFit(\"contain\")", false); + CriteriaHelper.pollUiThread(() -> { + Criteria.checkThat(mActivity.getWindow().getAttributes().layoutInDisplayCutoutMode, + Matchers.is(LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER)); + }); + } + + @Test + @SmallTest + @MinAndroidSdkLevel(Build.VERSION_CODES.P) + public void testWithNoFullscreen() { + Assert.assertEquals(mActivity.getWindow().getAttributes().layoutInDisplayCutoutMode, + LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT); + Assert.assertFalse( + mActivityTestRule.executeScriptAndExtractBoolean("document.webkitIsFullScreen")); + mActivityTestRule.executeScriptSync("setViewportFit(\"contain\")", false); + + try { + // When not in fullscreen, this criterion will not be fulfilled. + CriteriaHelper.pollUiThread(() -> { + Criteria.checkThat(mActivity.getWindow().getAttributes().layoutInDisplayCutoutMode, + Matchers.not(LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT)); + }); + } catch (AssertionError e) { + } + + Assert.assertEquals(mActivity.getWindow().getAttributes().layoutInDisplayCutoutMode, + LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DowngradeTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DowngradeTest.java new file mode 100644 index 0000000..e737eef --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DowngradeTest.java
@@ -0,0 +1,109 @@ +// Copyright 2020 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.weblayer.test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; + +import androidx.test.filters.SmallTest; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.ContextUtils; +import org.chromium.base.PathUtils; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.io.File; +import java.io.IOException; + +/** + * Tests that WebLayer version changes handle data correctly. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class DowngradeTest { + public static final String PREF_LAST_VERSION_CODE = + "org.chromium.weblayer.last_version_code_used"; + + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + // A test file in the app's data directory. This should never get deleted. + private File mAppFile; + // A test file in WebLayer's data directory. This should get deleted when we downgrade. + private File mWebLayerDataFile; + + @Before + public void setUp() throws IOException, PackageManager.NameNotFoundException { + PathUtils.setPrivateDataDirectorySuffix("weblayer", "weblayer"); + mWebLayerDataFile = new File(PathUtils.getDataDirectory(), "testWebLayerFile"); + assertTrue(mWebLayerDataFile.createNewFile()); + + Context context = ContextUtils.getApplicationContext(); + PackageManager packageManager = context.getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0); + mAppFile = new File(packageInfo.applicationInfo.dataDir, "testAppFile"); + assertTrue(mAppFile.createNewFile()); + } + + @After + public void tearDown() { + mAppFile.delete(); + mWebLayerDataFile.delete(); + } + + @Test + @SmallTest + public void testDowngradeDeletesData() throws IOException { + SharedPreferences prefs = ContextUtils.getAppSharedPreferences(); + prefs.edit().putInt(PREF_LAST_VERSION_CODE, 9999_000_00).apply(); + + InstrumentationActivity activity = mActivityTestRule.launchWithProfile("profile"); + runOnUiThreadBlocking( + () -> { activity.loadWebLayerSync(ContextUtils.getApplicationContext()); }); + + assertFalse(mWebLayerDataFile.exists()); + assertTrue(mAppFile.exists()); + } + + @Test + @SmallTest + public void testUnknownLastVersionKeepsData() throws IOException { + SharedPreferences prefs = ContextUtils.getAppSharedPreferences(); + assertFalse(prefs.contains(PREF_LAST_VERSION_CODE)); + + InstrumentationActivity activity = mActivityTestRule.launchWithProfile("profile"); + runOnUiThreadBlocking( + () -> { activity.loadWebLayerSync(ContextUtils.getApplicationContext()); }); + + assertTrue(mWebLayerDataFile.exists()); + assertTrue(mAppFile.exists()); + } + + @Test + @SmallTest + public void testNewVersionKeepsData() { + SharedPreferences prefs = ContextUtils.getAppSharedPreferences(); + prefs.edit().putInt(PREF_LAST_VERSION_CODE, 1_000_00).apply(); + + InstrumentationActivity activity = mActivityTestRule.launchWithProfile("profile"); + runOnUiThreadBlocking( + () -> { activity.loadWebLayerSync(ContextUtils.getApplicationContext()); }); + + assertTrue(mWebLayerDataFile.exists()); + assertTrue(mAppFile.exists()); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DownloadCallbackTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DownloadCallbackTest.java new file mode 100644 index 0000000..06e07fa --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DownloadCallbackTest.java
@@ -0,0 +1,252 @@ +// 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. + +package org.chromium.weblayer.test; + +import android.net.Uri; +import android.support.test.InstrumentationRegistry; +import android.util.Pair; +import android.webkit.ValueCallback; + +import androidx.test.filters.SmallTest; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.net.test.util.TestWebServer; +import org.chromium.weblayer.Browser; +import org.chromium.weblayer.Download; +import org.chromium.weblayer.DownloadCallback; +import org.chromium.weblayer.DownloadError; +import org.chromium.weblayer.DownloadState; +import org.chromium.weblayer.Profile; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.TabListCallback; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * Tests that the DownloadCallback method is invoked for downloads. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class DownloadCallbackTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + private Callback mCallback; + + private static class Callback extends DownloadCallback { + public String mUrl; + public String mUserAgent; + public String mContentDisposition; + public String mMimetype; + public String mLocation; + public String mFileName; + public @DownloadState int mState; + public @DownloadError int mError; + public long mContentLength; + public boolean mIntercept; + public boolean mSeenStarted; + public boolean mSeenCompleted; + public boolean mSeenFailed; + + @Override + public boolean onInterceptDownload(Uri uri, String userAgent, String contentDisposition, + String mimetype, long contentLength) { + mUrl = uri.toString(); + mUserAgent = userAgent; + mContentDisposition = contentDisposition; + mMimetype = mimetype; + mContentLength = contentLength; + return mIntercept; + } + + @Override + public void allowDownload(Uri uri, String requestMethod, Uri requestInitiator, + ValueCallback<Boolean> callback) { + callback.onReceiveValue(true); + } + + @Override + public void onDownloadStarted(Download download) { + mSeenStarted = true; + download.disableNotification(); + } + + @Override + public void onDownloadCompleted(Download download) { + mSeenCompleted = true; + mLocation = download.getLocation().toString(); + mFileName = download.getFileNameToReportToUser().toString(); + mState = download.getState(); + mError = download.getError(); + mMimetype = download.getMimeType(); + } + + @Override + public void onDownloadFailed(Download download) { + mSeenFailed = true; + mState = download.getState(); + mError = download.getError(); + } + + public void waitForIntercept() { + CriteriaHelper.pollInstrumentationThread( + () -> Criteria.checkThat(mUrl, Matchers.notNullValue())); + } + + public void waitForStarted() { + CriteriaHelper.pollInstrumentationThread(() -> mSeenStarted); + } + + public void waitForCompleted() { + CriteriaHelper.pollInstrumentationThread(() -> mSeenCompleted); + } + + public void waitForFailed() { + CriteriaHelper.pollInstrumentationThread(() -> mSeenFailed); + } + } + + @Before + public void setUp() { + mActivity = mActivityTestRule.launchShellWithUrl(null); + Assert.assertNotNull(mActivity); + + // Don't fill up the default download directory on the device. + String tempDownloadDirectory = + InstrumentationRegistry.getInstrumentation().getTargetContext().getCacheDir() + + "/weblayer/Downloads"; + + mCallback = new Callback(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Profile profile = mActivity.getBrowser().getProfile(); + profile.setDownloadCallback(mCallback); + profile.setDownloadDirectory(new File(tempDownloadDirectory)); + }); + } + + /** + * Verifies the DownloadCallback is informed of downloads resulting from navigations to pages + * with Content-Disposition attachment. + */ + @Test + @SmallTest + public void testInterceptDownloadByContentDisposition() throws Throwable { + mCallback.mIntercept = true; + final String data = "download data"; + final String contentDisposition = "attachment;filename=\"download.txt\""; + final String mimetype = "text/plain"; + + List<Pair<String, String>> downloadHeaders = new ArrayList<Pair<String, String>>(); + downloadHeaders.add(Pair.create("Content-Disposition", contentDisposition)); + downloadHeaders.add(Pair.create("Content-Type", mimetype)); + downloadHeaders.add(Pair.create("Content-Length", Integer.toString(data.length()))); + + TestWebServer webServer = TestWebServer.start(); + try { + final String pageUrl = webServer.setResponse("/download.txt", data, downloadHeaders); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.getTab().getNavigationController().navigate(Uri.parse(pageUrl)); + }); + mCallback.waitForIntercept(); + + Assert.assertEquals(pageUrl, mCallback.mUrl); + Assert.assertEquals(contentDisposition, mCallback.mContentDisposition); + Assert.assertEquals(mimetype, mCallback.mMimetype); + Assert.assertEquals(data.length(), mCallback.mContentLength); + // TODO(estade): verify mUserAgent. + } finally { + webServer.shutdown(); + } + } + + /** + * Verifies that if the first navigation in a Tab is for a download then it is deleted. + */ + @Test + @SmallTest + public void testFirstNavigationIsDownloadClosesTab() throws Throwable { + // Set up listening for the tab removal that we expect to happen. + CallbackHelper onTabRemovedCallbackHelper = new CallbackHelper(); + TabListCallback tabListCallback = new TabListCallback() { + @Override + public void onTabRemoved(Tab tab) { + onTabRemovedCallbackHelper.notifyCalled(); + } + }; + Browser browser = mActivityTestRule.getActivity().getBrowser(); + TestThreadUtils.runOnUiThreadBlocking( + () -> { browser.registerTabListCallback(tabListCallback); }); + + final String data = "download data"; + final String contentDisposition = "attachment;filename=\"download.txt\""; + final String mimetype = "text/plain"; + + List<Pair<String, String>> downloadHeaders = new ArrayList<Pair<String, String>>(); + downloadHeaders.add(Pair.create("Content-Disposition", contentDisposition)); + downloadHeaders.add(Pair.create("Content-Type", mimetype)); + downloadHeaders.add(Pair.create("Content-Length", Integer.toString(data.length()))); + + TestWebServer webServer = TestWebServer.start(); + try { + final String pageUrl = webServer.setResponse("/download.txt", data, downloadHeaders); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.getTab().getNavigationController().navigate(Uri.parse(pageUrl)); + }); + mCallback.waitForCompleted(); + onTabRemovedCallbackHelper.waitForFirst(); + } finally { + webServer.shutdown(); + } + } + + /** + * Verifies the DownloadCallback is informed of downloads resulting from the user clicking on a + * download link. + */ + @Test + @SmallTest + public void testInterceptDownloadByLinkAttribute() { + mCallback.mIntercept = true; + String pageUrl = mActivityTestRule.getTestDataURL("download.html"); + mActivityTestRule.navigateAndWait(pageUrl); + + EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView()); + mCallback.waitForIntercept(); + Assert.assertEquals(mActivityTestRule.getTestDataURL("lorem_ipsum.txt"), mCallback.mUrl); + } + + @Test + @SmallTest + public void testBasic() { + String url = mActivityTestRule.getTestDataURL("content-disposition.html"); + TestThreadUtils.runOnUiThreadBlocking( + () -> { mActivity.getTab().getNavigationController().navigate(Uri.parse(url)); }); + mCallback.waitForStarted(); + mCallback.waitForCompleted(); + + // Location varies depending upon version. See PathUtils.getDownloadsDirectory(). + Assert.assertTrue(mCallback.mLocation.contains( + "org.chromium.weblayer.shell/cache/weblayer/Downloads/") + || mCallback.mLocation.contains("/media/")); + Assert.assertTrue(mCallback.mFileName.contains("test")); + Assert.assertEquals(DownloadState.COMPLETE, mCallback.mState); + Assert.assertEquals(DownloadError.NO_ERROR, mCallback.mError); + Assert.assertEquals("text/html", mCallback.mMimetype); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ErrorPageCallbackTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ErrorPageCallbackTest.java new file mode 100644 index 0000000..a47ba33a2 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ErrorPageCallbackTest.java
@@ -0,0 +1,166 @@ +// 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. + +package org.chromium.weblayer.test; + +import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking; + +import android.net.Uri; + +import androidx.test.filters.SmallTest; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.net.test.EmbeddedTestServer; +import org.chromium.net.test.ServerCertificate; +import org.chromium.net.test.util.TestWebServer; +import org.chromium.weblayer.ErrorPage; +import org.chromium.weblayer.ErrorPageCallback; +import org.chromium.weblayer.Navigation; +import org.chromium.weblayer.NavigationController; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Tests that ErrorPageCallback works as expected for handling error page interactions. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class ErrorPageCallbackTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + private InstrumentationActivity mActivity; + // Only one EmbeddedTestServer may be used at a time. + private TestWebServer mGoodServer; + private EmbeddedTestServer mBadSslServer; + private String mGoodUrl; + private String mBadUrl; + private Callback mCallback; + + private static class Callback extends ErrorPageCallback { + public boolean mSignaled; + public String mSafetyPage; + public ErrorPage mErrorPage; + public Tab mTab; + + public Callback(Tab tab) { + mTab = tab; + } + + @Override + public boolean onBackToSafety() { + mSignaled = true; + if (mSafetyPage == null) { + return false; + } + + mTab.getNavigationController().navigate(Uri.parse(mSafetyPage)); + return true; + } + + @Override + public ErrorPage getErrorPage(Navigation navigation) { + return mErrorPage; + } + } + + @Before + public void setUp() throws Throwable { + mActivity = mActivityTestRule.launchShellWithUrl(null); + Assert.assertNotNull(mActivity); + + mGoodServer = TestWebServer.start(); + mGoodUrl = mGoodServer.setResponse("/ok.html", "<html>ok</html>", null); + + mBadSslServer = EmbeddedTestServer.createAndStartHTTPSServer( + mActivity, ServerCertificate.CERT_MISMATCHED_NAME); + mBadUrl = mBadSslServer.getURL("/weblayer/test/data/simple_page.html"); + + mCallback = new Callback(mActivity.getTab()); + + mActivityTestRule.navigateAndWait(mGoodUrl); + mActivityTestRule.navigateAndWaitForFailure(mBadUrl); + } + + @After + public void tearDown() { + mBadSslServer.stopAndDestroyServer(); + } + + /** + * Verifies that if there's no ErrorPageCallback, when the user clicks "back to safety", + * WebLayer provides default behavior (navigating back). + */ + @Test + @SmallTest + public void testBackToSafetyDefaultBehavior() throws Throwable { + NavigationWaiter navigationWaiter = new NavigationWaiter( + mGoodUrl, mActivity.getTab(), false /* expectFailure */, true /* waitForPaint */); + mActivityTestRule.executeScriptSync( + "window.certificateErrorPageController.dontProceed();", false); + navigationWaiter.waitForNavigation(); + Assert.assertFalse(mCallback.mSignaled); + } + + /** + * Verifies that if there's an ErrorPageCallback and onBackToSafety returns true, WebLayer does + * *not* provide default behavior. + */ + @Test + @SmallTest + public void testBackToSafetyOverride() throws Throwable { + TestThreadUtils.runOnUiThreadBlocking( + () -> { mActivity.getTab().setErrorPageCallback(mCallback); }); + + mCallback.mSafetyPage = mGoodServer.setResponse("/safe.html", "<html>safe</html>", null); + + NavigationWaiter navigationWaiter = new NavigationWaiter(mCallback.mSafetyPage, + mActivity.getTab(), false /* expectFailure */, true /* waitForPaint */); + mActivityTestRule.executeScriptSync( + "window.certificateErrorPageController.dontProceed();", false); + navigationWaiter.waitForNavigation(); + Assert.assertTrue(mCallback.mSignaled); + } + + /** + * Verifies that if there's an ErrorPageCallback and onBackToSafety returns false, WebLayer + * *does* provide default behavior. + */ + @Test + @SmallTest + public void testBackToSafetyDontOverride() throws Throwable { + TestThreadUtils.runOnUiThreadBlocking( + () -> { mActivity.getTab().setErrorPageCallback(mCallback); }); + + NavigationWaiter navigationWaiter = new NavigationWaiter( + mGoodUrl, mActivity.getTab(), false /* expectFailure */, true /* waitForPaint */); + mActivityTestRule.executeScriptSync( + "window.certificateErrorPageController.dontProceed();", false); + navigationWaiter.waitForNavigation(); + Assert.assertTrue(mCallback.mSignaled); + } + + @Test + @SmallTest + public void testOverrideErrorPage() throws Throwable { + mCallback.mErrorPage = new ErrorPage("<html><head><title>test error</title>"); + TestThreadUtils.runOnUiThreadBlocking( + () -> { mActivity.getTab().setErrorPageCallback(mCallback); }); + String errorPageUrl = "http://localhost:7/non_existent"; + mActivityTestRule.navigateAndWaitForFailure(errorPageUrl); + runOnUiThreadBlocking(() -> { + NavigationController navigationController = + mActivity.getTab().getNavigationController(); + Assert.assertEquals("test error", + navigationController.getNavigationEntryTitle( + navigationController.getNavigationListCurrentIndex())); + }); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/EventUtils.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/EventUtils.java new file mode 100644 index 0000000..21589873 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/EventUtils.java
@@ -0,0 +1,52 @@ +// 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. + +package org.chromium.weblayer.test; + +import android.os.SystemClock; +import android.view.MotionEvent; +import android.view.View; + +/** + * Utilities related to event generation. + */ +public final class EventUtils { + private EventUtils() {} + + /** + * Asynchronously posts a touch-down and touch-up event at the center of the supplied View. + */ + public static void simulateTouchCenterOfView(final View view) { + simulateDragFromCenterOfView(view, 0, 0); + } + + /** + * Asynchronously posts a touch-down and touch-up event at the center of the supplied View. If + * deltaX or deltaY is non-zero a touch-move is generated between the down/up. Each individual + * event is posted asynchronously wrt the other events, which is necessary for scrolling events + * to be fully processed by //content and ripple out to observers (e.g., the infobar container). + */ + public static void simulateDragFromCenterOfView( + final View view, final float deltaX, final float deltaY) { + view.post(() -> { + long eventTime = SystemClock.uptimeMillis(); + float x = (float) (view.getRight() - view.getLeft()) / 2; + float y = (float) (view.getBottom() - view.getTop()) / 2; + view.dispatchTouchEvent( + MotionEvent.obtain(eventTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0)); + view.post(() -> { + long newEventTime = SystemClock.uptimeMillis(); + if (deltaX != 0 || deltaY != 0) { + view.dispatchTouchEvent(MotionEvent.obtain(newEventTime, newEventTime, + MotionEvent.ACTION_MOVE, x + deltaX, y + deltaY, 0)); + } + view.post(() -> { + long newestEventTime = SystemClock.uptimeMillis(); + view.dispatchTouchEvent(MotionEvent.obtain(newestEventTime, newestEventTime, + MotionEvent.ACTION_UP, x + deltaX, y + deltaY, 0)); + }); + }); + }); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ExecuteScriptTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ExecuteScriptTest.java new file mode 100644 index 0000000..18bde07 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ExecuteScriptTest.java
@@ -0,0 +1,93 @@ +// 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. + +package org.chromium.weblayer.test; + +import androidx.test.filters.SmallTest; + +import org.json.JSONObject; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.UrlUtils; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Tests that script execution works as expected. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class ExecuteScriptTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private static final String DATA_URL = UrlUtils.encodeHtmlDataUri( + "<html><head><script>var bar = 10;</script></head><body>foo</body></html>"); + + @Test + @SmallTest + public void testBasicScript() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(DATA_URL); + JSONObject result = mActivityTestRule.executeScriptSync( + "document.body.innerHTML", true /* useSeparateIsolate */); + Assert.assertEquals( + result.getString(InstrumentationActivityTestRule.SCRIPT_RESULT_KEY), "foo"); + } + + @Test + @SmallTest + public void testScriptIsolatedFromPage() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(DATA_URL); + JSONObject result = + mActivityTestRule.executeScriptSync("bar", true /* useSeparateIsolate */); + Assert.assertTrue(result.isNull(InstrumentationActivityTestRule.SCRIPT_RESULT_KEY)); + } + + @Test + @SmallTest + public void testMainWorldScriptNotIsolatedFromPage() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(DATA_URL); + JSONObject result = + mActivityTestRule.executeScriptSync("bar", false /* useSeparateIsolate */); + Assert.assertEquals(result.getInt(InstrumentationActivityTestRule.SCRIPT_RESULT_KEY), 10); + } + + @Test + @SmallTest + public void testScriptNotIsolatedFromOtherScript() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(DATA_URL); + mActivityTestRule.executeScriptSync("var foo = 20;", true /* useSeparateIsolate */); + JSONObject result = + mActivityTestRule.executeScriptSync("foo", true /* useSeparateIsolate */); + Assert.assertEquals(result.getInt(InstrumentationActivityTestRule.SCRIPT_RESULT_KEY), 20); + } + + @Test + @SmallTest + public void testClearedOnNavigate() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(DATA_URL); + mActivityTestRule.executeScriptSync("var foo = 20;", true /* useSeparateIsolate */); + + String newUrl = UrlUtils.encodeHtmlDataUri("<html></html>"); + mActivityTestRule.navigateAndWait(newUrl); + JSONObject result = + mActivityTestRule.executeScriptSync("foo", true /* useSeparateIsolate */); + Assert.assertTrue(result.isNull(InstrumentationActivityTestRule.SCRIPT_RESULT_KEY)); + } + + @Test + @SmallTest + public void testNullCallback() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(DATA_URL); + TestThreadUtils.runOnUiThreadBlocking(() -> { + // Null callback should not crash. + activity.getTab().executeScript("null", true /* useSeparateIsolate */, null); + }); + // Execute a sync script to make sure the other script finishes. + mActivityTestRule.executeScriptSync("null", true /* useSeparateIsolate */); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ExternalNavigationTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ExternalNavigationTest.java new file mode 100644 index 0000000..063d56c --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ExternalNavigationTest.java
@@ -0,0 +1,1578 @@ +// Copyright 2020 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.weblayer.test; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.support.test.InstrumentationRegistry; + +import androidx.annotation.NonNull; +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.DisableIf; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.Browser; +import org.chromium.weblayer.Callback; +import org.chromium.weblayer.ExternalIntentInIncognitoCallback; +import org.chromium.weblayer.ExternalIntentInIncognitoUserDecision; +import org.chromium.weblayer.NavigateParams; +import org.chromium.weblayer.Navigation; +import org.chromium.weblayer.NavigationCallback; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.TabListCallback; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Tests handling of external intents. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class ExternalNavigationTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private static final boolean EXPECT_NAVIGATION_COMPLETION = true; + private static final boolean EXPECT_NAVIGATION_FAILURE = false; + private static final boolean RESULTS_IN_EXTERNAL_INTENT = true; + private static final boolean DOESNT_RESULT_IN_EXTERNAL_INTENT = false; + private static final boolean RESULTS_IN_USER_DECIDING_EXTERNAL_INTENT = true; + private static final boolean DOESNT_RESULT_IN_USER_DECIDING_EXTERNAL_INTENT = false; + + private static final String ABOUT_BLANK_URL = "about:blank"; + private static final String CUSTOM_SCHEME_URL_WITH_DEFAULT_EXTERNAL_HANDLER = + "weblayer://weblayertest/intent"; + private static final String INTENT_TO_DUMMY_ACTIVITY_FOR_SPECIAL_SCHEME_DATA_STRING = + CUSTOM_SCHEME_URL_WITH_DEFAULT_EXTERNAL_HANDLER; + private static final String INTENT_TO_DUMMY_ACTIVITY_FOR_SPECIAL_SCHEME_ACTION = + "android.intent.action.VIEW"; + // The package is not specified in the intent that gets created when navigating to the special + // scheme. + private static final String INTENT_TO_DUMMY_ACTIVITY_FOR_SPECIAL_SCHEME_PACKAGE = null; + private static final String INTENT_TO_SELF_DATA_CONTENT = "example.test"; + private static final String INTENT_TO_SELF_SCHEME = "https"; + private static final String INTENT_TO_SELF_DATA_STRING = + INTENT_TO_SELF_SCHEME + "://" + INTENT_TO_SELF_DATA_CONTENT; + private static final String INTENT_TO_SELF_ACTION = "android.intent.action.VIEW"; + private static final String INTENT_TO_SELF_PACKAGE = + InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName(); + + // An intent that opens the test app to view a specified URL. Note that the "end" is left off to + // allow appending extras when constructing URLs. + private static final String INTENT_TO_SELF = "intent://" + INTENT_TO_SELF_DATA_CONTENT + + "#Intent;scheme=" + INTENT_TO_SELF_SCHEME + ";action=" + INTENT_TO_SELF_ACTION + + ";package=" + INTENT_TO_SELF_PACKAGE + ";"; + private static final String INTENT_TO_SELF_URL = INTENT_TO_SELF + "end"; + + // An intent URL that gets rejected as malformed. + private static final String MALFORMED_INTENT_URL = "intent://garbage;end"; + + // An intent that is properly formed but wishes to open an app that is not present on the + // device. Note that the "end" is left off to allow appending extras when constructing URLs. + private static final String NON_RESOLVABLE_INTENT = + "intent://dummy.com/#Intent;scheme=https;action=android.intent.action.VIEW;package=com.missing.app;"; + + private static final String LINK_WITH_INTENT_TO_SELF_IN_SAME_TAB_FILE = + "link_with_intent_to_package_in_same_tab.html#" + INTENT_TO_SELF_PACKAGE; + private static final String LINK_WITH_INTENT_TO_SELF_IN_NEW_TAB_FILE = + "link_with_intent_to_package_in_new_tab.html#" + INTENT_TO_SELF_PACKAGE; + private static final String PAGE_THAT_INTENTS_TO_CHROME_ON_LOAD_FILE = + "page_that_intents_to_package_on_load.html#" + INTENT_TO_SELF_PACKAGE; + private static final String LINK_TO_PAGE_THAT_INTENTS_TO_CHROME_ON_LOAD_FILE = + "link_to_page_that_intents_to_package_on_load.html#" + INTENT_TO_SELF_PACKAGE; + + // The test server handles "echo" with a response containing "Echo" :). + private final String mTestServerSiteUrl = mActivityTestRule.getTestServer().getURL("/echo"); + + private final String mTestServerSiteFallbackUrlExtra = + "S.browser_fallback_url=" + android.net.Uri.encode(mTestServerSiteUrl) + ";"; + private final String mIntentToSelfWithFallbackUrl = + INTENT_TO_SELF + mTestServerSiteFallbackUrlExtra + "end"; + private final String mNonResolvableIntentWithFallbackUrl = + NON_RESOLVABLE_INTENT + mTestServerSiteFallbackUrlExtra + "end"; + + private final String mRedirectToCustomSchemeUrlWithDefaultExternalHandler = + mActivityTestRule.getTestServer().getURL( + "/server-redirect?" + CUSTOM_SCHEME_URL_WITH_DEFAULT_EXTERNAL_HANDLER); + private final String mRedirectToIntentToSelfURL = + mActivityTestRule.getTestServer().getURL("/server-redirect?" + INTENT_TO_SELF_URL); + private final String mNonResolvableIntentWithFallbackUrlThatLaunchesIntent = + NON_RESOLVABLE_INTENT + "S.browser_fallback_url=" + + android.net.Uri.encode(mRedirectToIntentToSelfURL) + ";end"; + + private static final String SPECIALIZED_DATA_URL = "data://externalnavtest"; + + private class IntentInterceptor implements InstrumentationActivity.IntentInterceptor { + public Intent mLastIntent; + private CallbackHelper mCallbackHelper = new CallbackHelper(); + + @Override + public void interceptIntent(Intent intent, int requestCode, Bundle options) { + mLastIntent = intent; + mCallbackHelper.notifyCalled(); + } + + public void waitForIntent() { + try { + mCallbackHelper.waitForFirst(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + private class ExternalIntentInIncognitoCallbackTestImpl + extends ExternalIntentInIncognitoCallback { + private Callback<Integer> mOnUserDecisionCallback; + CallbackHelper mCallbackHelper = new CallbackHelper(); + + @Override + public void onExternalIntentInIncognito(@NonNull Callback<Integer> onUserDecisionCallback) { + mOnUserDecisionCallback = onUserDecisionCallback; + mCallbackHelper.notifyCalled(); + } + + public Callback<Integer> getOnUserDecisionCallback() { + return mOnUserDecisionCallback; + } + + public void waitForNotificationOnExternalIntentLaunch() throws Throwable { + mCallbackHelper.waitForFirst(); + } + } + + /* + * Navigates to |urlToNavigateTo| and waits for a completed/failed navigation to |urlToWaitFor| + * as appropriate. In the callback verifies that the values of the relevant params on the + * Navigation match the passed-in expected values. + */ + private void navigateAndCheckExternalIntentParams(String urlToNavigateTo, String urlToWaitFor, + boolean expectNavigationCompletion, boolean resultsInExternalIntent, + boolean resultsInUserDecidingIntentLaunch) throws Throwable { + Tab tab = mActivityTestRule.getActivity().getTab(); + + CallbackHelper navigationCompletedCallbackHelper = new CallbackHelper(); + CallbackHelper navigationFailedCallbackHelper = new CallbackHelper(); + + NavigationCallback navigationCallback = new NavigationCallback() { + @Override + public void onNavigationCompleted(Navigation navigation) { + String url = navigation.getUri().toString(); + if (!url.equals(urlToWaitFor)) return; + + Assert.assertEquals(true, expectNavigationCompletion); + + // A navigation should never be expected to both complete and result in an external + // intent. + Assert.assertEquals(false, resultsInExternalIntent); + Assert.assertEquals(false, navigation.wasIntentLaunched()); + Assert.assertEquals(false, resultsInUserDecidingIntentLaunch); + Assert.assertEquals(false, navigation.isUserDecidingIntentLaunch()); + + navigationCompletedCallbackHelper.notifyCalled(); + } + + @Override + public void onNavigationFailed(Navigation navigation) { + String url = navigation.getUri().toString(); + if (!url.equals(urlToWaitFor)) return; + + Assert.assertEquals(false, expectNavigationCompletion); + + Assert.assertEquals(resultsInExternalIntent, navigation.wasIntentLaunched()); + Assert.assertEquals( + resultsInUserDecidingIntentLaunch, navigation.isUserDecidingIntentLaunch()); + + navigationFailedCallbackHelper.notifyCalled(); + } + }; + + TestThreadUtils.runOnUiThreadBlocking(() -> { + tab.getNavigationController().registerNavigationCallback(navigationCallback); + tab.getNavigationController().navigate(Uri.parse(urlToNavigateTo)); + }); + + if (expectNavigationCompletion) { + navigationCompletedCallbackHelper.waitForFirst(); + } else { + navigationFailedCallbackHelper.waitForFirst(); + } + + TestThreadUtils.runOnUiThreadBlocking(() -> { + tab.getNavigationController().unregisterNavigationCallback(navigationCallback); + }); + } + + /* + * A convenience variant of the above method that navigates to and waits for the same URL. See + * comments on the above method. + */ + private void navigateAndCheckExternalIntentParams(String urlToNavigateTo, + boolean expectNavigationCompletion, boolean resultsInExternalIntent, + boolean resultsInUserDecidingIntentLaunch) throws Throwable { + navigateAndCheckExternalIntentParams(urlToNavigateTo, urlToNavigateTo, + expectNavigationCompletion, resultsInExternalIntent, + resultsInUserDecidingIntentLaunch); + } + + /** + * Verifies that for a navigation to a URI that WebLayer can handle internally, there + * is no external intent triggered. + */ + @Test + @SmallTest + public void testBrowserNavigation() throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + mActivityTestRule.navigateAndWait(mTestServerSiteUrl); + + Assert.assertNull(intentInterceptor.mLastIntent); + Assert.assertEquals(mTestServerSiteUrl, mActivityTestRule.getCurrentDisplayUrl()); + } + + /** + * Tests that a direct navigation to an external intent in a background tab is blocked. + */ + @Test + @SmallTest + public void testExternalIntentWithNoRedirectInBackgroundTabBlockedByDefault() throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + Tab backgroundTab = TestThreadUtils.runOnUiThreadBlocking( + () -> activity.getTab().getBrowser().createTab()); + Tab activeTab = TestThreadUtils.runOnUiThreadBlocking( + () -> { return activity.getTab().getBrowser().getActiveTab(); }); + Assert.assertNotEquals(backgroundTab, activeTab); + + // Navigate directly to an intent in the background and verify that the intent is not + // launched. + NavigationWaiter waiter = new NavigationWaiter(INTENT_TO_SELF_URL, backgroundTab, + /*expectFailure=*/true, /*waitForPaint=*/false); + TestThreadUtils.runOnUiThreadBlocking(() -> { + backgroundTab.getNavigationController().navigate(Uri.parse(INTENT_TO_SELF_URL)); + }); + + waiter.waitForNavigation(); + + Assert.assertNull(intentInterceptor.mLastIntent); + int numNavigationsInBackgroundTab = TestThreadUtils.runOnUiThreadBlocking( + () -> { return backgroundTab.getNavigationController().getNavigationListSize(); }); + Assert.assertEquals(0, numNavigationsInBackgroundTab); + } + + /** + * Tests that a direct navigation to an external intent in a background tab is launched when + * intent launches are allowed in the background for this navigation. + */ + @Test + @SmallTest + @MinWebLayerVersion(89) + public void + testExternalIntentWithNoRedirectInBackgroundTabLaunchedWhenBackgroundLaunchesAllowed() + throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + Tab backgroundTab = TestThreadUtils.runOnUiThreadBlocking( + () -> activity.getTab().getBrowser().createTab()); + Tab activeTab = TestThreadUtils.runOnUiThreadBlocking( + () -> { return activity.getTab().getBrowser().getActiveTab(); }); + Assert.assertNotEquals(backgroundTab, activeTab); + + // Put a initial navigation in the background tab to ease verification of state + // afterward (note that this navigation will not result in a paint due to the tab being in + // the background). + mActivityTestRule.navigateAndWait(backgroundTab, ABOUT_BLANK_URL, /*waitForPaint=*/false); + + // Navigate directly to an intent in the background tab with intent launching in the + // background allowed and verify that the intent is launched. + TestThreadUtils.runOnUiThreadBlocking(() -> { + NavigateParams.Builder navigateParamsBuilder = new NavigateParams.Builder(); + navigateParamsBuilder.allowIntentLaunchesInBackground(); + backgroundTab.getNavigationController().navigate( + Uri.parse(INTENT_TO_SELF_URL), navigateParamsBuilder.build()); + }); + + intentInterceptor.waitForIntent(); + + // The intent should have been launched, and there should still be only the initial + // navigation in the background tab. + Intent intent = intentInterceptor.mLastIntent; + Assert.assertNotNull(intent); + Assert.assertEquals(INTENT_TO_SELF_PACKAGE, intent.getPackage()); + Assert.assertEquals(INTENT_TO_SELF_ACTION, intent.getAction()); + Assert.assertEquals(INTENT_TO_SELF_DATA_STRING, intent.getDataString()); + + int numNavigationsInBackgroundTab = TestThreadUtils.runOnUiThreadBlocking( + () -> { return backgroundTab.getNavigationController().getNavigationListSize(); }); + Assert.assertEquals(1, numNavigationsInBackgroundTab); + } + + /** + * Tests that a redirect to an external intent in a background tab is blocked. + */ + @Test + @SmallTest + public void testExternalIntentAfterRedirectInBackgroundTabBlockedByDefault() throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + Tab backgroundTab = TestThreadUtils.runOnUiThreadBlocking( + () -> activity.getTab().getBrowser().createTab()); + Tab activeTab = TestThreadUtils.runOnUiThreadBlocking( + () -> { return activity.getTab().getBrowser().getActiveTab(); }); + Assert.assertNotEquals(backgroundTab, activeTab); + + // Perform a navigation that redirects to an intent in the background and verify that the + // intent is not launched. + NavigationWaiter waiter = new NavigationWaiter(INTENT_TO_SELF_URL, backgroundTab, + /*expectFailure=*/true, /*waitForPaint=*/false); + TestThreadUtils.runOnUiThreadBlocking(() -> { + backgroundTab.getNavigationController().navigate(Uri.parse(mRedirectToIntentToSelfURL)); + }); + + waiter.waitForNavigation(); + + Assert.assertNull(intentInterceptor.mLastIntent); + int numNavigationsInBackgroundTab = TestThreadUtils.runOnUiThreadBlocking( + () -> { return backgroundTab.getNavigationController().getNavigationListSize(); }); + Assert.assertEquals(0, numNavigationsInBackgroundTab); + } + + /** + * Tests that a redirect to an external intent in a background tab is launched when + * intent launches are allowed in the background for this navigation. + */ + @Test + @SmallTest + @MinWebLayerVersion(89) + public void + testExternalIntentAfterRedirectInBackgroundTabLaunchedWhenBackgroundLaunchesAllowed() + throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + Tab backgroundTab = TestThreadUtils.runOnUiThreadBlocking( + () -> activity.getTab().getBrowser().createTab()); + Tab activeTab = TestThreadUtils.runOnUiThreadBlocking( + () -> { return activity.getTab().getBrowser().getActiveTab(); }); + Assert.assertNotEquals(backgroundTab, activeTab); + + // Put a initial navigation in the background tab to ease verification of state + // afterward (note that this navigation will not result in a paint due to the tab being in + // the background). + mActivityTestRule.navigateAndWait(backgroundTab, ABOUT_BLANK_URL, /*waitForPaint=*/false); + + // Perform a navigation that redirects to an intent in the background tab with intent + // launching in the background allowed and verify that the intent is launched. + TestThreadUtils.runOnUiThreadBlocking(() -> { + NavigateParams.Builder navigateParamsBuilder = new NavigateParams.Builder(); + navigateParamsBuilder.allowIntentLaunchesInBackground(); + backgroundTab.getNavigationController().navigate( + Uri.parse(mRedirectToIntentToSelfURL), navigateParamsBuilder.build()); + }); + + intentInterceptor.waitForIntent(); + + // The intent should have been launched, and there should still be only the initial + // navigation in the background tab. + Intent intent = intentInterceptor.mLastIntent; + Assert.assertNotNull(intent); + Assert.assertEquals(INTENT_TO_SELF_PACKAGE, intent.getPackage()); + Assert.assertEquals(INTENT_TO_SELF_ACTION, intent.getAction()); + Assert.assertEquals(INTENT_TO_SELF_DATA_STRING, intent.getDataString()); + + int numNavigationsInBackgroundTab = TestThreadUtils.runOnUiThreadBlocking( + () -> { return backgroundTab.getNavigationController().getNavigationListSize(); }); + Assert.assertEquals(1, numNavigationsInBackgroundTab); + } + + /** + * Tests that a direct navigation to an external intent in browser startup is blocked as the + * browser is not yet attached to the window at the time of the navigation and thus the tab is + * not visible. + */ + @Test + @SmallTest + public void testExternalIntentWithNoRedirectInBrowserStartupBlockedByDefault() + throws Throwable { + CallbackHelper onNavigationFailedCallbackHelper = new CallbackHelper(); + NavigationCallback navigationCallback = new NavigationCallback() { + @Override + public void onNavigationFailed(Navigation navigation) { + if (navigation.getUri().toString().equals(INTENT_TO_SELF_URL)) { + onNavigationFailedCallbackHelper.notifyCalled(); + } + } + }; + + // The flow being tested is where the navigation occurs synchronously with initial browser + // creation. + final IntentInterceptor intentInterceptor = new IntentInterceptor(); + InstrumentationActivity.registerOnCreatedCallback( + new InstrumentationActivity.OnCreatedCallback() { + @Override + public void onCreated(Browser browser, InstrumentationActivity activity) { + activity.setIntentInterceptor(intentInterceptor); + browser.getActiveTab().getNavigationController().registerNavigationCallback( + navigationCallback); + browser.getActiveTab().getNavigationController().navigate( + Uri.parse(INTENT_TO_SELF_URL)); + } + }); + + mActivityTestRule.launchShell(new Bundle()); + + // The navigation should fail... + onNavigationFailedCallbackHelper.waitForFirst(); + + // ...the intent should not have been launched... + Assert.assertNull(intentInterceptor.mLastIntent); + + // ...and there should be one tab in the browser without any navigations in it. + Browser browser = mActivityTestRule.getActivity().getBrowser(); + int numTabs = + TestThreadUtils.runOnUiThreadBlocking(() -> { return browser.getTabs().size(); }); + Assert.assertEquals(1, numTabs); + int numNavigationsInTab = TestThreadUtils.runOnUiThreadBlocking(() -> { + return browser.getActiveTab().getNavigationController().getNavigationListSize(); + }); + Assert.assertEquals(0, numNavigationsInTab); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + browser.getActiveTab().getNavigationController().unregisterNavigationCallback( + navigationCallback); + }); + } + + /** + * Tests that a direct navigation to an external intent in browser startup is launched if the + * embedder specifies that intent launches in the background are allowed for this navigation. + */ + @Test + @SmallTest + @MinWebLayerVersion(89) + public void + testExternalIntentWithNoRedirectInBrowserStartupLaunchedWhenBackgroundLaunchesAllowed() + throws Throwable { + CallbackHelper onTabRemovedCallbackHelper = new CallbackHelper(); + TabListCallback tabListCallback = new TabListCallback() { + @Override + public void onTabRemoved(Tab tab) { + onTabRemovedCallbackHelper.notifyCalled(); + } + }; + + final IntentInterceptor intentInterceptor = new IntentInterceptor(); + InstrumentationActivity.registerOnCreatedCallback( + new InstrumentationActivity.OnCreatedCallback() { + @Override + public void onCreated(Browser browser, InstrumentationActivity activity) { + activity.setIntentInterceptor(intentInterceptor); + browser.registerTabListCallback(tabListCallback); + + NavigateParams.Builder navigateParamsBuilder = new NavigateParams.Builder(); + navigateParamsBuilder.allowIntentLaunchesInBackground(); + browser.getActiveTab().getNavigationController().navigate( + Uri.parse(INTENT_TO_SELF_URL), navigateParamsBuilder.build()); + } + }); + + mActivityTestRule.launchShell(new Bundle()); + + // The intent should be launched... + intentInterceptor.waitForIntent(); + Intent intent = intentInterceptor.mLastIntent; + Assert.assertNotNull(intent); + Assert.assertEquals(INTENT_TO_SELF_PACKAGE, intent.getPackage()); + Assert.assertEquals(INTENT_TO_SELF_ACTION, intent.getAction()); + Assert.assertEquals(INTENT_TO_SELF_DATA_STRING, intent.getDataString()); + + // ...the tab created for the initial navigation should be closed... + onTabRemovedCallbackHelper.waitForFirst(); + + // ...and there should now be no tabs in the browser. + Browser browser = mActivityTestRule.getActivity().getBrowser(); + int numTabs = + TestThreadUtils.runOnUiThreadBlocking(() -> { return browser.getTabs().size(); }); + Assert.assertEquals(0, numTabs); + + TestThreadUtils.runOnUiThreadBlocking( + () -> { browser.unregisterTabListCallback(tabListCallback); }); + } + + /** + * Tests that a direct navigation to an external intent in browser startup in incognito mode is + * blocked as the browser is not yet attached to the window at the time of the navigation and + * thus the tab is not visible. + */ + @Test + @SmallTest + public void testExternalIntentWithNoRedirectOnBrowserStartupInIncognitoBlockedByDefault() + throws Throwable { + CallbackHelper onNavigationFailedCallbackHelper = new CallbackHelper(); + NavigationCallback navigationCallback = new NavigationCallback() { + @Override + public void onNavigationFailed(Navigation navigation) { + if (navigation.getUri().toString().equals(INTENT_TO_SELF_URL)) { + onNavigationFailedCallbackHelper.notifyCalled(); + } + } + }; + + final IntentInterceptor intentInterceptor = new IntentInterceptor(); + InstrumentationActivity.registerOnCreatedCallback( + new InstrumentationActivity.OnCreatedCallback() { + @Override + public void onCreated(Browser browser, InstrumentationActivity activity) { + Assert.assertEquals(true, browser.getProfile().isIncognito()); + activity.setIntentInterceptor(intentInterceptor); + browser.getActiveTab().getNavigationController().registerNavigationCallback( + navigationCallback); + browser.getActiveTab().getNavigationController().navigate( + Uri.parse(INTENT_TO_SELF_URL)); + } + }); + + Bundle extras = new Bundle(); + extras.putBoolean(InstrumentationActivity.EXTRA_IS_INCOGNITO, true); + mActivityTestRule.launchShell(extras); + + // The navigation should fail... + onNavigationFailedCallbackHelper.waitForFirst(); + + // ...the intent should not have been launched... + Assert.assertNull(intentInterceptor.mLastIntent); + + // ...and there should be one tab in the browser without any navigations in it. + Browser browser = mActivityTestRule.getActivity().getBrowser(); + int numTabs = + TestThreadUtils.runOnUiThreadBlocking(() -> { return browser.getTabs().size(); }); + Assert.assertEquals(1, numTabs); + int numNavigationsInTab = TestThreadUtils.runOnUiThreadBlocking(() -> { + return browser.getActiveTab().getNavigationController().getNavigationListSize(); + }); + Assert.assertEquals(0, numNavigationsInTab); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + browser.getActiveTab().getNavigationController().unregisterNavigationCallback( + navigationCallback); + }); + } + + /** + * Tests that a direct navigation to an external intent in browser startup in incognito mode + * causes an alert dialog to be shown if the embedder specifies that intent launches in the + * background are allowed for this navigation, and that it is then launched if the user consents + * via the dialog. + */ + @Test + @SmallTest + @MinWebLayerVersion(89) + public void + testExternalIntentWithNoRedirectInBrowserStartupInIncognitoLaunchedWhenBackgroundLaunchesAllowedAndUserConsents() + throws Throwable { + CallbackHelper onTabRemovedCallbackHelper = new CallbackHelper(); + TabListCallback tabListCallback = new TabListCallback() { + @Override + public void onTabRemoved(Tab tab) { + onTabRemovedCallbackHelper.notifyCalled(); + } + }; + + final IntentInterceptor intentInterceptor = new IntentInterceptor(); + InstrumentationActivity.registerOnCreatedCallback( + new InstrumentationActivity.OnCreatedCallback() { + @Override + public void onCreated(Browser browser, InstrumentationActivity activity) { + Assert.assertEquals(true, browser.getProfile().isIncognito()); + activity.setIntentInterceptor(intentInterceptor); + browser.registerTabListCallback(tabListCallback); + + NavigateParams.Builder navigateParamsBuilder = new NavigateParams.Builder(); + navigateParamsBuilder.allowIntentLaunchesInBackground(); + browser.getActiveTab().getNavigationController().navigate( + Uri.parse(INTENT_TO_SELF_URL), navigateParamsBuilder.build()); + } + }); + + Bundle extras = new Bundle(); + extras.putBoolean(InstrumentationActivity.EXTRA_IS_INCOGNITO, true); + mActivityTestRule.launchShell(extras); + + // The alert dialog notifying the user that they are about to leave incognito should pop up. + // Click the AlertDialog positive button (button1) when it does. + onView(withId(android.R.id.button1)) + .check(matches(withText("Leave"))) + .check(matches(isDisplayed())) + .perform(click()); + + // The intent should be launched... + intentInterceptor.waitForIntent(); + Intent intent = intentInterceptor.mLastIntent; + Assert.assertNotNull(intent); + Assert.assertEquals(INTENT_TO_SELF_PACKAGE, intent.getPackage()); + Assert.assertEquals(INTENT_TO_SELF_ACTION, intent.getAction()); + Assert.assertEquals(INTENT_TO_SELF_DATA_STRING, intent.getDataString()); + + // ...the tab created for the initial navigation should be closed... + onTabRemovedCallbackHelper.waitForFirst(); + + // ...and there should now be no tabs in the browser. + Browser browser = mActivityTestRule.getActivity().getBrowser(); + int numTabs = + TestThreadUtils.runOnUiThreadBlocking(() -> { return browser.getTabs().size(); }); + Assert.assertEquals(0, numTabs); + + TestThreadUtils.runOnUiThreadBlocking( + () -> { browser.unregisterTabListCallback(tabListCallback); }); + } + + /** + * Tests that a direct navigation to an external intent in browser startup in incognito mode + * causes an alert dialog to be shown if the embedder specifies that intent launches in the + * background are allowed for this navigation, and that it is blocked if the user forbids it via + * the dialog. + */ + @Test + @SmallTest + @MinWebLayerVersion(92) + public void + testExternalIntentWithNoRedirectInBrowserStartupInIncognitoBlockedWhenBackgroundLaunchesAllowedAndUserForbids() + throws Throwable { + CallbackHelper onNavigationToIntentFailedCallbackHelper = new CallbackHelper(); + CallbackHelper onNavigationToIntentDataStringStartedCallbackHelper = new CallbackHelper(); + NavigationCallback navigationCallback = new NavigationCallback() { + @Override + public void onNavigationStarted(Navigation navigation) { + // There should be no additional navigations after the initial one. + Assert.assertEquals(INTENT_TO_DUMMY_ACTIVITY_FOR_SPECIAL_SCHEME_DATA_STRING, + navigation.getUri().toString()); + } + @Override + public void onNavigationFailed(Navigation navigation) { + if (navigation.getUri().toString().equals( + INTENT_TO_DUMMY_ACTIVITY_FOR_SPECIAL_SCHEME_DATA_STRING)) { + onNavigationToIntentFailedCallbackHelper.notifyCalled(); + } + } + }; + + final IntentInterceptor intentInterceptor = new IntentInterceptor(); + InstrumentationActivity.registerOnCreatedCallback( + new InstrumentationActivity.OnCreatedCallback() { + @Override + public void onCreated(Browser browser, InstrumentationActivity activity) { + activity.setIntentInterceptor(intentInterceptor); + Assert.assertEquals(true, browser.getProfile().isIncognito()); + browser.getActiveTab().getNavigationController().registerNavigationCallback( + navigationCallback); + + NavigateParams.Builder navigateParamsBuilder = new NavigateParams.Builder(); + navigateParamsBuilder.allowIntentLaunchesInBackground(); + browser.getActiveTab().getNavigationController().navigate( + Uri.parse(INTENT_TO_DUMMY_ACTIVITY_FOR_SPECIAL_SCHEME_DATA_STRING), + navigateParamsBuilder.build()); + } + }); + + Bundle extras = new Bundle(); + extras.putBoolean(InstrumentationActivity.EXTRA_IS_INCOGNITO, true); + mActivityTestRule.launchShell(extras); + + // The alert dialog notifying the user that they are about to leave incognito should pop up. + // Click the AlertDialog negative button (button2) when it does. + onView(withId(android.R.id.button2)) + .check(matches(withText("Stay"))) + .check(matches(isDisplayed())) + .perform(click()); + + // The navigation should fail... + onNavigationToIntentFailedCallbackHelper.waitForFirst(); + + // ...the intent should not have been launched. + Assert.assertNull(intentInterceptor.mLastIntent); + + // As there was no fallback Url, there should be zero navigations in the tab. + Browser browser = mActivityTestRule.getActivity().getBrowser(); + int numNavigationsInTab = TestThreadUtils.runOnUiThreadBlocking(() -> { + return browser.getActiveTab().getNavigationController().getNavigationListSize(); + }); + Assert.assertEquals(0, numNavigationsInTab); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + browser.getActiveTab().getNavigationController().unregisterNavigationCallback( + navigationCallback); + }); + } + + /** + * Tests that a direct navigation to an external intent in browser startup in incognito mode + * with the embedder having set an ExternalIntentInIncognitoCallback instance causes that + * instance to be notified if the embedder specifies that intent launches in the background are + * allowed for this navigation, and that the intent is then launched if the embedder calls back + * that the user has consented. + */ + @Test + @SmallTest + @MinWebLayerVersion(93) + public void + testExternalIntentWithNoRedirectInBrowserStartupInIncognitoWithEmbedderPresentingWarningDialogLaunchedWhenBackgroundLaunchesAllowedAndUserConsents() + throws Throwable { + CallbackHelper onTabRemovedCallbackHelper = new CallbackHelper(); + TabListCallback tabListCallback = new TabListCallback() { + @Override + public void onTabRemoved(Tab tab) { + onTabRemovedCallbackHelper.notifyCalled(); + } + }; + + final IntentInterceptor intentInterceptor = new IntentInterceptor(); + final ExternalIntentInIncognitoCallbackTestImpl externalIntentCallback = + new ExternalIntentInIncognitoCallbackTestImpl(); + InstrumentationActivity.registerOnCreatedCallback( + new InstrumentationActivity.OnCreatedCallback() { + @Override + public void onCreated(Browser browser, InstrumentationActivity activity) { + Assert.assertEquals(true, browser.getProfile().isIncognito()); + activity.setIntentInterceptor(intentInterceptor); + browser.registerTabListCallback(tabListCallback); + + browser.getActiveTab().setExternalIntentInIncognitoCallback( + externalIntentCallback); + + NavigateParams.Builder navigateParamsBuilder = new NavigateParams.Builder(); + navigateParamsBuilder.allowIntentLaunchesInBackground(); + browser.getActiveTab().getNavigationController().navigate( + Uri.parse(INTENT_TO_SELF_URL), navigateParamsBuilder.build()); + } + }); + + Bundle extras = new Bundle(); + extras.putBoolean(InstrumentationActivity.EXTRA_IS_INCOGNITO, true); + mActivityTestRule.launchShell(extras); + + // The embedder should be invoked to present the warning dialog rather than WebLayer + // presenting the default warning dialog. + externalIntentCallback.waitForNotificationOnExternalIntentLaunch(); + onView(withText(android.R.id.button1)).check(doesNotExist()); + + // Have the embedder notify the implementation that the user has consented. + TestThreadUtils.runOnUiThreadBlocking(() -> { + externalIntentCallback.getOnUserDecisionCallback().onResult( + new Integer(ExternalIntentInIncognitoUserDecision.ALLOW)); + }); + + // The intent should be launched... + intentInterceptor.waitForIntent(); + Intent intent = intentInterceptor.mLastIntent; + Assert.assertNotNull(intent); + Assert.assertEquals(INTENT_TO_SELF_PACKAGE, intent.getPackage()); + Assert.assertEquals(INTENT_TO_SELF_ACTION, intent.getAction()); + Assert.assertEquals(INTENT_TO_SELF_DATA_STRING, intent.getDataString()); + + // ...the tab created for the initial navigation should be closed... + onTabRemovedCallbackHelper.waitForFirst(); + + // ...and there should now be no tabs in the browser. + Browser browser = mActivityTestRule.getActivity().getBrowser(); + int numTabs = + TestThreadUtils.runOnUiThreadBlocking(() -> { return browser.getTabs().size(); }); + Assert.assertEquals(0, numTabs); + + TestThreadUtils.runOnUiThreadBlocking( + () -> { browser.unregisterTabListCallback(tabListCallback); }); + } + + /** + * Tests that a direct navigation to an external intent in browser startup in incognito mode + * with the embedder having set an ExternalIntentInIncognitoCallback instance causes that + * instance to be notified if the embedder specifies that intent launches in the background are + * allowed for this navigation, and that the intent is then blocked if the embedder calls back + * that the user has not consented. + */ + @Test + @SmallTest + @MinWebLayerVersion(93) + public void + testExternalIntentWithNoRedirectInBrowserStartupInIncognitoWithEmbedderPresentingWarningDialogBlockedWhenBackgroundLaunchesAllowedAndUserForbids() + throws Throwable { + CallbackHelper onNavigationToIntentFailedCallbackHelper = new CallbackHelper(); + NavigationCallback navigationCallback = new NavigationCallback() { + @Override + public void onNavigationStarted(Navigation navigation) { + // There should be no additional navigations after the initial one. + Assert.assertEquals(INTENT_TO_DUMMY_ACTIVITY_FOR_SPECIAL_SCHEME_DATA_STRING, + navigation.getUri().toString()); + } + @Override + public void onNavigationFailed(Navigation navigation) { + if (navigation.getUri().toString().equals( + INTENT_TO_DUMMY_ACTIVITY_FOR_SPECIAL_SCHEME_DATA_STRING)) { + onNavigationToIntentFailedCallbackHelper.notifyCalled(); + } + } + }; + + final IntentInterceptor intentInterceptor = new IntentInterceptor(); + final ExternalIntentInIncognitoCallbackTestImpl externalIntentCallback = + new ExternalIntentInIncognitoCallbackTestImpl(); + InstrumentationActivity.registerOnCreatedCallback( + new InstrumentationActivity.OnCreatedCallback() { + @Override + public void onCreated(Browser browser, InstrumentationActivity activity) { + activity.setIntentInterceptor(intentInterceptor); + Assert.assertEquals(true, browser.getProfile().isIncognito()); + browser.getActiveTab().setExternalIntentInIncognitoCallback( + externalIntentCallback); + browser.getActiveTab().getNavigationController().registerNavigationCallback( + navigationCallback); + + NavigateParams.Builder navigateParamsBuilder = new NavigateParams.Builder(); + navigateParamsBuilder.allowIntentLaunchesInBackground(); + browser.getActiveTab().getNavigationController().navigate( + Uri.parse(INTENT_TO_DUMMY_ACTIVITY_FOR_SPECIAL_SCHEME_DATA_STRING), + navigateParamsBuilder.build()); + } + }); + + Bundle extras = new Bundle(); + extras.putBoolean(InstrumentationActivity.EXTRA_IS_INCOGNITO, true); + mActivityTestRule.launchShell(extras); + + // The embedder should be invoked to present the warning dialog rather than WebLayer + // presenting the default warning dialog. + externalIntentCallback.waitForNotificationOnExternalIntentLaunch(); + onView(withText(android.R.id.button1)).check(doesNotExist()); + + // Have the embedder notify the implementation that the user has forbidden the launch. + TestThreadUtils.runOnUiThreadBlocking(() -> { + externalIntentCallback.getOnUserDecisionCallback().onResult( + new Integer(ExternalIntentInIncognitoUserDecision.DENY)); + }); + + // The navigation should fail... + onNavigationToIntentFailedCallbackHelper.waitForFirst(); + + // ...the intent should not have been launched. + Assert.assertNull(intentInterceptor.mLastIntent); + + // As there was no fallback Url, there should be zero navigations in the tab. + Browser browser = mActivityTestRule.getActivity().getBrowser(); + int numNavigationsInTab = TestThreadUtils.runOnUiThreadBlocking(() -> { + return browser.getActiveTab().getNavigationController().getNavigationListSize(); + }); + Assert.assertEquals(0, numNavigationsInTab); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + browser.getActiveTab().getNavigationController().unregisterNavigationCallback( + navigationCallback); + }); + } + + /** + * Tests that a direct navigation to an external intent is launched due to the navigation type + * being set as from a link with a user gesture. + */ + @Test + @SmallTest + @DisableIf. + Build(sdk_is_less_than = Build.VERSION_CODES.N, message = "https://crbug.com/1176658") + public void testExternalIntentWithNoRedirectLaunched() throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + Tab tab = mActivityTestRule.getActivity().getTab(); + TestThreadUtils.runOnUiThreadBlocking( + () -> { tab.getNavigationController().navigate(Uri.parse(INTENT_TO_SELF_URL)); }); + + intentInterceptor.waitForIntent(); + + // The current URL should not have changed, and the intent should have been launched. + Assert.assertEquals(ABOUT_BLANK_URL, mActivityTestRule.getCurrentDisplayUrl()); + Intent intent = intentInterceptor.mLastIntent; + Assert.assertNotNull(intent); + Assert.assertEquals(INTENT_TO_SELF_PACKAGE, intent.getPackage()); + Assert.assertEquals(INTENT_TO_SELF_ACTION, intent.getAction()); + Assert.assertEquals(INTENT_TO_SELF_DATA_STRING, intent.getDataString()); + } + + /** + * Tests that a direct navigation to an external intent is blocked if the client calls + * Navigation#disableIntentProcessing() from the onNavigationStarted() callback. + */ + @Test + @SmallTest + @MinWebLayerVersion(97) + public void + testExternalIntentWithNoRedirectBlockedIfIntentProcessingDisabledOnNavigationStarted() + throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + CallbackHelper onNavigationToIntentFailedCallbackHelper = new CallbackHelper(); + NavigationCallback navigationCallback = new NavigationCallback() { + @Override + public void onNavigationStarted(Navigation navigation) { + if (navigation.getUri().toString().equals(INTENT_TO_SELF_URL)) { + navigation.disableIntentProcessing(); + } + } + @Override + public void onNavigationFailed(Navigation navigation) { + if (navigation.getUri().toString().equals(INTENT_TO_SELF_URL)) { + onNavigationToIntentFailedCallbackHelper.notifyCalled(); + } + } + }; + + Tab tab = mActivityTestRule.getActivity().getTab(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + tab.getNavigationController().registerNavigationCallback(navigationCallback); + tab.getNavigationController().navigate(Uri.parse(INTENT_TO_SELF_URL)); + }); + + // The navigation should fail... + onNavigationToIntentFailedCallbackHelper.waitForFirst(); + + // ...the intent should not have been launched. + Assert.assertNull(intentInterceptor.mLastIntent); + + // As there was no fallback Url, there should be only the initial navigation in the tab. + Browser browser = mActivityTestRule.getActivity().getBrowser(); + int numNavigationsInTab = TestThreadUtils.runOnUiThreadBlocking(() -> { + return browser.getActiveTab().getNavigationController().getNavigationListSize(); + }); + Assert.assertEquals(1, numNavigationsInTab); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + tab.getNavigationController().unregisterNavigationCallback(navigationCallback); + }); + } + + /** + * Tests that a navigation to an external intent after a server redirect is blocked if the + * client calls Navigation#disableIntentProcessing() from the onNavigationStarted() + * callback. + */ + @Test + @SmallTest + @MinWebLayerVersion(97) + public void + testExternalIntentAfterRedirectBlockedIfIntentProcessingDisabledOnNavigationStarted() + throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + CallbackHelper onNavigationToIntentFailedCallbackHelper = new CallbackHelper(); + NavigationCallback navigationCallback = new NavigationCallback() { + @Override + public void onNavigationStarted(Navigation navigation) { + if (navigation.getUri().toString().equals(mRedirectToIntentToSelfURL)) { + navigation.disableIntentProcessing(); + } + } + @Override + public void onNavigationFailed(Navigation navigation) { + if (navigation.getUri().toString().equals(INTENT_TO_SELF_URL)) { + onNavigationToIntentFailedCallbackHelper.notifyCalled(); + } + } + }; + + Tab tab = mActivityTestRule.getActivity().getTab(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + tab.getNavigationController().registerNavigationCallback(navigationCallback); + tab.getNavigationController().navigate(Uri.parse(mRedirectToIntentToSelfURL)); + }); + + // The navigation should fail... + onNavigationToIntentFailedCallbackHelper.waitForFirst(); + + // ...the intent should not have been launched. + Assert.assertNull(intentInterceptor.mLastIntent); + + // As there was no fallback Url, there should be only the initial navigation in the tab. + Browser browser = mActivityTestRule.getActivity().getBrowser(); + int numNavigationsInTab = TestThreadUtils.runOnUiThreadBlocking(() -> { + return browser.getActiveTab().getNavigationController().getNavigationListSize(); + }); + Assert.assertEquals(1, numNavigationsInTab); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + tab.getNavigationController().unregisterNavigationCallback(navigationCallback); + }); + } + + /** + * Tests that external intent-related navigation params are not set on browser navigations. + */ + @Test + @SmallTest + @MinWebLayerVersion(89) + public void testExternalIntentNavigationParamsNotSetOnBrowserNavigations() throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + + navigateAndCheckExternalIntentParams(mTestServerSiteUrl, EXPECT_NAVIGATION_COMPLETION, + DOESNT_RESULT_IN_EXTERNAL_INTENT, DOESNT_RESULT_IN_USER_DECIDING_EXTERNAL_INTENT); + + // Navigating to an unresolvable intent with a fallback URL should result in a followup + // browser navigation to the fallback URL. + navigateAndCheckExternalIntentParams(mNonResolvableIntentWithFallbackUrl, + mTestServerSiteUrl, EXPECT_NAVIGATION_COMPLETION, DOESNT_RESULT_IN_EXTERNAL_INTENT, + DOESNT_RESULT_IN_USER_DECIDING_EXTERNAL_INTENT); + } + + /** + * Tests that Navigation#wasIntentLaunched() is correctly set on embedder navigations that + * resolve to intents. + */ + @Test + @SmallTest + @MinWebLayerVersion(89) + public void testExternalIntentNavigationParamSetOnNavigationsToIntents() throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + navigateAndCheckExternalIntentParams(INTENT_TO_SELF_URL, EXPECT_NAVIGATION_FAILURE, + RESULTS_IN_EXTERNAL_INTENT, DOESNT_RESULT_IN_USER_DECIDING_EXTERNAL_INTENT); + navigateAndCheckExternalIntentParams(mIntentToSelfWithFallbackUrl, + EXPECT_NAVIGATION_FAILURE, RESULTS_IN_EXTERNAL_INTENT, + DOESNT_RESULT_IN_USER_DECIDING_EXTERNAL_INTENT); + navigateAndCheckExternalIntentParams(mRedirectToIntentToSelfURL, INTENT_TO_SELF_URL, + EXPECT_NAVIGATION_FAILURE, RESULTS_IN_EXTERNAL_INTENT, + DOESNT_RESULT_IN_USER_DECIDING_EXTERNAL_INTENT); + navigateAndCheckExternalIntentParams(mRedirectToCustomSchemeUrlWithDefaultExternalHandler, + CUSTOM_SCHEME_URL_WITH_DEFAULT_EXTERNAL_HANDLER, EXPECT_NAVIGATION_FAILURE, + RESULTS_IN_EXTERNAL_INTENT, DOESNT_RESULT_IN_USER_DECIDING_EXTERNAL_INTENT); + + // A navigation that results in an intent that cannot be launched should still fail, but + // should not have the wasIntentLaunched() parameter set. + navigateAndCheckExternalIntentParams(MALFORMED_INTENT_URL, EXPECT_NAVIGATION_FAILURE, + DOESNT_RESULT_IN_EXTERNAL_INTENT, DOESNT_RESULT_IN_USER_DECIDING_EXTERNAL_INTENT); + + // The presence of a fallback URL should not impact the state in the navigation failure + // callback for a navigation that results in an unresolvable intent. + navigateAndCheckExternalIntentParams(mNonResolvableIntentWithFallbackUrl, + EXPECT_NAVIGATION_FAILURE, DOESNT_RESULT_IN_EXTERNAL_INTENT, + DOESNT_RESULT_IN_USER_DECIDING_EXTERNAL_INTENT); + } + + /** + * Tests that Navigation#isUserDecidingIntentLaunch() is correctly set on embedder navigations + * that resolve to intents in incognito mode. + */ + @Test + @SmallTest + @MinWebLayerVersion(89) + public void testUserDecidingExternalIntentNavigationParamSetOnNavigationsToIntentsInIncognito() + throws Throwable { + Bundle extras = new Bundle(); + extras.putBoolean(InstrumentationActivity.EXTRA_IS_INCOGNITO, true); + mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL, extras); + + navigateAndCheckExternalIntentParams(INTENT_TO_SELF_URL, EXPECT_NAVIGATION_FAILURE, + DOESNT_RESULT_IN_EXTERNAL_INTENT, RESULTS_IN_USER_DECIDING_EXTERNAL_INTENT); + } + + /** + * Tests that Navigation#wasIntentLaunched() is correctly set on a navigation to an intent that + * is initiated via a link click. + */ + @Test + @SmallTest + @MinWebLayerVersion(89) + public void testExternalIntentNavigationParamSetOnIntentLaunchViaLinkClick() throws Throwable { + // Set up all the prerequisites. + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + Tab tab = mActivityTestRule.getActivity().getTab(); + + CallbackHelper navigationFailureCallbackHelper = new CallbackHelper(); + NavigationCallback navigationCallback = new NavigationCallback() { + @Override + public void onNavigationFailed(Navigation navigation) { + Assert.assertEquals(INTENT_TO_SELF_URL, navigation.getUri().toString()); + Assert.assertEquals(true, navigation.wasIntentLaunched()); + Assert.assertEquals(false, navigation.isUserDecidingIntentLaunch()); + + navigationFailureCallbackHelper.notifyCalled(); + } + }; + TestThreadUtils.runOnUiThreadBlocking(() -> { + tab.getNavigationController().registerNavigationCallback(navigationCallback); + }); + + // Navigate to a URL that has a link to an intent, click on the link, and verify via the + // callback that the navigation to the intent fails with the expected state set. + String url = mActivityTestRule.getTestDataURL(LINK_WITH_INTENT_TO_SELF_IN_SAME_TAB_FILE); + mActivityTestRule.navigateAndWait(url); + mActivityTestRule.executeScriptSync( + "document.onclick = function() {document.getElementById('link').click()}", + true /* useSeparateIsolate */); + EventUtils.simulateTouchCenterOfView( + mActivityTestRule.getActivity().getWindow().getDecorView()); + navigationFailureCallbackHelper.waitForFirst(); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + tab.getNavigationController().unregisterNavigationCallback(navigationCallback); + }); + } + + /** + * Tests that a navigation that redirects to an external intent results in the external intent + * being launched. + */ + @Test + @SmallTest + @DisableIf. + Build(sdk_is_less_than = Build.VERSION_CODES.N, message = "https://crbug.com/1176658") + public void testExternalIntentAfterRedirectLaunched() throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + Tab tab = mActivityTestRule.getActivity().getTab(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + tab.getNavigationController().navigate(Uri.parse(mRedirectToIntentToSelfURL)); + }); + + intentInterceptor.waitForIntent(); + + // The current URL should not have changed, and the intent should have been launched. + Assert.assertEquals(ABOUT_BLANK_URL, mActivityTestRule.getCurrentDisplayUrl()); + Intent intent = intentInterceptor.mLastIntent; + Assert.assertNotNull(intent); + Assert.assertEquals(INTENT_TO_SELF_PACKAGE, intent.getPackage()); + Assert.assertEquals(INTENT_TO_SELF_ACTION, intent.getAction()); + Assert.assertEquals(INTENT_TO_SELF_DATA_STRING, intent.getDataString()); + } + + /** + * Tests that a navigation that redirects to a URL with a special scheme that has a default + * external handler results in an external intent being launched. + */ + @Test + @SmallTest + public void testRedirectToCustomSchemeUrlWithDefaultExternalHandlerLaunchesIntent() + throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + Tab tab = mActivityTestRule.getActivity().getTab(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + tab.getNavigationController().navigate( + Uri.parse(mRedirectToCustomSchemeUrlWithDefaultExternalHandler)); + }); + + intentInterceptor.waitForIntent(); + + // The current URL should not have changed, and the intent should have been launched. + Assert.assertEquals(ABOUT_BLANK_URL, mActivityTestRule.getCurrentDisplayUrl()); + Intent intent = intentInterceptor.mLastIntent; + Assert.assertNotNull(intent); + + Assert.assertEquals( + INTENT_TO_DUMMY_ACTIVITY_FOR_SPECIAL_SCHEME_PACKAGE, intent.getPackage()); + Assert.assertEquals(INTENT_TO_DUMMY_ACTIVITY_FOR_SPECIAL_SCHEME_ACTION, intent.getAction()); + Assert.assertEquals( + INTENT_TO_DUMMY_ACTIVITY_FOR_SPECIAL_SCHEME_DATA_STRING, intent.getDataString()); + } + + /** + * Tests that clicking on a link that goes to an external intent in the same tab results in the + * external intent being launched. + */ + @Test + @SmallTest + @DisableIf. + Build(sdk_is_less_than = Build.VERSION_CODES.N, message = "https://crbug.com/1176658") + public void testExternalIntentInSameTabLaunchedOnLinkClick() throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + String url = mActivityTestRule.getTestDataURL(LINK_WITH_INTENT_TO_SELF_IN_SAME_TAB_FILE); + + mActivityTestRule.navigateAndWait(url); + + mActivityTestRule.executeScriptSync( + "document.onclick = function() {document.getElementById('link').click()}", + true /* useSeparateIsolate */); + EventUtils.simulateTouchCenterOfView( + mActivityTestRule.getActivity().getWindow().getDecorView()); + + intentInterceptor.waitForIntent(); + + // The current URL should not have changed, and the intent should have been launched. + Assert.assertEquals(url, mActivityTestRule.getCurrentDisplayUrl()); + Intent intent = intentInterceptor.mLastIntent; + Assert.assertNotNull(intent); + Assert.assertEquals(INTENT_TO_SELF_PACKAGE, intent.getPackage()); + Assert.assertEquals(INTENT_TO_SELF_ACTION, intent.getAction()); + Assert.assertEquals(INTENT_TO_SELF_DATA_STRING, intent.getDataString()); + } + + /** + * Tests that clicking on a link that goes to an external intent in a new tab results in + * a new tab being opened whose URL is that of the intent and the intent being launched, + * followed by the new tab being closed. + */ + @Test + @SmallTest + @DisableIf. + Build(sdk_is_less_than = Build.VERSION_CODES.N, message = "https://crbug.com/1176658") + public void testExternalIntentInNewTabLaunchedOnLinkClick() throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + String url = mActivityTestRule.getTestDataURL(LINK_WITH_INTENT_TO_SELF_IN_NEW_TAB_FILE); + + mActivityTestRule.navigateAndWait(url); + + // Set up listening for the tab addition and removal that we expect to happen. + CallbackHelper onTabAddedCallbackHelper = new CallbackHelper(); + CallbackHelper onTabRemovedCallbackHelper = new CallbackHelper(); + TabListCallback tabListCallback = new TabListCallback() { + @Override + public void onTabAdded(Tab tab) { + onTabAddedCallbackHelper.notifyCalled(); + } + + @Override + public void onTabRemoved(Tab tab) { + onTabRemovedCallbackHelper.notifyCalled(); + } + }; + Browser browser = mActivityTestRule.getActivity().getBrowser(); + TestThreadUtils.runOnUiThreadBlocking( + () -> { browser.registerTabListCallback(tabListCallback); }); + + // Grab the original tab before it changes. + Tab originalTab = mActivityTestRule.getActivity().getTab(); + + mActivityTestRule.executeScriptSync( + "document.onclick = function() {document.getElementById('link').click()}", + true /* useSeparateIsolate */); + EventUtils.simulateTouchCenterOfView( + mActivityTestRule.getActivity().getWindow().getDecorView()); + + // (1) A new tab should be created... + onTabAddedCallbackHelper.waitForFirst(); + + // (2) The intent should be launched in that tab... + intentInterceptor.waitForIntent(); + Intent intent = intentInterceptor.mLastIntent; + Assert.assertNotNull(intent); + Assert.assertEquals(INTENT_TO_SELF_PACKAGE, intent.getPackage()); + Assert.assertEquals(INTENT_TO_SELF_ACTION, intent.getAction()); + Assert.assertEquals(INTENT_TO_SELF_DATA_STRING, intent.getDataString()); + + // (3) And finally the new tab should be closed. + onTabRemovedCallbackHelper.waitForFirst(); + + // Now the original tab should be all that's left in the browser, with the display URL being + // the original URL. + int numTabs = + TestThreadUtils.runOnUiThreadBlocking(() -> { return browser.getTabs().size(); }); + Assert.assertEquals(1, numTabs); + Assert.assertEquals(mActivityTestRule.getActivity().getTab(), originalTab); + Assert.assertEquals(url, mActivityTestRule.getCurrentDisplayUrl()); + + TestThreadUtils.runOnUiThreadBlocking( + () -> { browser.unregisterTabListCallback(tabListCallback); }); + } + + /** + * Tests that a navigation that redirects to an external intent with a fallback URL results in + * the external intent being launched. + */ + @Test + @SmallTest + @DisableIf. + Build(sdk_is_less_than = Build.VERSION_CODES.N, message = "https://crbug.com/1176658") + public void testExternalIntentWithFallbackUrlAfterRedirectLaunched() throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + String url = mActivityTestRule.getTestServer().getURL( + "/server-redirect?" + mIntentToSelfWithFallbackUrl); + + Tab tab = mActivityTestRule.getActivity().getTab(); + TestThreadUtils.runOnUiThreadBlocking( + () -> { tab.getNavigationController().navigate(Uri.parse(url)); }); + + intentInterceptor.waitForIntent(); + + // The current URL should not have changed, and the intent should have been launched. + Assert.assertEquals(ABOUT_BLANK_URL, mActivityTestRule.getCurrentDisplayUrl()); + Intent intent = intentInterceptor.mLastIntent; + Assert.assertNotNull(intent); + Assert.assertEquals(INTENT_TO_SELF_PACKAGE, intent.getPackage()); + Assert.assertEquals(INTENT_TO_SELF_ACTION, intent.getAction()); + Assert.assertEquals(INTENT_TO_SELF_DATA_STRING, intent.getDataString()); + } + + /** + * Tests that a navigation that redirects to an external intent that can't be handled results in + * a failed navigation. + */ + @Test + @SmallTest + public void testNonHandledExternalIntentAfterRedirectBlocked() throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + String url = mActivityTestRule.getTestServer().getURL( + "/server-redirect?" + MALFORMED_INTENT_URL); + + Tab tab = mActivityTestRule.getActivity().getTab(); + + // Note that this navigation will not result in a paint. + NavigationWaiter waiter = new NavigationWaiter( + MALFORMED_INTENT_URL, tab, /*expectFailure=*/true, /*waitForPaint=*/false); + + TestThreadUtils.runOnUiThreadBlocking( + () -> { tab.getNavigationController().navigate(Uri.parse(url)); }); + waiter.waitForNavigation(); + + Assert.assertNull(intentInterceptor.mLastIntent); + + // The current URL should not have changed. + Assert.assertEquals(ABOUT_BLANK_URL, mActivityTestRule.getCurrentDisplayUrl()); + } + + /** + * Tests that a navigation that redirects to an external intent that can't be handled but has a + * fallback URL results in a navigation to the fallback URL. + */ + @Test + @SmallTest + public void testNonHandledExternalIntentWithFallbackUrlAfterRedirectGoesToFallbackUrl() + throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + String url = mActivityTestRule.getTestServer().getURL( + "/server-redirect?" + mNonResolvableIntentWithFallbackUrl); + + Tab tab = mActivityTestRule.getActivity().getTab(); + + NavigationWaiter waiter = new NavigationWaiter( + mTestServerSiteUrl, tab, /*expectFailure=*/false, /*waitForPaint=*/true); + + TestThreadUtils.runOnUiThreadBlocking( + () -> { tab.getNavigationController().navigate(Uri.parse(url)); }); + waiter.waitForNavigation(); + + Assert.assertNull(intentInterceptor.mLastIntent); + + // The current URL should now be the fallback URL. + Assert.assertEquals(mTestServerSiteUrl, mActivityTestRule.getCurrentDisplayUrl()); + } + + /** + * |url| is a URL that redirects to an unhandleable intent but has a fallback URL that redirects + * to a handleable intent. + * Tests that a navigation to |url| blocks the handleable intent by policy on chained redirects. + */ + @Test + @SmallTest + @DisabledTest(message = "crbug.com/1329813") + public void + testNonHandledExternalIntentWithFallbackUrlThatLaunchesIntentAfterRedirectBlocksFallbackIntent() + throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + String url = mActivityTestRule.getTestServer().getURL( + "/server-redirect?" + mNonResolvableIntentWithFallbackUrlThatLaunchesIntent); + + Tab tab = mActivityTestRule.getActivity().getTab(); + + TestThreadUtils.runOnUiThreadBlocking( + () -> { tab.getNavigationController().navigate(Uri.parse(url)); }); + + NavigationWaiter waiter = new NavigationWaiter( + INTENT_TO_SELF_URL, tab, /*expectFailure=*/true, /*waitForPaint=*/false); + waiter.waitForNavigation(); + + Assert.assertNull(intentInterceptor.mLastIntent); + + // The current URL should not have changed. + Assert.assertEquals(ABOUT_BLANK_URL, mActivityTestRule.getCurrentDisplayUrl()); + } + + /** + * Tests that going to a page that loads an intent that can be handled in onload() results in + * the external intent being launched due to the navigation being specified as being from a link + * with a user gesture (if the navigation were specified as being from user typing the intent + * would be blocked due to Chrome's policy on not launching intents from user-typed navigations + * without a redirect). Also verifies that WebLayer eliminates the navigation entry that + * launched the intent, so that the user is back on the original URL (i.e., the URL before that + * of the page that launched the intent in onload(). + */ + @Test + @SmallTest + @DisableIf. + Build(sdk_is_less_than = Build.VERSION_CODES.N, message = "https://crbug.com/1176658") + public void testExternalIntentViaOnLoadLaunched() throws Throwable { + String initialUrl = ABOUT_BLANK_URL; + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(initialUrl); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + String url = mActivityTestRule.getTestDataURL(PAGE_THAT_INTENTS_TO_CHROME_ON_LOAD_FILE); + + Tab tab = mActivityTestRule.getActivity().getTab(); + + mActivityTestRule.navigateAndWait(url); + + intentInterceptor.waitForIntent(); + + // The intent should have been launched, and the user should now be back on the original + // URL. + Assert.assertEquals(initialUrl, mActivityTestRule.getCurrentDisplayUrl()); + Intent intent = intentInterceptor.mLastIntent; + Assert.assertNotNull(intent); + Assert.assertEquals(INTENT_TO_SELF_PACKAGE, intent.getPackage()); + Assert.assertEquals(INTENT_TO_SELF_ACTION, intent.getAction()); + Assert.assertEquals(INTENT_TO_SELF_DATA_STRING, intent.getDataString()); + } + + /** + * Tests the following flow: + * - The user clicks on a link + * - This link goes to a page that loads a handleable intent in onload() + * This flow should result in (a) the external intent being launched rather than blocked, + * because the initial navigation to the page did not occur via user typing, and (b) WebLayer + * eliminating the navigation entry that launched the intent, so that the user is back on the + * original URL (i.e., the URL before they clicked the link). + */ + @Test + @SmallTest + @DisableIf. + Build(sdk_is_less_than = Build.VERSION_CODES.N, message = "https://crbug.com/1176658") + public void testUserClicksLinkToPageWithExternalIntentLaunchedViaOnLoad() throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + String url = + mActivityTestRule.getTestDataURL(LINK_TO_PAGE_THAT_INTENTS_TO_CHROME_ON_LOAD_FILE); + + mActivityTestRule.navigateAndWait(url); + + // Clicking on the link on this page should result in a navigation to the page that loads an + // intent in onLoad(), followed by a launching of that intent. + Tab tab = mActivityTestRule.getActivity().getTab(); + String finalUrl = + mActivityTestRule.getTestDataURL(PAGE_THAT_INTENTS_TO_CHROME_ON_LOAD_FILE); + NavigationWaiter waiter = + new NavigationWaiter(finalUrl, tab, /*expectFailure=*/false, /*waitForPaint=*/true); + + mActivityTestRule.executeScriptSync( + "document.onclick = function() {document.getElementById('link').click()}", + true /* useSeparateIsolate */); + EventUtils.simulateTouchCenterOfView( + mActivityTestRule.getActivity().getWindow().getDecorView()); + + waiter.waitForNavigation(); + + intentInterceptor.waitForIntent(); + + // The intent should have been launched, and the user should now be back on the original + // URL. + Assert.assertEquals(url, mActivityTestRule.getCurrentDisplayUrl()); + Intent intent = intentInterceptor.mLastIntent; + Assert.assertNotNull(intent); + Assert.assertEquals(INTENT_TO_SELF_PACKAGE, intent.getPackage()); + Assert.assertEquals(INTENT_TO_SELF_ACTION, intent.getAction()); + Assert.assertEquals(INTENT_TO_SELF_DATA_STRING, intent.getDataString()); + } + + /** + * Verifies that disableIntentProcessing() does in fact disable intent processing. + */ + @Test + @SmallTest + public void testDisableIntentProcessing() throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + String url = mActivityTestRule.getTestDataURL(PAGE_THAT_INTENTS_TO_CHROME_ON_LOAD_FILE); + + Tab tab = mActivityTestRule.getActivity().getTab(); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + NavigateParams.Builder navigateParamsBuilder = new NavigateParams.Builder(); + navigateParamsBuilder.disableIntentProcessing(); + tab.getNavigationController().navigate(Uri.parse(url), navigateParamsBuilder.build()); + }); + + NavigationWaiter waiter = new NavigationWaiter( + INTENT_TO_SELF_URL, tab, /*expectFailure=*/true, /*waitForPaint=*/false); + waiter.waitForNavigation(); + + Assert.assertNull(intentInterceptor.mLastIntent); + + // The current URL should not have changed. + Assert.assertEquals(url, mActivityTestRule.getCurrentDisplayUrl()); + } + + /** + * Verifies that for an intent with multiple matching apps that weblayer can handle, we avoid + * the disambiguation dialog and stay in weblayer. + */ + @Test + @SmallTest + public void testAvoidDisambiguationDialog() throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + + // The data URL isn't valid and will fail the navigation. + mActivityTestRule.navigateAndWaitForFailure(SPECIALIZED_DATA_URL); + + Assert.assertNull(intentInterceptor.mLastIntent); + Assert.assertEquals(SPECIALIZED_DATA_URL, mActivityTestRule.getCurrentDisplayUrl()); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FaviconFetcherTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FaviconFetcherTest.java new file mode 100644 index 0000000..8e2aa40 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FaviconFetcherTest.java
@@ -0,0 +1,66 @@ +// Copyright 2020 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.weblayer.test; + +import android.graphics.Bitmap; +import android.net.Uri; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.FaviconCallback; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** Tests for FaviconFetcher. */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class FaviconFetcherTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + + @Test + @SmallTest + public void testFaviconFetcher() throws Exception { + mActivity = mActivityTestRule.launchShellWithUrl("about:blank"); + + final CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.getTab().createFaviconFetcher(new FaviconCallback() { + @Override + public void onFaviconChanged(Bitmap bitmap) { + if (bitmap != null) { + Assert.assertTrue(bitmap.getWidth() > 0); + Assert.assertTrue(bitmap.getHeight() > 0); + callbackHelper.notifyCalled(); + } + } + }); + }); + String url = mActivityTestRule.getTestDataURL("simple_page_with_favicon.html"); + mActivityTestRule.navigateAndWait(url); + callbackHelper.waitForFirst(); + + // Verify the favicon can get obtained from the Profile. + final CallbackHelper downloadCallbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.getBrowser().getProfile().getCachedFaviconForPageUri( + Uri.parse(url), (Bitmap bitmap) -> { + Assert.assertTrue(bitmap != null); + Assert.assertTrue(bitmap.getWidth() > 0); + Assert.assertTrue(bitmap.getHeight() > 0); + downloadCallbackHelper.notifyCalled(); + }); + }); + downloadCallbackHelper.waitForFirst(); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FindInPageTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FindInPageTest.java new file mode 100644 index 0000000..817942c0 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FindInPageTest.java
@@ -0,0 +1,162 @@ +// Copyright 2020 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.weblayer.test; + +import android.net.Uri; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.DisabledTest; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.FindInPageCallback; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Tests the behavior of FindInPageController and FindInPageCallback. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class FindInPageTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + private CallbackImpl mCallback; + + private static class CallbackImpl extends FindInPageCallback { + public int mNumberOfMatches; + public int mActiveMatchIndex; + public BoundedCountDownLatch mResultCountDown; + public BoundedCountDownLatch mEndedCountDown; + + @Override + public void onFindResult(int numberOfMatches, int activeMatchIndex, boolean finalUpdate) { + mNumberOfMatches = numberOfMatches; + mActiveMatchIndex = activeMatchIndex; + if (finalUpdate) mResultCountDown.countDown(); + } + + @Override + public void onFindEnded() { + if (mEndedCountDown != null) mEndedCountDown.countDown(); + } + } + + private void searchFor(String text, boolean forward) { + mCallback.mResultCountDown = new BoundedCountDownLatch(1); + TestThreadUtils.runOnUiThreadBlocking( + () -> { mActivity.getTab().getFindInPageController().find(text, forward); }); + mCallback.mResultCountDown.timedAwait(); + } + + private void searchFor(String text) { + searchFor(text, true); + } + + private void verifyFindSessionInactive() { + Tab tab = mActivity.getTab(); + // This verification only works on the active tab; if inactive setFindInPageCallback always + // fails. + Assert.assertTrue(TestThreadUtils.runOnUiThreadBlockingNoException( + () -> { return tab == tab.getBrowser().getActiveTab(); })); + + Assert.assertTrue(TestThreadUtils.runOnUiThreadBlockingNoException(() -> { + // This call will return false if there's already a find session active. + boolean settingCallbackWorked = + tab.getFindInPageController().setFindInPageCallback(new CallbackImpl()); + // Remove the new callback. + tab.getFindInPageController().setFindInPageCallback(null); + return settingCallbackWorked; + })); + } + + public void setUp(String testPage) { + String url = mActivityTestRule.getTestDataURL(testPage); + mActivity = mActivityTestRule.launchShellWithUrl(url); + Assert.assertNotNull(mActivity); + mCallback = new CallbackImpl(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.getTab().getFindInPageController().setFindInPageCallback(mCallback); + }); + } + + @Test + @SmallTest + public void testBasic() { + setUp("shakespeare.html"); + + // Search. + searchFor("de"); + Assert.assertEquals(mCallback.mNumberOfMatches, 5); + Assert.assertEquals(mCallback.mActiveMatchIndex, 0); + + // Search again to activate the next match. + searchFor("de"); + Assert.assertEquals(mCallback.mNumberOfMatches, 5); + Assert.assertEquals(mCallback.mActiveMatchIndex, 1); + + // Search backwards to activate the previous match. + searchFor("de", false); + Assert.assertEquals(mCallback.mNumberOfMatches, 5); + Assert.assertEquals(mCallback.mActiveMatchIndex, 0); + + // Add a character to narrow the search. + searchFor("des"); + Assert.assertEquals(mCallback.mNumberOfMatches, 2); + Assert.assertEquals(mCallback.mActiveMatchIndex, 0); + searchFor("des"); + Assert.assertEquals(mCallback.mActiveMatchIndex, 1); + + // Simulate a backspace; the active match shouldn't change (although the number of results + // and therefore indexing does change). + searchFor("de"); + Assert.assertEquals(mCallback.mNumberOfMatches, 5); + Assert.assertEquals(mCallback.mActiveMatchIndex, 4); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.getTab().getFindInPageController().setFindInPageCallback(null); + }); + verifyFindSessionInactive(); + } + + @Test + @SmallTest + public void testHideOnNavigate() { + setUp("shakespeare.html"); + + mCallback.mEndedCountDown = new BoundedCountDownLatch(1); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.getTab().getNavigationController().navigate(Uri.parse("simple_page.html")); + }); + + mCallback.mEndedCountDown.timedAwait(); + verifyFindSessionInactive(); + } + + @Test + @SmallTest + @DisabledTest(message = "https://crbug.com/1248187") + public void testHideOnNewTab() { + setUp("new_browser.html"); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.getBrowser().getActiveTab().setNewTabCallback(new NewTabCallbackImpl()); + }); + + mCallback.mEndedCountDown = new BoundedCountDownLatch(1); + + // This touch creates a new tab. + EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView()); + + mCallback.mEndedCountDown.timedAwait(); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FullscreenCallbackPrivateTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FullscreenCallbackPrivateTest.java new file mode 100644 index 0000000..4e5f9cb --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FullscreenCallbackPrivateTest.java
@@ -0,0 +1,95 @@ +// 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. + +package org.chromium.weblayer.test; + +import android.os.RemoteException; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.FullscreenCallback; +import org.chromium.weblayer.TestWebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Fullscreen test assertions that can only be done using private API. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class FullscreenCallbackPrivateTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + private TestFullscreenCallback mDelegate; + + @Before + public void setUp() { + String url = mActivityTestRule.getTestDataURL("fullscreen.html"); + mActivity = mActivityTestRule.launchShellWithUrl(url); + Assert.assertNotNull(mActivity); + mDelegate = new TestFullscreenCallback(mActivityTestRule); + } + + private TestWebLayer getTestWebLayer() { + return TestWebLayer.getTestWebLayer( + mActivityTestRule.getActivity().getApplicationContext()); + } + + @Test + @SmallTest + public void testToastNotShownWhenFullscreenRequestIgnored() throws Throwable { + Assert.assertFalse(TestThreadUtils.runOnUiThreadBlocking( + () -> { return getTestWebLayer().didShowFullscreenToast(mActivity.getTab()); })); + + // Touch to enter fullscreen + EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView()); + mDelegate.waitForFullscreen(); + Assert.assertEquals(1, mDelegate.mEnterFullscreenCount); + + Assert.assertFalse(TestThreadUtils.runOnUiThreadBlocking( + () -> { return getTestWebLayer().didShowFullscreenToast(mActivity.getTab()); })); + } + + @Test + @SmallTest + @DisabledTest(message = "https://crbug.com/1293419") + public void testToastShown() throws Throwable { + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.getTab().setFullscreenCallback(new FullscreenCallback() { + @Override + public void onEnterFullscreen(Runnable exitFullscreenRunner) { + mActivity.getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); + } + + @Override + public void onExitFullscreen() {} + }); + }); + + // Touch to enter fullscreen + EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView()); + CriteriaHelper.pollUiThread(() -> { + try { + Criteria.checkThat(getTestWebLayer().didShowFullscreenToast(mActivity.getTab()), + Matchers.is(true)); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + }); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FullscreenCallbackTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FullscreenCallbackTest.java new file mode 100644 index 0000000..4179eae7 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FullscreenCallbackTest.java
@@ -0,0 +1,145 @@ +// 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. + +package org.chromium.weblayer.test; + +import android.os.Build; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.DisableIf; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.Browser; +import org.chromium.weblayer.BrowserControlsOffsetCallback; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Tests that FullscreenCallback methods are invoked as expected. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class FullscreenCallbackTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + private TestFullscreenCallback mDelegate; + + // Launch WL and triggers html fullscreen. + private void enterFullscreen() { + String url = mActivityTestRule.getTestDataURL("fullscreen.html"); + mActivity = mActivityTestRule.launchShellWithUrl(url); + Assert.assertNotNull(mActivity); + mDelegate = new TestFullscreenCallback(mActivityTestRule); + + // First touch enters fullscreen. + EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView()); + mDelegate.waitForFullscreen(); + Assert.assertEquals(1, mDelegate.mEnterFullscreenCount); + } + + @Test + @SmallTest + public void testFullscreen() { + enterFullscreen(); + // Second touch exits. + EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView()); + mDelegate.waitForExitFullscreen(); + Assert.assertEquals(1, mDelegate.mExitFullscreenCount); + } + + @Test + @SmallTest + public void testExitFullscreenWhenDelegateCleared() { + enterFullscreen(); + // Clearing the FullscreenCallback should exit fullscreen. + TestThreadUtils.runOnUiThreadBlocking( + () -> { mActivity.getTab().setFullscreenCallback(null); }); + mDelegate.waitForExitFullscreen(); + Assert.assertEquals(1, mDelegate.mExitFullscreenCount); + } + + @Test + @SmallTest + public void testExitFullscreenUsingRunnable() { + enterFullscreen(); + // Running the runnable supplied to the delegate should exit fullscreen. + TestThreadUtils.runOnUiThreadBlocking(mDelegate.mExitFullscreenRunnable); + mDelegate.waitForExitFullscreen(); + Assert.assertEquals(1, mDelegate.mExitFullscreenCount); + } + + @Test + @SmallTest + public void testExitFullscreenWhenTabDestroyed() { + enterFullscreen(); + // Destroying the tab should exit fullscreen. + TestThreadUtils.runOnUiThreadBlocking( + () -> { mActivity.getTab().getBrowser().destroyTab(mActivity.getTab()); }); + mDelegate.waitForExitFullscreen(); + Assert.assertEquals(1, mDelegate.mExitFullscreenCount); + } + + /** + * Verifies there are no crashes when destroying the fragment in fullscreen. + */ + @Test + @SmallTest + public void testDestroyFragmentWhileFullscreen() { + enterFullscreen(); + TestThreadUtils.runOnUiThreadBlocking(() -> { mActivity.destroyFragment(); }); + } + + // Waits for the top offset to go to -height. This means the view is completely hidden. + private final class BrowserControlsOffsetCallbackImpl extends BrowserControlsOffsetCallback { + private final CallbackHelper mCallbackHelper; + BrowserControlsOffsetCallbackImpl(CallbackHelper callbackHelper) { + mCallbackHelper = callbackHelper; + } + @Override + public void onTopViewOffsetChanged(int offset) { + int height = mActivity.getTopContentsContainer().getHeight(); + if (height != 0 && offset == -height) { + mCallbackHelper.notifyCalled(); + } + } + } + + @Test + @SmallTest + @MinWebLayerVersion(88) + @DisableIf. + Build(sdk_is_less_than = Build.VERSION_CODES.M, message = "https://crbug.com/1159781") + public void testTopViewRemainsHiddenOnFullscreenRotation() throws Exception { + String url = mActivityTestRule.getTestDataURL("rotation2.html"); + mActivity = mActivityTestRule.launchShellWithUrl(url); + // Ensure the fragment is not recreated as otherwise things bounce around more. + mActivityTestRule.setRetainInstance(true); + Assert.assertNotNull(mActivity); + mDelegate = new TestFullscreenCallback(mActivityTestRule); + CallbackHelper callbackHelper = new CallbackHelper(); + // The offsets may move around during rotation. Wait for reattachment before installing + // the BrowserControlsOffsetCallback. + InstrumentationActivity.registerOnCreatedCallback( + new InstrumentationActivity.OnCreatedCallback() { + @Override + public void onCreated(Browser browser, InstrumentationActivity activity) { + browser.registerBrowserControlsOffsetCallback( + new BrowserControlsOffsetCallbackImpl(callbackHelper)); + } + }); + EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView()); + mDelegate.waitForFullscreen(); + Assert.assertEquals(1, mDelegate.mEnterFullscreenCount); + + // Rotation should trigger the view being totally hidden. + callbackHelper.waitForFirst(); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FullscreenSizeTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FullscreenSizeTest.java new file mode 100644 index 0000000..7ca2e88 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FullscreenSizeTest.java
@@ -0,0 +1,120 @@ +// Copyright 2020 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.weblayer.test; + +import android.app.Activity; +import android.content.res.Configuration; +import android.view.inputmethod.InputMethodManager; + +import androidx.test.filters.SmallTest; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Tests that the Web Contents is sized appropriately when in fullscreen and the onscreen keyboard + * is shown. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class FullscreenSizeTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + + @Test + @SmallTest + @DisabledTest(message = "https://crbug.com/1239002") + public void testOsk() { + // For this test to function, it *cannot* use {@link TestFullscreenCallback}, as that + // overrides the fullscreen handling in {@link InstrumentationActivity}. + String url = mActivityTestRule.getTestDataURL("fullscreen_with_input.html"); + mActivity = mActivityTestRule.launchShellWithUrl(url); + Assert.assertNotNull(mActivity); + + int initialHeight = getVisiblePageHeight(); + + // First, try without fullscreen. Android should automatically resize the activity window. + // First touch focuses input and brings up OSK (if it's enabled). + EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView()); + try { + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(getVisiblePageHeight(), Matchers.lessThan(initialHeight)); + }); + } catch (AssertionError e) { + // No soft keyboard found. This is possible when a hardware keyboard is attached. + // Abort test. + Assert.assertNotEquals(mActivity.getResources().getConfiguration().keyboard, + Configuration.KEYBOARD_NOKEYS); + Assert.assertNotEquals(mActivity.getResources().getConfiguration().keyboard, + Configuration.KEYBOARD_UNDEFINED); + return; + } + + // Reset the OSK state. + int withKeyboardHeight = getVisiblePageHeight(); + dismissOsk(); + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(getVisiblePageHeight(), Matchers.greaterThan(withKeyboardHeight)); + }); + + // Now, try with fullscreen. + // Second touch enters fullscreen. + EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView()); + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat( + mActivityTestRule.executeScriptAndExtractBoolean("document.webkitIsFullScreen"), + Matchers.is(true)); + }); + + int fsWidth = getVisiblePageWidth(); + int fsHeight = getVisiblePageHeight(); + + // Third touch focuses the input box, bringing up the on-screen keyboard. + EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView()); + CriteriaHelper.pollInstrumentationThread( + () -> { Criteria.checkThat(getVisiblePageHeight(), Matchers.lessThan(fsHeight)); }); + + int fsWithOskWidth = getVisiblePageWidth(); + int fsWithOskHeight = getVisiblePageHeight(); + + Assert.assertEquals(fsWithOskWidth, fsWidth); + Assert.assertThat(fsWithOskHeight, Matchers.lessThan(fsHeight)); + + dismissOsk(); + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(getVisiblePageHeight(), Matchers.greaterThan(fsWithOskHeight)); + }); + + Assert.assertEquals(getVisiblePageWidth(), fsWidth); + Assert.assertEquals(getVisiblePageHeight(), fsHeight); + } + + private int getVisiblePageWidth() { + return mActivityTestRule.executeScriptAndExtractInt("window.innerWidth"); + } + + private int getVisiblePageHeight() { + return mActivityTestRule.executeScriptAndExtractInt("window.innerHeight"); + } + + private void dismissOsk() { + TestThreadUtils.runOnUiThreadBlocking(() -> { + InputMethodManager inputManager = + (InputMethodManager) mActivity.getSystemService(Activity.INPUT_METHOD_SERVICE); + inputManager.hideSoftInputFromWindow(mActivity.getCurrentFocus().getWindowToken(), 0); + }); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/GeolocationTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/GeolocationTest.java new file mode 100644 index 0000000..11b0938 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/GeolocationTest.java
@@ -0,0 +1,282 @@ +// Copyright 2020 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.weblayer.test; + +import android.Manifest; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.RemoteException; + +import androidx.annotation.RequiresApi; +import androidx.core.app.ActivityCompat; +import androidx.test.filters.MediumTest; + +import org.hamcrest.Matchers; +import org.json.JSONException; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.ContextUtils; +import org.chromium.base.test.util.ApplicationContextWrapper; +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.CriteriaNotSatisfiedException; +import org.chromium.base.test.util.MinAndroidSdkLevel; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.net.test.util.TestWebServer; +import org.chromium.weblayer.Browser; +import org.chromium.weblayer.TestWebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Tests that Geolocation Web API works as expected. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public final class GeolocationTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + private TestWebLayer mTestWebLayer; + private TestWebServer mTestServer; + private int mLocationPermission = PackageManager.PERMISSION_GRANTED; + + private static final String RAW_JAVASCRIPT = "var positionCount = 0;" + + "var errorCount = 0;" + + "function gotPos(position) {" + + " positionCount++;" + + "}" + + "function errorCallback(error){" + + " errorCount++;" + + "}" + + "function initiate_getCurrentPosition() {" + + " navigator.geolocation.getCurrentPosition(" + + " gotPos, errorCallback, {});" + + "}" + + "function initiate_watchPosition() {" + + " navigator.geolocation.watchPosition(" + + " gotPos, errorCallback, {});" + + "}"; + + @RequiresApi(Build.VERSION_CODES.M) + private class PermissionCompatDelegate implements ActivityCompat.PermissionCompatDelegate { + private CallbackHelper mCallbackHelper = new CallbackHelper(); + + @Override + public boolean requestPermissions( + Activity activity, String[] permissions, int requestCode) { + mCallbackHelper.notifyCalled(); + return false; + } + + @Override + public boolean onActivityResult( + Activity activity, int requestCode, int resultCode, Intent data) { + return false; + } + + public void waitForPermissionsRequest() throws Exception { + mCallbackHelper.waitForFirst(); + } + } + + @Before + public void setUp() throws Throwable { + Context appContext = new ApplicationContextWrapper(ContextUtils.getApplicationContext()) { + @Override + public int checkPermission(String permission, int pid, int uid) { + if (permission.equals(Manifest.permission.ACCESS_FINE_LOCATION) + || permission.equals(Manifest.permission.ACCESS_COARSE_LOCATION)) { + return mLocationPermission; + } + return super.checkPermission(permission, pid, uid); + } + }; + ContextUtils.initApplicationContextForTests(appContext); + + Bundle extras = new Bundle(); + // We need to override the context with which to create WebLayer. + extras.putBoolean(InstrumentationActivity.EXTRA_CREATE_WEBLAYER, false); + mActivity = mActivityTestRule.launchShell(extras); + Assert.assertNotNull(mActivity); + TestThreadUtils.runOnUiThreadBlocking(() -> mActivity.loadWebLayerSync(appContext)); + mActivityTestRule.navigateAndWait("about:blank"); + + mTestWebLayer = TestWebLayer.getTestWebLayer(appContext); + mTestWebLayer.setSystemLocationSettingEnabled(true); + mTestWebLayer.setMockLocationProvider(true /* enable */); + + mTestServer = TestWebServer.start(); + + mActivityTestRule.navigateAndWait(mActivityTestRule.getTestDataURL("geolocation.html")); + ensureGeolocationIsRunning(false); + } + + @After + public void tearDown() throws Throwable { + mTestWebLayer.setMockLocationProvider(false /* enable */); + ensureGeolocationIsRunning(false); + } + + /** + * Test for navigator.getCurrentPosition. + */ + @Test + @MediumTest + public void testGeolocation_getPosition() throws Throwable { + mActivityTestRule.executeScriptSync("initiate_getCurrentPosition();", false); + waitForDialog(); + mTestWebLayer.clickPermissionDialogButton(true); + waitForCountEqual("positionCount", 1); + mActivityTestRule.executeScriptSync("initiate_getCurrentPosition();", false); + waitForCountEqual("positionCount", 2); + Assert.assertEquals(0, getCountFromJS("errorCount")); + } + + /** + * Test for navigator.watchPosition, should receive more than one position. + */ + @Test + @MediumTest + public void testGeolocation_watchPosition() throws Throwable { + mActivityTestRule.executeScriptSync("initiate_watchPosition();", false); + waitForDialog(); + mTestWebLayer.clickPermissionDialogButton(true); + waitForCountGreaterThan("positionCount", 1); + ensureGeolocationIsRunning(true); + Assert.assertEquals(0, getCountFromJS("errorCount")); + } + + /** + * Test that destroying a tab stops geolocation provider. + */ + @Test + @MediumTest + public void testGeolocation_destroyTabStopsGeolocation() throws Throwable { + mActivityTestRule.executeScriptSync("initiate_watchPosition();", false); + waitForDialog(); + mTestWebLayer.clickPermissionDialogButton(true); + ensureGeolocationIsRunning(true); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Browser browser = mActivity.getBrowser(); + browser.destroyTab(mActivity.getBrowser().getActiveTab()); + Assert.assertEquals(0, browser.getTabs().size()); + }); + ensureGeolocationIsRunning(false); + } + + /** + * Test geolocation denied on insecure origins (e.g. javascript). + */ + @Test + @MediumTest + public void testGeolocation_denyOnInsecureOrigins() throws Throwable { + mActivityTestRule.navigateAndWait("about:blank"); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.getTab().getNavigationController().navigate( + Uri.parse("javascript:" + RAW_JAVASCRIPT + "initiate_getCurrentPosition();")); + }); + waitForCountEqual("errorCount", 1); + Assert.assertEquals(0, getCountFromJS("positionCount")); + } + + @Test + @MediumTest + public void testGeolocation_denyFromPrompt() throws Throwable { + mActivityTestRule.executeScriptSync("initiate_watchPosition();", false); + waitForDialog(); + mTestWebLayer.clickPermissionDialogButton(false); + waitForCountEqual("errorCount", 1); + Assert.assertEquals(0, getCountFromJS("positionCount")); + } + + @Test + @MediumTest + @MinAndroidSdkLevel(Build.VERSION_CODES.M) + public void testRequestSystemPermission() throws Throwable { + mActivityTestRule.executeScriptSync("initiate_watchPosition();", false); + waitForDialog(); + mTestWebLayer.clickPermissionDialogButton(true); + + // Reload and deny the system permission, so it is prompted on the next call to geolocation. + mActivityTestRule.navigateAndWait(mActivityTestRule.getTestDataURL("geolocation.html")); + + PermissionCompatDelegate delegate = new PermissionCompatDelegate(); + ActivityCompat.setPermissionCompatDelegate(delegate); + mLocationPermission = PackageManager.PERMISSION_DENIED; + mActivityTestRule.executeScriptSync("initiate_watchPosition();", false); + + delegate.waitForPermissionsRequest(); + } + + // helper methods + + private void waitForCountEqual(String variableName, int count) { + CriteriaHelper.pollInstrumentationThread( + () -> Criteria.checkThat(getCountFromJS(variableName), Matchers.is(count))); + } + + private void waitForDialog() throws Exception { + // Make sure the current permission state is "prompt" before waiting for the dialog. + mActivityTestRule.executeScriptSync("var queryResult;" + + "navigator.permissions.query({name: 'geolocation'}).then(" + + "function(result) { queryResult = result.state; })", + false); + CriteriaHelper.pollInstrumentationThread(() -> { + try { + String result = + mActivityTestRule.executeScriptSync("queryResult || ''", false) + .getString(InstrumentationActivityTestRule.SCRIPT_RESULT_KEY); + Criteria.checkThat(result, Matchers.not("")); + } catch (JSONException ex) { + throw new CriteriaNotSatisfiedException(ex); + } + }); + Assert.assertEquals("prompt", + mActivityTestRule.executeScriptSync("queryResult", false) + .getString(InstrumentationActivityTestRule.SCRIPT_RESULT_KEY)); + CriteriaHelper.pollInstrumentationThread( + () -> { return mTestWebLayer.isPermissionDialogShown(); }); + } + + private void waitForCountGreaterThan(String variableName, int count) { + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(getCountFromJS(variableName), Matchers.greaterThan(count)); + }); + } + + private void ensureGeolocationIsRunning(boolean running) { + CriteriaHelper.pollInstrumentationThread(() -> { + try { + Criteria.checkThat( + mTestWebLayer.isMockLocationProviderRunning(), Matchers.is(running)); + } catch (RemoteException ex) { + throw new CriteriaNotSatisfiedException(ex); + } + }); + } + + private int getCountFromJS(String variableName) { + int result = -1; + try { + result = mActivityTestRule.executeScriptSync(variableName, false) + .getInt(InstrumentationActivityTestRule.SCRIPT_RESULT_KEY); + } catch (Exception e) { + Assert.fail("Unable to get " + variableName); + } + return result; + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/GoogleAccountAccessTokenFetcherTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/GoogleAccountAccessTokenFetcherTest.java new file mode 100644 index 0000000..1142c348 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/GoogleAccountAccessTokenFetcherTest.java
@@ -0,0 +1,257 @@ +// 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. + +package org.chromium.weblayer.test; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.Callback; +import org.chromium.weblayer.GoogleAccountAccessTokenFetcher; +import org.chromium.weblayer.Profile; +import org.chromium.weblayer.TestWebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +/** + * Tests to ensure that access token fetching in WebLayer works as expected. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class GoogleAccountAccessTokenFetcherTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + + @Before + public void setUp() throws Exception { + mActivity = mActivityTestRule.launchShellWithUrl("about:blank"); + } + + @Test + @SmallTest + // Ensures the viability of setting the fetcher to null. + public void testSetFetcherToNull() throws Exception { + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivityTestRule.getActivity() + .getBrowser() + .getProfile() + .setGoogleAccountAccessTokenFetcher(null); + }); + } + + @Test + @SmallTest + // Tests that fetching access tokens from within WebLayer returns the value from the embedder. + public void testFetchAccessToken() throws Exception { + Profile profile = TestThreadUtils.runOnUiThreadBlocking( + () -> { return mActivityTestRule.getActivity().getBrowser().getProfile(); }); + + final Set<String> expectedScopes = new HashSet<String>(Arrays.asList("scope1", "scope2")); + final String expectedToken = "accessToken1"; + + GoogleAccountAccessTokenFetcherEmbedderImpl fetcherImpl = + new GoogleAccountAccessTokenFetcherEmbedderImpl(); + TestThreadUtils.runOnUiThreadBlocking( + () -> { profile.setGoogleAccountAccessTokenFetcher(fetcherImpl); }); + + final CallbackHelper callbackHelper = new CallbackHelper(); + final Callback<String> onTokenFetchedCallback = new Callback<String>() { + @Override + public void onResult(String value) { + Assert.assertEquals(expectedToken, value); + callbackHelper.notifyCalled(); + } + }; + + TestWebLayer testWebLayer = TestWebLayer.getTestWebLayer(mActivity.getApplicationContext()); + testWebLayer.fetchAccessToken(profile, expectedScopes, onTokenFetchedCallback); + Assert.assertEquals(1, fetcherImpl.getNumOutstandingRequests()); + Assert.assertEquals(expectedScopes, fetcherImpl.getMostRecentRequestScopes()); + + fetcherImpl.respondWithTokenForRequest(fetcherImpl.getMostRecentRequestId(), expectedToken); + callbackHelper.waitForFirst(); + } + + @Test + @SmallTest + // Tests handling of multiple ongoing requests. + public void testMultipleAccessTokenRequests() throws Exception { + Profile profile = TestThreadUtils.runOnUiThreadBlocking( + () -> { return mActivityTestRule.getActivity().getBrowser().getProfile(); }); + + GoogleAccountAccessTokenFetcherEmbedderImpl fetcherImpl = + new GoogleAccountAccessTokenFetcherEmbedderImpl(); + TestThreadUtils.runOnUiThreadBlocking( + () -> { profile.setGoogleAccountAccessTokenFetcher(fetcherImpl); }); + + final String expectedToken1 = "accessToken1"; + final String expectedToken2 = "accessToken2"; + final String expectedToken3 = "accessToken3"; + + final Set<String> expectedScopes1 = new HashSet<String>(Arrays.asList("scope1", "scope2")); + final Set<String> expectedScopes2 = new HashSet<String>(Arrays.asList("scope2", "scope3")); + final Set<String> expectedScopes3 = new HashSet<String>(Arrays.asList("scope4", "scope5")); + + final String[] accessTokens = {"", "", ""}; + + final CallbackHelper callbackHelper1 = new CallbackHelper(); + final Callback<String> onTokenFetchedCallback1 = new Callback<String>() { + @Override + public void onResult(String value) { + accessTokens[0] = value; + callbackHelper1.notifyCalled(); + } + }; + + final CallbackHelper callbackHelper2 = new CallbackHelper(); + final Callback<String> onTokenFetchedCallback2 = new Callback<String>() { + @Override + public void onResult(String value) { + accessTokens[1] = value; + callbackHelper2.notifyCalled(); + } + }; + + final CallbackHelper callbackHelper3 = new CallbackHelper(); + final Callback<String> onTokenFetchedCallback3 = new Callback<String>() { + @Override + public void onResult(String value) { + accessTokens[2] = value; + callbackHelper3.notifyCalled(); + } + }; + + TestWebLayer testWebLayer = TestWebLayer.getTestWebLayer(mActivity.getApplicationContext()); + + // Make the first two access token requests. + testWebLayer.fetchAccessToken(profile, expectedScopes1, onTokenFetchedCallback1); + Assert.assertEquals(1, fetcherImpl.getNumOutstandingRequests()); + Assert.assertEquals(expectedScopes1, fetcherImpl.getMostRecentRequestScopes()); + int requestId1 = fetcherImpl.getMostRecentRequestId(); + + testWebLayer.fetchAccessToken(profile, expectedScopes2, onTokenFetchedCallback2); + Assert.assertEquals(2, fetcherImpl.getNumOutstandingRequests()); + Assert.assertEquals(expectedScopes2, fetcherImpl.getMostRecentRequestScopes()); + int requestId2 = fetcherImpl.getMostRecentRequestId(); + + // Resolve the second request. + fetcherImpl.respondWithTokenForRequest(requestId2, expectedToken2); + callbackHelper2.waitForFirst(); + Assert.assertEquals("", accessTokens[0]); + Assert.assertEquals(expectedToken2, accessTokens[1]); + Assert.assertEquals("", accessTokens[2]); + + // Make the third request. + testWebLayer.fetchAccessToken(profile, expectedScopes3, onTokenFetchedCallback3); + Assert.assertEquals(2, fetcherImpl.getNumOutstandingRequests()); + Assert.assertEquals(expectedScopes3, fetcherImpl.getMostRecentRequestScopes()); + int requestId3 = fetcherImpl.getMostRecentRequestId(); + + // Resolve the first request. + fetcherImpl.respondWithTokenForRequest(requestId1, expectedToken1); + callbackHelper1.waitForFirst(); + Assert.assertEquals(expectedToken1, accessTokens[0]); + Assert.assertEquals(expectedToken2, accessTokens[1]); + Assert.assertEquals("", accessTokens[2]); + + // Resolve the third request. + fetcherImpl.respondWithTokenForRequest(requestId3, expectedToken3); + callbackHelper2.waitForFirst(); + Assert.assertEquals(expectedToken1, accessTokens[0]); + Assert.assertEquals(expectedToken2, accessTokens[1]); + Assert.assertEquals(expectedToken3, accessTokens[2]); + } + + @Test + @SmallTest + // Tests that WebLayer forwards invalid access token notifications to the embedder. + public void testOnAccessTokenIdentifiedAsInvalid() throws Exception { + Profile profile = TestThreadUtils.runOnUiThreadBlocking( + () -> { return mActivityTestRule.getActivity().getBrowser().getProfile(); }); + + final Set<String> scopesForInvalidToken = + new HashSet<String>(Arrays.asList("scope1", "scope2")); + final String invalidToken = "accessToken1"; + + GoogleAccountAccessTokenFetcherEmbedderImpl fetcherImpl = + new GoogleAccountAccessTokenFetcherEmbedderImpl(); + TestThreadUtils.runOnUiThreadBlocking( + () -> { profile.setGoogleAccountAccessTokenFetcher(fetcherImpl); }); + + Assert.assertNull(fetcherImpl.getScopesForMostRecentInvalidToken()); + Assert.assertNull(fetcherImpl.getMostRecentInvalidToken()); + + TestWebLayer testWebLayer = TestWebLayer.getTestWebLayer(mActivity.getApplicationContext()); + testWebLayer.fireOnAccessTokenIdentifiedAsInvalid( + profile, scopesForInvalidToken, invalidToken); + + Assert.assertEquals( + scopesForInvalidToken, fetcherImpl.getScopesForMostRecentInvalidToken()); + Assert.assertEquals(invalidToken, fetcherImpl.getMostRecentInvalidToken()); + } + + private class GoogleAccountAccessTokenFetcherEmbedderImpl + extends GoogleAccountAccessTokenFetcher { + private HashMap<Integer, Callback<String>> mOutstandingRequests = + new HashMap<Integer, Callback<String>>(); + private int mMostRecentRequestId; + private Set<String> mMostRecentRequestScopes; + private Set<String> mScopesForMostRecentInvalidToken; + private String mMostRecentInvalidToken; + + @Override + public void fetchAccessToken(Set<String> scopes, Callback<String> onTokenFetched) { + mMostRecentRequestScopes = scopes; + mMostRecentRequestId++; + mOutstandingRequests.put(mMostRecentRequestId, onTokenFetched); + } + + @Override + public void onAccessTokenIdentifiedAsInvalid(Set<String> scopes, String token) { + mScopesForMostRecentInvalidToken = scopes; + mMostRecentInvalidToken = token; + } + + int getMostRecentRequestId() { + return mMostRecentRequestId; + } + + Set<String> getMostRecentRequestScopes() { + return mMostRecentRequestScopes; + } + + int getNumOutstandingRequests() { + return mOutstandingRequests.size(); + } + + Set<String> getScopesForMostRecentInvalidToken() { + return mScopesForMostRecentInvalidToken; + } + + String getMostRecentInvalidToken() { + return mMostRecentInvalidToken; + } + + void respondWithTokenForRequest(int requestId, String token) { + Callback<String> callback = mOutstandingRequests.get(requestId); + assert callback != null; + mOutstandingRequests.remove(requestId); + + callback.onResult(token); + } + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/GoogleAccountsTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/GoogleAccountsTest.java new file mode 100644 index 0000000..94b0691 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/GoogleAccountsTest.java
@@ -0,0 +1,108 @@ +// Copyright 2020 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.weblayer.test; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.CommandLine; +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.GoogleAccountServiceType; +import org.chromium.weblayer.GoogleAccountsCallback; +import org.chromium.weblayer.GoogleAccountsParams; +import org.chromium.weblayer.shell.InstrumentationActivity; +; + +/** + * Tests for the Google accounts API. + */ +@CommandLineFlags.Add({"ignore-certificate-errors"}) +@RunWith(WebLayerJUnit4ClassRunner.class) +public class GoogleAccountsTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + + @Before + public void setUp() throws Exception { + mActivityTestRule.getTestServerRule().setServerUsesHttps(true); + + // We need to add this to the command line directly instead of using @CommandLineFlags.Add + // since it uses the test server URL which is not available for the annotatoin. + CommandLine.getInstance().appendSwitchWithValue( + "gaia-url", mActivityTestRule.getTestServer().getURL("/")); + mActivityTestRule.writeCommandLineFile(); + mActivity = mActivityTestRule.launchShellWithUrl("about:blank"); + } + + @Test + @SmallTest + public void testBasic() throws Exception { + GoogleAccountsCallbackImpl callback = new GoogleAccountsCallbackImpl(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.getBrowser().getActiveTab().setGoogleAccountsCallback(callback); + }); + + mActivityTestRule.navigateAndWait(mActivityTestRule.getTestDataURL("google_accounts.html")); + GoogleAccountsParams params = callback.waitForGoogleAccounts(); + Assert.assertEquals(params.serviceType, GoogleAccountServiceType.ADD_SESSION); + Assert.assertEquals(params.email, "foo@bar.com"); + Assert.assertEquals(params.continueUri.toString(), "https://blah.com"); + Assert.assertTrue(params.isSameTab); + } + + @Test + @SmallTest + public void testRequestHeader() throws Exception { + GoogleAccountsCallbackImpl callback = new GoogleAccountsCallbackImpl(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.getBrowser().getActiveTab().setGoogleAccountsCallback(callback); + }); + + String url = mActivityTestRule.getTestServer().getURL("/echoheader?X-Chrome-Connected"); + mActivityTestRule.navigateAndWait(url); + Assert.assertEquals( + mActivityTestRule.executeScriptAndExtractString("document.body.innerText"), + "source=WebLayer,mode=3,enable_account_consistency=true," + + "consistency_enabled_by_default=false"); + + // Remove the callback, the header should no longer be sent. + TestThreadUtils.runOnUiThreadBlocking( + () -> { mActivity.getBrowser().getActiveTab().setGoogleAccountsCallback(null); }); + mActivityTestRule.navigateAndWait(url); + Assert.assertEquals( + mActivityTestRule.executeScriptAndExtractString("document.body.innerText"), "None"); + } + + private static class GoogleAccountsCallbackImpl extends GoogleAccountsCallback { + private CallbackHelper mHelper = new CallbackHelper(); + private GoogleAccountsParams mParams; + + @Override + public void onGoogleAccountsRequest(GoogleAccountsParams params) { + mParams = params; + mHelper.notifyCalled(); + } + + @Override + public String getGaiaId() { + return ""; + } + + public GoogleAccountsParams waitForGoogleAccounts() throws Exception { + mHelper.waitForFirst(); + return mParams; + } + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/InfoBarTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/InfoBarTest.java new file mode 100644 index 0000000..805023f --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/InfoBarTest.java
@@ -0,0 +1,200 @@ +// Copyright 2020 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.weblayer.test; + +import android.os.RemoteException; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.Browser; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.TestWebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Test for infobars. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +@CommandLineFlags.Add("enable-features=ImmediatelyHideBrowserControlsForTest") +@DisabledTest(message = "crbug.com/1223953, crbug.com/1098625") +public class InfoBarTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private Tab getActiveTab() { + return mActivityTestRule.getActivity().getBrowser().getActiveTab(); + } + + private TestWebLayer getTestWebLayer() { + return TestWebLayer.getTestWebLayer( + mActivityTestRule.getActivity().getApplicationContext()); + } + + private View getInfoBarContainerView() throws Exception { + return TestThreadUtils.runOnUiThreadBlocking(() -> { + try { + return getTestWebLayer().getInfoBarContainerView(getActiveTab()); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + }); + } + + private void addInfoBarToActiveTab() throws Exception { + CallbackHelper helper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + try { + getTestWebLayer().addInfoBar(getActiveTab(), () -> { helper.notifyCalled(); }); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + }); + helper.waitForFirst(); + } + + private void setAccessibilityEnabled(boolean value) { + TestThreadUtils.runOnUiThreadBlocking(() -> { + try { + getTestWebLayer().setAccessibilityEnabled(value); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + }); + } + + private boolean canInfoBarContainerScroll() throws Exception { + return TestThreadUtils.runOnUiThreadBlocking(() -> { + try { + return getTestWebLayer().canInfoBarContainerScroll(getActiveTab()); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + }); + } + + @Before + public void setUp() throws Throwable { + final String url = mActivityTestRule.getTestDataURL("tall_page.html"); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(url); + + BrowserControlsHelper.createAndBlockUntilBrowserControlsInitializedInSetUp(activity); + } + + @Test + @SmallTest + /** + * Tests that creation of an infobar impacts the state of the infobar container view as + * expected. + * + */ + public void testAddInfoBar() throws Exception { + View infoBarContainerView = getInfoBarContainerView(); + Assert.assertEquals(infoBarContainerView.getHeight(), 0); + + addInfoBarToActiveTab(); + + Assert.assertTrue(infoBarContainerView.getHeight() > 0); + Assert.assertEquals(View.VISIBLE, infoBarContainerView.getVisibility()); + } + + @Test + @SmallTest + /** + * Tests that infobars respond to scrolling. + * + */ + public void testScrolling() throws Exception { + addInfoBarToActiveTab(); + + View infoBarContainerView = getInfoBarContainerView(); + Assert.assertEquals(0, (int) infoBarContainerView.getTranslationY()); + + InstrumentationActivity activity = mActivityTestRule.getActivity(); + int infoBarContainerViewHeight = infoBarContainerView.getHeight(); + Assert.assertTrue(infoBarContainerViewHeight > 0); + + // Scroll down and check that infobar container view is translated in response. + EventUtils.simulateDragFromCenterOfView( + activity.getWindow().getDecorView(), 0, -infoBarContainerViewHeight); + CriteriaHelper.pollUiThread(() -> { + Criteria.checkThat((int) infoBarContainerView.getTranslationY(), + Matchers.is(infoBarContainerViewHeight)); + }); + + // Scroll back up and check that infobar container view is translated in response. + EventUtils.simulateDragFromCenterOfView( + activity.getWindow().getDecorView(), 0, infoBarContainerViewHeight); + CriteriaHelper.pollUiThread(() -> { + Criteria.checkThat((int) infoBarContainerView.getTranslationY(), Matchers.is(0)); + }); + } + + @Test + @SmallTest + /** + * Tests that the infobar container view is removed as part of tab destruction. + * + */ + public void testTabDestruction() throws Exception { + View infoBarContainerView = getInfoBarContainerView(); + Assert.assertNotNull(infoBarContainerView.getParent()); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + Browser browser = mActivityTestRule.getActivity().getBrowser(); + browser.destroyTab(getActiveTab()); + }); + + Assert.assertEquals(infoBarContainerView.getParent(), null); + } + + @Test + @SmallTest + /** + * Tests that if the infobar container view is hidden, its visibility is restored on navigation. + * + */ + public void testVisibilityRestoredOnNavigation() throws Exception { + View infoBarContainerView = getInfoBarContainerView(); + Assert.assertEquals(infoBarContainerView.getVisibility(), View.VISIBLE); + + TestThreadUtils.runOnUiThreadBlocking( + () -> { infoBarContainerView.setVisibility(View.GONE); }); + Assert.assertEquals(infoBarContainerView.getVisibility(), View.GONE); + + mActivityTestRule.navigateAndWait("about:blank"); + Assert.assertEquals(infoBarContainerView.getVisibility(), View.VISIBLE); + } + + // Tests that infobar container is blocked from scrolling when accessibility is enabled. + @Test + @SmallTest + public void testAccessibility() throws Exception { + InstrumentationActivity activity = mActivityTestRule.getActivity(); + + // Turn on accessibility, which should disable the infobar container from scrolling. This + // polls as setAccessibilityEnabled() is async. + setAccessibilityEnabled(true); + CriteriaHelper.pollInstrumentationThread(() -> !canInfoBarContainerScroll()); + + // Turn accessibility off and verify that the infobar container can scroll. + setAccessibilityEnabled(false); + CriteriaHelper.pollInstrumentationThread(() -> canInfoBarContainerScroll()); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/InputTypesTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/InputTypesTest.java new file mode 100644 index 0000000..fb188bae --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/InputTypesTest.java
@@ -0,0 +1,349 @@ +// 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. + +package org.chromium.weblayer.test; + +import android.Manifest; +import android.app.Activity; +import android.content.ClipData; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Parcelable; +import android.provider.MediaStore; + +import androidx.annotation.RequiresApi; +import androidx.core.app.ActivityCompat; +import androidx.test.filters.SmallTest; + +import org.hamcrest.Matchers; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.ContextUtils; +import org.chromium.base.test.util.ApplicationContextWrapper; +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.MinAndroidSdkLevel; +import org.chromium.base.test.util.UrlUtils; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.io.File; +import java.util.Arrays; + +/** + * Tests that file inputs work as expected. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class InputTypesTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private File mTempFile; + private File mTestDir; + private int mCameraPermission = PackageManager.PERMISSION_GRANTED; + + private class FileIntentInterceptor implements InstrumentationActivity.IntentInterceptor { + public Intent mLastIntent; + + private Intent mResponseIntent; + private int mResultCode = Activity.RESULT_CANCELED; + private CallbackHelper mCallbackHelper = new CallbackHelper(); + + @Override + public void interceptIntent(Intent intent, int requestCode, Bundle options) { + new Handler().post(() -> { + mActivityTestRule.getActivity().getActivityResultRegistry().dispatchResult( + requestCode, mResultCode, mResponseIntent); + mLastIntent = intent; + mCallbackHelper.notifyCalled(); + }); + } + + public void waitForIntent() { + try { + mCallbackHelper.waitForCallback(0); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public void setResponse(int resultCode, Intent response) { + mResponseIntent = response; + mResultCode = resultCode; + } + } + + private FileIntentInterceptor mIntentInterceptor = new FileIntentInterceptor(); + + @RequiresApi(Build.VERSION_CODES.M) + private class PermissionCompatDelegate implements ActivityCompat.PermissionCompatDelegate { + public int mResult = PackageManager.PERMISSION_DENIED; + private CallbackHelper mCallbackHelper = new CallbackHelper(); + + @Override + public boolean requestPermissions( + Activity activity, String[] permissions, int requestCode) { + new Handler().post(() -> { + int[] results = new int[permissions.length]; + Arrays.fill(results, mResult); + mCameraPermission = mResult; + activity.onRequestPermissionsResult(requestCode, permissions, results); + mCallbackHelper.notifyCalled(); + }); + return true; + } + + @Override + public boolean onActivityResult( + Activity activity, int requestCode, int resultCode, Intent data) { + return false; + } + + public void waitForPermissionsRequest() { + try { + mCallbackHelper.waitForCallback(0); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public void setResult(int result) { + mResult = result; + } + } + + private PermissionCompatDelegate mPermissionCompatDelegate = new PermissionCompatDelegate(); + + @Before + public void setUp() throws Exception { + ContextUtils.initApplicationContextForTests( + new ApplicationContextWrapper(ContextUtils.getApplicationContext()) { + @Override + public int checkPermission(String permission, int pid, int uid) { + if (permission.equals(Manifest.permission.CAMERA)) { + return mCameraPermission; + } + return super.checkPermission(permission, pid, uid); + } + }); + ActivityCompat.setPermissionCompatDelegate(mPermissionCompatDelegate); + + mTestDir = new File(UrlUtils.getIsolatedTestFilePath("weblayer")); + if (!mTestDir.exists()) mTestDir.mkdir(); + mTempFile = File.createTempFile("file", null, mTestDir); + Intent response = new Intent(); + response.setData(Uri.fromFile(mTempFile)); + mIntentInterceptor.setResponse(Activity.RESULT_OK, response); + + Bundle extras = new Bundle(); + // We need to override the context with which to create WebLayer. + extras.putBoolean(InstrumentationActivity.EXTRA_CREATE_WEBLAYER, false); + InstrumentationActivity activity = mActivityTestRule.launchShell(extras); + activity.setIntentInterceptor(mIntentInterceptor); + + TestThreadUtils.runOnUiThreadBlocking( + () -> activity.loadWebLayerSync(ContextUtils.getApplicationContext())); + mActivityTestRule.navigateAndWait(mActivityTestRule.getTestDataURL("input_types.html")); + } + + @After + public void tearDown() { + ActivityCompat.setPermissionCompatDelegate(null); + if (mTempFile != null) { + mTempFile.delete(); + } + if (mTestDir != null) { + mTestDir.delete(); + } + } + + @Test + @SmallTest + public void testFileInputBasic() { + String id = "input_file"; + + openFileInputWithId(id); + + Assert.assertFalse(getContentIntent().hasCategory(Intent.CATEGORY_OPENABLE)); + + waitForNumFiles(id, 1); + } + + @Test + @SmallTest + @MinAndroidSdkLevel(Build.VERSION_CODES.M) + public void testFileInputCameraPermissionGranted() throws Exception { + mCameraPermission = PackageManager.PERMISSION_DENIED; + mPermissionCompatDelegate.setResult(PackageManager.PERMISSION_GRANTED); + String id = "input_file"; + + openFileInputWithId(id); + mPermissionCompatDelegate.waitForPermissionsRequest(); + + Parcelable[] intents = mIntentInterceptor.mLastIntent.getParcelableArrayExtra( + Intent.EXTRA_INITIAL_INTENTS); + Assert.assertFalse(intents.length == 0); + Assert.assertEquals(MediaStore.ACTION_IMAGE_CAPTURE, ((Intent) intents[0]).getAction()); + + waitForNumFiles(id, 1); + } + + @Test + @SmallTest + @MinAndroidSdkLevel(Build.VERSION_CODES.M) + public void testFileInputCameraPermissionDenied() throws Exception { + mCameraPermission = PackageManager.PERMISSION_DENIED; + mPermissionCompatDelegate.setResult(PackageManager.PERMISSION_DENIED); + String id = "input_file"; + + openFileInputWithId(id); + mPermissionCompatDelegate.waitForPermissionsRequest(); + + Parcelable[] intents = mIntentInterceptor.mLastIntent.getParcelableArrayExtra( + Intent.EXTRA_INITIAL_INTENTS); + for (Parcelable intent : intents) { + Assert.assertNotEquals(MediaStore.ACTION_IMAGE_CAPTURE, ((Intent) intent).getAction()); + } + + waitForNumFiles(id, 1); + } + + @Test + @SmallTest + public void testFileInputCancel() { + String id = "input_file"; + + // First add a file. + openFileInputWithId(id); + waitForNumFiles(id, 1); + + // Now cancel the intent. + mIntentInterceptor.setResponse(Activity.RESULT_CANCELED, null); + openFileInputWithId(id); + waitForNumFiles(id, 0); + } + + @Test + @SmallTest + public void testFileInputText() { + String id = "input_text"; + + openFileInputWithId(id); + + Assert.assertTrue(getContentIntent().hasCategory(Intent.CATEGORY_OPENABLE)); + + waitForNumFiles(id, 1); + } + + @Test + @SmallTest + public void testFileInputAny() { + String id = "input_any"; + + openFileInputWithId(id); + + Assert.assertFalse(getContentIntent().hasCategory(Intent.CATEGORY_OPENABLE)); + + waitForNumFiles(id, 1); + } + + @Test + @SmallTest + public void testFileInputMultiple() throws Exception { + Intent response = new Intent(); + ClipData clipData = ClipData.newUri(mActivityTestRule.getActivity().getContentResolver(), + "uris", Uri.fromFile(mTempFile)); + File otherTempFile = File.createTempFile("file2", null, mTestDir); + clipData.addItem(new ClipData.Item(Uri.fromFile(otherTempFile))); + response.setClipData(clipData); + mIntentInterceptor.setResponse(Activity.RESULT_OK, response); + String id = "input_file_multiple"; + + openFileInputWithId(id); + + Intent contentIntent = getContentIntent(); + Assert.assertFalse(contentIntent.hasCategory(Intent.CATEGORY_OPENABLE)); + Assert.assertTrue(contentIntent.hasExtra(Intent.EXTRA_ALLOW_MULTIPLE)); + + waitForNumFiles(id, 2); + otherTempFile.delete(); + } + + @Test + @SmallTest + public void testFileInputImage() { + String id = "input_image"; + + openFileInputWithId(id); + + Assert.assertEquals( + MediaStore.ACTION_IMAGE_CAPTURE, mIntentInterceptor.mLastIntent.getAction()); + + waitForNumFiles(id, 1); + } + + @Test + @SmallTest + public void testFileInputAudio() { + String id = "input_audio"; + + openFileInputWithId(id); + + Assert.assertEquals(MediaStore.Audio.Media.RECORD_SOUND_ACTION, + mIntentInterceptor.mLastIntent.getAction()); + + waitForNumFiles(id, 1); + } + + @Test + @SmallTest + public void testColorInput() { + // Just make sure we don't crash when opening the color picker. + mActivityTestRule.executeScriptSync("var done = false; document.onclick = function() {" + + "document.getElementById('input_color').click(); done = true;}", + true /* useSeparateIsolate */); + EventUtils.simulateTouchCenterOfView( + mActivityTestRule.getActivity().getWindow().getDecorView()); + CriteriaHelper.pollInstrumentationThread( + () -> { return mActivityTestRule.executeScriptAndExtractBoolean("done"); }); + } + + private void openFileInputWithId(String id) { + // We need to click the input after user input, otherwise it won't open due to security + // policy. + mActivityTestRule.executeScriptSync( + "document.onclick = function() {document.getElementById('" + id + "').click()}", + true /* useSeparateIsolate */); + EventUtils.simulateTouchCenterOfView( + mActivityTestRule.getActivity().getWindow().getDecorView()); + mIntentInterceptor.waitForIntent(); + } + + private void waitForNumFiles(String id, int num) { + CriteriaHelper.pollInstrumentationThread(() -> { + int actual = mActivityTestRule.executeScriptAndExtractInt( + "document.getElementById('" + id + "').files.length"); + Criteria.checkThat(actual, Matchers.is(num)); + }); + } + + private Intent getContentIntent() { + Assert.assertEquals(Intent.ACTION_CHOOSER, mIntentInterceptor.mLastIntent.getAction()); + Intent contentIntent = + (Intent) mIntentInterceptor.mLastIntent.getParcelableExtra(Intent.EXTRA_INTENT); + Assert.assertNotNull(contentIntent); + return contentIntent; + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/InstrumentationActivityTestRule.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/InstrumentationActivityTestRule.java new file mode 100644 index 0000000..22ee3aba --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/InstrumentationActivityTestRule.java
@@ -0,0 +1,307 @@ +// 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. + +package org.chromium.weblayer.test; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.net.Uri; +import android.os.Bundle; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.lifecycle.Stage; + +import androidx.fragment.app.Fragment; + +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import org.chromium.base.test.util.ApplicationTestUtils; +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.net.test.EmbeddedTestServer; +import org.chromium.net.test.EmbeddedTestServerRule; +import org.chromium.weblayer.CookieManager; +import org.chromium.weblayer.NavigationController; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.WebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +/** + * ActivityTestRule for InstrumentationActivity. + * + * Test can use this ActivityTestRule to launch or get InstrumentationActivity. + */ +public class InstrumentationActivityTestRule + extends WebLayerActivityTestRule<InstrumentationActivity> { + /** The top level key of the JSON object returned by executeScriptSync(). */ + public static final String SCRIPT_RESULT_KEY = "result"; + + @Rule + private EmbeddedTestServerRule mTestServerRule = new EmbeddedTestServerRule(); + + private static final class StringCallbackHelper extends CallbackHelper { + private String mResult; + + public String getResult() { + return mResult; + } + + public void notifyCalled(String result) { + mResult = result; + notifyCalled(); + } + } + + public InstrumentationActivityTestRule() { + super(InstrumentationActivity.class); + } + + @Override + public Statement apply(final Statement base, Description description) { + return super.apply(mTestServerRule.apply(base, description), description); + } + + public WebLayer getWebLayer() { + return TestThreadUtils.runOnUiThreadBlockingNoException( + () -> { return WebLayer.loadSync(getContextForWebLayer()); }); + } + + public Context getContextForWebLayer() { + return InstrumentationRegistry.getTargetContext().getApplicationContext(); + } + + /** + * Starts the WebLayer activity with the given extras Bundle. This does not create and load + * WebLayer. + */ + public InstrumentationActivity launchShell(Bundle extras) { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.putExtras(extras); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setComponent( + new ComponentName(InstrumentationRegistry.getInstrumentation().getTargetContext(), + InstrumentationActivity.class)); + launchActivity(intent); + return getActivity(); + } + + /** + * Starts the WebLayer activity with the given extras Bundle and completely loads the given URL + * (this calls navigateAndWait()). + */ + public InstrumentationActivity launchShellWithUrl(String url, Bundle extras) { + InstrumentationActivity activity = launchShell(extras); + Assert.assertNotNull(activity); + try { + TestThreadUtils.runOnUiThreadBlocking( + () -> activity.loadWebLayerSync(getContextForWebLayer())); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + if (url != null) navigateAndWait(url); + return activity; + } + + /** + * Starts the WebLayer activity and completely loads the given URL (this calls + * navigateAndWait()). + */ + public InstrumentationActivity launchShellWithUrl(String url) { + return launchShellWithUrl(url, new Bundle()); + } + + /** + * Loads the given URL in the shell. + */ + public void navigateAndWait(String url) { + navigateAndWait(getActivity().getTab(), url, true /* waitForPaint */); + } + + public void navigateAndWait(Tab tab, String url, boolean waitForPaint) { + (new NavigationWaiter(url, tab, false /* expectFailure */, waitForPaint)).navigateAndWait(); + } + + /** + * Loads the given URL in the shell, expecting failure. + */ + public void navigateAndWaitForFailure(String url) { + navigateAndWaitForFailure(getActivity().getTab(), url, true /* waitForPaint */); + } + + public void navigateAndWaitForFailure(Tab tab, String url, boolean waitForPaint) { + (new NavigationWaiter(url, tab, true /* expectFailure */, waitForPaint)).navigateAndWait(); + } + + public void recreateByRotatingToLandscape() { + setActivity(ApplicationTestUtils.waitForActivityWithClass( + InstrumentationActivity.class, Stage.RESUMED, () -> { + getActivity().setRequestedOrientation( + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + })); + } + + /** + * Executes the script passed in and waits for the result. Wraps that result in a JSONObject for + * convenience of callers that want to process that result as a type other than String. + */ + public JSONObject executeScriptSync(String script, boolean useSeparateIsolate, Tab tab) { + StringCallbackHelper callbackHelper = new StringCallbackHelper(); + int count = callbackHelper.getCallCount(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Tab scriptTab = tab == null ? getActivity().getBrowser().getActiveTab() : tab; + scriptTab.executeScript(script, useSeparateIsolate, + (String result) -> { callbackHelper.notifyCalled(result); }); + }); + try { + callbackHelper.waitForCallback(count); + } catch (TimeoutException e) { + throw new RuntimeException(e); + } + JSONObject resultAsJSONObject; + try { + resultAsJSONObject = new JSONObject( + "{\"" + SCRIPT_RESULT_KEY + "\":" + callbackHelper.getResult() + "}"); + } catch (JSONException e) { + // This should never happen since the result should be well formed. + throw new RuntimeException(e); + } + return resultAsJSONObject; + } + + public JSONObject executeScriptSync(String script, boolean useSeparateIsolate) { + return executeScriptSync(script, useSeparateIsolate, null); + } + + public int executeScriptAndExtractInt(String script) { + try { + return executeScriptSync(script, true /* useSeparateIsolate */) + .getInt(SCRIPT_RESULT_KEY); + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + + public String executeScriptAndExtractString(String script) { + return executeScriptAndExtractString(script, true /* useSeparateIsolate */); + } + + public String executeScriptAndExtractString(String script, boolean useSeparateIsolate) { + try { + return executeScriptSync(script, useSeparateIsolate).getString(SCRIPT_RESULT_KEY); + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + + public boolean executeScriptAndExtractBoolean(String script) { + return executeScriptAndExtractBoolean(script, true /* useSeparateIsolate */); + } + + public boolean executeScriptAndExtractBoolean(String script, boolean useSeparateIsolate) { + try { + return executeScriptSync(script, useSeparateIsolate).getBoolean(SCRIPT_RESULT_KEY); + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + + public InstrumentationActivity launchWithProfile(String profileName) { + Bundle extras = new Bundle(); + extras.putString(InstrumentationActivity.EXTRA_PROFILE_NAME, profileName); + String url = "data:text,foo"; + return launchShellWithUrl(url, extras); + } + + public EmbeddedTestServer getTestServer() { + return mTestServerRule.getServer(); + } + + public EmbeddedTestServerRule getTestServerRule() { + return mTestServerRule; + } + + public String getTestDataURL(String path) { + return getTestServer().getURL("/weblayer/test/data/" + path); + } + + // Returns the URL that is currently being displayed to the user. + public String getCurrentDisplayUrl() { + InstrumentationActivity activity = getActivity(); + return TestThreadUtils.runOnUiThreadBlockingNoException(() -> { + NavigationController navigationController = + activity.getBrowser().getActiveTab().getNavigationController(); + + if (navigationController.getNavigationListSize() == 0) { + return null; + } + + // TODO(crbug.com/1066382): This will not be correct in the case where the initial + // navigation in |tab| was a failed navigation and there have been no more navigations + // since then. + return navigationController + .getNavigationEntryDisplayUri( + navigationController.getNavigationListCurrentIndex()) + .toString(); + }); + } + + public void setRetainInstance(boolean retain) { + TestThreadUtils.runOnUiThreadBlocking(() -> getActivity().setRetainInstance(retain)); + } + + public Fragment getFragment() { + return TestThreadUtils.runOnUiThreadBlockingNoException(() -> getActivity().getFragment()); + } + + public boolean setCookie(CookieManager cookieManager, Uri uri, String value) throws Exception { + Boolean[] resultHolder = new Boolean[1]; + CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + cookieManager.setCookie(uri, value, (Boolean result) -> { + resultHolder[0] = result; + callbackHelper.notifyCalled(); + }); + }); + callbackHelper.waitForFirst(); + return resultHolder[0]; + } + + public String getCookie(CookieManager cookieManager, Uri uri) throws Exception { + String[] resultHolder = new String[1]; + CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + cookieManager.getCookie(uri, (String result) -> { + resultHolder[0] = result; + callbackHelper.notifyCalled(); + }); + }); + callbackHelper.waitForFirst(); + return resultHolder[0]; + } + + public List<String> getResponseCookies(CookieManager cookieManager, Uri uri) throws Exception { + List<String> finalResult = new ArrayList<>(); + CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + cookieManager.getResponseCookies(uri, (List<String> result) -> { + finalResult.addAll(result); + callbackHelper.notifyCalled(); + }); + }); + callbackHelper.waitForFirst(); + return finalResult; + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MediaCaptureTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MediaCaptureTest.java new file mode 100644 index 0000000..57133bf1 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MediaCaptureTest.java
@@ -0,0 +1,266 @@ +// Copyright 2020 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.weblayer.test; + +import android.app.Notification; +import android.app.NotificationManager; +import android.content.Context; +import android.os.Build; +import android.service.notification.StatusBarNotification; +import android.support.test.InstrumentationRegistry; +import android.webkit.ValueCallback; + +import androidx.annotation.RequiresApi; +import androidx.test.filters.MediumTest; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.DisableIf; +import org.chromium.base.test.util.MinAndroidSdkLevel; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.ui.test.util.UiDisableIf; +import org.chromium.weblayer.MediaCaptureCallback; +import org.chromium.weblayer.TestWebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Tests that Media Capture and Streams Web API (MediaStream) works as expected. + */ +@CommandLineFlags.Add({"ignore-certificate-errors"}) +@RunWith(WebLayerJUnit4ClassRunner.class) +public final class MediaCaptureTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + private TestWebLayer mTestWebLayer; + private CallbackImpl mCaptureCallback; + + private static class CallbackImpl extends MediaCaptureCallback { + boolean mAudio; + boolean mVideo; + public BoundedCountDownLatch mRequestedCountDown; + public BoundedCountDownLatch mStateCountDown; + + @Override + public void onMediaCaptureRequested( + boolean audio, boolean video, ValueCallback<Boolean> requestResult) { + requestResult.onReceiveValue(true); + mRequestedCountDown.countDown(); + } + + @Override + public void onMediaCaptureStateChanged(boolean audio, boolean video) { + mAudio = audio; + mVideo = video; + mStateCountDown.countDown(); + } + } + + @Before + public void setUp() throws Throwable { + mActivity = mActivityTestRule.launchShellWithUrl("about:blank"); + Assert.assertNotNull(mActivity); + + mTestWebLayer = TestWebLayer.getTestWebLayer(mActivity.getApplicationContext()); + mActivityTestRule.getTestServerRule().setServerUsesHttps(true); + + mCaptureCallback = new CallbackImpl(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.getTab().getMediaCaptureController().setMediaCaptureCallback( + mCaptureCallback); + }); + } + + /** + * Basic test for a stream that includes audio and video. + */ + @Test + @MediumTest + @DisableIf.Device(type = {UiDisableIf.TABLET}) // https://crbug.com/1107380 + public void basic() throws Throwable { + mActivityTestRule.navigateAndWait( + mActivityTestRule.getTestServer().getURL("/weblayer/test/data/getusermedia.html")); + + grantPermissionAndWaitForStreamToStart(); + + Assert.assertTrue(mCaptureCallback.mAudio); + Assert.assertTrue(mCaptureCallback.mVideo); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(getMediaCaptureNotification(), Matchers.notNullValue()); + }); + } + + stopStream(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(getMediaCaptureNotification(), Matchers.nullValue()); + }); + } + } + + /** + * Tests that the per-site permission, once granted, is remembered the next time a stream is + * requested. + */ + @Test + @MediumTest + @DisableIf.Device(type = {UiDisableIf.TABLET}) // https://crbug.com/1107380 + public void rememberPermission() throws Throwable { + mActivityTestRule.navigateAndWait( + mActivityTestRule.getTestServer().getURL("/weblayer/test/data/getusermedia.html")); + + grantPermissionAndWaitForStreamToStart(); + + Assert.assertTrue(mCaptureCallback.mAudio); + Assert.assertTrue(mCaptureCallback.mVideo); + + stopStream(); + + // No permission prompt required the second time. + mCaptureCallback.mRequestedCountDown = new BoundedCountDownLatch(1); + mCaptureCallback.mStateCountDown = new BoundedCountDownLatch(1); + mActivityTestRule.navigateAndWait( + mActivityTestRule.getTestServer().getURL("/weblayer/test/data/getusermedia.html")); + mCaptureCallback.mRequestedCountDown.timedAwait(); + mCaptureCallback.mStateCountDown.timedAwait(); + + Assert.assertTrue(mCaptureCallback.mAudio); + Assert.assertTrue(mCaptureCallback.mVideo); + } + + /** + * Tests that a site can request two parallel streams and both are stopped via {@link + * stopMediaCapturing}. + */ + @Test + @MediumTest + @DisableIf.Device(type = {UiDisableIf.TABLET}) // https://crbug.com/1107380 + public void twoStreams() throws Throwable { + mActivityTestRule.navigateAndWait( + mActivityTestRule.getTestServer().getURL("/weblayer/test/data/getusermedia2.html")); + + // Audio stream. + grantPermissionAndWaitForStreamToStart(); + Assert.assertTrue(mCaptureCallback.mAudio); + Assert.assertFalse(mCaptureCallback.mVideo); + + // Video stream. + grantPermissionAndWaitForStreamToStart(); + Assert.assertTrue(mCaptureCallback.mAudio); + Assert.assertTrue(mCaptureCallback.mVideo); + + mCaptureCallback.mStateCountDown = new BoundedCountDownLatch(2); + TestThreadUtils.runOnUiThreadBlocking( + () -> { mActivity.getTab().getMediaCaptureController().stopMediaCapturing(); }); + mCaptureCallback.mStateCountDown.timedAwait(); + Assert.assertFalse(mCaptureCallback.mAudio); + Assert.assertFalse(mCaptureCallback.mVideo); + } + + /** + * Tests that the notification posted for a tab will be updated if a second stream is started. + */ + @Test + @MediumTest + @MinAndroidSdkLevel(Build.VERSION_CODES.M) + @DisableIf.Device(type = {UiDisableIf.TABLET}) // https://crbug.com/1107380 + public void twoStreamsNotification() throws Throwable { + mActivityTestRule.navigateAndWait( + mActivityTestRule.getTestServer().getURL("/weblayer/test/data/getusermedia2.html")); + + // Audio stream. + grantPermissionAndWaitForStreamToStart(); + Assert.assertTrue(mCaptureCallback.mAudio); + Assert.assertFalse(mCaptureCallback.mVideo); + + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(getMediaCaptureNotification(), Matchers.notNullValue()); + }); + Notification audioNotification = getMediaCaptureNotification(); + + // Video stream. + grantPermissionAndWaitForStreamToStart(); + Assert.assertTrue(mCaptureCallback.mAudio); + Assert.assertTrue(mCaptureCallback.mVideo); + + CriteriaHelper.pollInstrumentationThread(() -> { + Notification combinedNotification = getMediaCaptureNotification(); + Criteria.checkThat(combinedNotification, Matchers.notNullValue()); + Criteria.checkThat(combinedNotification.getSmallIcon().getResId(), + Matchers.not(audioNotification.getSmallIcon().getResId())); + }); + + mCaptureCallback.mStateCountDown = new BoundedCountDownLatch(2); + TestThreadUtils.runOnUiThreadBlocking( + () -> { mActivity.getTab().getMediaCaptureController().stopMediaCapturing(); }); + mCaptureCallback.mStateCountDown.timedAwait(); + Assert.assertFalse(mCaptureCallback.mAudio); + Assert.assertFalse(mCaptureCallback.mVideo); + + CriteriaHelper.pollInstrumentationThread( + () -> Criteria.checkThat(getMediaCaptureNotification(), Matchers.nullValue())); + } + + private void grantPermissionAndWaitForStreamToStart() throws Throwable { + CriteriaHelper.pollInstrumentationThread( + () -> { return mTestWebLayer.isPermissionDialogShown(); }); + mCaptureCallback.mRequestedCountDown = new BoundedCountDownLatch(1); + mCaptureCallback.mStateCountDown = new BoundedCountDownLatch(1); + mTestWebLayer.clickPermissionDialogButton(true); + + mCaptureCallback.mRequestedCountDown.timedAwait(); + mCaptureCallback.mStateCountDown.timedAwait(); + } + + private void stopStream() throws Throwable { + mCaptureCallback.mStateCountDown = new BoundedCountDownLatch(1); + TestThreadUtils.runOnUiThreadBlocking( + () -> { mActivity.getTab().getMediaCaptureController().stopMediaCapturing(); }); + mCaptureCallback.mStateCountDown.timedAwait(); + Assert.assertFalse(mCaptureCallback.mAudio); + Assert.assertFalse(mCaptureCallback.mVideo); + } + + /** + * Retrieves the active media capture notification, or null if none exists. + * Asserts that at most one notification exists. + * {@link NotificationManager#getActiveNotifications()} is only available from M. + */ + @RequiresApi(Build.VERSION_CODES.M) + private Notification getMediaCaptureNotification() { + StatusBarNotification notifications[]; + try { + // Workaround for Android bug fixed in 34a80841cb8fa8cdbe6c584831f0e531618d331d. + notifications = + ((NotificationManager) mActivity.getApplicationContext().getSystemService( + Context.NOTIFICATION_SERVICE)) + .getActiveNotifications(); + } catch (NullPointerException e) { + return null; + } + Notification notification = null; + for (StatusBarNotification statusBarNotification : notifications) { + if (statusBarNotification.getTag().equals("org.chromium.weblayer.webrtc.avstream")) { + Assert.assertNull(notification); + notification = statusBarNotification.getNotification(); + Assert.assertNotNull(notification.getSmallIcon().loadDrawable( + InstrumentationRegistry.getContext())); + } + } + return notification; + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MediaRouterTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MediaRouterTest.java new file mode 100644 index 0000000..9db80b9 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MediaRouterTest.java
@@ -0,0 +1,275 @@ +// Copyright 2020 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.weblayer.test; + +import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE; +import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking; + +import android.support.test.InstrumentationRegistry; +import android.view.View; + +import androidx.test.filters.LargeTest; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.base.test.util.Feature; +import org.chromium.base.test.util.Restriction; +import org.chromium.content_public.browser.test.util.ClickUtils; +import org.chromium.content_public.browser.test.util.TestTouchUtils; +import org.chromium.content_public.common.ContentSwitches; +import org.chromium.ui.test.util.UiRestriction; +import org.chromium.weblayer.Browser; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.TestWebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Tests of the Presentation API. + */ +@MinWebLayerVersion(88) +@RunWith(WebLayerJUnit4ClassRunner.class) +@CommandLineFlags.Add({ContentSwitches.DISABLE_GESTURE_REQUIREMENT_FOR_PRESENTATION}) +public class MediaRouterTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + + private static final String TEST_PAGE = "media_router/basic_test.html"; + + private static final int SCRIPT_TIMEOUT_MS = 10000; + private static final int SCRIPT_RETRY_MS = 150; + + private static final String TEST_SINK_NAME = "test-sink-1"; + + // Javascript snippets. + private static final String WAIT_DEVICE_SCRIPT = "waitUntilDeviceAvailable();"; + private static final String START_PRESENTATION_SCRIPT = "startPresentation();"; + private static final String TERMINATE_CONNECTION_SCRIPT = + "terminateConnectionAndWaitForStateChange();"; + + @Before + public void setUp() { + mActivity = mActivityTestRule.launchShellWithUrl("about:blank"); + } + + private TestWebLayer getTestWebLayer() { + return TestWebLayer.getTestWebLayer(mActivity.getApplicationContext()); + } + + private void executeScriptAndWaitForResult(String script) throws Exception { + mActivityTestRule.executeScriptSync("lastExecutionResult = null", false); + mActivityTestRule.executeScriptSync(script, false); + CriteriaHelper.pollInstrumentationThread(() -> { + String result = + mActivityTestRule.executeScriptAndExtractString("lastExecutionResult", false); + Criteria.checkThat(result, Matchers.is("passed")); + }, SCRIPT_TIMEOUT_MS, SCRIPT_RETRY_MS); + } + + private void startPresentationAndSelectRoute() throws Exception { + // Request a presentation. + mActivityTestRule.navigateAndWait(mActivityTestRule.getTestDataURL(TEST_PAGE)); + executeScriptAndWaitForResult(WAIT_DEVICE_SCRIPT); + executeScriptAndWaitForResult(START_PRESENTATION_SCRIPT); + + // Verify the route selection dialog is showing and make a selection. + View testRouteButton = getTestWebLayer().getMediaRouteButton(TEST_SINK_NAME); + Assert.assertNotNull(testRouteButton); + ClickUtils.mouseSingleClickView( + InstrumentationRegistry.getInstrumentation(), testRouteButton); + } + + private String verifyPresentationStarted() throws Exception { + // Verify in javascript that a presentation has started. + executeScriptAndWaitForResult("checkConnection();"); + String connectionId = + mActivityTestRule.executeScriptAndExtractString("startedConnection.id", false); + Assert.assertFalse(connectionId.isEmpty()); + String defaultRequestConnectionId = mActivityTestRule.executeScriptAndExtractString( + "defaultRequestConnectionId", false); + Assert.assertEquals(connectionId, defaultRequestConnectionId); + return connectionId; + } + + void checkStartFailed(String errorName, String errorMessageSubstring) throws Exception { + String script = + String.format("checkStartFailed('%s', '%s');", errorName, errorMessageSubstring); + executeScriptAndWaitForResult(script); + } + + /** + * Basic test where the page requests a route, the user selects a route, and a connection is + * started. + */ + @Test + @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) + @Feature({"MediaRouter"}) + @LargeTest + public void testBasic() throws Exception { + getTestWebLayer().initializeMockMediaRouteProvider(/*closeRouteWithErrorOnSend=*/false, + /*disableIsSupportsSource=*/false, /*createRouteErrorMessage=*/null, + /*joinRouteErrorMessage=*/null); + startPresentationAndSelectRoute(); + verifyPresentationStarted(); + + executeScriptAndWaitForResult(TERMINATE_CONNECTION_SCRIPT); + } + + /** Test of PresentationConnection.onmessage. */ + @Test + @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) + @Feature({"MediaRouter"}) + @LargeTest + public void testSendAndOnMessage() throws Exception { + getTestWebLayer().initializeMockMediaRouteProvider(/*closeRouteWithErrorOnSend=*/false, + /*disableIsSupportsSource=*/false, /*createRouteErrorMessage=*/null, + /*joinRouteErrorMessage=*/null); + startPresentationAndSelectRoute(); + verifyPresentationStarted(); + + executeScriptAndWaitForResult("sendMessageAndExpectResponse('foo');"); + } + + /** Test of PresentationConnection.onclose. */ + @Test + @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) + @Feature({"MediaRouter"}) + @LargeTest + public void testOnClose() throws Exception { + getTestWebLayer().initializeMockMediaRouteProvider(/*closeRouteWithErrorOnSend=*/true, + /*disableIsSupportsSource=*/false, /*createRouteErrorMessage=*/null, + /*joinRouteErrorMessage=*/null); + startPresentationAndSelectRoute(); + verifyPresentationStarted(); + + executeScriptAndWaitForResult("sendMessageAndExpectConnectionCloseOnError()"); + } + + /** + * Test that starting the presentation fails when there are no providers that support the given + * source. + */ + @Test + @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) + @Feature({"MediaRouter"}) + @LargeTest + public void testFailNoProvider() throws Exception { + getTestWebLayer().initializeMockMediaRouteProvider(/*closeRouteWithErrorOnSend=*/false, + /*disableIsSupportsSource=*/true, /*createRouteErrorMessage=*/null, + /*joinRouteErrorMessage=*/null); + + startPresentationAndSelectRoute(); + checkStartFailed("UnknownError", "No provider supports createRoute with source"); + } + + /** Tests route creation failure. */ + @Test + @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) + @Feature({"MediaRouter"}) + @LargeTest + @DisabledTest(message = "https://crbug.com/1181337") + public void testFailCreateRoute() throws Exception { + getTestWebLayer().initializeMockMediaRouteProvider(/*closeRouteWithErrorOnSend=*/false, + /*disableIsSupportsSource=*/false, /*createRouteErrorMessage=*/"Unknown sink", + /*joinRouteErrorMessage=*/null); + + startPresentationAndSelectRoute(); + checkStartFailed("UnknownError", "Unknown sink"); + } + + /** Tests reconnecting to a presentation (joining a route) from a new tab. */ + @Test + @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) + @Feature({"MediaRouter"}) + @LargeTest + public void testJoinRoute() throws Exception { + getTestWebLayer().initializeMockMediaRouteProvider(/*closeRouteWithErrorOnSend=*/false, + /*disableIsSupportsSource=*/false, /*createRouteErrorMessage=*/null, + /*joinRouteErrorMessage=*/null); + + startPresentationAndSelectRoute(); + String connectionId = verifyPresentationStarted(); + + Tab firstTab = mActivity.getTab(); + Tab secondTab = runOnUiThreadBlocking(() -> { + Browser browser = mActivity.getTab().getBrowser(); + Tab tab = browser.createTab(); + browser.setActiveTab(tab); + return tab; + }); + mActivityTestRule.navigateAndWait( + secondTab, mActivityTestRule.getTestDataURL(TEST_PAGE), true); + executeScriptAndWaitForResult(String.format("reconnectConnection(\'%s\');", connectionId)); + String reconnectedConnectionId = + mActivityTestRule.executeScriptAndExtractString("reconnectedConnection.id", false); + Assert.assertEquals(connectionId, reconnectedConnectionId); + + runOnUiThreadBlocking(() -> { firstTab.getBrowser().setActiveTab(firstTab); }); + executeScriptAndWaitForResult(TERMINATE_CONNECTION_SCRIPT); + } + + /** Tests failure of reconnecting to a presentation (joining a route) from a new tab. */ + @Test + @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) + @Feature({"MediaRouter"}) + @LargeTest + public void testFailureToJoinRoute() throws Exception { + getTestWebLayer().initializeMockMediaRouteProvider(/*closeRouteWithErrorOnSend=*/false, + /*disableIsSupportsSource=*/false, /*createRouteErrorMessage=*/null, + /*joinRouteErrorMessage=*/"Unknown route"); + + startPresentationAndSelectRoute(); + String connectionId = verifyPresentationStarted(); + + Tab secondTab = runOnUiThreadBlocking(() -> { + Browser browser = mActivity.getTab().getBrowser(); + Tab tab = browser.createTab(); + browser.setActiveTab(tab); + return tab; + }); + mActivityTestRule.navigateAndWait( + secondTab, mActivityTestRule.getTestDataURL(TEST_PAGE), true); + executeScriptAndWaitForResult( + String.format("reconnectConnectionAndExpectFailure(\'%s\');", connectionId)); + } + + /** Tests the user cancelling the media route selection process. */ + @Test + @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) + @Feature({"MediaRouter"}) + @DisabledTest(message = "https://crbug.com/1144233") + @LargeTest + public void testFailStartCancelled() throws Exception { + getTestWebLayer().initializeMockMediaRouteProvider(/*closeRouteWithErrorOnSend=*/false, + /*disableIsSupportsSource=*/false, /*createRouteErrorMessage=*/null, + /*joinRouteErrorMessage=*/null); + + // Request a presentation. + mActivityTestRule.navigateAndWait(mActivityTestRule.getTestDataURL(TEST_PAGE)); + executeScriptAndWaitForResult(WAIT_DEVICE_SCRIPT); + executeScriptAndWaitForResult(START_PRESENTATION_SCRIPT); + + // Verify the route selection dialog is showing but then dismiss it. + View testRouteButton = getTestWebLayer().getMediaRouteButton(TEST_SINK_NAME); + Assert.assertNotNull(testRouteButton); + + // Click outside the dialog to dismiss it. + View topContents = mActivity.getTopContentsContainer(); + TestTouchUtils.singleClick( + InstrumentationRegistry.getInstrumentation(), 1, topContents.getHeight() + 10); + checkStartFailed("NotAllowedError", "Dialog closed."); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MediaSessionTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MediaSessionTest.java new file mode 100644 index 0000000..f6e0fe7 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MediaSessionTest.java
@@ -0,0 +1,71 @@ +// Copyright 2020 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.weblayer.test; + +import static org.chromium.weblayer.R.id.weblayer_media_session_notification; + +import android.app.Notification; +import android.app.NotificationManager; +import android.content.Context; +import android.os.Build; +import android.service.notification.StatusBarNotification; + +import androidx.annotation.RequiresApi; +import androidx.test.filters.MediumTest; + +import org.hamcrest.Matchers; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.MinAndroidSdkLevel; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Tests that MediaSession works as expected. + */ +@CommandLineFlags.Add({"ignore-certificate-errors"}) +@RunWith(WebLayerJUnit4ClassRunner.class) +public final class MediaSessionTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + + @Test + @MediumTest + @MinAndroidSdkLevel(Build.VERSION_CODES.M) + public void basic() throws Throwable { + mActivity = mActivityTestRule.launchShellWithUrl( + mActivityTestRule.getTestDataURL("media_session.html")); + EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView()); + + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(getMediaSessionNotification(), Matchers.notNullValue()); + }); + } + + /** + * Retrieves the active media session notification, or null if none exists. + * {@link NotificationManager#getActiveNotifications()} is only available from M. + */ + @RequiresApi(Build.VERSION_CODES.M) + private Notification getMediaSessionNotification() { + StatusBarNotification notifications[] = + ((NotificationManager) mActivity.getApplicationContext().getSystemService( + Context.NOTIFICATION_SERVICE)) + .getActiveNotifications(); + for (StatusBarNotification statusBarNotification : notifications) { + if (statusBarNotification.getId() == weblayer_media_session_notification) { + return statusBarNotification.getNotification(); + } + } + return null; + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MinWebLayerVersion.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MinWebLayerVersion.java new file mode 100644 index 0000000..288138b --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MinWebLayerVersion.java
@@ -0,0 +1,18 @@ +// Copyright 2020 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.weblayer.test; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface MinWebLayerVersion { + int value() default 0; +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MinWebLayerVersionSkipCheck.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MinWebLayerVersionSkipCheck.java new file mode 100644 index 0000000..2bd89c28 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MinWebLayerVersionSkipCheck.java
@@ -0,0 +1,45 @@ +// Copyright 2020 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.weblayer.test; + +import org.junit.runners.model.FrameworkMethod; + +import org.chromium.base.CommandLine; +import org.chromium.base.Log; +import org.chromium.base.test.util.AnnotationProcessingUtils; +import org.chromium.base.test.util.SkipCheck; + +/** + * Checks the WebLayer version against any specified minimum requirement. + */ +public class MinWebLayerVersionSkipCheck extends SkipCheck { + private static final String TAG = "MinWebLayerVersionSC"; + + /** + * If {@link MinWebLayerVersion} is present, checks its value + * against the WebLayer version. + * + * @param testCase The test to check. + * @return true if WebLayer's version is below the specified minimum. + */ + @Override + public boolean shouldSkip(FrameworkMethod frameworkMethod) { + int minWebLayerVersion = 0; + for (MinWebLayerVersion m : AnnotationProcessingUtils.getAnnotations( + frameworkMethod.getMethod(), MinWebLayerVersion.class)) { + minWebLayerVersion = Math.max(minWebLayerVersion, m.value()); + } + String stringVersion = CommandLine.getInstance().getSwitchValue("impl-version", "-1"); + int version = Integer.valueOf(stringVersion); + if (version != -1 && version < minWebLayerVersion) { + Log.i(TAG, + "Test " + frameworkMethod.getDeclaringClass().getName() + "#" + + frameworkMethod.getName() + " is not enabled at WebLayer version " + + version + "."); + return true; + } + return false; + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java new file mode 100644 index 0000000..7118d7d9 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java
@@ -0,0 +1,1752 @@ +// 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. + +package org.chromium.weblayer.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.SystemClock; +import android.util.Pair; +import android.webkit.WebResourceResponse; + +import androidx.test.filters.SmallTest; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.blink_public.common.BlinkFeatures; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.net.test.util.TestWebServer; +import org.chromium.weblayer.Browser; +import org.chromium.weblayer.LoadError; +import org.chromium.weblayer.NavigateParams; +import org.chromium.weblayer.Navigation; +import org.chromium.weblayer.NavigationCallback; +import org.chromium.weblayer.NavigationController; +import org.chromium.weblayer.NavigationState; +import org.chromium.weblayer.NewTabCallback; +import org.chromium.weblayer.Page; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.TabCallback; +import org.chromium.weblayer.TabListCallback; +import org.chromium.weblayer.WebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Example test that just starts the weblayer shell. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class NavigationTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + // URLs used for base tests. + private static final String URL1 = "data:text,foo"; + private static final String URL2 = "data:text,bar"; + private static final String URL3 = "data:text,baz"; + private static final String URL4 = "data:text,bat"; + private static final String ENGLISH_PAGE = "english_page.html"; + private static final String FRENCH_PAGE = "french_page.html"; + private static final String STREAM_URL = "https://doesntreallyexist123.com/bar"; + private static final String STREAM_HTML = "<html>foobar</html>"; + private static final String STREAM_INNER_BODY = "foobar"; + + // A URL with a custom scheme/host that is handled by WebLayer Shell. + private static final String CUSTOM_SCHEME_URL_WITH_DEFAULT_EXTERNAL_HANDLER = + "weblayer://weblayertest/intent"; + // An intent that sends an url with a custom scheme that is handled by WebLayer Shell. + private static final String INTENT_TO_CUSTOM_SCHEME_URL = + "intent://weblayertest/intent#Intent;scheme=weblayer;" + + "action=android.intent.action.VIEW;end"; + + // An IntentInterceptor that simply drops intents to ensure that intent launches don't interfere + // with running of tests. + private class IntentInterceptor implements InstrumentationActivity.IntentInterceptor { + @Override + public void interceptIntent(Intent intent, int requestCode, Bundle options) {} + } + + private <E extends Throwable> void assertThrows(Class<E> exceptionType, Runnable runnable) { + Throwable actualException = null; + try { + runnable.run(); + } catch (Throwable e) { + actualException = e; + } + assertNotNull("Exception not thrown", actualException); + assertEquals(exceptionType, actualException.getClass()); + } + + private class Callback extends NavigationCallback { + public class NavigationCallbackHelper extends CallbackHelper { + private Uri mUri; + private boolean mIsSameDocument; + private int mHttpStatusCode; + private Map<String, String> mResponseHeaders; + private List<Uri> mRedirectChain; + private @LoadError int mLoadError; + private @NavigationState int mNavigationState; + private boolean mIsKnownProtocol; + private boolean mIsPageInitiatedNavigation; + private boolean mIsServedFromBackForwardCache; + private boolean mIsFormSubmission; + private Uri mReferrer; + private Page mPage; + private int mNavigationEntryOffset; + private boolean mWasFetchedFromCache; + + public void notifyCalled(Navigation navigation) { + notifyCalled(navigation, false); + } + + public void notifyCalled(Navigation navigation, boolean getPage) { + mUri = navigation.getUri(); + mIsSameDocument = navigation.isSameDocument(); + mHttpStatusCode = navigation.getHttpStatusCode(); + mRedirectChain = navigation.getRedirectChain(); + mLoadError = navigation.getLoadError(); + mNavigationState = navigation.getState(); + mIsPageInitiatedNavigation = navigation.isPageInitiated(); + int majorVersion = TestThreadUtils.runOnUiThreadBlockingNoException( + () -> WebLayer.getSupportedMajorVersion(mActivityTestRule.getActivity())); + if (majorVersion >= 89) { + mIsKnownProtocol = navigation.isKnownProtocol(); + mIsServedFromBackForwardCache = navigation.isServedFromBackForwardCache(); + } + if (majorVersion >= 90) { + mIsFormSubmission = navigation.isFormSubmission(); + mReferrer = navigation.getReferrer(); + if (getPage) { + mPage = navigation.getPage(); + } + } + if (majorVersion >= 91) { + mResponseHeaders = navigation.getResponseHeaders(); + } + if (majorVersion >= 92) { + mNavigationEntryOffset = navigation.getNavigationEntryOffset(); + } + if (majorVersion >= 102) { + mWasFetchedFromCache = navigation.wasFetchedFromCache(); + } + notifyCalled(); + } + + public void assertCalledWith(int currentCallCount, String uri) throws TimeoutException { + waitForCallback(currentCallCount); + assertEquals(mUri.toString(), uri); + } + + public void assertCalledWith(int currentCallCount, String uri, boolean isSameDocument) + throws TimeoutException { + waitForCallback(currentCallCount); + assertEquals(mUri.toString(), uri); + assertEquals(mIsSameDocument, isSameDocument); + } + + public void assertCalledWith(int currentCallCount, List<Uri> redirectChain) + throws TimeoutException { + waitForCallback(currentCallCount); + assertEquals(mRedirectChain, redirectChain); + } + + public void assertCalledWith(int currentCallCount, String uri, @LoadError int loadError) + throws TimeoutException { + waitForCallback(currentCallCount); + assertEquals(mUri.toString(), uri); + assertEquals(mLoadError, loadError); + } + + public int getHttpStatusCode() { + return mHttpStatusCode; + } + + public Map<String, String> getResponseHeaders() { + return mResponseHeaders; + } + + @NavigationState + public int getNavigationState() { + return mNavigationState; + } + + public boolean isKnownProtocol() { + return mIsKnownProtocol; + } + + public boolean isServedFromBackForwardCache() { + return mIsServedFromBackForwardCache; + } + + public boolean isPageInitiated() { + return mIsPageInitiatedNavigation; + } + + public boolean isFormSubmission() { + return mIsFormSubmission; + } + + public Uri getReferrer() { + return mReferrer; + } + + public Page getPage() { + return mPage; + } + + public int getNavigationEntryOffset() { + return mNavigationEntryOffset; + } + + public boolean wasFetchedFromCache() { + return mWasFetchedFromCache; + } + } + + public class UriCallbackHelper extends CallbackHelper { + private Uri mUri; + + public void notifyCalled(Uri uri) { + mUri = uri; + notifyCalled(); + } + + public Uri getUri() { + return mUri; + } + } + + public class PageCallbackHelper extends CallbackHelper { + private Page mPage; + + public void notifyCalled(Page page) { + mPage = page; + notifyCalled(); + } + + public Page getPage() { + return mPage; + } + + public void assertCalledWith(int currentCallCount, Page page) throws TimeoutException { + waitForCallback(currentCallCount); + assertEquals(mPage, page); + } + } + + public class NavigationCallbackValueRecorder { + private List<String> mObservedValues = + Collections.synchronizedList(new ArrayList<String>()); + + public void recordValue(String parameter) { + mObservedValues.add(parameter); + } + + public List<String> getObservedValues() { + return mObservedValues; + } + + public void waitUntilValueObserved(String expectation) { + CriteriaHelper.pollInstrumentationThread( + () -> Criteria.checkThat(expectation, Matchers.isIn(mObservedValues))); + } + } + + public class FirstContentfulPaintCallbackHelper extends CallbackHelper { + private long mNavigationStartMillis; + private long mFirstContentfulPaintMs; + + public void notifyCalled(long navigationStartMillis, long firstContentfulPaintMs) { + mNavigationStartMillis = navigationStartMillis; + mFirstContentfulPaintMs = firstContentfulPaintMs; + notifyCalled(); + } + + public long getNavigationStartMillis() { + return mNavigationStartMillis; + } + + public long getFirstContentfulPaintMs() { + return mFirstContentfulPaintMs; + } + } + + public class LargestContentfulPaintCallbackHelper extends CallbackHelper { + private long mNavigationStartMillis; + private long mLargestContentfulPaintMs; + + public void notifyCalled(long navigationStartMillis, long largestContentfulPaintMs) { + mNavigationStartMillis = navigationStartMillis; + mLargestContentfulPaintMs = largestContentfulPaintMs; + notifyCalled(); + } + + public long getNavigationStartMillis() { + return mNavigationStartMillis; + } + + public long getLargestContentfulPaintMs() { + return mLargestContentfulPaintMs; + } + } + + public class PageLanguageDeterminedCallbackHelper extends CallbackHelper { + private Page mPage; + private String mLanguage; + + public void notifyCalled(Page page, String language) { + mPage = page; + mLanguage = language; + notifyCalled(); + } + + public Page getPage() { + return mPage; + } + + public String getLanguage() { + return mLanguage; + } + } + + public NavigationCallbackHelper onStartedCallback = new NavigationCallbackHelper(); + public NavigationCallbackHelper onRedirectedCallback = new NavigationCallbackHelper(); + public NavigationCallbackHelper onCompletedCallback = new NavigationCallbackHelper(); + public NavigationCallbackHelper onFailedCallback = new NavigationCallbackHelper(); + public NavigationCallbackValueRecorder loadStateChangedCallback = + new NavigationCallbackValueRecorder(); + public NavigationCallbackValueRecorder loadProgressChangedCallback = + new NavigationCallbackValueRecorder(); + public CallbackHelper onFirstContentfulPaintCallback = new CallbackHelper(); + public FirstContentfulPaintCallbackHelper onFirstContentfulPaint2Callback = + new FirstContentfulPaintCallbackHelper(); + public LargestContentfulPaintCallbackHelper onLargestContentfulPaintCallback = + new LargestContentfulPaintCallbackHelper(); + public UriCallbackHelper onOldPageNoLongerRenderedCallback = new UriCallbackHelper(); + public PageCallbackHelper onPageDestroyedCallback = new PageCallbackHelper(); + public PageLanguageDeterminedCallbackHelper onPageLanguageDeterminedCallback = + new PageLanguageDeterminedCallbackHelper(); + + @Override + public void onNavigationStarted(Navigation navigation) { + onStartedCallback.notifyCalled(navigation); + } + + @Override + public void onNavigationRedirected(Navigation navigation) { + onRedirectedCallback.notifyCalled(navigation); + } + + @Override + public void onNavigationCompleted(Navigation navigation) { + onCompletedCallback.notifyCalled(navigation, true); + } + + @Override + public void onNavigationFailed(Navigation navigation) { + onFailedCallback.notifyCalled(navigation); + } + + @Override + public void onFirstContentfulPaint() { + onFirstContentfulPaintCallback.notifyCalled(); + } + + @Override + public void onFirstContentfulPaint( + long navigationStartMillis, long firstContentfulPaintMs) { + onFirstContentfulPaint2Callback.notifyCalled( + navigationStartMillis, firstContentfulPaintMs); + } + + @Override + public void onLargestContentfulPaint( + long navigationStartMillis, long largestContentfulPaintMs) { + onLargestContentfulPaintCallback.notifyCalled( + navigationStartMillis, largestContentfulPaintMs); + } + + @Override + public void onOldPageNoLongerRendered(Uri newNavigationUri) { + onOldPageNoLongerRenderedCallback.notifyCalled(newNavigationUri); + } + + @Override + public void onLoadStateChanged(boolean isLoading, boolean shouldShowLoadingUi) { + loadStateChangedCallback.recordValue( + Boolean.toString(isLoading) + " " + Boolean.toString(shouldShowLoadingUi)); + } + + @Override + public void onLoadProgressChanged(double progress) { + loadProgressChangedCallback.recordValue( + progress == 1 ? "load complete" : "load started"); + } + + @Override + public void onPageDestroyed(Page page) { + onPageDestroyedCallback.notifyCalled(page); + } + + @Override + public void onPageLanguageDetermined(Page page, String language) { + onPageLanguageDeterminedCallback.notifyCalled(page, language); + } + } + + private final Callback mCallback = new Callback(); + + @Test + @SmallTest + public void testNavigationEvents() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + + setNavigationCallback(activity); + int curStartedCount = mCallback.onStartedCallback.getCallCount(); + int curCompletedCount = mCallback.onCompletedCallback.getCallCount(); + int curOnFirstContentfulPaintCount = + mCallback.onFirstContentfulPaintCallback.getCallCount(); + + mActivityTestRule.navigateAndWait(URL2); + + mCallback.onStartedCallback.assertCalledWith(curStartedCount, URL2); + mCallback.onCompletedCallback.assertCalledWith(curCompletedCount, URL2); + mCallback.onFirstContentfulPaintCallback.waitForCallback(curOnFirstContentfulPaintCount); + assertEquals(mCallback.onCompletedCallback.getHttpStatusCode(), 200); + } + + @Test + @SmallTest + public void testOldPageNoLongerRendered() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + setNavigationCallback(activity); + + int renderedCount = mCallback.onOldPageNoLongerRenderedCallback.getCallCount(); + mActivityTestRule.navigateAndWait(URL2); + mCallback.onOldPageNoLongerRenderedCallback.waitForCallback(renderedCount); + assertEquals(Uri.parse(URL2), mCallback.onOldPageNoLongerRenderedCallback.getUri()); + } + + @Test + @SmallTest + public void testLoadStateUpdates() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); + setNavigationCallback(activity); + mActivityTestRule.navigateAndWait(URL1); + + /* Wait until the NavigationCallback is notified of load completion. */ + mCallback.loadStateChangedCallback.waitUntilValueObserved("false false"); + mCallback.loadProgressChangedCallback.waitUntilValueObserved("load complete"); + + /* Verify that the NavigationCallback was notified of load progress /before/ load + * completion. + */ + int finishStateIndex = + mCallback.loadStateChangedCallback.getObservedValues().indexOf("false false"); + int finishProgressIndex = + mCallback.loadProgressChangedCallback.getObservedValues().indexOf("load complete"); + int startStateIndex = + mCallback.loadStateChangedCallback.getObservedValues().lastIndexOf("true true"); + int startProgressIndex = + mCallback.loadProgressChangedCallback.getObservedValues().lastIndexOf( + "load started"); + + assertNotEquals(startStateIndex, -1); + assertNotEquals(startProgressIndex, -1); + assertNotEquals(finishStateIndex, -1); + assertNotEquals(finishProgressIndex, -1); + + assertTrue(startStateIndex < finishStateIndex); + assertTrue(startProgressIndex < finishProgressIndex); + } + + @Test + @SmallTest + public void testReplace() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + setNavigationCallback(activity); + + final NavigateParams params = + new NavigateParams.Builder().setShouldReplaceCurrentEntry(true).build(); + navigateAndWaitForCompletion(URL2, + () + -> activity.getTab().getNavigationController().navigate( + Uri.parse(URL2), params)); + runOnUiThreadBlocking(() -> { + NavigationController navigationController = activity.getTab().getNavigationController(); + assertFalse(navigationController.canGoForward()); + assertFalse(navigationController.canGoBack()); + assertEquals(1, navigationController.getNavigationListSize()); + }); + + // Verify getter works as expected. + assertTrue(params.getShouldReplaceCurrentEntry()); + + // Verify that a default NavigateParams does not replace. + final NavigateParams params2 = new NavigateParams(); + navigateAndWaitForCompletion(URL3, + () + -> activity.getTab().getNavigationController().navigate( + Uri.parse(URL3), params2)); + runOnUiThreadBlocking(() -> { + NavigationController navigationController = activity.getTab().getNavigationController(); + assertFalse(navigationController.canGoForward()); + assertTrue(navigationController.canGoBack()); + assertEquals(2, navigationController.getNavigationListSize()); + }); + } + + @Test + @SmallTest + public void testGoBackAndForward() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + setNavigationCallback(activity); + + mActivityTestRule.navigateAndWait(URL2); + mActivityTestRule.navigateAndWait(URL3); + + NavigationController navigationController = + runOnUiThreadBlocking(() -> activity.getTab().getNavigationController()); + + navigateAndWaitForCompletion(URL2, () -> { + assertTrue(navigationController.canGoBack()); + navigationController.goBack(); + }); + + navigateAndWaitForCompletion(URL1, () -> { + assertTrue(navigationController.canGoBack()); + navigationController.goBack(); + }); + + navigateAndWaitForCompletion(URL2, () -> { + assertFalse(navigationController.canGoBack()); + assertTrue(navigationController.canGoForward()); + navigationController.goForward(); + }); + + navigateAndWaitForCompletion(URL3, () -> { + assertTrue(navigationController.canGoForward()); + navigationController.goForward(); + }); + + runOnUiThreadBlocking(() -> { assertFalse(navigationController.canGoForward()); }); + } + + @Test + @SmallTest + public void testGoToIndex() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + setNavigationCallback(activity); + + mActivityTestRule.navigateAndWait(URL2); + mActivityTestRule.navigateAndWait(URL3); + mActivityTestRule.navigateAndWait(URL4); + + // Navigate back to the 2nd url. + assertEquals(URL2, goToIndexAndReturnUrl(activity.getTab(), 1)); + + // Navigate forwards to the 4th url. + assertEquals(URL4, goToIndexAndReturnUrl(activity.getTab(), 3)); + } + + @Test + @SmallTest + public void testGetNavigationEntryTitle() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl( + "data:text/html,<head><title>Page A</title></head>"); + setNavigationCallback(activity); + + mActivityTestRule.navigateAndWait("data:text/html,<head><title>Page B</title></head>"); + mActivityTestRule.navigateAndWait("data:text/html,<head><title>Page C</title></head>"); + + runOnUiThreadBlocking(() -> { + NavigationController navigationController = activity.getTab().getNavigationController(); + assertEquals("Page A", navigationController.getNavigationEntryTitle(0)); + assertEquals("Page B", navigationController.getNavigationEntryTitle(1)); + assertEquals("Page C", navigationController.getNavigationEntryTitle(2)); + }); + } + + @Test + @SmallTest + public void testSameDocument() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + setNavigationCallback(activity); + + int curCompletedCount = mCallback.onCompletedCallback.getCallCount(); + + mActivityTestRule.executeScriptSync( + "history.pushState(null, '', '#bar');", true /* useSeparateIsolate */); + + mCallback.onCompletedCallback.assertCalledWith( + curCompletedCount, "data:text,foo#bar", true); + } + + @Test + @SmallTest + public void testReload() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + setNavigationCallback(activity); + + navigateAndWaitForCompletion( + URL1, () -> { activity.getTab().getNavigationController().reload(); }); + } + + @Test + @SmallTest + public void testStop() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + setNavigationCallback(activity); + + int curFailedCount = mCallback.onFailedCallback.getCallCount(); + + runOnUiThreadBlocking(() -> { + NavigationController navigationController = activity.getTab().getNavigationController(); + navigationController.registerNavigationCallback(new NavigationCallback() { + @Override + public void onNavigationStarted(Navigation navigation) { + navigationController.stop(); + } + }); + navigationController.navigate(Uri.parse(URL2)); + }); + + mCallback.onFailedCallback.assertCalledWith(curFailedCount, URL2); + } + + @Test + @SmallTest + public void testRedirect() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + setNavigationCallback(activity); + + int curRedirectedCount = mCallback.onRedirectedCallback.getCallCount(); + + String finalUrl = mActivityTestRule.getTestServer().getURL("/echo"); + String url = mActivityTestRule.getTestServer().getURL("/server-redirect?" + finalUrl); + navigateAndWaitForCompletion(finalUrl, + () -> { activity.getTab().getNavigationController().navigate(Uri.parse(url)); }); + + mCallback.onRedirectedCallback.assertCalledWith( + curRedirectedCount, Arrays.asList(Uri.parse(url), Uri.parse(finalUrl))); + } + + /** + * This test verifies that calling getPage() from within onNavigationFailed for a + * navigation that results in an error page returns a non-null Page object, and that an + * onPageDestroyed() callback is triggered for that page when the user navigates away. + */ + @MinWebLayerVersion(93) + @Test + @SmallTest + public void testPageCallbacksForNavigationResultingInErrorPage() throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); + CallbackHelper navigationFailedCallbackHelper = new CallbackHelper(); + CallbackHelper pageDestroyedCallbackHelper = new CallbackHelper(); + final Page[] pageForFailedNavigation = {null}; + runOnUiThreadBlocking(() -> { + NavigationController navigationController = activity.getTab().getNavigationController(); + navigationController.registerNavigationCallback(new NavigationCallback() { + @Override + public void onNavigationFailed(Navigation navigation) { + assertTrue(navigation.isErrorPage()); + pageForFailedNavigation[0] = navigation.getPage(); + assertNotNull(pageForFailedNavigation[0]); + navigationFailedCallbackHelper.notifyCalled(); + } + @Override + public void onPageDestroyed(Page page) { + assertEquals(pageForFailedNavigation[0], page); + navigationController.unregisterNavigationCallback(this); + pageDestroyedCallbackHelper.notifyCalled(); + } + }); + }); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + // Do a navigation that will result in an error page. + activity.getTab().getNavigationController().navigate( + Uri.parse("http://localhost:7/non_existent")); + }); + navigationFailedCallbackHelper.waitForFirst(); + + TestThreadUtils.runOnUiThreadBlocking( + () -> { activity.getTab().getNavigationController().navigate(Uri.parse(URL1)); }); + pageDestroyedCallbackHelper.waitForFirst(); + } + + /** + * This is a regression test for crbug.com/1233480, adapted for a change in + * //content to have such navigations commit rather than fail. It also + * should not crash nor throw an exception. + */ + @MinWebLayerVersion(98) + @Test + @SmallTest + @CommandLineFlags.Add("enable-features=" + BlinkFeatures.INITIAL_NAVIGATION_ENTRY) + public void testInitialRendererInitiatedNavigationToAboutBlankSucceeds() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + + // Setup a callback for when the navigation in a new tab fails. + CallbackHelper callbackHelper = new CallbackHelper(); + NewTabCallback newTabCallback = new NewTabCallback() { + @Override + public void onNewTab(Tab tab, int mode) { + NavigationController navigationController = tab.getNavigationController(); + navigationController.registerNavigationCallback(new NavigationCallback() { + @Override + public void onNavigationCompleted(Navigation navigation) { + assertEquals(NavigationState.COMPLETE, navigation.getState()); + // There should be a valid page for this navigation. + assertNotNull(navigation.getPage()); + navigationController.unregisterNavigationCallback(this); + callbackHelper.notifyCalled(); + } + }); + } + }; + TestThreadUtils.runOnUiThreadBlocking( + () -> { activity.getBrowser().getActiveTab().setNewTabCallback(newTabCallback); }); + + // Click on the document to invoke window.open(), which results in a renderer-initiated + // navigation to about:blank in a new tab. + mActivityTestRule.executeScriptSync( + "document.onclick = () => window.open();", true /* useSeparateIsolate */); + EventUtils.simulateTouchCenterOfView(activity.getWindow().getDecorView()); + + callbackHelper.waitForFirst(); + } + + @Test + @SmallTest + public void testNavigationList() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + setNavigationCallback(activity); + + mActivityTestRule.navigateAndWait(URL2); + mActivityTestRule.navigateAndWait(URL3); + + NavigationController navigationController = + runOnUiThreadBlocking(() -> activity.getTab().getNavigationController()); + + runOnUiThreadBlocking(() -> { + assertEquals(3, navigationController.getNavigationListSize()); + assertEquals(2, navigationController.getNavigationListCurrentIndex()); + assertEquals(URL1, navigationController.getNavigationEntryDisplayUri(0).toString()); + assertEquals(URL2, navigationController.getNavigationEntryDisplayUri(1).toString()); + assertEquals(URL3, navigationController.getNavigationEntryDisplayUri(2).toString()); + }); + + navigateAndWaitForCompletion(URL2, () -> { navigationController.goBack(); }); + + runOnUiThreadBlocking(() -> { + assertEquals(3, navigationController.getNavigationListSize()); + assertEquals(1, navigationController.getNavigationListCurrentIndex()); + }); + } + + @Test + @SmallTest + public void testLoadError() throws Exception { + String url = mActivityTestRule.getTestDataURL("non_empty404.html"); + + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + setNavigationCallback(activity); + + int curCompletedCount = mCallback.onCompletedCallback.getCallCount(); + + // navigateAndWait() expects a success code, so it won't work here. + runOnUiThreadBlocking( + () -> { activity.getTab().getNavigationController().navigate(Uri.parse(url)); }); + + mCallback.onCompletedCallback.assertCalledWith( + curCompletedCount, url, LoadError.HTTP_CLIENT_ERROR); + assertEquals(mCallback.onCompletedCallback.getHttpStatusCode(), 404); + assertEquals(mCallback.onCompletedCallback.getNavigationState(), NavigationState.COMPLETE); + } + + @MinWebLayerVersion(89) + @Test + @SmallTest + public void testIsKnownProtocol() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + IntentInterceptor intentInterceptor = new IntentInterceptor(); + activity.setIntentInterceptor(intentInterceptor); + setNavigationCallback(activity); + + // Test various known protocol cases. + String httpUrl = mActivityTestRule.getTestDataURL("simple_page.html"); + mActivityTestRule.navigateAndWait(httpUrl); + assertEquals(true, mCallback.onStartedCallback.isKnownProtocol()); + assertEquals(true, mCallback.onCompletedCallback.isKnownProtocol()); + + mActivityTestRule.navigateAndWait("about:blank"); + assertEquals(true, mCallback.onStartedCallback.isKnownProtocol()); + assertEquals(true, mCallback.onCompletedCallback.isKnownProtocol()); + + String dataUrl = "data:text,foo"; + mActivityTestRule.navigateAndWait(dataUrl); + assertEquals(true, mCallback.onStartedCallback.isKnownProtocol()); + assertEquals(true, mCallback.onCompletedCallback.isKnownProtocol()); + + // Test external protocol cases. + mActivityTestRule.navigateAndWaitForFailure(activity.getTab(), INTENT_TO_CUSTOM_SCHEME_URL, + /*waitForPaint=*/false); + assertEquals(false, mCallback.onStartedCallback.isKnownProtocol()); + assertEquals(false, mCallback.onFailedCallback.isKnownProtocol()); + + mActivityTestRule.navigateAndWaitForFailure(activity.getTab(), + CUSTOM_SCHEME_URL_WITH_DEFAULT_EXTERNAL_HANDLER, + /*waitForPaint=*/false); + assertEquals(false, mCallback.onStartedCallback.isKnownProtocol()); + assertEquals(false, mCallback.onFailedCallback.isKnownProtocol()); + } + + @Test + @SmallTest + public void testRepostConfirmation() throws Exception { + // Load a page with a form. + InstrumentationActivity activity = + mActivityTestRule.launchShellWithUrl(mActivityTestRule.getTestDataURL("form.html")); + assertNotNull(activity); + setNavigationCallback(activity); + + // Touch the page; this should submit the form. + int currentCallCount = mCallback.onCompletedCallback.getCallCount(); + EventUtils.simulateTouchCenterOfView(activity.getWindow().getDecorView()); + String targetUrl = mActivityTestRule.getTestDataURL("simple_page.html"); + mCallback.onCompletedCallback.assertCalledWith(currentCallCount, targetUrl); + + // Make sure a tab modal shows after we attempt a reload. + Boolean isTabModalShowingResult[] = new Boolean[1]; + CallbackHelper callbackHelper = new CallbackHelper(); + runOnUiThreadBlocking(() -> { + Tab tab = activity.getTab(); + TabCallback callback = new TabCallback() { + @Override + public void onTabModalStateChanged(boolean isTabModalShowing) { + isTabModalShowingResult[0] = isTabModalShowing; + callbackHelper.notifyCalled(); + } + }; + tab.registerTabCallback(callback); + tab.getNavigationController().reload(); + }); + + callbackHelper.waitForFirst(); + assertTrue(isTabModalShowingResult[0]); + } + + private void setNavigationCallback(InstrumentationActivity activity) { + runOnUiThreadBlocking( + () + -> activity.getTab().getNavigationController().registerNavigationCallback( + mCallback)); + } + + private void registerNavigationCallback(NavigationCallback callback) { + runOnUiThreadBlocking(() + -> mActivityTestRule.getActivity() + .getTab() + .getNavigationController() + .registerNavigationCallback(callback)); + } + + private void unregisterNavigationCallback(NavigationCallback callback) { + runOnUiThreadBlocking(() + -> mActivityTestRule.getActivity() + .getTab() + .getNavigationController() + .unregisterNavigationCallback(callback)); + } + + private void navigateAndWaitForCompletion(String expectedUrl, Runnable navigateRunnable) + throws Exception { + int currentCallCount = mCallback.onCompletedCallback.getCallCount(); + runOnUiThreadBlocking(navigateRunnable); + mCallback.onCompletedCallback.assertCalledWith(currentCallCount, expectedUrl); + } + + private String goToIndexAndReturnUrl(Tab tab, int index) throws Exception { + NavigationController navigationController = + runOnUiThreadBlocking(() -> tab.getNavigationController()); + + final BoundedCountDownLatch navigationComplete = new BoundedCountDownLatch(1); + final AtomicReference<String> navigationUrl = new AtomicReference<String>(); + NavigationCallback navigationCallback = new NavigationCallback() { + @Override + public void onNavigationCompleted(Navigation navigation) { + navigationComplete.countDown(); + navigationUrl.set(navigation.getUri().toString()); + } + }; + + runOnUiThreadBlocking(() -> { + navigationController.registerNavigationCallback(navigationCallback); + navigationController.goToIndex(index); + }); + + navigationComplete.timedAwait(); + + runOnUiThreadBlocking( + () -> { navigationController.unregisterNavigationCallback(navigationCallback); }); + + return navigationUrl.get(); + } + + @Test + @SmallTest + public void testStopFromOnNavigationStarted() throws Exception { + final InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); + final BoundedCountDownLatch doneLatch = new BoundedCountDownLatch(1); + NavigationCallback navigationCallback = new NavigationCallback() { + @Override + public void onNavigationStarted(Navigation navigation) { + activity.getTab().getNavigationController().stop(); + doneLatch.countDown(); + } + }; + runOnUiThreadBlocking(() -> { + NavigationController controller = activity.getTab().getNavigationController(); + controller.registerNavigationCallback(navigationCallback); + controller.navigate(Uri.parse(URL1)); + }); + doneLatch.timedAwait(); + } + + // NavigationCallback implementation that sets a header in either start or redirect. + private static final class HeaderSetter extends NavigationCallback { + private final String mName; + private final String mValue; + private final boolean mInStart; + public boolean mGotIllegalArgumentException; + + HeaderSetter(String name, String value, boolean inStart) { + mName = name; + mValue = value; + mInStart = inStart; + } + + @Override + public void onNavigationStarted(Navigation navigation) { + if (mInStart) applyHeader(navigation); + } + + @Override + public void onNavigationRedirected(Navigation navigation) { + if (!mInStart) applyHeader(navigation); + } + + private void applyHeader(Navigation navigation) { + try { + navigation.setRequestHeader(mName, mValue); + } catch (IllegalArgumentException e) { + mGotIllegalArgumentException = true; + } + } + } + + @Test + @SmallTest + public void testSetRequestHeaderInStart() throws Exception { + TestWebServer testServer = TestWebServer.start(); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); + String headerName = "header"; + String headerValue = "value"; + HeaderSetter setter = new HeaderSetter(headerName, headerValue, true); + registerNavigationCallback(setter); + String url = testServer.setResponse("/ok.html", "<html>ok</html>", null); + mActivityTestRule.navigateAndWait(url); + assertFalse(setter.mGotIllegalArgumentException); + assertEquals(headerValue, testServer.getLastRequest("/ok.html").headerValue(headerName)); + } + + @Test + @SmallTest + public void testSetRequestHeaderInRedirect() throws Exception { + TestWebServer testServer = TestWebServer.start(); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); + String headerName = "header"; + String headerValue = "value"; + HeaderSetter setter = new HeaderSetter(headerName, headerValue, false); + registerNavigationCallback(setter); + // The destination of the redirect. + String finalUrl = testServer.setResponse("/ok.html", "<html>ok</html>", null); + // The url that redirects to |finalUrl|. + String redirectingUrl = testServer.setRedirect("/redirect.html", finalUrl); + Tab tab = mActivityTestRule.getActivity().getTab(); + NavigationWaiter waiter = new NavigationWaiter(finalUrl, tab, false, false); + TestThreadUtils.runOnUiThreadBlocking( + () -> { tab.getNavigationController().navigate(Uri.parse(redirectingUrl)); }); + waiter.waitForNavigation(); + assertFalse(setter.mGotIllegalArgumentException); + assertEquals(headerValue, testServer.getLastRequest("/ok.html").headerValue(headerName)); + } + + @Test + @SmallTest + public void testSetRequestHeaderThrowsExceptionInCompleted() throws Exception { + mActivityTestRule.launchShellWithUrl(null); + boolean gotCompleted[] = new boolean[1]; + NavigationCallback navigationCallback = new NavigationCallback() { + @Override + public void onNavigationCompleted(Navigation navigation) { + gotCompleted[0] = true; + boolean gotException = false; + try { + navigation.setRequestHeader("name", "value"); + } catch (IllegalStateException e) { + gotException = true; + } + assertTrue(gotException); + } + }; + registerNavigationCallback(navigationCallback); + mActivityTestRule.navigateAndWait(URL1); + assertTrue(gotCompleted[0]); + } + + @Test + @SmallTest + public void testSetRequestHeaderThrowsExceptionWithInvalidValue() throws Exception { + mActivityTestRule.launchShellWithUrl(null); + HeaderSetter setter = new HeaderSetter("name", "\0", true); + registerNavigationCallback(setter); + mActivityTestRule.navigateAndWait(URL1); + assertTrue(setter.mGotIllegalArgumentException); + } + + // NavigationCallback implementation that sets the user-agent string in onNavigationStarted(). + private static final class UserAgentSetter extends NavigationCallback { + private final String mValue; + public boolean mGotIllegalStateException; + + UserAgentSetter(String value) { + mValue = value; + } + + @Override + public void onNavigationStarted(Navigation navigation) { + try { + navigation.setUserAgentString(mValue); + } catch (IllegalStateException e) { + mGotIllegalStateException = true; + } + } + } + + @Test + @SmallTest + public void testSetUserAgentString() throws Exception { + TestWebServer testServer = TestWebServer.start(); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); + String customUserAgent = "custom-ua"; + UserAgentSetter setter = new UserAgentSetter(customUserAgent); + registerNavigationCallback(setter); + String url = testServer.setResponse("/ok.html", "<html>ok</html>", null); + mActivityTestRule.navigateAndWait(url); + String actualUserAgent = testServer.getLastRequest("/ok.html").headerValue("User-Agent"); + assertEquals(customUserAgent, actualUserAgent); + } + + @Test + @SmallTest + @MinWebLayerVersion(88) + public void testCantUsePerNavigationAndDesktopMode() throws Exception { + TestWebServer testServer = TestWebServer.start(); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); + UserAgentSetter setter = new UserAgentSetter("foo"); + registerNavigationCallback(setter); + String url = testServer.setResponse("/ok.html", "<html>ok</html>", null); + runOnUiThreadBlocking(() -> { activity.getTab().setDesktopUserAgentEnabled(true); }); + mActivityTestRule.navigateAndWait(url); + assertTrue(setter.mGotIllegalStateException); + } + + @Test + @SmallTest + @MinWebLayerVersion(88) + public void testDesktopMode() throws Exception { + TestWebServer testServer = TestWebServer.start(); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + String url = testServer.setResponse("/ok.html", "<html>ok</html>", null); + runOnUiThreadBlocking(() -> { activity.getTab().setDesktopUserAgentEnabled(true); }); + mActivityTestRule.navigateAndWait(url); + String actualUserAgent = testServer.getLastRequest("/ok.html").headerValue("User-Agent"); + assertFalse(actualUserAgent.contains("Android")); + } + + @Test + @SmallTest + @MinWebLayerVersion(88) + public void testDesktopModeSticks() throws Exception { + TestWebServer testServer = TestWebServer.start(); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + String url = testServer.setResponse("/ok.html", "<html>ok</html>", null); + String url2 = testServer.setResponse("/ok2.html", "<html>ok</html>", null); + runOnUiThreadBlocking(() -> { activity.getTab().setDesktopUserAgentEnabled(true); }); + mActivityTestRule.navigateAndWait(url); + mActivityTestRule.navigateAndWait(url2); + String actualUserAgent = testServer.getLastRequest("/ok2.html").headerValue("User-Agent"); + assertFalse(actualUserAgent.contains("Android")); + } + + @Test + @SmallTest + @MinWebLayerVersion(88) + public void testDesktopModeGetter() throws Exception { + TestWebServer testServer = TestWebServer.start(); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); + setNavigationCallback(activity); + + UserAgentSetter setter = new UserAgentSetter("foo"); + registerNavigationCallback(setter); + mActivityTestRule.navigateAndWait(URL1); + unregisterNavigationCallback(setter); + runOnUiThreadBlocking( + () -> { assertFalse(activity.getTab().isDesktopUserAgentEnabled()); }); + + runOnUiThreadBlocking(() -> { activity.getTab().setDesktopUserAgentEnabled(true); }); + mActivityTestRule.navigateAndWait(URL2); + runOnUiThreadBlocking(() -> { assertTrue(activity.getTab().isDesktopUserAgentEnabled()); }); + + navigateAndWaitForCompletion( + URL1, () -> activity.getTab().getNavigationController().goBack()); + runOnUiThreadBlocking( + () -> { assertFalse(activity.getTab().isDesktopUserAgentEnabled()); }); + } + + @Test + @SmallTest + public void testSkippedNavigationEntry() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + setNavigationCallback(activity); + + int curCompletedCount = mCallback.onCompletedCallback.getCallCount(); + mActivityTestRule.executeScriptSync( + "history.pushState(null, '', '#foo');", true /* useSeparateIsolate */); + mCallback.onCompletedCallback.assertCalledWith(curCompletedCount, URL1 + "#foo", true); + + curCompletedCount = mCallback.onCompletedCallback.getCallCount(); + mActivityTestRule.executeScriptSync( + "history.pushState(null, '', '#bar');", true /* useSeparateIsolate */); + mCallback.onCompletedCallback.assertCalledWith(curCompletedCount, URL1 + "#bar", true); + + runOnUiThreadBlocking(() -> { + NavigationController navigationController = activity.getTab().getNavigationController(); + int currentIndex = navigationController.getNavigationListCurrentIndex(); + // Should skip the two previous same document entries, but not the most recent. + assertFalse(navigationController.isNavigationEntrySkippable(currentIndex)); + assertTrue(navigationController.isNavigationEntrySkippable(currentIndex - 1)); + assertTrue(navigationController.isNavigationEntrySkippable(currentIndex - 2)); + }); + } + + @Test + @SmallTest + public void testIndexOutOfBounds() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); + runOnUiThreadBlocking(() -> { + NavigationController controller = activity.getTab().getNavigationController(); + assertIndexOutOfBoundsException(() -> controller.goBack()); + assertIndexOutOfBoundsException(() -> controller.goForward()); + assertIndexOutOfBoundsException(() -> controller.goToIndex(10)); + assertIndexOutOfBoundsException(() -> controller.getNavigationEntryDisplayUri(10)); + assertIndexOutOfBoundsException(() -> controller.getNavigationEntryTitle(10)); + assertIndexOutOfBoundsException(() -> controller.isNavigationEntrySkippable(10)); + }); + } + + private static void assertIndexOutOfBoundsException(Runnable runnable) { + try { + runnable.run(); + } catch (IndexOutOfBoundsException e) { + // Expected exception. + return; + } + Assert.fail("Expected IndexOutOfBoundsException."); + } + + @Test + @SmallTest + public void testPageInitiated() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); + setNavigationCallback(activity); + String initialUrl = mActivityTestRule.getTestDataURL("simple_page4.html"); + mActivityTestRule.navigateAndWait(initialUrl); + String refreshUrl = mActivityTestRule.getTestDataURL("simple_page.html"); + mCallback.onCompletedCallback.assertCalledWith( + mCallback.onCompletedCallback.getCallCount(), refreshUrl); + assertTrue(mCallback.onCompletedCallback.isPageInitiated()); + } + + @Test + @SmallTest + public void testPageInitiatedFromClient() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + setNavigationCallback(activity); + mActivityTestRule.navigateAndWait(URL2); + assertFalse(mCallback.onStartedCallback.isPageInitiated()); + } + + // Verifies the following sequence doesn't crash: + // 1. create a new background tab. + // 2. show modal dialog. + // 3. destroy tab with modal dialog. + // 4. switch to background tab created in step 1. + // This is a regression test for https://crbug.com/1121388. + @Test + @SmallTest + public void testDestroyTabWithModalDialog() throws Exception { + // Load a page with a form. + InstrumentationActivity activity = + mActivityTestRule.launchShellWithUrl(mActivityTestRule.getTestDataURL("form.html")); + assertNotNull(activity); + setNavigationCallback(activity); + + // Touch the page; this should submit the form. + int currentCallCount = mCallback.onCompletedCallback.getCallCount(); + EventUtils.simulateTouchCenterOfView(activity.getWindow().getDecorView()); + String targetUrl = mActivityTestRule.getTestDataURL("simple_page.html"); + mCallback.onCompletedCallback.assertCalledWith(currentCallCount, targetUrl); + + Tab secondTab = runOnUiThreadBlocking(() -> activity.getTab().getBrowser().createTab()); + // Make sure a tab modal shows after we attempt a reload. + Boolean isTabModalShowingResult[] = new Boolean[1]; + CallbackHelper callbackHelper = new CallbackHelper(); + runOnUiThreadBlocking(() -> { + Tab tab = activity.getTab(); + Browser browser = tab.getBrowser(); + TabCallback callback = new TabCallback() { + @Override + public void onTabModalStateChanged(boolean isTabModalShowing) { + tab.unregisterTabCallback(this); + isTabModalShowingResult[0] = isTabModalShowing; + callbackHelper.notifyCalled(); + } + }; + tab.registerTabCallback(callback); + + browser.registerTabListCallback(new TabListCallback() { + @Override + public void onTabRemoved(Tab tab) { + browser.unregisterTabListCallback(this); + browser.setActiveTab(secondTab); + } + }); + tab.getNavigationController().reload(); + }); + + callbackHelper.waitForFirst(); + runOnUiThreadBlocking(() -> { + Tab tab = activity.getTab(); + tab.getBrowser().destroyTab(tab); + }); + } + + /** + * This test verifies calling destroyTab() from within onNavigationFailed doesn't crash. + */ + @Test + @SmallTest + public void testDestroyTabInNavigationFailed() throws Throwable { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); + CallbackHelper callbackHelper = new CallbackHelper(); + runOnUiThreadBlocking(() -> { + NavigationController navigationController = activity.getTab().getNavigationController(); + navigationController.registerNavigationCallback(new NavigationCallback() { + @Override + public void onNavigationFailed(Navigation navigation) { + navigationController.unregisterNavigationCallback(this); + Tab tab = activity.getTab(); + tab.getBrowser().destroyTab(tab); + callbackHelper.notifyCalled(); + } + }); + }); + TestThreadUtils.runOnUiThreadBlocking(() -> { + activity.getTab().getNavigationController().navigate( + Uri.parse("http://localhost:7/non_existent")); + }); + callbackHelper.waitForFirst(); + } + + private void navigateToStream(InstrumentationActivity activity, String mimeType, + String cacheControl, String html) throws Exception { + int curOnFirstContentfulPaintCount = + mCallback.onFirstContentfulPaintCallback.getCallCount(); + InputStream stream = new ByteArrayInputStream(html.getBytes(StandardCharsets.UTF_8)); + WebResourceResponse response = new WebResourceResponse(mimeType, "UTF-8", stream); + if (cacheControl != null) { + Map<String, String> headers = new HashMap<>(); + headers.put("Cache-Control", cacheControl); + response.setResponseHeaders(headers); + } + + final NavigateParams params = new NavigateParams.Builder().setResponse(response).build(); + navigateAndWaitForCompletion(STREAM_URL, + () + -> activity.getTab().getNavigationController().navigate( + Uri.parse(STREAM_URL), params)); + mCallback.onFirstContentfulPaintCallback.waitForCallback(curOnFirstContentfulPaintCount); + } + + private void navigateToStream(InstrumentationActivity activity, String mimeType, + String cacheControl) throws Exception { + navigateToStream(activity, mimeType, cacheControl, STREAM_HTML); + } + + private void assertStreamContent() throws Exception { + assertEquals(STREAM_INNER_BODY, + mActivityTestRule.executeScriptAndExtractString("document.body.innerText")); + } + + @Test + @SmallTest + public void testWebResponse() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + // The code asserts that when InputStreams are used that the stock URL bar is not visible. + TestThreadUtils.runOnUiThreadBlocking(() -> { activity.getBrowser().setTopView(null); }); + setNavigationCallback(activity); + + navigateToStream(activity, "text/html", null); + assertStreamContent(); + } + + @Test + @SmallTest + public void testWebResponseMimeSniff() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + TestThreadUtils.runOnUiThreadBlocking(() -> { activity.getBrowser().setTopView(null); }); + setNavigationCallback(activity); + + navigateToStream(activity, "", null); + assertStreamContent(); + } + + @Test + @SmallTest + public void testWebResponseNoCacheControl() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + TestThreadUtils.runOnUiThreadBlocking(() -> { activity.getBrowser().setTopView(null); }); + setNavigationCallback(activity); + + navigateToStream(activity, "text/html", null); + + mActivityTestRule.navigateAndWait(URL1); + + int curFailedCount = mCallback.onFailedCallback.getCallCount(); + runOnUiThreadBlocking(() -> { activity.getTab().getNavigationController().goBack(); }); + mCallback.onFailedCallback.assertCalledWith( + curFailedCount, STREAM_URL, LoadError.CONNECTIVITY_ERROR); + } + + @Test + @SmallTest + public void testWebResponseCached() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + TestThreadUtils.runOnUiThreadBlocking(() -> { activity.getBrowser().setTopView(null); }); + setNavigationCallback(activity); + + navigateToStream(activity, "text/html", "private, max-age=60"); + + // Now check that the data can be reused from the cache if it had the correct headers. + mActivityTestRule.navigateAndWait(URL1); + int curOnFirstContentfulPaintCount = + mCallback.onFirstContentfulPaintCallback.getCallCount(); + navigateAndWaitForCompletion( + STREAM_URL, () -> { activity.getTab().getNavigationController().goBack(); }); + mCallback.onFirstContentfulPaintCallback.waitForCallback(curOnFirstContentfulPaintCount); + assertStreamContent(); + } + + @Test + @SmallTest + public void testWebResponseCachedWithSniffedMimeType() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + TestThreadUtils.runOnUiThreadBlocking(() -> { activity.getBrowser().setTopView(null); }); + setNavigationCallback(activity); + + navigateToStream(activity, "", "private, max-age=60"); + + mActivityTestRule.navigateAndWait(URL1); + + int curOnFirstContentfulPaintCount = + mCallback.onFirstContentfulPaintCallback.getCallCount(); + navigateAndWaitForCompletion( + STREAM_URL, () -> { activity.getTab().getNavigationController().goBack(); }); + mCallback.onFirstContentfulPaintCallback.waitForCallback(curOnFirstContentfulPaintCount); + assertStreamContent(); + } + + @DisabledTest(message = "https://crbug.com/1271989") + @Test + @SmallTest + public void testWebResponseNoStore() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + TestThreadUtils.runOnUiThreadBlocking(() -> { activity.getBrowser().setTopView(null); }); + setNavigationCallback(activity); + + navigateToStream(activity, "text/html", "no-store"); + + mActivityTestRule.navigateAndWait(URL1); + + int curFailedCount = mCallback.onFailedCallback.getCallCount(); + runOnUiThreadBlocking(() -> { activity.getTab().getNavigationController().goBack(); }); + mCallback.onFailedCallback.assertCalledWith( + curFailedCount, STREAM_URL, LoadError.CONNECTIVITY_ERROR); + } + + @DisabledTest(message = "https://crbug.com/1238151") + @Test + @SmallTest + public void testWebResponseExpired() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + TestThreadUtils.runOnUiThreadBlocking(() -> { activity.getBrowser().setTopView(null); }); + setNavigationCallback(activity); + + navigateToStream(activity, "text/html", "private, max-age=2"); + + Thread.sleep(5000); + + mActivityTestRule.navigateAndWait(URL1); + + int curFailedCount = mCallback.onFailedCallback.getCallCount(); + runOnUiThreadBlocking(() -> { activity.getTab().getNavigationController().goBack(); }); + mCallback.onFailedCallback.assertCalledWith( + curFailedCount, STREAM_URL, LoadError.CONNECTIVITY_ERROR); + } + + // Verifies that a request which uses a stream can still set the user agent that is used for + // subresources. + @Test + @SmallTest + // The flags are necessary for the following reasons: + // ignore-certificate-errors: TestWebServer doesn't have a real cert. + @CommandLineFlags.Add({"ignore-certificate-errors"}) + public void testWebResponseWithUserAgent() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + TestThreadUtils.runOnUiThreadBlocking(() -> { activity.getBrowser().setTopView(null); }); + setNavigationCallback(activity); + + // Avoid mixed http/https errors since the stream url is https. + TestWebServer testServer = TestWebServer.startSsl(); + String scriptUrl = testServer.setResponse("/foo.js", "", null); + String streamHtml = "<html><script src='" + scriptUrl + "'/>bar</html>"; + + String customUserAgent = "custom-ua"; + UserAgentSetter setter = new UserAgentSetter(customUserAgent); + registerNavigationCallback(setter); + + navigateToStream(activity, "", null, streamHtml); + + // Ensure the script is fetched. + CriteriaHelper.pollInstrumentationThread( + () + -> Criteria.checkThat( + testServer.getLastRequest("/foo.js"), Matchers.notNullValue())); + + String actualUserAgent = testServer.getLastRequest("/foo.js").headerValue("User-Agent"); + assertEquals(customUserAgent, actualUserAgent); + } + + @MinWebLayerVersion(88) + @Test + @SmallTest + public void testOnFirstContentfulPaintTiming() throws Exception { + long activityStartTimeMs = SystemClock.uptimeMillis(); + + TestWebServer testServer = TestWebServer.start(); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); + setNavigationCallback(activity); + String url = testServer.setResponse("/ok.html", "<html>ok</html>", null); + + int count = mCallback.onFirstContentfulPaint2Callback.getCallCount(); + mActivityTestRule.navigateAndWait(url); + mCallback.onFirstContentfulPaint2Callback.waitForCallback(count); + + long navigationStart = mCallback.onFirstContentfulPaint2Callback.getNavigationStartMillis(); + long current = SystemClock.uptimeMillis(); + Assert.assertTrue(navigationStart <= current); + Assert.assertTrue(navigationStart >= activityStartTimeMs); + + long firstContentfulPaint = + mCallback.onFirstContentfulPaint2Callback.getFirstContentfulPaintMs(); + Assert.assertTrue(firstContentfulPaint <= (current - navigationStart)); + } + + @MinWebLayerVersion(88) + @Test + @SmallTest + public void testOnLargestContentfulPaintTiming() throws Exception { + long activityStartTimeMs = SystemClock.uptimeMillis(); + + TestWebServer testServer = TestWebServer.start(); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); + setNavigationCallback(activity); + String url = testServer.setResponse("/ok.html", "<html>ok</html>", null); + + int count = mCallback.onLargestContentfulPaintCallback.getCallCount(); + mActivityTestRule.navigateAndWait(url); + + // Navigate to a new page, as metrics like LCP are only reported at the end of the page load + // lifetime. + mActivityTestRule.navigateAndWait("about:blank"); + mCallback.onLargestContentfulPaintCallback.waitForCallback(count); + + long navigationStart = + mCallback.onLargestContentfulPaintCallback.getNavigationStartMillis(); + long current = SystemClock.uptimeMillis(); + Assert.assertTrue(navigationStart <= current); + Assert.assertTrue(navigationStart >= activityStartTimeMs); + + long largestContentfulPaint = + mCallback.onLargestContentfulPaintCallback.getLargestContentfulPaintMs(); + Assert.assertTrue(largestContentfulPaint <= (current - navigationStart)); + } + + /* Disable BackForwardCacheMemoryControls to allow BackForwardCache for all devices regardless + * of their memory. */ + @MinWebLayerVersion(89) + @Test + @SmallTest + @CommandLineFlags. + Add({"enable-features=BackForwardCache", "disable-features=BackForwardCacheMemoryControls"}) + public void testServedFromBackForwardCache() throws Exception { + TestWebServer testServer = TestWebServer.start(); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); + setNavigationCallback(activity); + + String url = mActivityTestRule.getTestServer().getURL("/echo"); + navigateAndWaitForCompletion(url, + () -> { activity.getTab().getNavigationController().navigate(Uri.parse(url)); }); + Assert.assertFalse(mCallback.onStartedCallback.isServedFromBackForwardCache()); + + String url2 = testServer.setResponse("/ok.html", "<html>ok</html>", null); + mActivityTestRule.navigateAndWait(url2); + Assert.assertFalse(mCallback.onStartedCallback.isServedFromBackForwardCache()); + + navigateAndWaitForCompletion( + url, () -> { activity.getTab().getNavigationController().goBack(); }); + Assert.assertTrue(mCallback.onStartedCallback.isServedFromBackForwardCache()); + } + + @MinWebLayerVersion(90) + @Test + @SmallTest + public void testIsFormSubmission() throws Exception { + InstrumentationActivity activity = + mActivityTestRule.launchShellWithUrl(mActivityTestRule.getTestDataURL("form.html")); + setNavigationCallback(activity); + + // Touch the page; this should submit the form. + int currentCallCount = mCallback.onStartedCallback.getCallCount(); + EventUtils.simulateTouchCenterOfView(activity.getWindow().getDecorView()); + + mCallback.onStartedCallback.waitForCallback(currentCallCount); + assertEquals(true, mCallback.onStartedCallback.isFormSubmission()); + } + + @MinWebLayerVersion(90) + @Test + @SmallTest + public void testGetReferrer() throws Exception { + TestWebServer testServer = TestWebServer.start(); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); + setNavigationCallback(activity); + String referrer = "http://foo.com/"; + NavigationCallback navigationCallback = new NavigationCallback() { + @Override + public void onNavigationStarted(Navigation navigation) { + try { + navigation.setRequestHeader("Referer", referrer); + } catch (IllegalStateException e) { + } + } + }; + + registerNavigationCallback(navigationCallback); + int currentCallCount = mCallback.onCompletedCallback.getCallCount(); + String url = testServer.setResponse("/ok.html", "<html>ok</html>", null); + mActivityTestRule.navigateAndWait(url); + mCallback.onCompletedCallback.waitForCallback(currentCallCount); + assertEquals(referrer, mCallback.onCompletedCallback.getReferrer().toString()); + } + + /* Disable BackForwardCacheMemoryControls to allow BackForwardCache for all devices regardless + * of their memory. */ + @MinWebLayerVersion(90) + @Test + @SmallTest + @CommandLineFlags. + Add({"enable-features=BackForwardCache", "disable-features=BackForwardCacheMemoryControls"}) + public void testPageApi() throws Exception { + TestWebServer testServer = TestWebServer.start(); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); + setNavigationCallback(activity); + + String url1 = mActivityTestRule.getTestServer().getURL("/echo"); + navigateAndWaitForCompletion(url1, + () -> { activity.getTab().getNavigationController().navigate(Uri.parse(url1)); }); + Page page1 = mCallback.onCompletedCallback.getPage(); + + // Ensure the second page doesn't go into bfcache so that we can observe its Page object + // being destroyed. + List<Pair<String, String>> headers = + Collections.singletonList(Pair.create("Cache-Control", "no-store")); + String url2 = testServer.setResponse("/ok.html", "<html>ok</html>", headers); + mActivityTestRule.navigateAndWait(url2); + Page page2 = mCallback.onCompletedCallback.getPage(); + assertNotEquals(page1, page2); + + int curOnPageDestroyedCount = mCallback.onPageDestroyedCallback.getCallCount(); + + navigateAndWaitForCompletion( + url1, () -> { activity.getTab().getNavigationController().goBack(); }); + Assert.assertTrue(mCallback.onCompletedCallback.isServedFromBackForwardCache()); + Page page3 = mCallback.onCompletedCallback.getPage(); + assertEquals(page1, page3); + + mCallback.onPageDestroyedCallback.assertCalledWith(curOnPageDestroyedCount, page2); + } + + @MinWebLayerVersion(91) + @Test + @SmallTest + public void testResponseHeaders() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); + setNavigationCallback(activity); + + int curCompletedCount = mCallback.onCompletedCallback.getCallCount(); + + String url = mActivityTestRule.getTestServer().getURL("/echo"); + mActivityTestRule.navigateAndWait(url); + + mCallback.onCompletedCallback.assertCalledWith(curCompletedCount, url); + + Map<String, String> headers = mCallback.onCompletedCallback.getResponseHeaders(); + assertEquals(headers.get("Content-Type"), "text/html"); + } + + @MinWebLayerVersion(92) + @Test + @SmallTest + public void testGetNavigationEntryOffset() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + setNavigationCallback(activity); + + mActivityTestRule.navigateAndWait(URL2); + assertEquals(1, mCallback.onCompletedCallback.getNavigationEntryOffset()); + + mActivityTestRule.navigateAndWait(URL3); + assertEquals(1, mCallback.onCompletedCallback.getNavigationEntryOffset()); + + NavigationController navigationController = + runOnUiThreadBlocking(() -> activity.getTab().getNavigationController()); + + navigateAndWaitForCompletion(URL2, () -> navigationController.goBack()); + assertEquals(-1, mCallback.onCompletedCallback.getNavigationEntryOffset()); + + navigateAndWaitForCompletion(URL3, () -> navigationController.goForward()); + assertEquals(1, mCallback.onCompletedCallback.getNavigationEntryOffset()); + + navigateAndWaitForCompletion(URL3, () -> navigationController.reload()); + assertEquals(0, mCallback.onCompletedCallback.getNavigationEntryOffset()); + + navigateAndWaitForCompletion(URL1, () -> navigationController.goToIndex(0)); + assertEquals(-2, mCallback.onCompletedCallback.getNavigationEntryOffset()); + + navigateAndWaitForCompletion(URL3, () -> navigationController.goToIndex(2)); + assertEquals(2, mCallback.onCompletedCallback.getNavigationEntryOffset()); + } + + @MinWebLayerVersion(93) + @Test + @SmallTest + public void testOnPageLanguageDetermined() throws Exception { + TestWebServer testServer = TestWebServer.start(); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); + setNavigationCallback(activity); + + int curLanguageDeterminedCount = mCallback.onPageLanguageDeterminedCallback.getCallCount(); + + // Navigate to a page in English. + String url = mActivityTestRule.getTestDataURL(ENGLISH_PAGE); + mActivityTestRule.navigateAndWait(url); + + Page committedPage = mCallback.onCompletedCallback.getPage(); + assertNotNull(committedPage); + + // Verify that the language determined callback is fired as expected. + mCallback.onPageLanguageDeterminedCallback.waitForCallback(curLanguageDeterminedCount); + + assertEquals(committedPage, mCallback.onPageLanguageDeterminedCallback.getPage()); + assertEquals("en", mCallback.onPageLanguageDeterminedCallback.getLanguage()); + + // Now navigate to a page in French. + committedPage = null; + curLanguageDeterminedCount = mCallback.onPageLanguageDeterminedCallback.getCallCount(); + + url = mActivityTestRule.getTestDataURL(FRENCH_PAGE); + mActivityTestRule.navigateAndWait(url); + + committedPage = mCallback.onCompletedCallback.getPage(); + assertNotNull(committedPage); + + // Verify that the language determined callback is fired as expected. + mCallback.onPageLanguageDeterminedCallback.waitForCallback(curLanguageDeterminedCount); + + assertEquals(committedPage, mCallback.onPageLanguageDeterminedCallback.getPage()); + assertEquals("fr", mCallback.onPageLanguageDeterminedCallback.getLanguage()); + } + + @MinWebLayerVersion(102) + @Test + @SmallTest + public void testWasFetchedFromCache() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); + setNavigationCallback(activity); + String url = mActivityTestRule.getTestServer().getURL("/cachetime"); + + mActivityTestRule.navigateAndWait(url); + assertFalse(mCallback.onCompletedCallback.wasFetchedFromCache()); + + mActivityTestRule.navigateAndWait( + mActivityTestRule.getTestServer().getURL("/cachetime?foo")); + assertFalse(mCallback.onCompletedCallback.wasFetchedFromCache()); + + mActivityTestRule.navigateAndWait(url); + assertTrue(mCallback.onCompletedCallback.wasFetchedFromCache()); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NavigationWaiter.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NavigationWaiter.java new file mode 100644 index 0000000..afa1203 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NavigationWaiter.java
@@ -0,0 +1,108 @@ +// 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. + +package org.chromium.weblayer.test; + +import static org.hamcrest.Matchers.lessThan; + +import android.net.Uri; + +import org.junit.Assert; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.Navigation; +import org.chromium.weblayer.NavigationCallback; +import org.chromium.weblayer.Tab; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * Helper that blocks until a navigation completes. + */ +public class NavigationWaiter { + private String mUrl; + private Tab mTab; + private boolean mNavigationObserved; + /* True indicates that the expected navigation event is a failure. False indicates that the + * expected event is completion. */ + private boolean mExpectFailure; + private boolean mDoneLoading; + private boolean mContentfulPaint; + private CallbackHelper mCallbackHelper = new CallbackHelper(); + + private NavigationCallback mNavigationCallback = new NavigationCallback() { + @Override + public void onNavigationCompleted(Navigation navigation) { + if (navigation.getUri().toString().equals(mUrl) && !mExpectFailure) { + Assert.assertThat(navigation.getHttpStatusCode(), lessThan(300)); + mNavigationObserved = true; + checkComplete(); + } + } + + @Override + public void onNavigationFailed(Navigation navigation) { + if (navigation.getUri().toString().equals(mUrl) && mExpectFailure) { + mNavigationObserved = true; + checkComplete(); + } + } + + @Override + public void onLoadStateChanged(boolean isLoading, boolean shouldShowLoadingUi) { + mDoneLoading = !isLoading; + checkComplete(); + } + + @Override + public void onFirstContentfulPaint() { + mContentfulPaint = true; + checkComplete(); + } + }; + + // |waitForPaint| should generally be set to true, unless there is a specific reason for + // onFirstContentfulPaint to not fire. + public NavigationWaiter( + String url, Tab controller, boolean expectFailure, boolean waitForPaint) { + mUrl = url; + mTab = controller; + if (!waitForPaint) mContentfulPaint = true; + mExpectFailure = expectFailure; + TestThreadUtils.runOnUiThreadBlocking(() -> { + mTab.getNavigationController().registerNavigationCallback(mNavigationCallback); + }); + } + + /** + * Blocks until the navigation specified in the constructor completes. + * + * This also cleans up state, so it should only be called once per class instance. + */ + public void waitForNavigation() { + try { + mCallbackHelper.waitForCallback( + 0, 1, CallbackHelper.WAIT_TIMEOUT_SECONDS * 2, TimeUnit.SECONDS); + } catch (TimeoutException e) { + throw new RuntimeException(e); + } + TestThreadUtils.runOnUiThreadBlocking(() -> { + mTab.getNavigationController().unregisterNavigationCallback(mNavigationCallback); + }); + } + + public void navigateAndWait() { + TestThreadUtils.runOnUiThreadBlocking( + () -> { mTab.getNavigationController().navigate(Uri.parse(mUrl)); }); + waitForNavigation(); + } + + private void checkComplete() { + if (mNavigationObserved && mDoneLoading && mContentfulPaint) { + mCallbackHelper.notifyCalled(); + } + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NetworkChangeNotifierTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NetworkChangeNotifierTest.java new file mode 100644 index 0000000..b289391e --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NetworkChangeNotifierTest.java
@@ -0,0 +1,39 @@ +// Copyright 2020 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.weblayer.test; + +import android.os.RemoteException; + +import androidx.test.filters.SmallTest; + +import org.json.JSONException; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.Feature; +import org.chromium.weblayer.TestWebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** Tests that integration with NetworkStateNotifier works as expected. */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public final class NetworkChangeNotifierTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + @Test + @SmallTest + @Feature({"WebLayer"}) + public void testNetworkChangeNotifierAutoDetectRegistered() + throws RemoteException, JSONException { + // This creates an activity with WebLayer loaded, and causes WebLayer + // code to start listening to changes in network connectivity. + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + TestWebLayer testWebLayer = TestWebLayer.getTestWebLayer(activity.getApplicationContext()); + Assert.assertTrue(testWebLayer.isNetworkChangeAutoDetectOn()); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NewTabCallbackImpl.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NewTabCallbackImpl.java new file mode 100644 index 0000000..9a0f216 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NewTabCallbackImpl.java
@@ -0,0 +1,32 @@ +// 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. + +package org.chromium.weblayer.test; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.weblayer.NewTabCallback; +import org.chromium.weblayer.Tab; + +/** + * NewTabCallback test helper. Primarily used to wait for a new tab to be created. + */ +public class NewTabCallbackImpl extends NewTabCallback { + private final CallbackHelper mCallbackHelper = new CallbackHelper(); + + @Override + public void onNewTab(Tab tab, int mode) { + mCallbackHelper.notifyCalled(); + tab.getBrowser().setActiveTab(tab); + } + + public void waitForNewTab() { + try { + // waitForFirst() only handles a single call. If you need more convert from + // waitForFirst(). + mCallbackHelper.waitForFirst(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NewTabCallbackTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NewTabCallbackTest.java new file mode 100644 index 0000000..5e63286 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NewTabCallbackTest.java
@@ -0,0 +1,95 @@ +// 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. + +package org.chromium.weblayer.test; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.NewTabCallback; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Tests that NewTabCallback methods are invoked as expected. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class NewTabCallbackTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + + @Test + @SmallTest + public void testNewBrowser() { + String url = mActivityTestRule.getTestDataURL("new_browser.html"); + mActivity = mActivityTestRule.launchShellWithUrl(url); + Assert.assertNotNull(mActivity); + NewTabCallbackImpl callback = new NewTabCallbackImpl(); + Tab firstTab = TestThreadUtils.runOnUiThreadBlockingNoException(() -> { + Tab tab = mActivity.getBrowser().getActiveTab(); + tab.setNewTabCallback(callback); + return tab; + }); + + EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView()); + callback.waitForNewTab(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Assert.assertEquals(2, mActivity.getBrowser().getTabs().size()); + Tab secondTab = mActivity.getBrowser().getActiveTab(); + Assert.assertNotSame(firstTab, secondTab); + }); + } + + @Test + @SmallTest + public void testDestroyTabInOnNewTab() throws Throwable { + String url = mActivityTestRule.getTestDataURL("new_browser.html"); + mActivity = mActivityTestRule.launchShellWithUrl(url); + Assert.assertNotNull(mActivity); + CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Tab tab = mActivity.getBrowser().getActiveTab(); + tab.setNewTabCallback(new NewTabCallback() { + @Override + public void onNewTab(Tab newTab, int mode) { + newTab.getBrowser().destroyTab(newTab); + Assert.assertTrue(newTab.isDestroyed()); + Assert.assertEquals(1, mActivity.getBrowser().getTabs().size()); + Assert.assertFalse(mActivity.getBrowser().getActiveTab().isDestroyed()); + callbackHelper.notifyCalled(); + } + }); + }); + + EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView()); + callbackHelper.waitForFirst(); + } + + @Test + @SmallTest + public void testNewTabHasFocus() { + String url = mActivityTestRule.getTestDataURL("new_browser.html"); + mActivity = mActivityTestRule.launchShellWithUrl(url); + Assert.assertNotNull(mActivity); + NewTabCallbackImpl callback = new NewTabCallbackImpl(); + TestThreadUtils.runOnUiThreadBlocking( + () -> { mActivity.getBrowser().getActiveTab().setNewTabCallback(callback); }); + + EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView()); + callback.waitForNewTab(); + CriteriaHelper.pollInstrumentationThread(() -> { + return mActivityTestRule.executeScriptAndExtractBoolean("document.hasFocus()"); + }); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/OnTabRemovedTabListCallbackImpl.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/OnTabRemovedTabListCallbackImpl.java new file mode 100644 index 0000000..62dd156 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/OnTabRemovedTabListCallbackImpl.java
@@ -0,0 +1,35 @@ +// Copyright 2020 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.weblayer.test; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.TabListCallback; + +/** + * TabListCallback test helper. Primarily used to wait for a tab to be closed. + */ +public class OnTabRemovedTabListCallbackImpl extends TabListCallback { + private final CallbackHelper mCallbackHelper = new CallbackHelper(); + + @Override + public void onTabRemoved(Tab tab) { + mCallbackHelper.notifyCalled(); + } + + public void waitForCloseTab() { + try { + // waitForFirst() only handles a single call. If you need more convert from + // waitForFirst(). + mCallbackHelper.waitForFirst(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public boolean hasClosed() { + return mCallbackHelper.getCallCount() > 0; + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/PopupTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/PopupTest.java new file mode 100644 index 0000000..2bf0af2 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/PopupTest.java
@@ -0,0 +1,107 @@ +// Copyright 2020 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.weblayer.test; + +import android.content.Context; + +import androidx.test.filters.SmallTest; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.NewTabCallback; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.TestWebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Tests that popup blocking works as expected. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public final class PopupTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + private Context mRemoteContext; + + @Before + public void setUp() { + mActivity = mActivityTestRule.launchShellWithUrl("about:blank"); + Assert.assertNotNull(mActivity); + + mRemoteContext = TestWebLayer.getRemoteContext(mActivity.getApplicationContext()); + } + + @Test + @SmallTest + public void testOpenPopupFromInfoBar() throws Exception { + NewTabCallbackImpl callback = new NewTabCallbackImpl(); + TestThreadUtils.runOnUiThreadBlocking( + () -> { mActivity.getBrowser().getActiveTab().setNewTabCallback(callback); }); + + // Try to open a popup. + mActivityTestRule.executeScriptSync("window.open('about:blank')", true); + + // Make sure the infobar shows up and the popup has not been opened. + String packageName = + TestWebLayer.getWebLayerContext(mActivity.getApplicationContext()).getPackageName(); + int buttonId = ResourceUtil.getIdentifier(mRemoteContext, "id/button_primary", packageName); + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(mActivity.findViewById(buttonId), Matchers.notNullValue()); + }); + TestThreadUtils.runOnUiThreadBlocking( + () -> { Assert.assertEquals(mActivity.getBrowser().getTabs().size(), 1); }); + + // Click the button on the infobar to open the popup. + EventUtils.simulateTouchCenterOfView(mActivity.findViewById(buttonId)); + callback.waitForNewTab(); + } + + @Test + @SmallTest + // This is a regression test for https://crbug.com/1142090 and verifies no crash when + // NewTabCallback.onNewTab() destroys the supplied tab. + public void testOpenPopupFromInfoBarAndNewTabCallbackDestroysTab() throws Exception { + CallbackHelper helper = new CallbackHelper(); + NewTabCallback callback = new NewTabCallback() { + @Override + public void onNewTab(Tab tab, int mode) { + tab.getBrowser().destroyTab(tab); + helper.notifyCalled(); + } + }; + TestThreadUtils.runOnUiThreadBlocking( + () -> { mActivity.getBrowser().getActiveTab().setNewTabCallback(callback); }); + + // Try to open a popup. + mActivityTestRule.executeScriptSync("window.open('about:blank')", true); + + // Make sure the infobar shows up and the popup has not been opened. + String packageName = + TestWebLayer.getWebLayerContext(mActivity.getApplicationContext()).getPackageName(); + int buttonId = ResourceUtil.getIdentifier(mRemoteContext, "id/button_primary", packageName); + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(mActivity.findViewById(buttonId), Matchers.notNullValue()); + }); + TestThreadUtils.runOnUiThreadBlocking( + () -> { Assert.assertEquals(mActivity.getBrowser().getTabs().size(), 1); }); + + // Click the button on the infobar to open the popup. + EventUtils.simulateTouchCenterOfView(mActivity.findViewById(buttonId)); + + // Wait for tab to be destroyed. + helper.waitForFirst(); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/PrerenderControllerTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/PrerenderControllerTest.java new file mode 100644 index 0000000..dfdad866 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/PrerenderControllerTest.java
@@ -0,0 +1,69 @@ +// Copyright 2020 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.weblayer.test; + +import android.net.Uri; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.Feature; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.net.test.util.TestWebServer; +import org.chromium.net.test.util.WebServer; +import org.chromium.weblayer.PrerenderController; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.io.IOException; +import java.io.OutputStream; + +/** Tests verifying PrerenderController behavior. */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class PrerenderControllerTest { + private static final String DEFAULT_BODY = "<html><title>TestPage</title>Hello World!</html>"; + private CallbackHelper mPrerenderedPageFetched; + + private WebServer.RequestHandler mRequestHandler = new WebServer.RequestHandler() { + @Override + public void handleRequest(WebServer.HTTPRequest request, OutputStream stream) { + try { + if (request.getURI().contains("prerendered_page.html")) { + TestThreadUtils.runOnUiThreadBlocking( + () -> mPrerenderedPageFetched.notifyCalled()); + } + WebServer.writeResponse(stream, WebServer.STATUS_OK, DEFAULT_BODY.getBytes()); + } catch (IOException exception) { + Assert.fail(exception.getMessage() + + " \n while handling request: " + request.toString()); + } + } + }; + + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + @Test + @SmallTest + @Feature({"WebLayer"}) + public void testAddingPrerender() throws Exception { + TestWebServer testServer = TestWebServer.start(); + testServer.setRequestHandler(mRequestHandler); + mPrerenderedPageFetched = new CallbackHelper(); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); + TestThreadUtils.runOnUiThreadBlocking(() -> { + PrerenderController prerenderController = + activity.getBrowser().getProfile().getPrerenderController(); + prerenderController.schedulePrerender( + Uri.parse(testServer.getResponseUrl("/prerendered_page.html"))); + }); + mPrerenderedPageFetched.waitForFirst(); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ProfileTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ProfileTest.java new file mode 100644 index 0000000..fa424b7 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ProfileTest.java
@@ -0,0 +1,305 @@ +// 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. + +package org.chromium.weblayer.test; + +import android.net.Uri; +import android.webkit.ValueCallback; + +import androidx.fragment.app.FragmentManager; +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.ApplicationTestUtils; +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.Callback; +import org.chromium.weblayer.CookieManager; +import org.chromium.weblayer.Profile; +import org.chromium.weblayer.WebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.util.Arrays; +import java.util.Collection; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; + +/** + * Tests that Profile works as expected. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class ProfileTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + @Test + @SmallTest + public void testCreateAndGetAllProfiles() { + WebLayer weblayer = mActivityTestRule.getWebLayer(); + { + // Start with empty profile. + Collection<Profile> profiles = getAllProfiles(); + Assert.assertTrue(profiles.isEmpty()); + } + + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + Profile firstProfile = TestThreadUtils.runOnUiThreadBlockingNoException( + () -> activity.getBrowser().getProfile()); + { + // Launching an activity with a fragment creates one profile. + Collection<Profile> profiles = getAllProfiles(); + Assert.assertEquals(1, profiles.size()); + Assert.assertTrue(profiles.contains(firstProfile)); + } + + Profile secondProfile = TestThreadUtils.runOnUiThreadBlockingNoException( + () -> { return weblayer.getProfile("second_test"); }); + + { + Collection<Profile> profiles = getAllProfiles(); + Assert.assertEquals(2, profiles.size()); + Assert.assertTrue(profiles.contains(firstProfile)); + Assert.assertTrue(profiles.contains(secondProfile)); + } + } + + @Test + @SmallTest + public void testDestroyAndDeleteDataFromDiskWhenInUse() { + WebLayer weblayer = mActivityTestRule.getWebLayer(); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + final Profile profile = TestThreadUtils.runOnUiThreadBlockingNoException( + () -> activity.getBrowser().getProfile()); + + try { + Callable<Void> c = () -> { + profile.destroyAndDeleteDataFromDisk(null); + return null; + }; + TestThreadUtils.runOnUiThreadBlocking(c); + Assert.fail(); + } catch (ExecutionException e) { + // Expected. + } + } + + @Test + @SmallTest + public void testDestroyAndDeleteDataFromDiskSoonWhenInUse() throws Exception { + WebLayer weblayer = mActivityTestRule.getWebLayer(); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + final CallbackHelper callbackHelper = new CallbackHelper(); + Profile profile = + TestThreadUtils.runOnUiThreadBlocking(() -> activity.getBrowser().getProfile()); + TestThreadUtils.runOnUiThreadBlocking(() -> { + profile.destroyAndDeleteDataFromDiskSoon(callbackHelper::notifyCalled); + FragmentManager fm = activity.getSupportFragmentManager(); + fm.beginTransaction() + .remove(fm.getFragments().get(0)) + .runOnCommit(callbackHelper::notifyCalled) + .commit(); + }); + callbackHelper.waitForCallback(0, 2); + Collection<Profile> profiles = getAllProfiles(); + Assert.assertFalse(profiles.contains(profile)); + } + + @Test + @SmallTest + public void testDestroyAndDeleteDataFromDisk() throws Exception { + doTestDestroyAndDeleteDataFromDiskIncognito("testDestroyAndDeleteDataFromDisk"); + } + + @Test + @SmallTest + public void testDestroyAndDeleteDataFromDiskIncognito() throws Exception { + doTestDestroyAndDeleteDataFromDiskIncognito(null); + } + + private void doTestDestroyAndDeleteDataFromDiskIncognito(final String name) throws Exception { + WebLayer weblayer = mActivityTestRule.getWebLayer(); + final Profile profile = + TestThreadUtils.runOnUiThreadBlockingNoException(() -> weblayer.getProfile(name)); + + { + Collection<Profile> profiles = getAllProfiles(); + Assert.assertTrue(profiles.contains(profile)); + } + + final CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking( + () -> profile.destroyAndDeleteDataFromDisk(callbackHelper::notifyCalled)); + { + Collection<Profile> profiles = getAllProfiles(); + Assert.assertFalse(profiles.contains(profile)); + } + callbackHelper.waitForFirst(); + } + + @Test + @SmallTest + @DisabledTest(message = "see crbug.com/1359894") + public void testEnumerateAllProfileNames() throws Exception { + final String profileName = "TestEnumerateAllProfileNames"; + final InstrumentationActivity activity = mActivityTestRule.launchWithProfile(profileName); + final Profile profile = TestThreadUtils.runOnUiThreadBlockingNoException( + () -> activity.getBrowser().getProfile()); + + Assert.assertTrue(Arrays.asList(enumerateAllProfileNames()).contains(profileName)); + + ApplicationTestUtils.finishActivity(activity); + final CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking( + () -> profile.destroyAndDeleteDataFromDisk(callbackHelper::notifyCalled)); + callbackHelper.waitForFirst(); + + Assert.assertFalse(Arrays.asList(enumerateAllProfileNames()).contains(profileName)); + } + + private Profile launchAndDestroyActivity( + String profileName, ValueCallback<InstrumentationActivity> callback) throws Exception { + final InstrumentationActivity activity = mActivityTestRule.launchWithProfile(profileName); + final Profile profile = TestThreadUtils.runOnUiThreadBlockingNoException( + () -> activity.getBrowser().getProfile()); + + callback.onReceiveValue(activity); + + ApplicationTestUtils.finishActivity(activity); + return profile; + } + + private void destroyAndDeleteDataFromDisk(Profile profile) throws Exception { + final CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking( + () -> profile.destroyAndDeleteDataFromDisk(callbackHelper::notifyCalled)); + callbackHelper.waitForFirst(); + } + + @Test + @SmallTest + @DisabledTest(message = "see crbug.com/1359894") + public void testReuseProfile() throws Exception { + final String profileName = "ReusedProfile"; + final Uri uri = Uri.parse("https://foo.bar"); + final String expectedCookie = "foo=bar"; + { + // Create profile and activity and set a cookie. + launchAndDestroyActivity(profileName, (InstrumentationActivity activity) -> { + CookieManager cookieManager = TestThreadUtils.runOnUiThreadBlockingNoException( + () -> { return activity.getBrowser().getProfile().getCookieManager(); }); + try { + boolean result = + mActivityTestRule.setCookie(cookieManager, uri, expectedCookie); + Assert.assertTrue(result); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + + { + // Without deleting proifle data, create profile and activity again, and check the + // cookie is there. + Profile profile = + launchAndDestroyActivity(profileName, (InstrumentationActivity activity) -> { + CookieManager cookieManager = + TestThreadUtils.runOnUiThreadBlockingNoException(() -> { + return activity.getBrowser().getProfile().getCookieManager(); + }); + try { + String cookie = mActivityTestRule.getCookie(cookieManager, uri); + Assert.assertEquals(expectedCookie, cookie); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + + destroyAndDeleteDataFromDisk(profile); + } + + { + // After deleting profile data, create profile and activity again, and check the cookie + // is deleted as well. + launchAndDestroyActivity(profileName, (InstrumentationActivity activity) -> { + CookieManager cookieManager = TestThreadUtils.runOnUiThreadBlockingNoException( + () -> { return activity.getBrowser().getProfile().getCookieManager(); }); + try { + String cookie = mActivityTestRule.getCookie(cookieManager, uri); + Assert.assertEquals("", cookie); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + } + + private String[] enumerateAllProfileNames() throws Exception { + final String[][] holder = new String[1][]; + final CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Callback<String[]> callback = new Callback<String[]>() { + @Override + public void onResult(String[] names) { + holder[0] = names; + callbackHelper.notifyCalled(); + } + }; + mActivityTestRule.getWebLayer().enumerateAllProfileNames(callback); + }); + callbackHelper.waitForFirst(); + return holder[0]; + } + + private static Collection<Profile> getAllProfiles() { + return TestThreadUtils.runOnUiThreadBlockingNoException(() -> Profile.getAllProfiles()); + } + + @Test + @SmallTest + public void testMultipleIncognitoProfiles() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + WebLayer weblayer = mActivityTestRule.getWebLayer(); + Profile profile = activity.getBrowser().getProfile(); + Assert.assertFalse(profile.isIncognito()); + final String incognitoProfileName1 = "incognito1"; + Profile incognitoProfile1 = weblayer.getIncognitoProfile(incognitoProfileName1); + Assert.assertTrue(incognitoProfile1.isIncognito()); + Assert.assertEquals(incognitoProfileName1, incognitoProfile1.getName()); + + final String incognitoProfileName2 = "incognito2"; + Profile incognitoProfile2 = weblayer.getIncognitoProfile(incognitoProfileName2); + Assert.assertTrue(incognitoProfile2.isIncognito()); + Assert.assertEquals(incognitoProfileName2, incognitoProfile2.getName()); + Assert.assertNotEquals(incognitoProfile1, incognitoProfile2); + + // Calling getIncognitoProfile() should return the same Profile. + Assert.assertEquals( + incognitoProfile1, weblayer.getIncognitoProfile(incognitoProfileName1)); + Assert.assertEquals( + incognitoProfile2, weblayer.getIncognitoProfile(incognitoProfileName2)); + + // getAllProfiles() should return the incognito profiles. + Collection<Profile> allProfiles = Profile.getAllProfiles(); + Assert.assertEquals(3, allProfiles.size()); + Assert.assertTrue(allProfiles.contains(profile)); + Assert.assertTrue(allProfiles.contains(incognitoProfile1)); + Assert.assertTrue(allProfiles.contains(incognitoProfile2)); + }); + + // Incognito profiles should not be returned from enumerateAllProfileNames(). + String[] profileNames = enumerateAllProfileNames(); + Assert.assertEquals(1, profileNames.length); + Assert.assertEquals(profileNames[0], + TestThreadUtils.runOnUiThreadBlocking( + () -> activity.getBrowser().getProfile().getName())); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/RegisterExternalExperimentIdsTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/RegisterExternalExperimentIdsTest.java new file mode 100644 index 0000000..69eea2a --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/RegisterExternalExperimentIdsTest.java
@@ -0,0 +1,64 @@ +// Copyright 2020 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.weblayer.test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; + +import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking; + +import androidx.test.filters.SmallTest; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.net.test.util.TestWebServer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Assertions for WebLayer.registerExternalExperimentIDs(). + */ +// The flags are necessary for the following reasons: +// host-resolver-rules: to make 'google.com' redirect to the port created by TestWebServer. +// ignore-certificate-errors: TestWebServer doesn't have a real cert. +@CommandLineFlags.Add({"host-resolver-rules='MAP * 127.0.0.1'", "ignore-certificate-errors"}) +@RunWith(WebLayerJUnit4ClassRunner.class) +public class RegisterExternalExperimentIdsTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + /** + * This is an end-to-end test of registerExternalExperimentIDs. It also covers + * https://crbug.com/1147827. + */ + @Test + @SmallTest + @MinWebLayerVersion(88) // Fix landed in 88. + public void testRegisterExternalExperimentIds() throws Exception { + mActivityTestRule.getTestServerRule().setServerUsesHttps(true); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); + TestWebServer testServer = TestWebServer.startSsl(); + // Experiment ids are only added for certain domains, of which google is one. + testServer.setServerHost("google.com"); + String url = testServer.setResponse("/ok.html", "<html>ok</html>", null); + mActivityTestRule.navigateAndWait(url); + String initialValue = testServer.getLastRequest("/ok.html").headerValue("X-Client-Data"); + runOnUiThreadBlocking(() -> { + mActivityTestRule.getWebLayer().registerExternalExperimentIDs(new int[] {1}); + }); + + String url2 = testServer.setResponse("/ok2.html", "<html>ok</html>", null); + mActivityTestRule.navigateAndWait(url2); + String secondValue = testServer.getLastRequest("/ok2.html").headerValue("X-Client-Data"); + // The contents of the header are an encoded protobuf, which is a bit hard to verify in a + // test. The key things to assert is registering an id changes the value, and that it's not + // empty. + assertFalse(secondValue.isEmpty()); + assertNotEquals(initialValue, secondValue); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ResourceLoadingTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ResourceLoadingTest.java new file mode 100644 index 0000000..892abaf --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ResourceLoadingTest.java
@@ -0,0 +1,68 @@ +// Copyright 2020 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.weblayer.test; + +import android.content.Context; +import android.graphics.drawable.ColorDrawable; +import android.util.TypedValue; +import android.view.ContextThemeWrapper; +import android.view.LayoutInflater; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.weblayer.TestWebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** Tests that resources are loaded correctly. */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public final class ResourceLoadingTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private Context mRemoteContext; + private String mPackageName; + + @Before + public void setUp() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + mRemoteContext = TestWebLayer.getRemoteContext(activity.getApplicationContext()); + mPackageName = + TestWebLayer.getWebLayerContext(activity.getApplicationContext()).getPackageName(); + } + + @Test + @SmallTest + public void testLayout() throws Exception { + Context themedContext = + new ContextThemeWrapper(mRemoteContext, getIdentifier("style/TestStyle")); + View view = LayoutInflater.from(themedContext) + .inflate(getIdentifier("layout/test_layout"), null); + Assert.assertEquals(((ColorDrawable) view.getBackground()).getColor(), 0xff010101); + } + + @Test + @SmallTest + public void testStyle() throws Exception { + Context themedContext = + new ContextThemeWrapper(mRemoteContext, getIdentifier("style/TestStyle")); + TypedValue tv = new TypedValue(); + Assert.assertTrue(themedContext.getTheme().resolveAttribute( + getIdentifier("attr/testAttrColor"), tv, true)); + Assert.assertEquals(tv.type, TypedValue.TYPE_INT_COLOR_RGB8); + Assert.assertEquals(tv.data, 0xff010101); + } + + private int getIdentifier(String name) { + return ResourceUtil.getIdentifier(mRemoteContext, name, mPackageName); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ResourceUtil.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ResourceUtil.java new file mode 100644 index 0000000..563fe6c --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ResourceUtil.java
@@ -0,0 +1,30 @@ +// Copyright 2020 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.weblayer.test; + +import android.content.Context; + +/** + * Util class for dealing with resources. + */ +public class ResourceUtil { + public static final int REQUIRED_PACKAGE_IDENTIFIER = 36; + + private ResourceUtil() {} + + /** Gets the ID of a resource in a remote context. */ + public static int getIdentifier(Context context, String name, String packageName) { + int id = context.getResources().getIdentifier(name, null, packageName); + // This was build with app_as_shared_lib, no need to modify package ID. + if ((id & 0xff000000) == 0x7f000000) { + return id; + } + + // Force the returned ID to use our magic package ID. + id &= 0x00ffffff; + id |= (0x01000000 * REQUIRED_PACKAGE_IDENTIFIER); + return id; + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ScrollOffsetCallbackTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ScrollOffsetCallbackTest.java new file mode 100644 index 0000000..411f768 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ScrollOffsetCallbackTest.java
@@ -0,0 +1,96 @@ +// Copyright 2020 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.weblayer.test; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.ScrollOffsetCallback; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * Test for ScrollOffsetCallback. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class ScrollOffsetCallbackTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + @Test + @SmallTest + @DisabledTest(message = "crbug.com/1190427") + public void testBasic() throws Throwable { + final String url = mActivityTestRule.getTestDataURL("tall_page.html"); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(url); + + // As registering for scroll offsets requires sending a message to the renderer, and + // requires viz to be in a particular state, ensuring scrolls will generate a message is a + // bit finicky. This tries to adjust the top-offset 10 times before giving up. + CallbackHelper callbackHelper = new CallbackHelper(); + ScrollOffsetCallback scrollOffsetCallback = new ScrollOffsetCallback() { + @Override + public void onVerticalScrollOffsetChanged(int scrollOffset) { + callbackHelper.notifyCalled(); + } + }; + TestThreadUtils.runOnUiThreadBlocking(() -> { + activity.getBrowser().getActiveTab().registerScrollOffsetCallback(scrollOffsetCallback); + }); + + boolean gotInitialScroll = false; + for (int i = 1; i < 10 && !gotInitialScroll; ++i) { + mActivityTestRule.executeScriptSync("window.scroll(0, " + (i * 100) + ");", false); + try { + callbackHelper.waitForCallback( + "Waiting for initial scroll", 0, 1, 100, TimeUnit.MILLISECONDS); + gotInitialScroll = true; + } catch (TimeoutException e) { + } + } + if (!gotInitialScroll) { + Assert.fail("Was unable to get initial scroll, failing"); + } + + // Scroll back to the origin. + CallbackHelper scrollBackCallbackHelper = new CallbackHelper(); + ScrollOffsetCallback scrollBackOffsetCallback = new ScrollOffsetCallback() { + @Override + public void onVerticalScrollOffsetChanged(int scrollOffset) { + if (scrollOffset == 0) { + scrollBackCallbackHelper.notifyCalled(); + } + } + }; + TestThreadUtils.runOnUiThreadBlocking(() -> { + activity.getBrowser().getActiveTab().registerScrollOffsetCallback( + scrollBackOffsetCallback); + }); + mActivityTestRule.executeScriptSync("window.scroll(0, 0);", false); + scrollBackCallbackHelper.waitForFirst(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + activity.getBrowser().getActiveTab().unregisterScrollOffsetCallback( + scrollOffsetCallback); + }); + + // At this point scrollOffsetCallback is still registered. If this code were to remove the + // callback the renderer might temporarily disable scroll-offsets notification, which + // could potentially lead to raciness. In other words, best to leave it for future + // assertions. + } + + // NOTE: if adding another test, you'll likely want to make testBasic() a setUp type function + // that is shared. +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/SettingsActivityTestRule.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/SettingsActivityTestRule.java new file mode 100644 index 0000000..f57678d8 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/SettingsActivityTestRule.java
@@ -0,0 +1,47 @@ +// Copyright 2020 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.weblayer.test; + +import android.content.Context; +import android.content.Intent; +import android.support.test.InstrumentationRegistry; + +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.SettingsActivity; +import org.chromium.weblayer.WebLayer; + +/** + * ActivityTestRule for SettingsActivity. + * + * Test can use this ActivityTestRule to launch SettingsActivity. + */ +public class SettingsActivityTestRule extends WebLayerActivityTestRule<SettingsActivity> { + private Context mAppContext; + + public SettingsActivityTestRule() { + super(SettingsActivity.class); + } + + @Override + public void launchActivity(Intent settingsIntent) { + TestThreadUtils.runOnUiThreadBlocking(() -> { + // We load WebLayer here so it gets initialized with the test/instrumentation Context, + // which has an in-memory SharedPreferences. Without this call, WebLayer gets + // initialized with the Application as its appContext, which breaks tests because a + // SharedPreferences xml file is persisted to disk. + WebLayer.loadSync(getContext()); + }); + super.launchActivity(settingsIntent); + } + + public Context getContext() { + if (mAppContext == null) { + mAppContext = InstrumentationRegistry.getInstrumentation() + .getTargetContext() + .getApplicationContext(); + } + return mAppContext; + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/SiteSettingsTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/SiteSettingsTest.java new file mode 100644 index 0000000..4e3583b --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/SiteSettingsTest.java
@@ -0,0 +1,140 @@ +// Copyright 2020 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.weblayer.test; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +import static org.hamcrest.core.StringContains.containsString; + +import android.os.RemoteException; + +import androidx.test.filters.SmallTest; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.DisabledTest; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.SettingsTestUtils; +import org.chromium.weblayer.SiteSettingsActivity; +import org.chromium.weblayer.TestWebLayer; +import org.chromium.weblayer.WebLayer; + +/** + * Tests the behavior of the Site Settings UI. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class SiteSettingsTest { + private static final String GOOGLE_URL = "https://www.google.com"; + private static final String PROFILE_NAME = "DefaultProfile"; + + @Rule + public SettingsActivityTestRule mSettingsTestRule = new SettingsActivityTestRule(); + + @Test + @SmallTest + public void testSiteSettingsLaunches() throws InterruptedException { + mSettingsTestRule.launchActivity( + SiteSettingsActivity.createIntentForSiteSettingsCategoryList( + mSettingsTestRule.getContext(), PROFILE_NAME, /*isIncognito=*/false)); + + onView(withText("All sites")).check(matches(isDisplayed())); + } + + @Test + @SmallTest + public void testAllSitesLaunches() throws Exception { + mSettingsTestRule.launchActivity( + SiteSettingsActivity.createIntentForSiteSettingsCategoryList( + mSettingsTestRule.getContext(), PROFILE_NAME, /*isIncognito=*/false)); + TestWebLayer.getTestWebLayer(mSettingsTestRule.getContext()) + .grantLocationPermission(GOOGLE_URL); + + onView(withText("All sites")).perform(click()); + + // Check that the google.com item is visible. + onView(withText(GOOGLE_URL)).check(matches(isDisplayed())); + } + + @Test + @SmallTest + public void testJavascriptExceptionPopupLaunches() throws InterruptedException { + mSettingsTestRule.launchActivity( + SiteSettingsActivity.createIntentForSiteSettingsCategoryList( + mSettingsTestRule.getContext(), PROFILE_NAME, /*isIncognito=*/false)); + + onView(withText("JavaScript")).perform(click()); + onView(withText("Add site exception")).perform(click()); + + onView(withText("Block JavaScript for a specific site.")).check(matches(isDisplayed())); + } + + @DisabledTest(message = "https://crbug.com/1174618") + @Test + @SmallTest + public void testAdBlockingSiteSettingPageLaunches() throws InterruptedException { + // The setting for ad blocking is below the fold on the main site settings page, and it's + // challenging to scroll to it. Launch directly to the category instead. See the discussion + // on https://chromium-review.googlesource.com/c/chromium/src/+/2673520 for further details. + // Note that this means that this test unfortunately doesn't verify that the Ads setting is + // actually present on the main site settings page. + mSettingsTestRule.launchActivity( + SettingsTestUtils.createIntentForSiteSettingsSingleCategory( + mSettingsTestRule.getContext(), PROFILE_NAME, /*isIncognito=*/false, "ads", + "Ads")); + + onView(withText("Block ads on sites that show intrusive or misleading ads")) + .perform(click()); + + onView(withText("Allowed")).check(matches(isDisplayed())); + } + + @Test + @SmallTest + public void testSingleSiteSoundToggle() throws InterruptedException { + mSettingsTestRule.launchActivity(SettingsTestUtils.createIntentForSiteSettingsSingleWebsite( + mSettingsTestRule.getContext(), PROFILE_NAME, /*isIncognito=*/false, GOOGLE_URL)); + + onView(withText("Allowed")).check(matches(isDisplayed())); + + onView(withText("Sound")).perform(click()); + + onView(withText("Blocked")).check(matches(isDisplayed())); + } + + @Test + @SmallTest + public void testSingleSiteClearPopupLaunches() throws InterruptedException { + mSettingsTestRule.launchActivity(SettingsTestUtils.createIntentForSiteSettingsSingleWebsite( + mSettingsTestRule.getContext(), PROFILE_NAME, /*isIncognito=*/false, GOOGLE_URL)); + + onView(withText("Clear & reset")).perform(click()); + + onView(withText(containsString("Are you sure you want to clear all local data"))) + .check(matches(isDisplayed())); + } + + @Test + @SmallTest + public void testSingleSiteLocationAccess() throws InterruptedException, RemoteException { + TestWebLayer testWebLayer = TestWebLayer.getTestWebLayer(mSettingsTestRule.getContext()); + testWebLayer.setSystemLocationSettingEnabled(true); + TestThreadUtils.runOnUiThreadBlocking(() -> { + WebLayer.loadSync(mSettingsTestRule.getContext()).getProfile(PROFILE_NAME); + }); + testWebLayer.grantLocationPermission(GOOGLE_URL); + mSettingsTestRule.launchActivity(SettingsTestUtils.createIntentForSiteSettingsSingleWebsite( + mSettingsTestRule.getContext(), PROFILE_NAME, /*isIncognito=*/false, GOOGLE_URL)); + + onView(withText("Location")).perform(click()); + + onView(withText("Blocked")).check(matches(isDisplayed())); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/SmokeTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/SmokeTest.java new file mode 100644 index 0000000..9f2444c --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/SmokeTest.java
@@ -0,0 +1,178 @@ +// 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. + +package org.chromium.weblayer.test; + +import android.app.Activity; +import android.content.pm.ActivityInfo; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.view.PixelCopy; + +import androidx.annotation.RequiresApi; +import androidx.fragment.app.Fragment; +import androidx.test.filters.SmallTest; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.CriteriaNotSatisfiedException; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; + +/** + * Basic tests to make sure WebLayer works as expected. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class SmokeTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + // Should be used only for solid color pages, since the window is downsampled significantly. + @RequiresApi(Build.VERSION_CODES.O) + private int getWindowCenterPixelColor(Activity activity) { + BoundedCountDownLatch latch = new BoundedCountDownLatch(1); + Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888, /*hasAlpha=*/true); + TestThreadUtils.runOnUiThreadBlocking(() -> { + PixelCopy.request(activity.getWindow(), bitmap, + (int copyResult) -> { latch.countDown(); }, new Handler(Looper.myLooper())); + }); + latch.timedAwait(); + int color = bitmap.getPixel(100, 100); + bitmap.recycle(); + return color; + } + + private void checkColorIsClose(int color1, int color2, int tolerance) { + Criteria.checkThat( + Math.abs(Color.alpha(color1) - Color.alpha(color2)), Matchers.lessThan(tolerance)); + Criteria.checkThat( + Math.abs(Color.red(color1) - Color.red(color2)), Matchers.lessThan(tolerance)); + Criteria.checkThat( + Math.abs(Color.green(color1) - Color.green(color2)), Matchers.lessThan(tolerance)); + Criteria.checkThat( + Math.abs(Color.blue(color1) - Color.blue(color2)), Matchers.lessThan(tolerance)); + } + + @Test + @SmallTest + public void testActivityShouldNotLeak() { + ReferenceQueue<InstrumentationActivity> referenceQueue = new ReferenceQueue<>(); + PhantomReference<InstrumentationActivity> reference; + { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + // This installs a fullscreen callback, and is to ensure setting a fullscreen callback + // doesn't leak. + TestFullscreenCallback fullscreenCallback = + new TestFullscreenCallback(mActivityTestRule); + mActivityTestRule.recreateActivity(); + boolean destroyed = + TestThreadUtils.runOnUiThreadBlockingNoException(() -> activity.isDestroyed()); + Assert.assertTrue(destroyed); + + reference = new PhantomReference<>(activity, referenceQueue); + } + + Runtime.getRuntime().gc(); + CriteriaHelper.pollInstrumentationThread(() -> { + Reference enqueuedReference = referenceQueue.poll(); + if (enqueuedReference == null) { + Runtime.getRuntime().gc(); + throw new CriteriaNotSatisfiedException("No enqueued reference"); + } + Assert.assertEquals(reference, enqueuedReference); + }); + } + + @Test + @SmallTest + public void testRecreateInstance() { + try { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + TestThreadUtils.runOnUiThreadBlocking(() -> { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + }); + mActivityTestRule.setRetainInstance(false); + Fragment firstFragment = mActivityTestRule.getFragment(); + + mActivityTestRule.recreateByRotatingToLandscape(); + boolean destroyed = + TestThreadUtils.runOnUiThreadBlockingNoException(() -> activity.isDestroyed()); + Assert.assertTrue(destroyed); + + Fragment secondFragment = mActivityTestRule.getFragment(); + Assert.assertNotSame(firstFragment, secondFragment); + } finally { + Activity activity = mActivityTestRule.getActivity(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + }); + } + } + + @Test + @SmallTest + public void testSetRetainInstance() { + ReferenceQueue<InstrumentationActivity> referenceQueue = new ReferenceQueue<>(); + PhantomReference<InstrumentationActivity> reference; + { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + + mActivityTestRule.setRetainInstance(true); + Fragment firstFragment = mActivityTestRule.getFragment(); + mActivityTestRule.recreateActivity(); + Fragment secondFragment = mActivityTestRule.getFragment(); + Assert.assertEquals(firstFragment, secondFragment); + + boolean destroyed = + TestThreadUtils.runOnUiThreadBlockingNoException(() -> activity.isDestroyed()); + Assert.assertTrue(destroyed); + reference = new PhantomReference<>(activity, referenceQueue); + } + + CriteriaHelper.pollInstrumentationThread(() -> { + Reference enqueuedReference = referenceQueue.poll(); + if (enqueuedReference == null) { + Runtime.getRuntime().gc(); + throw new CriteriaNotSatisfiedException("No enqueued reference"); + } + Assert.assertEquals(reference, enqueuedReference); + }); + } + + // Verifies recreating the Activity destroys the original Fragment when using ViewModel. + @Test + @SmallTest + public void testRecreateActivityDestroysFragmentUseViewModel() throws Throwable { + Bundle extras = new Bundle(); + extras.putBoolean(InstrumentationActivity.EXTRA_USE_VIEW_MODEL, true); + mActivityTestRule.launchShellWithUrl("about:blank", extras); + ReferenceQueue<Fragment> referenceQueue = new ReferenceQueue<>(); + PhantomReference<Fragment> reference = + new PhantomReference<>(mActivityTestRule.getFragment(), referenceQueue); + mActivityTestRule.recreateActivity(); + CriteriaHelper.pollInstrumentationThread(() -> { + Reference enqueuedReference = referenceQueue.poll(); + if (enqueuedReference == null) { + Runtime.getRuntime().gc(); + throw new CriteriaNotSatisfiedException("No enqueued reference"); + } + Assert.assertEquals(reference, enqueuedReference); + }); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabCallbackTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabCallbackTest.java new file mode 100644 index 0000000..6013659b --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabCallbackTest.java
@@ -0,0 +1,468 @@ +// 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. + +package org.chromium.weblayer.test; + +import android.net.Uri; +import android.os.Build; +import android.support.test.InstrumentationRegistry; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.DisableIf; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.content_public.browser.test.util.TestTouchUtils; +import org.chromium.weblayer.ContextMenuParams; +import org.chromium.weblayer.Profile; +import org.chromium.weblayer.ScrollNotificationType; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.TabCallback; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeoutException; + +/** + * Tests that TabCallback methods are invoked as expected. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class TabCallbackTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private static class Callback extends TabCallback { + public static class TabCallbackValueRecorder { + private List<String> mObservedValues = + Collections.synchronizedList(new ArrayList<String>()); + + public void recordValue(String parameter) { + mObservedValues.add(parameter); + } + + public List<String> getObservedValues() { + return mObservedValues; + } + + public void waitUntilValueObserved(String expectation) { + CriteriaHelper.pollInstrumentationThread( + () -> Criteria.checkThat(expectation, Matchers.isIn(mObservedValues))); + } + } + + public TabCallbackValueRecorder visibleUriChangedCallback = new TabCallbackValueRecorder(); + + @Override + public void onVisibleUriChanged(Uri uri) { + visibleUriChangedCallback.recordValue(uri.toString()); + } + } + + @Test + @SmallTest + public void testLoadEvents() { + String startupUrl = "about:blank"; + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(startupUrl); + + Callback callback = new Callback(); + TestThreadUtils.runOnUiThreadBlocking( + () -> { activity.getTab().registerTabCallback(callback); }); + + String url = "data:text,foo"; + mActivityTestRule.navigateAndWait(url); + + /* Verify that the visible URL changes to the target. */ + callback.visibleUriChangedCallback.waitUntilValueObserved(url); + } + + private ContextMenuParams runContextMenuTest(String file) throws TimeoutException { + String pageUrl = mActivityTestRule.getTestDataURL(file); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(pageUrl); + + ContextMenuParams params[] = new ContextMenuParams[1]; + CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Tab tab = activity.getTab(); + TabCallback callback = new TabCallback() { + @Override + public void showContextMenu(ContextMenuParams param) { + params[0] = param; + callbackHelper.notifyCalled(); + } + }; + tab.registerTabCallback(callback); + }); + + TestTouchUtils.longClickView( + InstrumentationRegistry.getInstrumentation(), activity.getWindow().getDecorView()); + callbackHelper.waitForFirst(); + Assert.assertEquals(Uri.parse(pageUrl), params[0].pageUri); + return params[0]; + } + + @Test + @SmallTest + public void testShowContextMenu() throws TimeoutException { + ContextMenuParams params = runContextMenuTest("download.html"); + Assert.assertEquals( + Uri.parse(mActivityTestRule.getTestDataURL("lorem_ipsum.txt")), params.linkUri); + Assert.assertEquals("anchor text", params.linkText); + } + + @Test + @SmallTest + public void testShowContextMenuImg() throws TimeoutException { + ContextMenuParams params = runContextMenuTest("img.html"); + Assert.assertEquals( + Uri.parse(mActivityTestRule.getTestDataURL("favicon.png")), params.srcUri); + Assert.assertEquals("alt_text", params.titleOrAltText); + } + + private File setTempDownloadDir() { + // Don't fill up the default download directory on the device. + File tempDownloadDirectory = new File( + InstrumentationRegistry.getInstrumentation().getTargetContext().getCacheDir() + + "/weblayer/Downloads"); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + Profile profile = mActivityTestRule.getActivity().getBrowser().getProfile(); + profile.setDownloadDirectory(tempDownloadDirectory); + }); + return tempDownloadDirectory; + } + + private void waitForFileExist(File filePath, String fileName) { + File file = new File(filePath, fileName); + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat("Invalid file existence state for: " + fileName, file.exists(), + Matchers.is(true)); + }); + file.delete(); + } + + @Test + @SmallTest + @DisableIf.Build(supported_abis_includes = "x86", sdk_is_greater_than = Build.VERSION_CODES.P, + message = "https://crbug.com/1201813") + @DisableIf.Build(supported_abis_includes = "x86_64", + sdk_is_greater_than = Build.VERSION_CODES.R, message = "https://crbug.com/1201813") + public void + testDownloadFromContextMenu() throws TimeoutException { + ContextMenuParams params = runContextMenuTest("download.html"); + ; + Assert.assertFalse(params.isImage); + Assert.assertFalse(params.isVideo); + Assert.assertTrue(params.canDownload); + + File tempDownloadDirectory = setTempDownloadDir(); + TestThreadUtils.runOnUiThreadBlocking( + () -> { mActivityTestRule.getActivity().getTab().download(params); }); + waitForFileExist(tempDownloadDirectory, "lorem_ipsum.txt"); + } + + @Test + @SmallTest + @DisableIf.Build(supported_abis_includes = "x86", sdk_is_greater_than = Build.VERSION_CODES.P, + message = "https://crbug.com/1201813") + @DisableIf.Build(supported_abis_includes = "x86_64", + sdk_is_greater_than = Build.VERSION_CODES.R, message = "https://crbug.com/1201813") + public void + testDownloadFromContextMenuImg() throws TimeoutException { + ContextMenuParams params = runContextMenuTest("img.html"); + ; + Assert.assertTrue(params.isImage); + Assert.assertFalse(params.isVideo); + Assert.assertTrue(params.canDownload); + + File tempDownloadDirectory = setTempDownloadDir(); + TestThreadUtils.runOnUiThreadBlocking( + () -> { mActivityTestRule.getActivity().getTab().download(params); }); + waitForFileExist(tempDownloadDirectory, "favicon.png"); + } + + @Test + @SmallTest + public void testTabModalOverlay() throws TimeoutException { + String pageUrl = mActivityTestRule.getTestDataURL("alert.html"); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(pageUrl); + Assert.assertNotNull(activity); + + Boolean isTabModalShowingResult[] = new Boolean[1]; + CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Tab tab = activity.getTab(); + TabCallback callback = new TabCallback() { + @Override + public void onTabModalStateChanged(boolean isTabModalShowing) { + isTabModalShowingResult[0] = isTabModalShowing; + callbackHelper.notifyCalled(); + } + }; + tab.registerTabCallback(callback); + }); + + int callCount = callbackHelper.getCallCount(); + EventUtils.simulateTouchCenterOfView(activity.getWindow().getDecorView()); + callbackHelper.waitForCallback(callCount); + Assert.assertEquals(true, isTabModalShowingResult[0]); + + callCount = callbackHelper.getCallCount(); + Assert.assertTrue(TestThreadUtils.runOnUiThreadBlockingNoException( + () -> activity.getTab().dismissTransientUi())); + callbackHelper.waitForCallback(callCount); + Assert.assertEquals(false, isTabModalShowingResult[0]); + } + + @Test + @SmallTest + public void testDismissTransientUi() throws TimeoutException { + String pageUrl = mActivityTestRule.getTestDataURL("alert.html"); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(pageUrl); + Assert.assertNotNull(activity); + + Boolean isTabModalShowingResult[] = new Boolean[1]; + CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Tab tab = activity.getTab(); + TabCallback callback = new TabCallback() { + @Override + public void onTabModalStateChanged(boolean isTabModalShowing) { + isTabModalShowingResult[0] = isTabModalShowing; + callbackHelper.notifyCalled(); + } + }; + tab.registerTabCallback(callback); + }); + + int callCount = callbackHelper.getCallCount(); + EventUtils.simulateTouchCenterOfView(activity.getWindow().getDecorView()); + callbackHelper.waitForCallback(callCount); + Assert.assertEquals(true, isTabModalShowingResult[0]); + + callCount = callbackHelper.getCallCount(); + Assert.assertTrue(TestThreadUtils.runOnUiThreadBlockingNoException( + () -> activity.getTab().dismissTransientUi())); + callbackHelper.waitForCallback(callCount); + Assert.assertEquals(false, isTabModalShowingResult[0]); + + Assert.assertFalse(TestThreadUtils.runOnUiThreadBlockingNoException( + () -> activity.getTab().dismissTransientUi())); + } + + @Test + @SmallTest + public void testTabModalOverlayOnBackgroundTab() throws TimeoutException { + // Create a tab. + String url = mActivityTestRule.getTestDataURL("new_browser.html"); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(url); + Assert.assertNotNull(activity); + NewTabCallbackImpl callback = new NewTabCallbackImpl(); + Tab firstTab = TestThreadUtils.runOnUiThreadBlockingNoException(() -> { + Tab tab = activity.getBrowser().getActiveTab(); + tab.setNewTabCallback(callback); + return tab; + }); + + // Tapping it creates a second tab, which is active. + EventUtils.simulateTouchCenterOfView(activity.getWindow().getDecorView()); + callback.waitForNewTab(); + + Tab secondTab = TestThreadUtils.runOnUiThreadBlockingNoException(() -> { + Assert.assertEquals(2, activity.getBrowser().getTabs().size()); + return activity.getBrowser().getActiveTab(); + }); + Assert.assertNotSame(firstTab, secondTab); + + // Track tab modal updates. + Boolean isTabModalShowingResult[] = new Boolean[1]; + CallbackHelper callbackHelper = new CallbackHelper(); + int callCount = callbackHelper.getCallCount(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + firstTab.registerTabCallback(new TabCallback() { + @Override + public void onTabModalStateChanged(boolean isTabModalShowing) { + isTabModalShowingResult[0] = isTabModalShowing; + callbackHelper.notifyCalled(); + } + }); + }); + + // Create an alert from the background tab. It shouldn't display. There's no way to + // consistently verify that nothing happens, but the script execution should finish, which + // is not the case for dialogs that show on an active tab until they're dismissed. + mActivityTestRule.executeScriptSync("window.alert('foo');", true, firstTab); + Assert.assertEquals(0, callbackHelper.getCallCount()); + + // When that tab becomes active again, the alert should show. + TestThreadUtils.runOnUiThreadBlocking( + () -> { activity.getBrowser().setActiveTab(firstTab); }); + callbackHelper.waitForCallback(callCount); + Assert.assertEquals(true, isTabModalShowingResult[0]); + + // Switch away from the tab again; the alert should be hidden. + callCount = callbackHelper.getCallCount(); + TestThreadUtils.runOnUiThreadBlocking( + () -> { activity.getBrowser().setActiveTab(secondTab); }); + callbackHelper.waitForCallback(callCount); + Assert.assertEquals(false, isTabModalShowingResult[0]); + } + + @Test + @SmallTest + public void testOnTitleUpdated() throws TimeoutException { + String startupUrl = "about:blank"; + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(startupUrl); + + String titles[] = new String[1]; + TestThreadUtils.runOnUiThreadBlocking(() -> { + Tab tab = activity.getTab(); + TabCallback callback = new TabCallback() { + @Override + public void onTitleUpdated(String title) { + titles[0] = title; + } + }; + tab.registerTabCallback(callback); + }); + + String url = mActivityTestRule.getTestDataURL("simple_page.html"); + mActivityTestRule.navigateAndWait(url); + // Use polling because title is allowed to go through multiple transitions. + CriteriaHelper.pollUiThread(() -> Criteria.checkThat(titles[0], Matchers.is("OK"))); + + url = mActivityTestRule.getTestDataURL("shakespeare.html"); + mActivityTestRule.navigateAndWait(url); + CriteriaHelper.pollUiThread( + () -> Criteria.checkThat(titles[0], Matchers.endsWith("shakespeare.html"))); + + mActivityTestRule.executeScriptSync("document.title = \"foobar\";", false); + Assert.assertEquals("foobar", titles[0]); + CriteriaHelper.pollUiThread(() -> Criteria.checkThat(titles[0], Matchers.is("foobar"))); + } + + @Test + @SmallTest + public void testOnBackgroundColorChanged() throws TimeoutException { + String startupUrl = "about:blank"; + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(startupUrl); + + Integer backgroundColors[] = new Integer[1]; + CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Tab tab = activity.getTab(); + TabCallback callback = new TabCallback() { + @Override + public void onBackgroundColorChanged(int color) { + backgroundColors[0] = color; + callbackHelper.notifyCalled(); + } + }; + tab.registerTabCallback(callback); + }); + + mActivityTestRule.executeScriptSync( + "document.body.style.backgroundColor = \"rgb(255, 0, 0)\"", + /*useSeparateIsolate=*/false); + + callbackHelper.waitForFirst(); + Assert.assertEquals(0xffff0000, (int) backgroundColors[0]); + } + + @Test + @SmallTest + @DisabledTest(message = "crbug.com/1339982") + public void testScrollNotificationDirectionChange() throws TimeoutException { + final String url = mActivityTestRule.getTestDataURL("tall_page.html"); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(url); + + Integer notificationTypes[] = new Integer[1]; + Float scrollRatio[] = new Float[1]; + CallbackHelper callbackHelper = new CallbackHelper(); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + Tab tab = activity.getTab(); + TabCallback callback = new TabCallback() { + @Override + public void onScrollNotification( + @ScrollNotificationType int notificationType, float currentScrollRatio) { + notificationTypes[0] = notificationType; + scrollRatio[0] = currentScrollRatio; + callbackHelper.notifyCalled(); + } + }; + tab.registerTabCallback(callback); + }); + + // Scroll to bottom of page. + int callCount = callbackHelper.getCallCount(); + mActivityTestRule.executeScriptSync("window.scroll(0, 5000)", + /*useSeparateIsolate=*/false); + callbackHelper.waitForCallback(callCount); + Assert.assertEquals( + ScrollNotificationType.DIRECTION_CHANGED_DOWN, (int) notificationTypes[0]); + Assert.assertTrue(scrollRatio[0] > 0.5); + + // Scroll to top of page. + callCount = callbackHelper.getCallCount(); + mActivityTestRule.executeScriptSync("window.scroll(0, 0)", + /*useSeparateIsolate=*/false); + callbackHelper.waitForCallback(callCount); + Assert.assertEquals( + ScrollNotificationType.DIRECTION_CHANGED_UP, (int) notificationTypes[0]); + Assert.assertTrue(scrollRatio[0] < 0.5); + } + + @Test + @SmallTest + @MinWebLayerVersion(101) + public void testOnVerticalOverscroll() throws TimeoutException { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + + float overscrollY[] = new float[1]; + CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Tab tab = activity.getTab(); + TabCallback callback = new TabCallback() { + @Override + public void onVerticalOverscroll(float accumulatedOverscrollY) { + overscrollY[0] = accumulatedOverscrollY; + callbackHelper.notifyCalled(); + tab.unregisterTabCallback(this); + } + }; + tab.registerTabCallback(callback); + }); + + View decorView[] = new View[1]; + int dimension[] = new int[2]; + TestThreadUtils.runOnUiThreadBlocking(() -> { + decorView[0] = activity.getWindow().getDecorView(); + dimension[0] = decorView[0].getWidth(); + dimension[1] = decorView[0].getHeight(); + }); + + int x = dimension[0] / 2; + int fromY = dimension[1] / 3; + int toY = dimension[1] / 3 * 2; + + TestTouchUtils.dragCompleteView(InstrumentationRegistry.getInstrumentation(), decorView[0], + /*fromX=*/x, /*toX=*/x, fromY, toY, /*stepCount=*/10); + callbackHelper.waitForFirst(); + Assert.assertThat(overscrollY[0], Matchers.lessThan(0f)); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabListCallbackTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabListCallbackTest.java new file mode 100644 index 0000000..329c019 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabListCallbackTest.java
@@ -0,0 +1,220 @@ +// 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. + +package org.chromium.weblayer.test; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.Browser; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.TabListCallback; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Tests that NewTabCallback methods are invoked as expected. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class TabListCallbackTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + private Tab mFirstTab; + private Tab mSecondTab; + + private static class TabListCallbackImpl extends TabListCallback { + public static final String ADDED = "added"; + public static final String ACTIVE = "active"; + public static final String REMOVED = "removed"; + public static final String WILL_DESTROY = "willdestroy"; + + private List<String> mObservedValues = + Collections.synchronizedList(new ArrayList<String>()); + + @Override + public void onActiveTabChanged(Tab activeTab) { + recordValue(ACTIVE); + } + + @Override + public void onTabAdded(Tab tab) { + recordValue(ADDED); + } + + @Override + public void onTabRemoved(Tab tab) { + recordValue(REMOVED); + } + + @Override + public void onWillDestroyBrowserAndAllTabs() { + recordValue(WILL_DESTROY); + } + + private void recordValue(String parameter) { + mObservedValues.add(parameter); + } + + public List<String> getObservedValues() { + return mObservedValues; + } + } + + protected void initialize(String testDataFile) { + String url = mActivityTestRule.getTestDataURL(testDataFile); + mActivity = mActivityTestRule.launchShellWithUrl(url); + Assert.assertNotNull(mActivity); + NewTabCallbackImpl callback = new NewTabCallbackImpl(); + mFirstTab = TestThreadUtils.runOnUiThreadBlockingNoException(() -> { + Tab tab = mActivity.getBrowser().getActiveTab(); + tab.setNewTabCallback(callback); + return tab; + }); + + EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView()); + callback.waitForNewTab(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Assert.assertEquals(2, mActivity.getBrowser().getTabs().size()); + mSecondTab = mActivity.getBrowser().getActiveTab(); + Assert.assertNotSame(mFirstTab, mSecondTab); + }); + } + + @Test + @SmallTest + public void testActiveTabChanged() { + initialize("new_browser.html"); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + TabListCallbackImpl callback = new TabListCallbackImpl(); + mActivity.getBrowser().registerTabListCallback(callback); + mActivity.getBrowser().setActiveTab(mFirstTab); + Assert.assertTrue(callback.getObservedValues().contains(TabListCallbackImpl.ACTIVE)); + }); + } + + @Test + @SmallTest + public void testMoveToDifferentFragment() { + initialize("new_browser.html"); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + Browser browser2 = Browser.fromFragment(mActivity.createBrowserFragment(0)); + Browser browser1 = mActivity.getBrowser(); + TabListCallbackImpl callback1 = new TabListCallbackImpl(); + browser1.registerTabListCallback(callback1); + + TabListCallbackImpl callback2 = new TabListCallbackImpl(); + browser2.registerTabListCallback(callback2); + + // Move the active tab from browser1 to browser2. + Tab tabToMove = browser1.getActiveTab(); + browser2.addTab(tabToMove); + // This should notify callback1 the active tab changed and a tab was removed. + int browser1ActiveIndex = + callback1.getObservedValues().indexOf(TabListCallbackImpl.ACTIVE); + Assert.assertNotSame(-1, browser1ActiveIndex); + int browser1RemoveIndex = + callback1.getObservedValues().indexOf(TabListCallbackImpl.REMOVED); + Assert.assertNotSame(-1, browser1RemoveIndex); + Assert.assertTrue(browser1ActiveIndex < browser1RemoveIndex); + Assert.assertSame(null, browser1.getActiveTab()); + Assert.assertEquals(1, browser1.getTabs().size()); + Assert.assertFalse(browser1.getTabs().contains(tabToMove)); + + // callback2 should be notified of the insert. + Assert.assertTrue(callback2.getObservedValues().contains(TabListCallbackImpl.ADDED)); + Assert.assertEquals(2, browser2.getTabs().size()); + Assert.assertTrue(browser2.getTabs().contains(tabToMove)); + }); + } + + @Test + @SmallTest + public void testDestroyTab() { + initialize("new_browser.html"); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + TabListCallbackImpl callback = new TabListCallbackImpl(); + Browser browser = mActivity.getBrowser(); + browser.registerTabListCallback(callback); + browser.destroyTab(mActivity.getBrowser().getActiveTab()); + Assert.assertTrue(callback.getObservedValues().contains(TabListCallbackImpl.ACTIVE)); + Assert.assertTrue(callback.getObservedValues().contains(TabListCallbackImpl.REMOVED)); + Assert.assertEquals(1, browser.getTabs().size()); + }); + } + + @Test + @SmallTest + public void testCallbackInvokedWhenTabClosedViaWebContents() { + initialize("new_tab_then_close.html"); + + OnTabRemovedTabListCallbackImpl closeTabCallback = new OnTabRemovedTabListCallbackImpl(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.getBrowser().registerTabListCallback(closeTabCallback); + // Switch to the first tab so clicking closes |secondTab|. + mSecondTab.getBrowser().setActiveTab(mFirstTab); + }); + + // Clicking on the tab again to callback to close the tab. + EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView()); + closeTabCallback.waitForCloseTab(); + } + + @Test + @SmallTest + public void testOnTabRemoved() throws Exception { + mActivity = mActivityTestRule.launchShellWithUrl("about:blank"); + CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Browser browser = mActivity.getBrowser(); + browser.registerTabListCallback(new TabListCallback() { + @Override + public void onTabRemoved(Tab tab) { + // |tab| should not be destroyed at this point. getGuid() is a good proxy + // for verifying the tab hasn't been destroyed. + tab.getGuid(); + callbackHelper.notifyCalled(); + } + }); + mActivity.getBrowser().destroyTab(mActivity.getBrowser().createTab()); + }); + callbackHelper.waitForFirst(); + } + + @Test + @SmallTest + public void testOnWillDestroyBrowserAndAllTabs() throws Exception { + mActivity = mActivityTestRule.launchShellWithUrl("about:blank"); + TabListCallbackImpl tabListCallback = new TabListCallbackImpl(); + CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.getBrowser().registerTabListCallback(tabListCallback); + mActivity.getBrowser().registerTabListCallback(new TabListCallback() { + @Override + public void onWillDestroyBrowserAndAllTabs() { + callbackHelper.notifyCalled(); + } + }); + mActivity.destroyFragment(); + }); + callbackHelper.waitForFirst(); + Assert.assertEquals(1, tabListCallback.getObservedValues().size()); + Assert.assertTrue( + tabListCallback.getObservedValues().contains(TabListCallbackImpl.WILL_DESTROY)); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabPrivateTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabPrivateTest.java new file mode 100644 index 0000000..cfd85ae --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabPrivateTest.java
@@ -0,0 +1,118 @@ +// Copyright 2020 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.weblayer.test; + +import android.os.RemoteException; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.Browser; +import org.chromium.weblayer.Navigation; +import org.chromium.weblayer.NavigationCallback; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.TabCallback; +import org.chromium.weblayer.TestWebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Tab tests that need to use WebLayerPrivate. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class TabPrivateTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private TestWebLayer getTestWebLayer() { + return TestWebLayer.getTestWebLayer(mActivityTestRule.getContextForWebLayer()); + } + + @Test + @SmallTest + public void testCreateTabWithAccessibilityEnabledCrashTest() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + TestThreadUtils.runOnUiThreadBlocking(() -> { + try { + getTestWebLayer().setAccessibilityEnabled(true); + } catch (RemoteException e) { + Assert.fail("Unable to enable accessibility"); + } + activity.getBrowser().createTab(); + }); + } + + @Test + @SmallTest + public void testAutoReloadOnBackgroundCrash() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + activity.setIgnoreRendererCrashes(); + + CallbackHelper renderProcessGoneHelper = new CallbackHelper(); + final Tab crashedTab = TestThreadUtils.runOnUiThreadBlocking(() -> { + Browser browser = activity.getBrowser(); + Tab originalTab = browser.getActiveTab(); + originalTab.registerTabCallback(new TabCallback() { + @Override + public void onRenderProcessGone() { + renderProcessGoneHelper.notifyCalled(); + } + }); + + // Place a different tab in the foreground. + browser.setActiveTab(browser.createTab()); + + return originalTab; + }); + + // Crash the background tab. + getTestWebLayer().crashTab(crashedTab); + renderProcessGoneHelper.waitForFirst(); + + // Expect a navigation to occur when the crashed background tab is brought to the front. + // See logic in content's navigation_controller_impl.cc for why this is not classified as a + // reload. + CallbackHelper navigationCompletedHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + crashedTab.getNavigationController().registerNavigationCallback( + new NavigationCallback() { + @Override + public void onNavigationCompleted(Navigation navigation) { + navigationCompletedHelper.notifyCalled(); + } + }); + + activity.getBrowser().setActiveTab(crashedTab); + }); + navigationCompletedHelper.waitForFirst(); + } + + @Test + @SmallTest + public void testOnRenderProcessGone() throws Exception { + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + CallbackHelper callbackHelper = new CallbackHelper(); + Tab tabToCrash = TestThreadUtils.runOnUiThreadBlocking(() -> { + Tab tab = activity.getTab(); + activity.setIgnoreRendererCrashes(); + TabCallback callback = new TabCallback() { + @Override + public void onRenderProcessGone() { + callbackHelper.notifyCalled(); + } + }; + tab.registerTabCallback(callback); + return tab; + }); + getTestWebLayer().crashTab(tabToCrash); + callbackHelper.waitForFirst(); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabTest.java new file mode 100644 index 0000000..e3116001 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabTest.java
@@ -0,0 +1,382 @@ +// Copyright 2020 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.weblayer.test; + +import android.graphics.Bitmap; +import android.graphics.Color; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +import androidx.fragment.app.Fragment; +import androidx.test.filters.SmallTest; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.ActionModeCallback; +import org.chromium.weblayer.ActionModeItemType; +import org.chromium.weblayer.Browser; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.TabListCallback; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +/** + * Tests for Tab. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class TabTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + + @Test + @SmallTest + public void testBeforeUnload() { + String url = mActivityTestRule.getTestDataURL("before_unload.html"); + mActivity = mActivityTestRule.launchShellWithUrl(url); + Assert.assertNotNull(mActivity); + Assert.assertTrue(mActivity.hasWindowFocus()); + + // Touch the view so that beforeunload will be respected (if the user doesn't interact with + // the tab, it's ignored). + EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView()); + // Round trip through the renderer to make sure te above touch is handled before we call + // dispatchBeforeUnloadAndClose(). + mActivityTestRule.executeScriptSync("var x = 1", true); + TestThreadUtils.runOnUiThreadBlocking( + () -> { mActivity.getBrowser().getActiveTab().dispatchBeforeUnloadAndClose(); }); + + // Wait till the main window loses focus due to the app modal beforeunload dialog. + BoundedCountDownLatch noFocusLatch = new BoundedCountDownLatch(1); + BoundedCountDownLatch hasFocusLatch = new BoundedCountDownLatch(1); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.getWindow() + .getDecorView() + .getViewTreeObserver() + .addOnWindowFocusChangeListener((boolean hasFocus) -> { + (hasFocus ? hasFocusLatch : noFocusLatch).countDown(); + }); + }); + noFocusLatch.timedAwait(); + + // Verify closing the tab works still while beforeunload is showing (no crash). + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.getBrowser().destroyTab(mActivity.getBrowser().getActiveTab()); + }); + + // Focus returns to the main window because the dialog is dismissed when the tab is + // destroyed. + hasFocusLatch.timedAwait(); + } + + @Test + @SmallTest + public void testBeforeUnloadNoHandler() { + String url = mActivityTestRule.getTestDataURL("simple_page.html"); + mActivity = mActivityTestRule.launchShellWithUrl(url); + Assert.assertNotNull(mActivity); + OnTabRemovedTabListCallbackImpl callback = new OnTabRemovedTabListCallbackImpl(); + // Verify that calling dispatchBeforeUnloadAndClose will close the tab asynchronously when + // there is no beforeunload handler. + Assert.assertFalse(TestThreadUtils.runOnUiThreadBlockingNoException(() -> { + mActivity.getBrowser().registerTabListCallback(callback); + Tab tab = mActivity.getBrowser().getActiveTab(); + tab.dispatchBeforeUnloadAndClose(); + return callback.hasClosed(); + })); + + callback.waitForCloseTab(); + } + + @Test + @SmallTest + public void testBeforeUnloadNoInteraction() { + String url = mActivityTestRule.getTestDataURL("before_unload.html"); + mActivity = mActivityTestRule.launchShellWithUrl(url); + Assert.assertNotNull(mActivity); + OnTabRemovedTabListCallbackImpl callback = new OnTabRemovedTabListCallbackImpl(); + // Verify that beforeunload is not run when there's no user action. + Assert.assertFalse(TestThreadUtils.runOnUiThreadBlockingNoException(() -> { + mActivity.getBrowser().registerTabListCallback(callback); + Tab tab = mActivity.getBrowser().getActiveTab(); + tab.dispatchBeforeUnloadAndClose(); + return callback.hasClosed(); + })); + + callback.waitForCloseTab(); + } + + private Bitmap captureScreenShot(float scale) throws TimeoutException { + Bitmap[] bitmapHolder = new Bitmap[1]; + int[] errorCodeHolder = new int[1]; + CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Tab tab = mActivity.getTab(); + tab.captureScreenShot(scale, (Bitmap bitmap, int errorCode) -> { + errorCodeHolder[0] = errorCode; + bitmapHolder[0] = bitmap; + callbackHelper.notifyCalled(); + }); + }); + callbackHelper.waitForFirst(); + Assert.assertNotNull(bitmapHolder[0]); + Assert.assertEquals(0, errorCodeHolder[0]); + return bitmapHolder[0]; + } + + private void checkQuadrantColors(Bitmap bitmap) { + int quarterWidth = bitmap.getWidth() / 4; + int quarterHeight = bitmap.getHeight() / 4; + Assert.assertEquals(Color.rgb(255, 0, 0), bitmap.getPixel(quarterWidth, quarterHeight)); + Assert.assertEquals(Color.rgb(0, 255, 0), bitmap.getPixel(quarterWidth * 3, quarterHeight)); + Assert.assertEquals(Color.rgb(0, 0, 255), bitmap.getPixel(quarterWidth, quarterHeight * 3)); + Assert.assertEquals( + Color.rgb(128, 128, 128), bitmap.getPixel(quarterWidth * 3, quarterHeight * 3)); + } + + @Test + @SmallTest + public void testCaptureScreenShot() throws TimeoutException { + String url = mActivityTestRule.getTestDataURL("quadrant_colors.html"); + mActivity = mActivityTestRule.launchShellWithUrl(url); + + Bitmap bitmap = captureScreenShot(1.f); + checkQuadrantColors(bitmap); + Bitmap halfBitmap = captureScreenShot(0.5f); + checkQuadrantColors(bitmap); + + final int allowedError = 10; + Assert.assertTrue(Math.abs(bitmap.getWidth() / 2 - halfBitmap.getWidth()) < allowedError); + Assert.assertTrue(Math.abs(bitmap.getHeight() / 2 - halfBitmap.getHeight()) < allowedError); + } + + @Test + @SmallTest + @MinWebLayerVersion(101) + @DisabledTest(message = "https://crbug.com/1319845") + public void testCaptureScreenShotAfterResize() throws TimeoutException, ExecutionException { + String url = mActivityTestRule.getTestDataURL("quadrant_colors.html"); + mActivity = mActivityTestRule.launchShellWithUrl(url); + + int newHeight = TestThreadUtils.runOnUiThreadBlocking(() -> { + View view = mActivity.getFragment().getView(); + int height = view.getHeight() + 10; + LinearLayout.LayoutParams params = + new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, height); + view.setLayoutParams(params); + view.requestLayout(); + return height; + }); + + CriteriaHelper.pollUiThread(() -> { + View view = mActivity.getFragment().getView(); + int height = view.getHeight(); + Criteria.checkThat(height, Matchers.is(newHeight)); + }); + + Bitmap bitmap = captureScreenShot(1.f); + checkQuadrantColors(bitmap); + } + + @Test + @SmallTest + public void testCaptureScreenShotDoesNotHang() throws TimeoutException { + String startupUrl = "about:blank"; + mActivity = mActivityTestRule.launchShellWithUrl(startupUrl); + + CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Tab tab = mActivity.getTab(); + tab.captureScreenShot(1.f, (Bitmap bitmap, int errorCode) -> { + // Failure is ok here, so not checking |bitmap| or |errorCode|. + callbackHelper.notifyCalled(); + }); + mActivity.destroyFragment(); + }); + callbackHelper.waitForFirst(); + } + + @Test + @SmallTest + public void testSetData() { + String startupUrl = "about:blank"; + mActivity = mActivityTestRule.launchShellWithUrl(startupUrl); + + Map<String, String> data = new HashMap<>(); + data.put("foo", "bar"); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Tab tab = mActivity.getTab(); + tab.setData(data); + Assert.assertEquals(data.get("foo"), tab.getData().get("foo")); + + tab.setData(new HashMap<>()); + Assert.assertTrue(tab.getData().isEmpty()); + }); + } + + @Test + @SmallTest + public void testSetDataMaxSize() { + String startupUrl = "about:blank"; + mActivity = mActivityTestRule.launchShellWithUrl(startupUrl); + + Map<String, String> data = new HashMap<>(); + data.put("big", new String(new char[10000])); + TestThreadUtils.runOnUiThreadBlocking(() -> { + try { + mActivity.getTab().setData(data); + } catch (IllegalArgumentException e) { + // Expected exception. + return; + } + Assert.fail("Expected IllegalArgumentException."); + }); + } + + @Test + @SmallTest + public void testCreateTab() throws Exception { + mActivity = mActivityTestRule.launchShellWithUrl("about:blank"); + CallbackHelper helper = new CallbackHelper(); + Tab tab = TestThreadUtils.runOnUiThreadBlocking(() -> { + Browser browser = mActivity.getBrowser(); + browser.registerTabListCallback(new TabListCallback() { + @Override + public void onTabAdded(Tab tab) { + helper.notifyCalled(); + } + }); + Tab newTab = mActivity.getBrowser().createTab(); + Assert.assertEquals(mActivity.getBrowser().getTabs().size(), 2); + Assert.assertNotEquals(newTab, mActivity.getTab()); + return newTab; + }); + helper.waitForFirst(); + + // Make sure the new tab can navigate correctly. + mActivityTestRule.navigateAndWait( + tab, mActivityTestRule.getTestDataURL("simple_page.html"), false); + } + + @Test + @SmallTest + public void testViewDetachedTabIsInvisible() throws Exception { + mActivity = mActivityTestRule.launchShellWithUrl("about:blank"); + + boolean hidden = mActivityTestRule.executeScriptAndExtractBoolean("document.hidden;"); + Assert.assertFalse(hidden); + + Fragment fragment = mActivityTestRule.getFragment(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + View fragmentView = fragment.getView(); + ViewGroup parent = (ViewGroup) fragmentView.getParent(); + parent.removeView(fragmentView); + }); + + hidden = mActivityTestRule.executeScriptAndExtractBoolean("document.hidden;"); + Assert.assertTrue(hidden); + } + + @Test + @SmallTest + @DisabledTest(message = "https://crbug.com/1248183") + // This is a regression test for https://crbug.com/1075744 . + public void testRotationDoesntChangeVisibility() throws Exception { + String url = mActivityTestRule.getTestDataURL("rotation.html"); + mActivity = mActivityTestRule.launchShellWithUrl(url); + mActivity.setRetainInstance(true); + Assert.assertNotNull(mActivity); + + // Touch to trigger fullscreen and rotation. + EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView()); + + // Wait for the page to be told the orientation changed. + CriteriaHelper.pollInstrumentationThread(() -> { + return mActivityTestRule.executeScriptAndExtractBoolean("gotOrientationChange", false); + }); + + // The WebContents should not have been hidden as a result of the rotation. + Assert.assertFalse(mActivityTestRule.executeScriptAndExtractBoolean("gotHide", false)); + } + + @Test + @SmallTest + public void setFloatingActionModeOverride() throws Exception { + mActivity = mActivityTestRule.launchShellWithUrl("about:blank"); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.getBrowser().getActiveTab().setFloatingActionModeOverride( + ActionModeItemType.SHARE, new ActionModeCallback() { + @Override + public void onActionItemClicked( + @ActionModeItemType int item, String selectedText) {} + }); + }); + + // Smoke test. It's not possible to trigger an action mode click in a test. + } + + @Test + @SmallTest + public void testWillAutomaticallyReloadAfterCrash() throws Exception { + mActivity = mActivityTestRule.launchShellWithUrl("about:blank"); + Browser browser2 = TestThreadUtils.runOnUiThreadBlocking(() -> { + Browser browser = mActivity.getBrowser(); + // Initial tab is foreground, so it won't automatically reload. + Tab initialTab = browser.getActiveTab(); + Assert.assertFalse(initialTab.willAutomaticallyReloadAfterCrash()); + + // New tab is background, so it will automatically reload. + Tab newTab = browser.createTab(); + Assert.assertEquals(browser.getTabs().size(), 2); + Assert.assertNotEquals(newTab, mActivity.getTab()); + Assert.assertNotEquals(newTab, browser.getActiveTab()); + Assert.assertTrue(newTab.willAutomaticallyReloadAfterCrash()); + + // New tab is foreground after being made active. + browser.setActiveTab(newTab); + Assert.assertEquals(newTab, browser.getActiveTab()); + Assert.assertFalse(newTab.willAutomaticallyReloadAfterCrash()); + Assert.assertTrue(initialTab.willAutomaticallyReloadAfterCrash()); + + // Add a second browser; both browsers can have tabs that think they're foreground. + Browser newBrowser = + Browser.fromFragment(mActivity.createBrowserFragment(android.R.id.content)); + Assert.assertTrue(initialTab.willAutomaticallyReloadAfterCrash()); + Assert.assertFalse(newTab.willAutomaticallyReloadAfterCrash()); + Assert.assertFalse(newBrowser.getActiveTab().willAutomaticallyReloadAfterCrash()); + + // Moving the activity to the background causes all tabs to be not foreground. + mActivity.moveTaskToBack(true); + return newBrowser; + }); + + CriteriaHelper.pollUiThread( + () + -> Criteria.checkThat(mActivity.getBrowser() + .getActiveTab() + .willAutomaticallyReloadAfterCrash(), + Matchers.is(true))); + TestThreadUtils.runOnUiThreadBlocking( + () + -> Assert.assertTrue( + browser2.getActiveTab().willAutomaticallyReloadAfterCrash())); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TestFullscreenCallback.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TestFullscreenCallback.java new file mode 100644 index 0000000..1e7b90d4 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TestFullscreenCallback.java
@@ -0,0 +1,73 @@ +// Copyright 2020 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.weblayer.test; + +import org.hamcrest.Matchers; +import org.junit.Assert; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.FullscreenCallback; + +import java.util.concurrent.TimeoutException; + +/** + * FullscreenCallback implementation for tests. + */ +public class TestFullscreenCallback extends FullscreenCallback { + public int mEnterFullscreenCount; + public int mExitFullscreenCount; + public Runnable mExitFullscreenRunnable; + private int mCallCountToWaitFor; + private final InstrumentationActivityTestRule mTestRule; + private final CallbackHelper mCallbackHelper; + + public TestFullscreenCallback(InstrumentationActivityTestRule testRule) { + mTestRule = testRule; + mCallbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking( + () -> { mTestRule.getActivity().getTab().setFullscreenCallback(this); }); + } + + @Override + public void onEnterFullscreen(Runnable exitFullscreenRunner) { + mEnterFullscreenCount++; + mExitFullscreenRunnable = exitFullscreenRunner; + mCallbackHelper.notifyCalled(); + } + + @Override + public void onExitFullscreen() { + mExitFullscreenCount++; + mCallbackHelper.notifyCalled(); + } + + public void waitForFullscreen() { + waitForFullscreenImpl(true); + } + + public void waitForExitFullscreen() { + waitForFullscreenImpl(false); + } + + private void waitForFullscreenImpl(boolean isFullscreen) { + try { + mCallbackHelper.waitForCallback(mCallCountToWaitFor++); + } catch (TimeoutException e) { + Assert.fail("Timeout waiting for fullscreen change"); + return; + } + // Handles tests that destroy tab. + if (mTestRule.getActivity().getTab() == null) return; + CriteriaHelper.pollInstrumentationThread( + () -> { Criteria.checkThat(isPageFullscreen(), Matchers.is(isFullscreen)); }); + } + + private boolean isPageFullscreen() { + return mTestRule.executeScriptAndExtractBoolean("document.fullscreenElement != null"); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TranslateTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TranslateTest.java new file mode 100644 index 0000000..9fff05da --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TranslateTest.java
@@ -0,0 +1,146 @@ +// Copyright 2020 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.weblayer.test; + +import android.content.Context; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.TestWebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Basic tests to make sure WebLayer works as expected. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class TranslateTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + private Context mRemoteContext; + private String mPackageName; + + @Before + public void setUp() throws Exception { + mActivity = mActivityTestRule.launchShellWithUrl("about:blank"); + mRemoteContext = TestWebLayer.getRemoteContext(mActivity.getApplicationContext()); + mPackageName = + TestWebLayer.getWebLayerContext(mActivity.getApplicationContext()).getPackageName(); + TestWebLayer testWebLayer = TestWebLayer.getTestWebLayer(mActivity.getApplicationContext()); + testWebLayer.setIgnoreMissingKeyForTranslateManager(true); + testWebLayer.forceNetworkConnectivityState(true); + } + + @Test + @SmallTest + public void testCanTranslate() { + TestThreadUtils.runOnUiThreadBlocking(() -> { + Assert.assertFalse(mActivity.getBrowser().getActiveTab().canTranslate()); + }); + mActivityTestRule.navigateAndWait(mActivityTestRule.getTestDataURL("fr_test.html")); + TestThreadUtils.runOnUiThreadBlocking( + () -> { Assert.assertTrue(mActivity.getBrowser().getActiveTab().canTranslate()); }); + } + + @Test + @SmallTest + public void testShowTranslateUi() throws Exception { + mActivityTestRule.navigateAndWait(mActivityTestRule.getTestDataURL("fr_test.html")); + waitForInfoBarToShow(); + Assert.assertEquals("English", getInfoBarTargetLanguage()); + + EventUtils.simulateTouchCenterOfView(findViewByStringId("id/infobar_close_button")); + waitForInfoBarToHide(); + + TestThreadUtils.runOnUiThreadBlocking( + () -> { mActivity.getBrowser().getActiveTab().showTranslateUi(); }); + waitForInfoBarToShow(); + } + + @Test + @SmallTest + @DisabledTest(message = "https://crbug.com/1261207") + public void testOverridingOfTargetLanguage() throws Exception { + // Sanity-check that by default the infobar appears with the target language of the user's + // locale. + mActivityTestRule.navigateAndWait(mActivityTestRule.getTestDataURL("french_page.html")); + waitForInfoBarToShow(); + Assert.assertEquals("English", getInfoBarTargetLanguage()); + + EventUtils.simulateTouchCenterOfView(findViewByStringId("id/infobar_close_button")); + waitForInfoBarToHide(); + + // Verify overriding of the target language. + Tab tab = mActivityTestRule.getActivity().getTab(); + TestThreadUtils.runOnUiThreadBlocking(() -> { tab.setTranslateTargetLanguage("de"); }); + mActivityTestRule.navigateAndWait(mActivityTestRule.getTestDataURL("french_page.html")); + waitForInfoBarToShow(); + Assert.assertEquals("German", getInfoBarTargetLanguage()); + + EventUtils.simulateTouchCenterOfView(findViewByStringId("id/infobar_close_button")); + waitForInfoBarToHide(); + + // Check that the setting persists in the Tab by navigating to another page in French via a + // link click. + mActivityTestRule.executeScriptSync( + "document.onclick = function() {document.getElementById('link_to_french_page2').click()}", + true /* useSeparateIsolate */); + EventUtils.simulateTouchCenterOfView( + mActivityTestRule.getActivity().getWindow().getDecorView()); + waitForInfoBarToShow(); + Assert.assertEquals("German", getInfoBarTargetLanguage()); + + EventUtils.simulateTouchCenterOfView(findViewByStringId("id/infobar_close_button")); + waitForInfoBarToHide(); + + // Check that setting an empty string as the predefined target language causes behavior to + // revert to default. + TestThreadUtils.runOnUiThreadBlocking(() -> { tab.setTranslateTargetLanguage(""); }); + mActivityTestRule.navigateAndWait(mActivityTestRule.getTestDataURL("french_page.html")); + waitForInfoBarToShow(); + Assert.assertEquals("English", getInfoBarTargetLanguage()); + } + + private View findViewByStringId(String id) { + return mActivity.findViewById(ResourceUtil.getIdentifier(mRemoteContext, id, mPackageName)); + } + + private void waitForInfoBarToShow() { + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(findViewByStringId("id/weblayer_translate_infobar_content"), + Matchers.notNullValue()); + }); + } + + private void waitForInfoBarToHide() { + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(findViewByStringId("id/weblayer_translate_infobar_content"), + Matchers.nullValue()); + }); + } + + private String getInfoBarTargetLanguage() throws Exception { + TestWebLayer testWebLayer = TestWebLayer.getTestWebLayer(mActivity.getApplicationContext()); + return TestThreadUtils.runOnUiThreadBlocking(() -> { + return testWebLayer.getTranslateInfoBarTargetLanguage( + mActivity.getBrowser().getActiveTab()); + }); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebAuthnTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebAuthnTest.java new file mode 100644 index 0000000..cbdb7ed --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebAuthnTest.java
@@ -0,0 +1,89 @@ +// 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. + +package org.chromium.weblayer.test; + +import static org.junit.Assert.assertEquals; + +import android.os.Build; +import android.os.RemoteException; + +import androidx.test.filters.MediumTest; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.MinAndroidSdkLevel; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.content_public.common.ContentSwitches; +import org.chromium.weblayer.TabCallback; +import org.chromium.weblayer.TestWebLayer; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.util.concurrent.TimeoutException; + +/** + * Tests WebLayer's implementation of the WebAuthn API. + */ +@CommandLineFlags. +Add({ContentSwitches.HOST_RESOLVER_RULES + "=\"MAP * 127.0.0.1\"", "ignore-certificate-errors"}) +@RunWith(WebLayerJUnit4ClassRunner.class) +@MinAndroidSdkLevel(Build.VERSION_CODES.N) // WebAuthn is only supported on Android N+ +public class WebAuthnTest { + // Should match the domain specified in authenticator.html + private static final String TEST_DOMAIN = "subdomain.example.test"; + private static final String TEST_FILE = "/content/test/data/android/authenticator.html"; + + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + private InstrumentationActivity mActivity; + + private static class TitleWatcher extends TabCallback { + private final CallbackHelper mCallbackHelper = new CallbackHelper(); + private String mTitle; + + @Override + public void onTitleUpdated(String title) { + mTitle = title; + mCallbackHelper.notifyCalled(); + } + + public String waitForTitleChange() throws TimeoutException { + if (mTitle == null) { + mCallbackHelper.waitForCallback(mCallbackHelper.getCallCount()); + } + return mTitle; + } + } + + @Before + public void setUp() throws RemoteException { + mActivityTestRule.getTestServerRule().setServerUsesHttps(true); + mActivity = mActivityTestRule.launchShellWithUrl("about:blank"); + TestWebLayer.getTestWebLayer(mActivity.getApplicationContext()) + .setMockWebAuthnEnabled(true); + } + + @Test + @MediumTest + public void testCreatePublicKeyCredential() throws Exception { + String url = mActivityTestRule.getTestServer().getURLWithHostName(TEST_DOMAIN, TEST_FILE); + mActivityTestRule.navigateAndWait(url); + TitleWatcher titleWatcher = new TitleWatcher(); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity.getTab().registerTabCallback(titleWatcher); + mActivity.getTab().executeScript( + "doCreatePublicKeyCredential()", false /* useSeparateIsolate */, null); + }); + + String title = titleWatcher.waitForTitleChange(); + assertEquals("Success", title); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebLayerActivityTestRule.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebLayerActivityTestRule.java new file mode 100644 index 0000000..a7de5c43 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebLayerActivityTestRule.java
@@ -0,0 +1,68 @@ +// Copyright 2020 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.weblayer.test; + +import android.app.Activity; +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.text.TextUtils; + +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import org.chromium.base.CommandLine; +import org.chromium.base.test.BaseActivityTestRule; + +import java.io.File; +import java.io.OutputStreamWriter; +import java.io.Writer; + +/** + * Base ActivityTestRule for WebLayer instrumentation tests. + * + * This rule contains some common setup needed to deal with WebLayer's multiple classloaders. + */ +abstract class WebLayerActivityTestRule<T extends Activity> extends BaseActivityTestRule<T> { + private static final String COMMAND_LINE_FILE = "weblayer-command-line"; + + public WebLayerActivityTestRule(Class<T> clazz) { + super(clazz); + } + + /** + * Writes the command line file. This can be useful if a test needs to dynamically add command + * line arguments before WebLayer has been loaded. + */ + public void writeCommandLineFile() throws Exception { + // The CommandLine instance we have here will not be picked up in the + // implementation since they use different class loaders, so we need to write + // all the switches to the WebLayer command line file. + try (Writer writer = new OutputStreamWriter( + InstrumentationRegistry.getInstrumentation().getTargetContext().openFileOutput( + COMMAND_LINE_FILE, Context.MODE_PRIVATE), + "UTF-8")) { + writer.write(TextUtils.join(" ", CommandLine.getJavaSwitchesOrNull())); + } + } + + @Override + public Statement apply(final Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + try { + writeCommandLineFile(); + base.evaluate(); + } finally { + new File(InstrumentationRegistry.getInstrumentation() + .getTargetContext() + .getFilesDir(), + COMMAND_LINE_FILE) + .delete(); + } + } + }; + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebLayerJUnit4ClassRunner.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebLayerJUnit4ClassRunner.java new file mode 100644 index 0000000..e19b5de --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebLayerJUnit4ClassRunner.java
@@ -0,0 +1,35 @@ +// 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. + +package org.chromium.weblayer.test; + +import android.support.test.InstrumentationRegistry; + +import org.junit.runners.model.InitializationError; + +import org.chromium.base.test.BaseJUnit4ClassRunner; +import org.chromium.base.test.util.SkipCheck; +import org.chromium.ui.test.util.UiDisableIfSkipCheck; + +import java.util.List; + +/** + * A custom runner for //weblayer JUnit4 tests. + */ +public class WebLayerJUnit4ClassRunner extends BaseJUnit4ClassRunner { + /** + * Create a WebLayerJUnit4ClassRunner to run {@code klass} and initialize values + * + * @throws InitializationError if the test class malformed + */ + public WebLayerJUnit4ClassRunner(final Class<?> klass) throws InitializationError { + super(klass); + } + + @Override + protected List<SkipCheck> getSkipChecks() { + return addToList(super.getSkipChecks(), new MinWebLayerVersionSkipCheck(), + new UiDisableIfSkipCheck(InstrumentationRegistry.getContext())); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebLayerLoadingTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebLayerLoadingTest.java new file mode 100644 index 0000000..b49835d --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebLayerLoadingTest.java
@@ -0,0 +1,142 @@ +// 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. + +package org.chromium.weblayer.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.ContextUtils; +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.Callback; +import org.chromium.weblayer.UnsupportedVersionException; +import org.chromium.weblayer.WebLayer; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +/** + * Tests for {@link Weblayer#createAsync} and {@link Weblayer#createSync}. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class WebLayerLoadingTest { + private Context mContext; + + @Before + public void setUp() { + mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + ContextUtils.initApplicationContextForTests(mContext); + } + + @Test + @SmallTest + public void loadsSync() { + assertNotNull(loadSync()); + } + + @Test + @SmallTest + public void loadsAsync() { + loadAsyncAndWait(webLayer -> { assertNotNull(webLayer); }); + } + + @Test + @SmallTest + public void twoSequentialAsyncLoadsYieldSameInstance() { + loadAsyncAndWait(webLayer1 -> { + loadAsyncAndWait(webLayer2 -> { assertEquals(webLayer1, webLayer2); }); + }); + } + + @Test + @SmallTest + public void twoParallelAsyncLoadsYieldSameInstance() { + List<WebLayer> asyncResults = new ArrayList<>(); + for (int i = 0; i < 2; i++) { + loadAsyncAndWait(webLayer -> { asyncResults.add(webLayer); }); + } + assertEquals(asyncResults.get(0), asyncResults.get(1)); + } + + @Test + @SmallTest + public void syncLoadAfterAsyncLoadYieldsTheSameInstance() { + loadAsyncAndWait(webLayer1 -> { + WebLayer webLayer2 = loadSync(); + assertEquals(webLayer1, webLayer2); + }); + } + + @Test + @SmallTest + public void asyncLoadAfterSyncLoadYieldsTheSameInstance() { + WebLayer webLayer1 = loadSync(); + loadAsyncAndWait(webLayer2 -> { assertEquals(webLayer1, webLayer2); }); + } + + @Test + @SmallTest + public void syncLoadDuringAsyncLoadYieldsTheSameInstance() throws Exception { + List<WebLayer> asyncResults = new ArrayList<>(); + CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + try { + WebLayer.loadAsync(mContext, webLayer -> { + asyncResults.add(webLayer); + callbackHelper.notifyCalled(); + }); + } catch (UnsupportedVersionException e) { + throw new RuntimeException(e); + } + }); + WebLayer webLayer2 = loadSync(); + callbackHelper.waitForFirst(); + assertEquals(asyncResults.get(0), webLayer2); + } + + @Test + @SmallTest + public void twoSyncLoadsYieldSameInstance() { + assertEquals(loadSync(), loadSync()); + } + + private WebLayer loadSync() { + try { + return TestThreadUtils.runOnUiThreadBlocking(() -> WebLayer.loadSync(mContext)); + } catch (ExecutionException e) { + throw new RuntimeException(e.getCause()); + } + } + + private void loadAsyncAndWait(Callback<WebLayer> callback) { + CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + try { + WebLayer.loadAsync(mContext, webLayer -> { + callback.onResult(webLayer); + callbackHelper.notifyCalled(); + }); + } catch (UnsupportedVersionException e) { + throw new RuntimeException(e); + } + }); + try { + callbackHelper.waitForFirst(); + } catch (TimeoutException e) { + throw new RuntimeException(e); + } + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebLayerTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebLayerTest.java new file mode 100644 index 0000000..8565818 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebLayerTest.java
@@ -0,0 +1,80 @@ +// Copyright 2020 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.weblayer.test; + +import android.net.Uri; + +import androidx.test.filters.SmallTest; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.Callback; +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.components.browser_ui.share.ShareImageFileUtils; +import org.chromium.content_public.browser.test.util.TestThreadUtils; + +import java.io.File; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * Tests for the WebLayer class. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class WebLayerTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private class GenerateUriCallback extends CallbackHelper implements Callback<Uri> { + private Uri mImageUri; + + public Uri getImageUri() { + return mImageUri; + } + + @Override + public void onResult(Uri uri) { + mImageUri = uri; + notifyCalled(); + } + } + + @Test + @SmallTest + public void getUserAgentString() { + final String userAgent = TestThreadUtils.runOnUiThreadBlockingNoException( + () -> { return mActivityTestRule.getWebLayer().getUserAgentString(); }); + Assert.assertNotNull(userAgent); + Assert.assertFalse(userAgent.isEmpty()); + } + + @MinWebLayerVersion(94) + @Test + @SmallTest + public void deletesTemporaryImagesOnInit() throws TimeoutException { + // Create a temporary image to simulate an old file left from a previous share. + GenerateUriCallback imageCallback = new GenerateUriCallback(); + ShareImageFileUtils.generateTemporaryUriFromData(new byte[] {1, 2}, ".png", imageCallback); + imageCallback.waitForCallback(0, 1, 30L, TimeUnit.SECONDS); + File imageFile = new File(imageCallback.getImageUri().getPath()); + + // Verify that the file now exists. + Assert.assertTrue(imageFile.exists()); + + // Create a WebLayer instance which should delete any previously generated images. + mActivityTestRule.getWebLayer(); + + // Verify that the file has been deleted. Note that this happens in the background. + CriteriaHelper.pollInstrumentationThread( + () -> Criteria.checkThat(imageFile.exists(), Matchers.is(false))); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebMessageTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebMessageTest.java new file mode 100644 index 0000000..932bf11 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebMessageTest.java
@@ -0,0 +1,260 @@ +// Copyright 2020 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.weblayer.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking; + +import androidx.test.filters.SmallTest; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.net.test.util.TestWebServer; +import org.chromium.weblayer.WebLayer; +import org.chromium.weblayer.WebMessage; +import org.chromium.weblayer.WebMessageCallback; +import org.chromium.weblayer.WebMessageReplyProxy; +import org.chromium.weblayer.shell.InstrumentationActivity; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Verifies WebMessage related APIs. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class WebMessageTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + // WebMessageCallback that stores last values supplied to onWebMessageReceived(). + private static final class WebMessageCallbackImpl extends WebMessageCallback { + public WebMessageReplyProxy mLastProxy; + public WebMessage mLastMessage; + public CallbackHelper mCallbackHelper; + public CallbackHelper mClosedCallbackHelper; + public CallbackHelper mActiveChangedCallbackHelper; + public WebMessageReplyProxy mProxyClosed; + + WebMessageCallbackImpl(CallbackHelper callbackHelper) { + mCallbackHelper = callbackHelper; + } + @Override + public void onWebMessageReceived(WebMessageReplyProxy replyProxy, WebMessage message) { + mLastProxy = replyProxy; + mLastMessage = message; + mCallbackHelper.notifyCalled(); + } + + public void reset() { + mLastProxy = null; + mLastMessage = null; + mProxyClosed = null; + } + + @Override + public void onWebMessageReplyProxyClosed(WebMessageReplyProxy replyProxy) { + mProxyClosed = replyProxy; + if (mClosedCallbackHelper != null) mClosedCallbackHelper.notifyCalled(); + } + + @Override + public void onWebMessageReplyProxyActiveStateChanged(WebMessageReplyProxy replyProxy) { + if (mActiveChangedCallbackHelper != null) mActiveChangedCallbackHelper.notifyCalled(); + } + } + + @Test + @SmallTest + public void testPostMessage() throws Exception { + // Load a page with a form. + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + assertNotNull(activity); + CallbackHelper callbackHelper = new CallbackHelper(); + WebMessageCallbackImpl webMessageCallback = new WebMessageCallbackImpl(callbackHelper); + runOnUiThreadBlocking(() -> { + activity.getTab().registerWebMessageCallback( + webMessageCallback, "x", Arrays.asList("*")); + }); + + mActivityTestRule.navigateAndWait( + mActivityTestRule.getTestDataURL("web_message_test.html")); + // web_message_test.html posts a message, wait for it. + callbackHelper.waitForCallback(0); + assertNotNull(webMessageCallback.mLastMessage); + assertNotNull(webMessageCallback.mLastProxy); + assertEquals("from page", webMessageCallback.mLastMessage.getContents()); + final WebMessageReplyProxy lastProxy = webMessageCallback.mLastProxy; + int majorVersion = TestThreadUtils.runOnUiThreadBlockingNoException( + () -> WebLayer.getSupportedMajorVersion(mActivityTestRule.getActivity())); + if (majorVersion >= 99) { + assertNotNull(runOnUiThreadBlocking(() -> { return lastProxy.getPage(); })); + } + webMessageCallback.reset(); + + int currentCallCount = callbackHelper.getCallCount(); + // Send a message, which the page should ack back. + runOnUiThreadBlocking(() -> { lastProxy.postMessage(new WebMessage("Z")); }); + callbackHelper.waitForCallback(currentCallCount); + assertNotNull(webMessageCallback.mLastMessage); + assertEquals("bouncing Z", webMessageCallback.mLastMessage.getContents()); + assertEquals(lastProxy, webMessageCallback.mLastProxy); + + runOnUiThreadBlocking(() -> { activity.getTab().unregisterWebMessageCallback("x"); }); + } + + @Test + @SmallTest + public void testOnWebMessageReplyProxyClosed() throws Exception { + // Load a page with a form. + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + assertNotNull(activity); + CallbackHelper callbackHelper = new CallbackHelper(); + WebMessageCallbackImpl webMessageCallback = new WebMessageCallbackImpl(callbackHelper); + runOnUiThreadBlocking(() -> { + activity.getTab().registerWebMessageCallback( + webMessageCallback, "x", Arrays.asList("*")); + }); + + int majorVersion = TestThreadUtils.runOnUiThreadBlockingNoException( + () -> WebLayer.getSupportedMajorVersion(mActivityTestRule.getActivity())); + mActivityTestRule.navigateAndWait( + mActivityTestRule.getTestDataURL("web_message_test.html")); + // web_message_test.html posts a message, wait for it. + callbackHelper.waitForCallback(0); + assertNotNull(webMessageCallback.mLastMessage); + assertNotNull(webMessageCallback.mLastProxy); + WebMessageReplyProxy proxy = webMessageCallback.mLastProxy; + assertNull(webMessageCallback.mProxyClosed); + assertFalse( + runOnUiThreadBlocking(() -> { return webMessageCallback.mLastProxy.isClosed(); })); + if (majorVersion >= 90) { + assertTrue(runOnUiThreadBlocking( + () -> { return webMessageCallback.mLastProxy.isActive(); })); + } + webMessageCallback.reset(); + webMessageCallback.mClosedCallbackHelper = new CallbackHelper(); + + // Navigate to a new page. The proxy should be closed. + mActivityTestRule.navigateAndWait("about:blank"); + webMessageCallback.mClosedCallbackHelper.waitForFirst(); + assertNotNull(webMessageCallback.mProxyClosed); + assertEquals(webMessageCallback.mProxyClosed, proxy); + assertTrue(runOnUiThreadBlocking(() -> { return proxy.isClosed(); })); + if (majorVersion >= 90) { + assertFalse(runOnUiThreadBlocking(() -> { return proxy.isActive(); })); + } + } + + @Test + @SmallTest + public void testBadArguments() throws Exception { + // Load a page with a form. + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + assertNotNull(activity); + WebMessageCallback webMessageCallback = new WebMessageCallback() {}; + runOnUiThreadBlocking(() -> { + // Invalid JS object name. + try { + activity.getTab().registerWebMessageCallback( + webMessageCallback, "", Arrays.asList("*")); + fail(); + } catch (IllegalArgumentException e) { + } + + // Origins can not have empty strings. + try { + activity.getTab().registerWebMessageCallback( + webMessageCallback, "x", Arrays.asList("")); + fail(); + } catch (IllegalArgumentException e) { + } + + // Origins can not be empty. + try { + activity.getTab().registerWebMessageCallback( + webMessageCallback, "x", new ArrayList<String>()); + fail(); + } catch (IllegalArgumentException e) { + } + + // Invalid origin. + try { + activity.getTab().registerWebMessageCallback( + webMessageCallback, "x", Arrays.asList("***")); + fail(); + } catch (IllegalArgumentException e) { + } + }); + } + + /* Disable BackForwardCacheMemoryControls to allow BackForwardCache for all devices regardless + * of their memory. */ + @MinWebLayerVersion(90) + @Test + @SmallTest + @CommandLineFlags. + Add({"enable-features=BackForwardCache", "disable-features=BackForwardCacheMemoryControls"}) + public void onActiveChangedForBackForwardCache() throws Exception { + TestWebServer testServer = TestWebServer.start(); + InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); + assertNotNull(activity); + + // Load a page with a message proxy and wait for the respond. + CallbackHelper callbackHelper = new CallbackHelper(); + WebMessageCallbackImpl webMessageCallback = new WebMessageCallbackImpl(callbackHelper); + String url = mActivityTestRule.getTestDataURL("web_message_test.html"); + int index = url.indexOf("/weblayer"); + assertNotEquals(-1, index); + runOnUiThreadBlocking(() -> { + activity.getTab().registerWebMessageCallback( + webMessageCallback, "x", Arrays.asList(url.substring(0, index))); + }); + mActivityTestRule.navigateAndWait( + mActivityTestRule.getTestDataURL("web_message_test.html")); + callbackHelper.waitForFirst(); + + // There should be a proxy and it should be active. + WebMessageReplyProxy proxy = webMessageCallback.mLastProxy; + assertTrue( + runOnUiThreadBlocking(() -> { return webMessageCallback.mLastProxy.isActive(); })); + webMessageCallback.reset(); + webMessageCallback.mActiveChangedCallbackHelper = new CallbackHelper(); + + // Navigate to a new page. The proxy should be inactive, but not closed. + String url2 = testServer.setResponse("/ok.html", "<html>ok</html>", null); + mActivityTestRule.navigateAndWait(url2); + webMessageCallback.mActiveChangedCallbackHelper.waitForCallback(0); + assertFalse(runOnUiThreadBlocking(() -> { return proxy.isActive(); })); + assertFalse(runOnUiThreadBlocking(() -> { return proxy.isClosed(); })); + + // Navigate back and ensure the page is active. + runOnUiThreadBlocking(() -> { activity.getTab().getNavigationController().goBack(); }); + webMessageCallback.mActiveChangedCallbackHelper.waitForCallback(1); + assertTrue(runOnUiThreadBlocking(() -> { return proxy.isActive(); })); + assertFalse(runOnUiThreadBlocking(() -> { return proxy.isClosed(); })); + + // Post a message, to ensure the page can still get it. + webMessageCallback.reset(); + webMessageCallback.mCallbackHelper = new CallbackHelper(); + runOnUiThreadBlocking(() -> { proxy.postMessage(new WebMessage("2")); }); + webMessageCallback.mCallbackHelper.waitForFirst(); + assertNotNull(webMessageCallback.mLastMessage); + assertEquals("bouncing 2", webMessageCallback.mLastMessage.getContents()); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebViewCompatibilityTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebViewCompatibilityTest.java new file mode 100644 index 0000000..a901610 --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/WebViewCompatibilityTest.java
@@ -0,0 +1,84 @@ +// Copyright 2020 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.weblayer.test; + +import android.os.Bundle; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.StrictModeContext; +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Tests for compatibility with running WebView and WebLayer in the same process. These tests only + * make sense when WebView and WebLayer are both being loaded from the same APK. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class WebViewCompatibilityTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + @Test + @SmallTest + @DisabledTest(message = "http://crbug.com/1273417") // Failing on android-arm64-proguard-rel + public void testBothLoadPageWebLayerFirst() throws Exception { + mActivityTestRule.launchShellWithUrl(mActivityTestRule.getTestDataURL("simple_page.html")); + + loadPageWithWebView(mActivityTestRule.getTestDataURL("simple_page2.html")); + + // Make sure WebLayer still loads. + mActivityTestRule.navigateAndWait(mActivityTestRule.getTestDataURL("simple_page3.html")); + } + + @Test + @SmallTest + @DisabledTest(message = "http://crbug.com/1273417") // Failing on android-arm64-proguard-rel + public void testBothLoadPageWebViewFirst() throws Exception { + Bundle extras = new Bundle(); + extras.putBoolean(InstrumentationActivity.EXTRA_CREATE_WEBLAYER, false); + InstrumentationActivity activity = mActivityTestRule.launchShell(extras); + + loadPageWithWebView(mActivityTestRule.getTestDataURL("simple_page.html")); + + TestThreadUtils.runOnUiThreadBlocking( + () -> activity.loadWebLayerSync(activity.getApplicationContext())); + mActivityTestRule.navigateAndWait(mActivityTestRule.getTestDataURL("simple_page2.html")); + + // Make sure WebView still loads. + loadPageWithWebView(mActivityTestRule.getTestDataURL("simple_page3.html")); + } + + private void loadPageWithWebView(String urlToLoad) throws Exception { + WebView webView = TestThreadUtils.runOnUiThreadBlocking(() -> { + // Loading WebView triggers loading from disk. + try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) { + return new WebView(mActivityTestRule.getActivity()); + } + }); + CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + webView.setWebViewClient(new WebViewClient() { + @Override + public void onPageFinished(WebView view, String url) { + Assert.assertEquals(url, urlToLoad); + callbackHelper.notifyCalled(); + } + }); + webView.loadUrl(urlToLoad); + }); + callbackHelper.waitForFirst(); + } +}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/XClientDataTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/XClientDataTest.java new file mode 100644 index 0000000..63b176de --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/XClientDataTest.java
@@ -0,0 +1,36 @@ +// Copyright 2022 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.weblayer.test; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.content_public.browser.test.util.TestThreadUtils; + +/** + * WebLayer tests that need to use WebLayerPrivate. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +@CommandLineFlags.Add({"force-variation-ids=4,10,34"}) +public class XClientDataTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + @MinWebLayerVersion(101) + @Test + @SmallTest + public void getXClientDataHeader() { + final String header = TestThreadUtils.runOnUiThreadBlockingNoException( + () -> { return mActivityTestRule.getWebLayer().getXClientDataHeader(); }); + Assert.assertNotNull(header); + Assert.assertFalse(header.isEmpty()); + } +}
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java index 7c00999..a851229 100644 --- a/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java +++ b/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java
@@ -79,6 +79,7 @@ import org.chromium.weblayer_private.interfaces.ITabClient; import org.chromium.weblayer_private.interfaces.IWebMessageCallbackClient; import org.chromium.weblayer_private.interfaces.ObjectWrapper; +import org.chromium.weblayer_private.interfaces.RestrictedAPIException; import org.chromium.weblayer_private.interfaces.ScrollNotificationType; import org.chromium.weblayer_private.interfaces.StrictModeWorkaround; import org.chromium.weblayer_private.media.MediaSessionManager; @@ -764,8 +765,7 @@ assert unwrappedCallback != null; if (!verified) { - // TODO(crbug.com/1392110): Pass a RestrictedAPIException. - unwrappedCallback.onReceiveValue(null); + throw new RestrictedAPIException(); } Callback<String> nativeCallback = new Callback<String>() {
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java index 63df5ee..1f40103 100644 --- a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java +++ b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
@@ -254,11 +254,6 @@ mIsWebViewCompatMode = remoteContext != null && !remoteContext.getClassLoader().equals(WebLayerImpl.class.getClassLoader()); if (mIsWebViewCompatMode) { - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) { - // Load the library with the crazy linker. - LibraryLoader.getInstance().setLinkerImplementation(true, false); - WebViewCompatibilityHelperImpl.setRequiresManualJniRegistration(true); - } notifyWebViewRunningInProcess(remoteContext.getClassLoader()); }
diff --git a/weblayer/public/java/BUILD.gn b/weblayer/public/java/BUILD.gn index 3abed3f..36c17961 100644 --- a/weblayer/public/java/BUILD.gn +++ b/weblayer/public/java/BUILD.gn
@@ -212,7 +212,10 @@ } android_library("webengine_interfaces_java") { - sources = [ "org/chromium/webengine/interfaces/ExceptionType.java" ] + sources = [ + "org/chromium/webengine/interfaces/ExceptionType.java", + "org/chromium/webengine/interfaces/RestrictedAPIException.java", + ] deps = [ "//base:base_java", "//third_party/androidx:androidx_annotation_annotation_jvm_java", @@ -229,7 +232,6 @@ "org/chromium/webengine/Navigation.java", "org/chromium/webengine/NavigationObserver.java", "org/chromium/webengine/NavigationObserverDelegate.java", - "org/chromium/webengine/RestrictedAPIException.java", "org/chromium/webengine/Tab.java", "org/chromium/webengine/TabListObserver.java", "org/chromium/webengine/TabListObserverDelegate.java",
diff --git a/weblayer/public/java/org/chromium/webengine/ExceptionHelper.java b/weblayer/public/java/org/chromium/webengine/ExceptionHelper.java index 38e934b..adc8551 100644 --- a/weblayer/public/java/org/chromium/webengine/ExceptionHelper.java +++ b/weblayer/public/java/org/chromium/webengine/ExceptionHelper.java
@@ -5,6 +5,7 @@ package org.chromium.webengine; import org.chromium.webengine.interfaces.ExceptionType; +import org.chromium.webengine.interfaces.RestrictedAPIException; class ExceptionHelper { static Exception createException(@ExceptionType int type, String msg) {
diff --git a/weblayer/public/java/org/chromium/webengine/RestrictedAPIException.java b/weblayer/public/java/org/chromium/webengine/interfaces/RestrictedAPIException.java similarity index 88% rename from weblayer/public/java/org/chromium/webengine/RestrictedAPIException.java rename to weblayer/public/java/org/chromium/webengine/interfaces/RestrictedAPIException.java index 1732396..860d74c 100644 --- a/weblayer/public/java/org/chromium/webengine/RestrictedAPIException.java +++ b/weblayer/public/java/org/chromium/webengine/interfaces/RestrictedAPIException.java
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.webengine; +package org.chromium.webengine.interfaces; /** * Error thrown for API access violations.
diff --git a/weblayer/public/java/org/chromium/weblayer/Tab.java b/weblayer/public/java/org/chromium/weblayer/Tab.java index 4f28fea..c32b4ca 100644 --- a/weblayer/public/java/org/chromium/weblayer/Tab.java +++ b/weblayer/public/java/org/chromium/weblayer/Tab.java
@@ -257,6 +257,8 @@ mImpl.executeScript(script, useSeparateIsolate, ObjectWrapper.wrap(callback)); } catch (RemoteException e) { throw new APICallException(e); + } catch (RuntimeException e) { + ExceptionHelper.reraise(e); } }
diff --git a/weblayer/public/java/org/chromium/weblayer/TabProxy.java b/weblayer/public/java/org/chromium/weblayer/TabProxy.java index e300c10..a55f65c7 100644 --- a/weblayer/public/java/org/chromium/weblayer/TabProxy.java +++ b/weblayer/public/java/org/chromium/weblayer/TabProxy.java
@@ -13,6 +13,7 @@ import org.chromium.webengine.interfaces.ITabObserverDelegate; import org.chromium.webengine.interfaces.ITabProxy; import org.chromium.webengine.interfaces.IWebMessageCallback; +import org.chromium.weblayer_private.interfaces.RestrictedAPIException; import java.util.List; @@ -77,24 +78,19 @@ @Override public void executeScript(String script, boolean useSeparateIsolate, IStringCallback callback) { mHandler.post(() -> { - getTab().executeScript(script, useSeparateIsolate, (String result) -> { - try { - if (result != null) { - callback.onResult(result); - } else { - // TODO(crbug.com/1392110): Pass a useful exception message. - try { - callback.onException(ExceptionType.RESTRICTED_API, ""); - } catch (RemoteException e) { - } - } - } catch (RemoteException e) { + try { + getTab().executeScript(script, useSeparateIsolate, (String result) -> { try { - callback.onException(ExceptionType.UNKNOWN, e.getMessage()); - } catch (RemoteException e2) { + callback.onResult(result); + } catch (RemoteException e) { } + }); + } catch (RestrictedAPIException e) { + try { + callback.onException(ExceptionType.RESTRICTED_API, e.getMessage()); + } catch (RemoteException re) { } - }); + } }); }
diff --git a/weblayer/shell/android/webengine_shell_apk/res/values/strings.xml b/weblayer/shell/android/webengine_shell_apk/res/values/strings.xml index 49fed14..153aadc 100644 --- a/weblayer/shell/android/webengine_shell_apk/res/values/strings.xml +++ b/weblayer/shell/android/webengine_shell_apk/res/values/strings.xml
@@ -12,13 +12,6 @@ \"namespace\": \"web\", \"site\": \"https://example.com\" } - }, - { - \"relation\": [\"delegate_permission/common.handle_all_urls\"], - \"target\": { - \"namespace\": \"web\", - \"site\": \"http://localhost:8888\" - } }] </string> </resources> \ No newline at end of file
diff --git a/weblayer/shell/android/webengine_shell_apk/src/org/chromium/webengine/shell/InstrumentationActivity.java b/weblayer/shell/android/webengine_shell_apk/src/org/chromium/webengine/shell/InstrumentationActivity.java index b3ffae8..34fa31d 100644 --- a/weblayer/shell/android/webengine_shell_apk/src/org/chromium/webengine/shell/InstrumentationActivity.java +++ b/weblayer/shell/android/webengine_shell_apk/src/org/chromium/webengine/shell/InstrumentationActivity.java
@@ -7,11 +7,9 @@ import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.FragmentManager; import com.google.common.util.concurrent.ListenableFuture; -import org.chromium.webengine.WebFragment; import org.chromium.webengine.WebSandbox; /** @@ -31,17 +29,4 @@ public ListenableFuture<WebSandbox> getWebSandboxFuture() { return mWebSandboxFuture; } - - public void attachFragment(WebFragment fragment) { - FragmentManager fragmentManager = getSupportFragmentManager(); - fragmentManager.beginTransaction() - .setReorderingAllowed(true) - .add(R.id.fragment_container_view, fragment) - .commitNow(); - } - - public void detachFragment(WebFragment fragment) { - FragmentManager fragmentManager = getSupportFragmentManager(); - fragmentManager.beginTransaction().setReorderingAllowed(true).remove(fragment).commitNow(); - } -} +} \ No newline at end of file