diff --git a/DEPS b/DEPS index 4796328..cfb218fd 100644 --- a/DEPS +++ b/DEPS
@@ -195,11 +195,11 @@ # 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': '21efb7c7ddbc911adb60228934c0cdf14085f0ce', + 'skia_revision': '10f019c5068f9ff070c16e3571209012c78826e3', # 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': 'a8084f7d644a4e257d20d9414d831af8db8947c0', + 'v8_revision': 'ce4c3060f6fbfd5ed410de314f7e83a66fd792d9', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling swarming_client # and whatever else without interference from each other. @@ -207,11 +207,11 @@ # 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': 'd41280a787d1594470053dfa15b3330822049612', + 'angle_revision': 'adc250c38976277a15bd91605d06e1613bfdac86', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. - 'swiftshader_revision': 'a3e03e1086d5c85bd8435ff0b6f48666a8042603', + 'swiftshader_revision': 'be7c55a2a8cebd46ba6912e6b7d4dae8353d154c', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. @@ -258,7 +258,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': 'cf93e1de9eb8b7073397eb5aac874fd67e06b1a5', + 'catapult_revision': 'ac60992d41b7007e9c6c4329e5bbfc02c912f314', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -266,7 +266,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling devtools-frontend # and whatever else without interference from each other. - 'devtools_frontend_revision': '97713d6f163e086aad0560246b1e68e3b673115f', + 'devtools_frontend_revision': '580a4b1d52b65febb22c804a036d972898026b86', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libprotobuf-mutator # and whatever else without interference from each other. @@ -318,11 +318,11 @@ # 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': 'e84a1b13760d6ca251819569af8684af56fdee19', + 'dawn_revision': '0a4342793e0e31ed36e59d2eef36817c82ede555', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'quiche_revision': 'dd5382a4449c31e40aafc2ec60cd80041c32bdc0', + 'quiche_revision': '1b6221e157c76e14b4041f961f2e4c3a5f82673c', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ios_webkit # and whatever else without interference from each other. @@ -545,7 +545,7 @@ }, 'src/ios/third_party/material_components_ios/src': { - 'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '0dd6376c6de22ff059f68023f0e876d0e275aeb1', + 'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '8dac13ae51e05e60b394ac81dffa40cfcfd05bec', 'condition': 'checkout_ios', }, @@ -895,7 +895,7 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'a0c3f906bc6fdd7cfdc892e4e1c4a89ed97d5d5a', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '486f1812ef65fde35ad0c63e3939e89dc1c876a3', 'src/third_party/devtools-frontend/src': Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), @@ -1248,7 +1248,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '06e30bae5cbff2a72e6840bf6f845237187c44b0', + Var('android_git') + '/platform/external/perfetto.git' + '@' + 'f8ccdc63a188fd977953864bab7490eac9f9852f', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1537,7 +1537,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c6116bf6ce82d05a73126691f52c1ca1f9f4848b', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@d24a19d429243e3ae7fa087b2c1ed9b9cf921863', 'condition': 'checkout_src_internal', },
diff --git a/PRESUBMIT.py b/PRESUBMIT.py index 13fb2d09..6532a0d 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py
@@ -2162,28 +2162,6 @@ long_text=error.output)] -def _CheckTeamTags(input_api, output_api): - """Checks that OWNERS files have consistent TEAM and COMPONENT tags.""" - checkteamtags_tool = input_api.os_path.join( - input_api.PresubmitLocalPath(), - 'tools', 'checkteamtags', 'checkteamtags.py') - args = [input_api.python_executable, checkteamtags_tool, - '--root', input_api.change.RepositoryRoot()] - files = [f.LocalPath() for f in input_api.AffectedFiles(include_deletes=False) - if input_api.os_path.basename(f.AbsoluteLocalPath()).upper() == - 'OWNERS'] - try: - if files: - warnings = input_api.subprocess.check_output(args + files).splitlines() - if warnings: - return [output_api.PresubmitPromptWarning(warnings[0], warnings[1:])] - return [] - except input_api.subprocess.CalledProcessError as error: - return [output_api.PresubmitError( - 'checkteamtags.py failed:', - long_text=error.output)] - - def _CheckNoAuraWindowPropertyHInHeaders(input_api, output_api): """Makes sure we don't include ui/aura/window_property.h in header files. @@ -4425,7 +4403,6 @@ results.extend(_CheckNoTrinaryTrueFalse(input_api, output_api)) results.extend(_CheckUnwantedDependencies(input_api, output_api)) results.extend(_CheckFilePermissions(input_api, output_api)) - results.extend(_CheckTeamTags(input_api, output_api)) results.extend(_CheckNoAuraWindowPropertyHInHeaders(input_api, output_api)) results.extend(_CheckForVersionControlConflicts(input_api, output_api)) results.extend(_CheckPatchFiles(input_api, output_api)) @@ -4471,6 +4448,15 @@ results.extend(_CheckPythonDevilInit(input_api, output_api)) results.extend(_CheckStableMojomChanges(input_api, output_api)) + dirmd_bin = input_api.os_path.join( + input_api.PresubmitLocalPath(), 'third_party', 'depot_tools', 'dirmd') + results.extend(input_api.RunTests( + input_api.canned_checks.CheckDirMetadataFormat( + input_api, output_api, dirmd_bin))) + results.extend( + input_api.canned_checks.CheckOwnersDirMetadataExclusive( + input_api, output_api)) + for f in input_api.AffectedFiles(): path, name = input_api.os_path.split(f.LocalPath()) if name == 'PRESUBMIT.py':
diff --git a/android_webview/glue/BUILD.gn b/android_webview/glue/BUILD.gn index fddb8aae..10bf86b2 100644 --- a/android_webview/glue/BUILD.gn +++ b/android_webview/glue/BUILD.gn
@@ -45,6 +45,7 @@ "java/src/com/android/webview/chromium/GlueApiHelperForOMR1.java", "java/src/com/android/webview/chromium/GlueApiHelperForP.java", "java/src/com/android/webview/chromium/GlueApiHelperForQ.java", + "java/src/com/android/webview/chromium/GlueApiHelperForR.java", "java/src/com/android/webview/chromium/GraphicsUtils.java", "java/src/com/android/webview/chromium/MonochromeLibraryPreloader.java", "java/src/com/android/webview/chromium/PacProcessorImpl.java",
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/GlueApiHelperForR.java b/android_webview/glue/java/src/com/android/webview/chromium/GlueApiHelperForR.java new file mode 100644 index 0000000..f3aee53 --- /dev/null +++ b/android_webview/glue/java/src/com/android/webview/chromium/GlueApiHelperForR.java
@@ -0,0 +1,28 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package com.android.webview.chromium; + +import android.annotation.TargetApi; +import android.os.Build; +import android.webkit.PacProcessor; + +import org.chromium.base.annotations.VerifiesOnR; + +/** + * Utility class to use new APIs that were added in R (API level 30). These need to exist in a + * separate class so that Android framework can successfully verify glue layer classes without + * encountering the new APIs. Note that GlueApiHelper is only for APIs that cannot go to ApiHelper + * in base/, for reasons such as using system APIs or instantiating an adapter class that is + * specific to glue layer. + */ +@VerifiesOnR +@TargetApi(Build.VERSION_CODES.R) +public final class GlueApiHelperForR { + private GlueApiHelperForR() {} + + public static PacProcessor getPacProcessor() { + return PacProcessorImpl.getInstance(); + } +}
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java index 5c5c68b..682856ac 100644 --- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java +++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
@@ -17,6 +17,7 @@ import android.view.ViewGroup; import android.webkit.CookieManager; import android.webkit.GeolocationPermissions; +import android.webkit.PacProcessor; import android.webkit.ServiceWorkerController; import android.webkit.TokenBindingService; import android.webkit.TracingController; @@ -715,4 +716,9 @@ getSingleton().getBrowserContextOnUiThread().setWebLayerRunningInSameProcess(); }); } + + @Override + public PacProcessor getPacProcessor() { + return GlueApiHelperForR.getPacProcessor(); + } }
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProviderForR.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProviderForR.java index f06585a..925594aa 100644 --- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProviderForR.java +++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProviderForR.java
@@ -4,8 +4,6 @@ package com.android.webview.chromium; -import android.webkit.PacProcessor; - class WebViewChromiumFactoryProviderForR extends WebViewChromiumFactoryProvider { public static WebViewChromiumFactoryProvider create(android.webkit.WebViewDelegate delegate) { return new WebViewChromiumFactoryProviderForR(delegate); @@ -14,9 +12,4 @@ protected WebViewChromiumFactoryProviderForR(android.webkit.WebViewDelegate delegate) { super(delegate); } - - @Override - public PacProcessor getPacProcessor() { - return PacProcessorImpl.getInstance(); - } }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java index 61f09b2..985755d1 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
@@ -47,6 +47,7 @@ import org.chromium.base.task.PostTask; import org.chromium.base.test.util.CallbackHelper; import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.InMemorySharedPreferences; import org.chromium.components.safe_browsing.SafeBrowsingApiBridge; @@ -615,6 +616,7 @@ } @Test + @DisabledTest(message = "Wait for interstitial is flaky. crbug.com/1107540") @SmallTest @Feature({"AndroidWebView"}) public void testSafeBrowsingShowsInterstitialForSubresource() throws Throwable { @@ -643,6 +645,7 @@ } @Test + @DisabledTest(message = "Wait for interstitial is flaky. crbug.com/1107540") @SmallTest @Feature({"AndroidWebView"}) public void testSafeBrowsingProceedThroughInterstitialForSubresource() throws Throwable { @@ -692,6 +695,7 @@ } @Test + @DisabledTest(message = "Wait for interstitial is flaky. crbug.com/1107540") @SmallTest @Feature({"AndroidWebView"}) public void testSafeBrowsingDontProceedNavigatesBackForSubResource() throws Throwable {
diff --git a/ash/BUILD.gn b/ash/BUILD.gn index b9f43d5..bd44b65 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn
@@ -1883,6 +1883,7 @@ "power/hfp_battery_listener_unittest.cc", "power/hid_battery_listener_unittest.cc", "power/hid_battery_util_unittest.cc", + "quick_answers/quick_answers_ui_controller_unittest.cc", "quick_answers/ui/quick_answers_view_unittest.cc", "root_window_controller_unittest.cc", "rotator/screen_rotation_animation_unittest.cc",
diff --git a/ash/ambient/ambient_constants.h b/ash/ambient/ambient_constants.h index c9fb294..993a0dc 100644 --- a/ash/ambient/ambient_constants.h +++ b/ash/ambient/ambient_constants.h
@@ -17,7 +17,7 @@ // The default interval to refresh photos. // TODO(b/139953713): Change to a correct time interval. constexpr base::TimeDelta kPhotoRefreshInterval = - base::TimeDelta::FromSeconds(5); + base::TimeDelta::FromSeconds(60); // Directory name of ambient mode. constexpr char kAmbientModeDirectoryName[] = "ambient-mode";
diff --git a/ash/ambient/ambient_photo_controller.cc b/ash/ambient/ambient_photo_controller.cc index 6a3e4fd..a36df7d 100644 --- a/ash/ambient/ambient_photo_controller.cc +++ b/ash/ambient/ambient_photo_controller.cc
@@ -55,10 +55,8 @@ // The upper bound of delay to the fetch topics. An random value will be // generated in the range of |kTopicFetchDelayMax|/2 to |kTopicFetchDelayMax|. - -// TODO(b/139953713): Change to a correct time interval. -// E.g. it will be max 36 seconds if we want to fetch 50 batches in 30 mins. -constexpr base::TimeDelta kTopicFetchDelayMax = base::TimeDelta::FromSeconds(3); +constexpr base::TimeDelta kTopicFetchDelayMax = + base::TimeDelta::FromSeconds(36); constexpr int kMaxImageSizeInBytes = 5 * 1024 * 1024;
diff --git a/ash/clipboard/clipboard_history_controller.cc b/ash/clipboard/clipboard_history_controller.cc index 73fe8c9..ba0b9724 100644 --- a/ash/clipboard/clipboard_history_controller.cc +++ b/ash/clipboard/clipboard_history_controller.cc
@@ -23,6 +23,7 @@ #include "ui/base/ime/text_input_client.h" #include "ui/base/models/image_model.h" #include "ui/base/models/menu_separator_types.h" +#include "ui/base/models/simple_menu_model.h" #include "ui/base/resource/resource_bundle.h" #include "ui/display/screen.h" #include "ui/events/event.h" @@ -69,35 +70,18 @@ clipboard->WriteClipboardData(std::make_unique<ui::ClipboardData>(data)); } -class ClipboardHistoryMenuDelegate : public ui::SimpleMenuModel::Delegate { - public: - ClipboardHistoryMenuDelegate(ClipboardHistoryController* controller) - : controller_(controller) {} - ClipboardHistoryMenuDelegate(const ClipboardHistoryMenuDelegate&) = delete; - ClipboardHistoryMenuDelegate& operator=(const ClipboardHistoryMenuDelegate&) = - delete; - - // ui::SimpleMenuModel::Delegate: - void ExecuteCommand(int command_id, int event_flags) override { - controller_->MenuOptionSelected(/*index=*/command_id); - } - - private: - // The controller responsible for showing the Clipboard History menu. - ClipboardHistoryController* const controller_; -}; - } // namespace -class ClipboardHistoryAcceleratorTarget : public ui::AcceleratorTarget { +// ClipboardHistoryController::AcceleratorTarget ------------------------------- + +class ClipboardHistoryController::AcceleratorTarget + : public ui::AcceleratorTarget { public: - ClipboardHistoryAcceleratorTarget(ClipboardHistoryController* controller) + explicit AcceleratorTarget(ClipboardHistoryController* controller) : controller_(controller) {} - ClipboardHistoryAcceleratorTarget(const ClipboardHistoryAcceleratorTarget&) = - delete; - ClipboardHistoryAcceleratorTarget& operator=( - const ClipboardHistoryAcceleratorTarget&) = delete; - ~ClipboardHistoryAcceleratorTarget() override = default; + AcceleratorTarget(const AcceleratorTarget&) = delete; + AcceleratorTarget& operator=(const AcceleratorTarget&) = delete; + ~AcceleratorTarget() override = default; void Init() { ui::Accelerator show_menu_combo(ui::VKEY_V, ui::EF_COMMAND_DOWN); @@ -111,23 +95,47 @@ private: // ui::AcceleratorTarget: bool AcceleratorPressed(const ui::Accelerator& accelerator) override { - controller_->ShowMenu(); + if (controller_->IsMenuShowing()) + controller_->ExecuteSelectedMenuItem(); + else + controller_->ShowMenu(); return true; } bool CanHandleAccelerators() const override { - return controller_->CanShowMenu(); + return controller_->IsMenuShowing() || controller_->CanShowMenu(); } // The controller responsible for showing the Clipboard History menu. ClipboardHistoryController* const controller_; }; +// ClipboardHistoryController::MenuDelegate ------------------------------------ + +class ClipboardHistoryController::MenuDelegate + : public ui::SimpleMenuModel::Delegate { + public: + explicit MenuDelegate(ClipboardHistoryController* controller) + : controller_(controller) {} + MenuDelegate(const MenuDelegate&) = delete; + MenuDelegate& operator=(const MenuDelegate&) = delete; + + // ui::SimpleMenuModel::Delegate: + void ExecuteCommand(int command_id, int event_flags) override { + controller_->MenuOptionSelected(/*index=*/command_id); + } + + private: + // The controller responsible for showing the Clipboard History menu. + ClipboardHistoryController* const controller_; +}; + +// ClipboardHistoryController -------------------------------------------------- + ClipboardHistoryController::ClipboardHistoryController() : clipboard_history_(std::make_unique<ClipboardHistory>()), - accelerator_target_( - std::make_unique<ClipboardHistoryAcceleratorTarget>(this)), - menu_delegate_(std::make_unique<ClipboardHistoryMenuDelegate>(this)) {} + accelerator_target_(std::make_unique<AcceleratorTarget>(this)), + menu_delegate_(std::make_unique<MenuDelegate>(this)) {} ClipboardHistoryController::~ClipboardHistoryController() = default; @@ -135,12 +143,35 @@ accelerator_target_->Init(); } +bool ClipboardHistoryController::IsMenuShowing() const { + return context_menu_ && context_menu_->IsRunning(); +} + +gfx::Rect ClipboardHistoryController::GetMenuBoundsInScreenForTest() const { + return context_menu_->GetMenuBoundsInScreenForTest(); +} + bool ClipboardHistoryController::CanShowMenu() const { return !clipboard_history_->IsEmpty(); } +void ClipboardHistoryController::ExecuteSelectedMenuItem() { + DCHECK(IsMenuShowing()); + auto command = context_menu_->GetSelectedMenuItemCommand(); + + // TODO(crbug.com/1106849): Update once sequential paste is supported. + // Force close the context menu. Failure to do so before dispatching our + // synthetic key event will result in the context menu consuming the event. + // Currently we don't support sequential copy-paste. Once we do, we'll have to + // update this logic. + context_menu_->Cancel(); + + // If no menu item is currently selected, we'll fallback to the first item. + menu_delegate_->ExecuteCommand(command.value_or(0), ui::EF_NONE); +} + void ClipboardHistoryController::ShowMenu() { - if (!CanShowMenu()) + if (IsMenuShowing() || !CanShowMenu()) return; clipboard_items_ = @@ -205,20 +236,24 @@ // back onto the clipboard. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, - base::BindOnce(&WriteClipboardDataToClipboard, - *(clipboard_items_.begin())), + base::BindOnce( + [](const base::WeakPtr<ClipboardHistoryController>& weak_ptr, + ui::ClipboardData clipboard_data) { + // When restoring the original item back on top of the clipboard we + // need to pause clipboard history. Failure to do so will result in + // the original item being re-recorded when this restoration step + // should actually be opaque to the user. + std::unique_ptr<ClipboardHistory::ScopedPause> scoped_pause; + if (weak_ptr) { + scoped_pause = std::make_unique<ClipboardHistory::ScopedPause>( + weak_ptr->clipboard_history_.get()); + } + WriteClipboardDataToClipboard(clipboard_data); + }, + weak_ptr_factory_.GetWeakPtr(), *clipboard_items_.begin()), base::TimeDelta::FromMilliseconds(100)); } -bool ClipboardHistoryController::IsMenuShowing() const { - return context_menu_ && context_menu_->IsRunning(); -} - -gfx::Rect ClipboardHistoryController::GetClipboardHistoryMenuBoundsForTest() - const { - return context_menu_->GetClipboardHistoryMenuBoundsForTest(); -} - gfx::Rect ClipboardHistoryController::CalculateAnchorRect() const { display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay(); auto* host = ash::GetWindowTreeHostForDisplay(display.id());
diff --git a/ash/clipboard/clipboard_history_controller.h b/ash/clipboard/clipboard_history_controller.h index 662d6a00..84e61ce 100644 --- a/ash/clipboard/clipboard_history_controller.h +++ b/ash/clipboard/clipboard_history_controller.h
@@ -10,15 +10,18 @@ #include "ash/ash_export.h" #include "base/memory/weak_ptr.h" -#include "ui/base/models/simple_menu_model.h" + +namespace gfx { +class Rect; +} // namespace gfx namespace ui { class ClipboardData; } // namespace ui namespace ash { + class ClipboardHistory; -class ClipboardHistoryAcceleratorTarget; class ClipboardHistoryMenuModelAdapter; // Shows a menu with the last few things saved in the clipboard when the @@ -33,23 +36,24 @@ void Init(); - // Whether a menu can be shown. - bool CanShowMenu() const; - - // Shows a menu with the last few items copied. Executing one of the menu - // options results in that item being pasted into the active window. - void ShowMenu(); - - // Called when a menu option is selected. - void MenuOptionSelected(int index); - + // Returns if the contextual menu is currently showing. bool IsMenuShowing() const; - gfx::Rect GetClipboardHistoryMenuBoundsForTest() const; + // Returns bounds for the contextual menu in screen coordinates. + gfx::Rect GetMenuBoundsInScreenForTest() const; - ClipboardHistory* clipboard_history() { return clipboard_history_.get(); } + // Returns the history which tracks what is being copied to the clipboard. + const ClipboardHistory* history() const { return clipboard_history_.get(); } private: + class AcceleratorTarget; + class MenuDelegate; + + bool CanShowMenu() const; + void ShowMenu(); + void ExecuteSelectedMenuItem(); + void MenuOptionSelected(int index); + gfx::Rect CalculateAnchorRect() const; // The menu being shown. @@ -57,8 +61,9 @@ // Used to keep track of what is being copied to the clipboard. std::unique_ptr<ClipboardHistory> clipboard_history_; // Detects the search+v key combo. - std::unique_ptr<ClipboardHistoryAcceleratorTarget> accelerator_target_; - std::unique_ptr<ui::SimpleMenuModel::Delegate> menu_delegate_; + std::unique_ptr<AcceleratorTarget> accelerator_target_; + // Handles events on the contextual menu. + std::unique_ptr<MenuDelegate> menu_delegate_; // The items we show in the contextual menu. Saved so we can paste them later. std::vector<ui::ClipboardData> clipboard_items_;
diff --git a/ash/clipboard/clipboard_history_menu_model_adapter.cc b/ash/clipboard/clipboard_history_menu_model_adapter.cc index d287c36..971abed 100644 --- a/ash/clipboard/clipboard_history_menu_model_adapter.cc +++ b/ash/clipboard/clipboard_history_menu_model_adapter.cc
@@ -38,8 +38,22 @@ return menu_runner_ && menu_runner_->IsRunning(); } -gfx::Rect -ClipboardHistoryMenuModelAdapter::GetClipboardHistoryMenuBoundsForTest() const { +void ClipboardHistoryMenuModelAdapter::Cancel() { + DCHECK(menu_runner_); + menu_runner_->Cancel(); +} + +base::Optional<int> +ClipboardHistoryMenuModelAdapter::GetSelectedMenuItemCommand() const { + DCHECK(root_view_); + auto* menu_item = root_view_->GetMenuController()->GetSelectedMenuItem(); + return menu_item ? base::make_optional(menu_item->GetCommand()) + : base::nullopt; +} + +gfx::Rect ClipboardHistoryMenuModelAdapter::GetMenuBoundsInScreenForTest() + const { + DCHECK(root_view_); return root_view_->GetSubmenu()->GetBoundsInScreen(); }
diff --git a/ash/clipboard/clipboard_history_menu_model_adapter.h b/ash/clipboard/clipboard_history_menu_model_adapter.h index e5d5012..db5e0ef 100644 --- a/ash/clipboard/clipboard_history_menu_model_adapter.h +++ b/ash/clipboard/clipboard_history_menu_model_adapter.h
@@ -7,6 +7,7 @@ #include <memory> +#include "base/optional.h" #include "ui/views/controls/menu/menu_model_adapter.h" namespace gfx { @@ -39,9 +40,18 @@ // Shows the menu, anchored below |anchor_rect|. void Run(const gfx::Rect& anchor_rect); + // Returns if the menu is currently running. bool IsRunning() const; - gfx::Rect GetClipboardHistoryMenuBoundsForTest() const; + // Hides and cancels the menu. + void Cancel(); + + // Returns the command of the currently selected menu item. If no menu item is + // currently selected, returns |base::nullopt|. + base::Optional<int> GetSelectedMenuItemCommand() const; + + // Returns menu bounds in screen coordinates. + gfx::Rect GetMenuBoundsInScreenForTest() const; private: // The model which holds the contents of the menu.
diff --git a/ash/clipboard/clipboard_history_unittest.cc b/ash/clipboard/clipboard_history_unittest.cc index 8e6e5fd..581f1c90 100644 --- a/ash/clipboard/clipboard_history_unittest.cc +++ b/ash/clipboard/clipboard_history_unittest.cc
@@ -33,8 +33,8 @@ scoped_feature_list_.InitWithFeatures( {chromeos::features::kClipboardHistory}, {}); AshTestBase::SetUp(); - clipboard_history_ = - Shell::Get()->clipboard_history_controller()->clipboard_history(); + clipboard_history_ = const_cast<ClipboardHistory*>( + Shell::Get()->clipboard_history_controller()->history()); } const std::list<ui::ClipboardData>& GetClipboardHistoryData() {
diff --git a/ash/quick_answers/quick_answers_ui_controller.cc b/ash/quick_answers/quick_answers_ui_controller.cc index 2ce205c..5b8964f 100644 --- a/ash/quick_answers/quick_answers_ui_controller.cc +++ b/ash/quick_answers/quick_answers_ui_controller.cc
@@ -28,8 +28,8 @@ : controller_(controller) {} QuickAnswersUiController::~QuickAnswersUiController() { - CloseQuickAnswersView(); - CloseUserConsentView(); + quick_answers_view_ = nullptr; + user_consent_view_ = nullptr; } void QuickAnswersUiController::CreateQuickAnswersView(
diff --git a/ash/quick_answers/quick_answers_ui_controller_unittest.cc b/ash/quick_answers/quick_answers_ui_controller_unittest.cc new file mode 100644 index 0000000..1c6e3ad --- /dev/null +++ b/ash/quick_answers/quick_answers_ui_controller_unittest.cc
@@ -0,0 +1,66 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/quick_answers/ui/quick_answers_view.h" + +#include "ash/quick_answers/quick_answers_controller_impl.h" +#include "ash/quick_answers/quick_answers_ui_controller.h" +#include "ash/test/ash_test_base.h" +#include "base/test/scoped_feature_list.h" +#include "chromeos/constants/chromeos_features.h" + +namespace ash { + +namespace { + +constexpr gfx::Rect kDefaultAnchorBoundsInScreen = + gfx::Rect(gfx::Point(500, 250), gfx::Size(80, 140)); + +} // namespace + +class QuickAnswersUiControllerTest : public AshTestBase { + protected: + QuickAnswersUiControllerTest() { + scoped_feature_list_.InitWithFeatures( + {chromeos::features::kQuickAnswers, + chromeos::features::kQuickAnswersRichUi}, + {}); + } + QuickAnswersUiControllerTest(const QuickAnswersUiControllerTest&) = delete; + QuickAnswersUiControllerTest& operator=(const QuickAnswersUiControllerTest&) = + delete; + ~QuickAnswersUiControllerTest() override = default; + + // AshTestBase: + void SetUp() override { + AshTestBase::SetUp(); + + ui_controller_ = + static_cast<QuickAnswersControllerImpl*>(QuickAnswersController::Get()) + ->quick_answers_ui_controller(); + } + + // Currently instantiated QuickAnswersView instance. + QuickAnswersUiController* ui_controller() { return ui_controller_; } + + private: + QuickAnswersUiController* ui_controller_ = nullptr; + base::test::ScopedFeatureList scoped_feature_list_; +}; + +TEST_F(QuickAnswersUiControllerTest, TearDownWhileQuickAnswersViewShowing) { + EXPECT_FALSE(ui_controller()->is_showing_quick_answers_view()); + ui_controller()->CreateQuickAnswersView(kDefaultAnchorBoundsInScreen, + "default_title", "default_query"); + EXPECT_TRUE(ui_controller()->is_showing_quick_answers_view()); +} + +TEST_F(QuickAnswersUiControllerTest, TearDownWhileConsentViewShowing) { + EXPECT_FALSE(ui_controller()->is_showing_user_consent_view()); + ui_controller()->CreateUserConsentView(kDefaultAnchorBoundsInScreen, + base::string16(), base::string16()); + EXPECT_TRUE(ui_controller()->is_showing_user_consent_view()); +} + +} // namespace ash
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn index 17d947d..0fade24d 100644 --- a/ash/resources/vector_icons/BUILD.gn +++ b/ash/resources/vector_icons/BUILD.gn
@@ -30,6 +30,7 @@ "battery.icon", "bitmap.icon", "captive_portal.icon", + "capture_mode.icon", "check_circle.icon", "custom_data.icon", "delete.icon",
diff --git a/ash/resources/vector_icons/capture_mode.icon b/ash/resources/vector_icons/capture_mode.icon new file mode 100644 index 0000000..684abb8 --- /dev/null +++ b/ash/resources/vector_icons/capture_mode.icon
@@ -0,0 +1,29 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +CANVAS_DIMENSIONS, 20, +MOVE_TO, 13, 10, +R_ARC_TO, 3, 3, 0, 1, 1, -6, 0, +R_ARC_TO, 3, 3, 0, 0, 1, 6, 0, +CLOSE, +R_MOVE_TO, -2, 0, +R_ARC_TO, 1, 1, 0, 1, 1, -2, 0, +R_ARC_TO, 1, 1, 0, 0, 1, 2, 0, +CLOSE, +MOVE_TO, 3, 5, +R_ARC_TO, 2, 2, 0, 0, 1, 2, -2, +R_H_LINE_TO, 10, +R_ARC_TO, 2, 2, 0, 0, 1, 2, 2, +R_V_LINE_TO, 10, +R_ARC_TO, 2, 2, 0, 0, 1, -2, 2, +H_LINE_TO, 5, +R_ARC_TO, 2, 2, 0, 0, 1, -2, -2, +V_LINE_TO, 5, +CLOSE, +R_MOVE_TO, 2, 0, +R_H_LINE_TO, 10, +R_V_LINE_TO, 10, +H_LINE_TO, 5, +V_LINE_TO, 5, +CLOSE
diff --git a/ash/shelf/shelf_focus_cycler.cc b/ash/shelf/shelf_focus_cycler.cc index a55202b4..a3aaf44 100644 --- a/ash/shelf/shelf_focus_cycler.cc +++ b/ash/shelf/shelf_focus_cycler.cc
@@ -65,7 +65,7 @@ FocusOut(last_element, SourceView::kShelfNavigationView); return; } - navigation_widget->SetDefaultLastFocusableChild(last_element); + navigation_widget->PrepareForGettingFocus(last_element); Shell::Get()->focus_cycler()->FocusWidget(navigation_widget); }
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc index 1db7b17..11e7a28 100644 --- a/ash/shelf/shelf_layout_manager_unittest.cc +++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -577,6 +577,26 @@ GetShelfWidget()->GetBackgroundType()); } +// Verifies that the hidden shelf shows after triggering the FOCUS_SHELF +// accelerator (https://crbug.com/1111426). +TEST_F(ShelfLayoutManagerTest, ShowHiddenShelfByFocusShelfAccelerator) { + // Open a window so that the shelf will auto-hide. + std::unique_ptr<aura::Window> window(CreateTestWindow()); + window->Show(); + Shelf* shelf = GetPrimaryShelf(); + shelf->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways); + EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState()); + EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState()); + + // Focus on the shelf by accelerator. + Shell::Get()->accelerator_controller()->PerformActionIfEnabled(FOCUS_SHELF, + {}); + + // Shelf should be visible. + EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState()); + EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState()); +} + TEST_F(ShelfLayoutManagerTest, ShelfDoesNotAutoHideWithVoxAndTabletMode) { TabletModeControllerTestApi().EnterTabletMode(); // Open a window so that the shelf will auto-hide.
diff --git a/ash/shelf/shelf_navigation_widget.cc b/ash/shelf/shelf_navigation_widget.cc index 962e32f4..987836b 100644 --- a/ash/shelf/shelf_navigation_widget.cc +++ b/ash/shelf/shelf_navigation_widget.cc
@@ -666,6 +666,21 @@ return gfx::Rect(target_bounds_.origin(), clip_rect_.size()); } +void ShelfNavigationWidget::PrepareForGettingFocus(bool last_element) { + SetDefaultLastFocusableChild(last_element); + + // The native view of the navigation widget is not activatable when its target + // visibility is false. So show the widget before setting focus. + + // Layer opacity should be set first. Because it is not allowed that a window + // is visible but the layers alpha is fully transparent. + ui::Layer* layer = GetLayer(); + if (layer->GetTargetOpacity() != 1.f) + GetLayer()->SetOpacity(1.f); + if (!IsVisible()) + ShowInactive(); +} + void ShelfNavigationWidget::HandleLocaleChange() { delegate_->home_button()->HandleLocaleChange(); delegate_->back_button()->HandleLocaleChange();
diff --git a/ash/shelf/shelf_navigation_widget.h b/ash/shelf/shelf_navigation_widget.h index d25943b..af6c1b2 100644 --- a/ash/shelf/shelf_navigation_widget.h +++ b/ash/shelf/shelf_navigation_widget.h
@@ -84,6 +84,9 @@ // Returns the visible part's bounds in screen coordinates. gfx::Rect GetVisibleBounds() const; + // Do preparations before setting focus on the navigation widget. + void PrepareForGettingFocus(bool last_element); + // Called when shelf layout manager detects a locale change. Reloads the // home and back button tooltips and accessibility name strings. void HandleLocaleChange();
diff --git a/base/BUILD.gn b/base/BUILD.gn index fda1a07..e4ca016c 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -1816,11 +1816,6 @@ "strings/string16.cc", ] - # winternl.h and NTSecAPI.h have different definitions of UNICODE_STRING. - # There's only one client of NTSecAPI.h in base but several of winternl.h, - # so exclude the NTSecAPI.h one. - jumbo_excluded_sources = [ "rand_util_win.cc" ] - deps += [ "//base/win:base_win_buildflags" ] data_deps += [ "//build/win:runtime_libs" ] @@ -3556,6 +3551,7 @@ "android/java/src/org/chromium/base/annotations/VerifiesOnOMR1.java", "android/java/src/org/chromium/base/annotations/VerifiesOnP.java", "android/java/src/org/chromium/base/annotations/VerifiesOnQ.java", + "android/java/src/org/chromium/base/annotations/VerifiesOnR.java", "android/java/src/org/chromium/base/compat/ApiHelperForM.java", "android/java/src/org/chromium/base/compat/ApiHelperForN.java", "android/java/src/org/chromium/base/compat/ApiHelperForO.java",
diff --git a/base/android/java/src/org/chromium/base/annotations/VerifiesOnR.java b/base/android/java/src/org/chromium/base/annotations/VerifiesOnR.java new file mode 100644 index 0000000..6274f3d --- /dev/null +++ b/base/android/java/src/org/chromium/base/annotations/VerifiesOnR.java
@@ -0,0 +1,21 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.base.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The annotated method or class verifies on R, but not below. + * + * The annotated method (or methods on the annotated class) are guaranteed to not be inlined by R8 + * on builds targeted below R. This prevents class verification errors (which results in a very slow + * retry-verification-at-runtime) from spreading into other classes on these lower versions. + */ +@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.CLASS) +public @interface VerifiesOnR {}
diff --git a/base/profiler/module_cache_posix.cc b/base/profiler/module_cache_posix.cc index ca1b77e9..e38facd3 100644 --- a/base/profiler/module_cache_posix.cc +++ b/base/profiler/module_cache_posix.cc
@@ -55,6 +55,20 @@ return max_offset; } +FilePath GetDebugBasenameForModule(const Dl_info& dl_info) { +#if defined(OS_ANDROID) + // Preferentially identify the library using its soname on Android. Libraries + // mapped directly from apks have the apk filename in |dl_info.dli_fname|, and + // this doesn't distinguish the particular library. + Optional<StringPiece> library_name = + debug::ReadElfLibraryName(dl_info.dli_fbase); + if (library_name) + return FilePath(*library_name); +#endif + + return FilePath(dl_info.dli_fname).BaseName(); +} + class PosixModule : public ModuleCache::Module { public: PosixModule(const Dl_info& dl_info); @@ -79,7 +93,7 @@ PosixModule::PosixModule(const Dl_info& dl_info) : base_address_(reinterpret_cast<uintptr_t>(dl_info.dli_fbase)), id_(GetUniqueBuildId(dl_info.dli_fbase)), - debug_basename_(FilePath(dl_info.dli_fname).BaseName()), + debug_basename_(GetDebugBasenameForModule(dl_info)), size_(GetLastExecutableOffset(dl_info.dli_fbase)) {} } // namespace
diff --git a/base/profiler/module_cache_unittest.cc b/base/profiler/module_cache_unittest.cc index 9d8cddb..fc9bee8 100644 --- a/base/profiler/module_cache_unittest.cc +++ b/base/profiler/module_cache_unittest.cc
@@ -92,6 +92,21 @@ #define MAYBE_TEST(TestSuite, TestName) TEST(TestSuite, DISABLED_##TestName) #endif +MAYBE_TEST(ModuleCacheTest, GetDebugBasename) { + ModuleCache cache; + const ModuleCache::Module* module = + cache.GetModuleForAddress(reinterpret_cast<uintptr_t>(&AFunctionForTest)); + ASSERT_NE(nullptr, module); +#if defined(OS_ANDROID) + EXPECT_EQ("libbase_unittests__library.so", + module->GetDebugBasename().value()); +#elif defined(OS_POSIX) + EXPECT_EQ("base_unittests", module->GetDebugBasename().value()); +#elif defined(OS_WIN) + EXPECT_EQ(L"base_unittests.exe.pdb", module->GetDebugBasename().value()); +#endif +} + // Checks that ModuleCache returns the same module instance for // addresses within the module. MAYBE_TEST(ModuleCacheTest, LookupCodeAddresses) {
diff --git a/build/android/gyp/proguard.py b/build/android/gyp/proguard.py index 3242e31f..15e4756 100755 --- a/build/android/gyp/proguard.py +++ b/build/android/gyp/proguard.py
@@ -27,6 +27,7 @@ (27, 'OMR1'), (28, 'P'), (29, 'Q'), + (30, 'R'), ] _CHECKDISCARD_RE = re.compile(r'^\s*-checkdiscard[\s\S]*?}', re.MULTILINE) _DIRECTIVE_RE = re.compile(r'^\s*-', re.MULTILINE)
diff --git a/build/android/pylib/gtest/gtest_test_instance.py b/build/android/pylib/gtest/gtest_test_instance.py index 49d4892..b12bca9 100644 --- a/build/android/pylib/gtest/gtest_test_instance.py +++ b/build/android/pylib/gtest/gtest_test_instance.py
@@ -29,6 +29,9 @@ 'weblayer_browsertests', ] +# The max number of tests to run on a shard during the test run. +MAX_SHARDS = 256 + RUN_IN_SUB_THREAD_TEST_SUITES = [ # Multiprocess tests should be run outside of the main thread. 'base_unittests', # file_locking_unittest.cc uses a child process. @@ -301,6 +304,11 @@ if args.test_apk_incremental_install_json: incremental_part = '_incremental' + self._test_launcher_batch_limit = MAX_SHARDS + if (args.test_launcher_batch_limit + and 0 < args.test_launcher_batch_limit < MAX_SHARDS): + self._test_launcher_batch_limit = args.test_launcher_batch_limit + apk_path = os.path.join( constants.GetOutDirectory(), '%s_apk' % self._suite, '%s-debug%s.apk' % (self._suite, incremental_part)) @@ -455,6 +463,10 @@ return self._test_apk_incremental_install_json @property + def test_launcher_batch_limit(self): + return self._test_launcher_batch_limit + + @property def total_external_shards(self): return self._total_external_shards
diff --git a/build/android/pylib/local/device/local_device_gtest_run.py b/build/android/pylib/local/device/local_device_gtest_run.py index d6da1a41..0ae9a1b 100644 --- a/build/android/pylib/local/device/local_device_gtest_run.py +++ b/build/android/pylib/local/device/local_device_gtest_run.py
@@ -51,7 +51,6 @@ 'org.chromium.native_test.NativeTestInstrumentationTestRunner' '.TestList') -_MAX_SHARD_SIZE = 256 _SECONDS_TO_NANOS = int(1e9) # The amount of time a test executable may run before it gets killed. @@ -487,10 +486,14 @@ # Delete suspect testcase from tests. tests = [test for test in tests if not test in self._crashes] + batch_size = self._test_instance.test_launcher_batch_limit + for i in xrange(0, device_count): unbounded_shard = tests[i::device_count] - shards += [unbounded_shard[j:j+_MAX_SHARD_SIZE] - for j in xrange(0, len(unbounded_shard), _MAX_SHARD_SIZE)] + shards += [ + unbounded_shard[j:j + batch_size] + for j in xrange(0, len(unbounded_shard), batch_size) + ] return shards #override
diff --git a/build/android/test_runner.py b/build/android/test_runner.py index e4cd353..2d97a24 100755 --- a/build/android/test_runner.py +++ b/build/android/test_runner.py
@@ -389,6 +389,12 @@ '--test-apk-incremental-install-json', type=os.path.realpath, help='Path to install json for the test apk.') + parser.add_argument('--test-launcher-batch-limit', + dest='test_launcher_batch_limit', + type=int, + help='The max number of tests to run in a shard. ' + 'Ignores non-positive ints and those greater than ' + 'MAX_SHARDS') parser.add_argument( '-w', '--wait-for-java-debugger', action='store_true', help='Wait for java debugger to attach before running any application '
diff --git a/chrome/VERSION b/chrome/VERSION index 3fdc4ad2..88eb593 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=86 MINOR=0 -BUILD=4223 +BUILD=4224 PATCH=0
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni index f359f4c..5d310a2 100644 --- a/chrome/android/chrome_java_sources.gni +++ b/chrome/android/chrome_java_sources.gni
@@ -32,6 +32,7 @@ "java/src/org/chromium/chrome/browser/ChromeVersionInfo.java", "java/src/org/chromium/chrome/browser/ChromeWindow.java", "java/src/org/chromium/chrome/browser/DefaultBrowserInfo.java", + "java/src/org/chromium/chrome/browser/DefaultBrowserInfo2.java", "java/src/org/chromium/chrome/browser/DeferredStartupHandler.java", "java/src/org/chromium/chrome/browser/DelayedScreenLockIntentHandler.java", "java/src/org/chromium/chrome/browser/DevToolsServer.java",
diff --git a/chrome/android/expectations/monochrome_public_bundle.proguard_flags.expected b/chrome/android/expectations/monochrome_public_bundle.proguard_flags.expected index 0600ac76..7b28e62 100644 --- a/chrome/android/expectations/monochrome_public_bundle.proguard_flags.expected +++ b/chrome/android/expectations/monochrome_public_bundle.proguard_flags.expected
@@ -784,3 +784,13 @@ -keepclassmembers,allowobfuscation class ** { @org.chromium.base.annotations.VerifiesOnQ <methods>; } +-keep @interface org.chromium.base.annotations.VerifiesOnR +-if @org.chromium.base.annotations.VerifiesOnR class * { + *** *(...); +} +-keep,allowobfuscation class <1> { + *** <2>(...); +} +-keepclassmembers,allowobfuscation class ** { + @org.chromium.base.annotations.VerifiesOnR <methods>; +}
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 7b83efb..2136442f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -112,6 +112,7 @@ import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations; import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.reengagement.ReengagementNotificationController; import org.chromium.chrome.browser.search_engines.SearchEngineChoiceNotification; import org.chromium.chrome.browser.suggestions.SuggestionsEventReporterBridge; import org.chromium.chrome.browser.suggestions.SuggestionsMetrics; @@ -1037,15 +1038,19 @@ // values of this depending on when it is called after the activity was // shown. - if (mCallbackController != null) { - new OneShotCallback<>( - mTabModelProfileSupplier, mCallbackController.makeCancelable(profile -> { - assert profile != null : "Unexpectedly null profile from TabModel."; - if (profile == null) return; + // Temporary safety check to make sure none of this code runs if the feature is + // disabled. + if (ReengagementNotificationController.isEnabled()) { + if (mCallbackController != null) { + new OneShotCallback<>( + mTabModelProfileSupplier, mCallbackController.makeCancelable(profile -> { + assert profile != null : "Unexpectedly null profile from TabModel."; + if (profile == null) return; - TrackerFactory.getTrackerForProfile(profile).notifyEvent( - EventConstants.STARTED_FROM_MAIN_INTENT); - })); + TrackerFactory.getTrackerForProfile(profile).notifyEvent( + EventConstants.STARTED_FROM_MAIN_INTENT); + })); + } } mMainIntentMetrics.onMainIntentWithNative(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/DefaultBrowserInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/DefaultBrowserInfo.java index 526736e7..173a1bd3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/DefaultBrowserInfo.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/DefaultBrowserInfo.java
@@ -11,22 +11,17 @@ import android.text.TextUtils; import androidx.annotation.IntDef; -import androidx.annotation.VisibleForTesting; import org.chromium.base.BuildInfo; -import org.chromium.base.Callback; import org.chromium.base.ContextUtils; -import org.chromium.base.ObserverList; import org.chromium.base.PackageManagerUtils; -import org.chromium.base.ThreadUtils; import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.task.AsyncTask; import org.chromium.base.task.BackgroundOnlyAsyncTask; -import org.chromium.base.task.PostTask; import org.chromium.chrome.R; import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; import org.chromium.chrome.browser.preferences.SharedPreferencesManager; -import org.chromium.content_public.browser.UiThreadTaskTraits; +import org.chromium.content_public.browser.BrowserStartupController; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -36,10 +31,10 @@ import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.atomic.AtomicReference; /** - * A utility class for querying information about the default browser setting. + * A utility class for querying information about the default browser setting.\ + * TODO(crbug.com/1112519): Remove this and replace with DefaultBrowserInfo2. */ public final class DefaultBrowserInfo { /** @@ -62,40 +57,19 @@ int NUM_ENTRIES = 5; } - /** Contains all status related to the default browser state on the device. */ - public static class DefaultInfo { - /** Whether or not Chrome is the system browser. */ - public final boolean isChromeSystem; - - /** Whether or not Chrome is the default browser. */ - public final boolean isChromeDefault; - - /** Whether or not the default browser is the system browser. */ - public final boolean isDefaultSystem; - - /** Whether or not the user has set a default browser. */ - public final boolean hasDefault; - - /** The number of browsers installed on this device. */ - public final int browserCount; - - /** The number of system browsers installed on this device. */ - public final int systemCount; - - /** Creates an instance of the {@link DefaultInfo} class. */ - public DefaultInfo(boolean isChromeSystem, boolean isChromeDefault, boolean isDefaultSystem, - boolean hasDefault, int browserCount, int systemCount) { - this.isChromeSystem = isChromeSystem; - this.isChromeDefault = isChromeDefault; - this.isDefaultSystem = isDefaultSystem; - this.hasDefault = hasDefault; - this.browserCount = browserCount; - this.systemCount = systemCount; - } + /** + * Helper class for passing information about the system's default browser settings back from a + * worker task. + */ + private static class DefaultInfo { + public boolean isChromeSystem; + public boolean isChromeDefault; + public boolean isDefaultSystem; + public boolean hasDefault; + public int browserCount; + public int systemCount; } - private static DefaultInfoTask sDefaultInfoTask; - /** A lock to synchronize background tasks to retrieve browser information. */ private static final Object sDirCreationLock = new Object(); @@ -108,8 +82,6 @@ * Initialize an AsyncTask for getting menu title of opening a link in default browser. */ public static void initBrowserFetcher() { - // TODO(crbug.com/1107527): Make this depend on getDefaultBrowserInfo instead of rolling - // it's own AsyncTask. Potentially update DefaultInfo to include extra data if necessary. synchronized (sDirCreationLock) { if (sDefaultBrowserFetcher == null) { sDefaultBrowserFetcher = new BackgroundOnlyAsyncTask<ArrayList<String>>() { @@ -127,7 +99,7 @@ // Caches whether Chrome is set as a default browser on the device. boolean isDefault = info != null && info.match != 0 && TextUtils.equals( - context.getPackageName(), info.activityInfo.packageName); + context.getPackageName(), info.activityInfo.packageName); SharedPreferencesManager.getInstance().writeBoolean( ChromePreferenceKeys.CHROME_DEFAULT_BROWSER, isDefault); @@ -172,146 +144,61 @@ } /** - * Determines various information about browsers on the system. - * @param callback To be called with a {@link DefaultInfo} instance if possible. Can be {@code - * null}. - * @see DefaultInfo - */ - public static void getDefaultBrowserInfo(Callback<DefaultInfo> callback) { - ThreadUtils.checkUiThread(); - if (sDefaultInfoTask == null) sDefaultInfoTask = new DefaultInfoTask(); - sDefaultInfoTask.get(callback); - } - - /** * Log statistics about the current default browser to UMA. */ public static void logDefaultBrowserStats() { - getDefaultBrowserInfo(info -> { - if (info == null) return; + assert BrowserStartupController.getInstance().isFullBrowserStarted(); - RecordHistogram.recordCount100Histogram( - getSystemBrowserCountUmaName(info), info.systemCount); - RecordHistogram.recordCount100Histogram( - getDefaultBrowserCountUmaName(info), info.browserCount); - RecordHistogram.recordEnumeratedHistogram("Mobile.DefaultBrowser.State", - getDefaultBrowserUmaState(info), MobileDefaultBrowserState.NUM_ENTRIES); - }); - } + try { + new AsyncTask<DefaultInfo>() { + @Override + protected DefaultInfo doInBackground() { + Context context = ContextUtils.getApplicationContext(); - @VisibleForTesting - public static void setDefaultInfoForTests(DefaultInfo info) { - DefaultInfoTask.setDefaultInfoForTests(info); - } + DefaultInfo info = new DefaultInfo(); - @VisibleForTesting - public static void clearDefaultInfoForTests() { - DefaultInfoTask.clearDefaultInfoForTests(); - } - - private static class DefaultInfoTask extends AsyncTask<DefaultInfo> { - private static AtomicReference<DefaultInfo> sTestInfo; - - private final ObserverList<Callback<DefaultInfo>> mObservers = new ObserverList<>(); - - @VisibleForTesting - public static void setDefaultInfoForTests(DefaultInfo info) { - sTestInfo = new AtomicReference<DefaultInfo>(info); - } - - public static void clearDefaultInfoForTests() { - sTestInfo = null; - } - - /** - * Queues up {@code callback} to be notified of the result of this {@link AsyncTask}. If - * the task has not been started, this will start it. If the task is finished, this will - * send the result. If the task is running this will queue the callback up until the task - * is done. - * - * @param callback The {@link Callback} to notify with the right {@link DefaultInfo}. - */ - public void get(Callback<DefaultInfo> callback) { - ThreadUtils.checkUiThread(); - - if (getStatus() == Status.FINISHED) { - DefaultInfo info = null; - try { - info = sTestInfo == null ? get() : sTestInfo.get(); - } catch (InterruptedException | ExecutionException e) { - // Fail silently here since this is not a critical task. - } - - final DefaultInfo postInfo = info; - PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> callback.onResult(postInfo)); - } else { - if (getStatus() == Status.PENDING) { - try { - executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } catch (RejectedExecutionException e) { - // Fail silently here since this is not a critical task. - PostTask.postTask( - UiThreadTaskTraits.DEFAULT, () -> callback.onResult(null)); - return; + // Query the default handler first. + ResolveInfo defaultRi = PackageManagerUtils.resolveDefaultWebBrowserActivity(); + if (defaultRi != null && defaultRi.match != 0) { + info.hasDefault = true; + info.isChromeDefault = isSamePackage(context, defaultRi); + info.isDefaultSystem = isSystemPackage(defaultRi); } - } - mObservers.addObserver(callback); - } - } - @Override - protected DefaultInfo doInBackground() { - Context context = ContextUtils.getApplicationContext(); + // Query all other intent handlers. + Set<String> uniquePackages = new HashSet<>(); + List<ResolveInfo> ris = PackageManagerUtils.queryAllWebBrowsersInfo(); + if (ris != null) { + for (ResolveInfo ri : ris) { + String packageName = ri.activityInfo.applicationInfo.packageName; + if (!uniquePackages.add(packageName)) continue; - boolean isChromeSystem = false; - boolean isChromeDefault = false; - boolean isDefaultSystem = false; - boolean hasDefault = false; - int browserCount = 0; - int systemCount = 0; - - // Query the default handler first. - ResolveInfo defaultRi = PackageManagerUtils.resolveDefaultWebBrowserActivity(); - if (defaultRi != null && defaultRi.match != 0) { - hasDefault = true; - isChromeDefault = isSamePackage(context, defaultRi); - isDefaultSystem = isSystemPackage(defaultRi); - } - - // Query all other intent handlers. - Set<String> uniquePackages = new HashSet<>(); - List<ResolveInfo> ris = PackageManagerUtils.queryAllWebBrowsersInfo(); - if (ris != null) { - for (ResolveInfo ri : ris) { - String packageName = ri.activityInfo.applicationInfo.packageName; - if (!uniquePackages.add(packageName)) continue; - - if (isSystemPackage(ri)) { - if (isSamePackage(context, ri)) isChromeSystem = true; - systemCount++; + if (isSystemPackage(ri)) { + if (isSamePackage(context, ri)) info.isChromeSystem = true; + info.systemCount++; + } + } } + + info.browserCount = uniquePackages.size(); + + return info; } - } - browserCount = uniquePackages.size(); + @Override + protected void onPostExecute(DefaultInfo info) { + if (info == null) return; - return new DefaultInfo(isChromeSystem, isChromeDefault, isDefaultSystem, hasDefault, - browserCount, systemCount); - } - - @Override - protected void onPostExecute(DefaultInfo defaultInfo) { - flushCallbacks(sTestInfo == null ? defaultInfo : sTestInfo.get()); - } - - @Override - protected void onCancelled() { - flushCallbacks(null); - } - - private void flushCallbacks(DefaultInfo info) { - for (Callback<DefaultInfo> callback : mObservers) callback.onResult(info); - mObservers.clear(); + RecordHistogram.recordCount100Histogram( + getSystemBrowserCountUmaName(info), info.systemCount); + RecordHistogram.recordCount100Histogram( + getDefaultBrowserCountUmaName(info), info.browserCount); + RecordHistogram.recordEnumeratedHistogram("Mobile.DefaultBrowser.State", + getDefaultBrowserUmaState(info), MobileDefaultBrowserState.NUM_ENTRIES); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } catch (RejectedExecutionException ex) { + // Fail silently here since this is not a critical task. } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/DefaultBrowserInfo2.java b/chrome/android/java/src/org/chromium/chrome/browser/DefaultBrowserInfo2.java new file mode 100644 index 0000000..f3a2462 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/DefaultBrowserInfo2.java
@@ -0,0 +1,208 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.ResolveInfo; +import android.text.TextUtils; + +import androidx.annotation.VisibleForTesting; + +import org.chromium.base.Callback; +import org.chromium.base.ContextUtils; +import org.chromium.base.ObserverList; +import org.chromium.base.PackageManagerUtils; +import org.chromium.base.ThreadUtils; +import org.chromium.base.task.AsyncTask; +import org.chromium.base.task.PostTask; +import org.chromium.content_public.browser.UiThreadTaskTraits; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.atomic.AtomicReference; + +/** + * A utility class for querying information about the default browser setting. + * TODO(crbug.com/1112519): Remove DefaultBrowserInfo and replace with this. + */ +public final class DefaultBrowserInfo2 { + /** Contains all status related to the default browser state on the device. */ + public static class DefaultInfo { + /** Whether or not Chrome is the system browser. */ + public final boolean isChromeSystem; + + /** Whether or not Chrome is the default browser. */ + public final boolean isChromeDefault; + + /** Whether or not the default browser is the system browser. */ + public final boolean isDefaultSystem; + + /** Whether or not the user has set a default browser. */ + public final boolean hasDefault; + + /** The number of browsers installed on this device. */ + public final int browserCount; + + /** The number of system browsers installed on this device. */ + public final int systemCount; + + /** Creates an instance of the {@link DefaultInfo} class. */ + public DefaultInfo(boolean isChromeSystem, boolean isChromeDefault, boolean isDefaultSystem, + boolean hasDefault, int browserCount, int systemCount) { + this.isChromeSystem = isChromeSystem; + this.isChromeDefault = isChromeDefault; + this.isDefaultSystem = isDefaultSystem; + this.hasDefault = hasDefault; + this.browserCount = browserCount; + this.systemCount = systemCount; + } + } + + private static DefaultInfoTask sDefaultInfoTask; + + /** Don't instantiate me. */ + private DefaultBrowserInfo2() {} + + /** + * Determines various information about browsers on the system. + * @param callback To be called with a {@link DefaultInfo} instance if possible. Can be {@code + * null}. + * @see DefaultInfo + */ + public static void getDefaultBrowserInfo(Callback<DefaultInfo> callback) { + ThreadUtils.checkUiThread(); + if (sDefaultInfoTask == null) sDefaultInfoTask = new DefaultInfoTask(); + sDefaultInfoTask.get(callback); + } + + @VisibleForTesting + public static void setDefaultInfoForTests(DefaultInfo info) { + DefaultInfoTask.setDefaultInfoForTests(info); + } + + @VisibleForTesting + public static void clearDefaultInfoForTests() { + DefaultInfoTask.clearDefaultInfoForTests(); + } + + private static class DefaultInfoTask extends AsyncTask<DefaultInfo> { + private static AtomicReference<DefaultInfo> sTestInfo; + + private final ObserverList<Callback<DefaultInfo>> mObservers = new ObserverList<>(); + + @VisibleForTesting + public static void setDefaultInfoForTests(DefaultInfo info) { + sTestInfo = new AtomicReference<DefaultInfo>(info); + } + + public static void clearDefaultInfoForTests() { + sTestInfo = null; + } + + /** + * Queues up {@code callback} to be notified of the result of this {@link AsyncTask}. If + * the task has not been started, this will start it. If the task is finished, this will + * send the result. If the task is running this will queue the callback up until the task + * is done. + * + * @param callback The {@link Callback} to notify with the right {@link DefaultInfo}. + */ + public void get(Callback<DefaultInfo> callback) { + ThreadUtils.checkUiThread(); + + if (getStatus() == Status.FINISHED) { + DefaultInfo info = null; + try { + info = sTestInfo == null ? get() : sTestInfo.get(); + } catch (InterruptedException | ExecutionException e) { + // Fail silently here since this is not a critical task. + } + + final DefaultInfo postInfo = info; + PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> callback.onResult(postInfo)); + } else { + if (getStatus() == Status.PENDING) { + try { + executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } catch (RejectedExecutionException e) { + // Fail silently here since this is not a critical task. + PostTask.postTask( + UiThreadTaskTraits.DEFAULT, () -> callback.onResult(null)); + return; + } + } + mObservers.addObserver(callback); + } + } + + @Override + protected DefaultInfo doInBackground() { + Context context = ContextUtils.getApplicationContext(); + + boolean isChromeSystem = false; + boolean isChromeDefault = false; + boolean isDefaultSystem = false; + boolean hasDefault = false; + int browserCount = 0; + int systemCount = 0; + + // Query the default handler first. + ResolveInfo defaultRi = PackageManagerUtils.resolveDefaultWebBrowserActivity(); + if (defaultRi != null && defaultRi.match != 0) { + hasDefault = true; + isChromeDefault = isSamePackage(context, defaultRi); + isDefaultSystem = isSystemPackage(defaultRi); + } + + // Query all other intent handlers. + Set<String> uniquePackages = new HashSet<>(); + List<ResolveInfo> ris = PackageManagerUtils.queryAllWebBrowsersInfo(); + if (ris != null) { + for (ResolveInfo ri : ris) { + String packageName = ri.activityInfo.applicationInfo.packageName; + if (!uniquePackages.add(packageName)) continue; + + if (isSystemPackage(ri)) { + if (isSamePackage(context, ri)) isChromeSystem = true; + systemCount++; + } + } + } + + browserCount = uniquePackages.size(); + + return new DefaultInfo(isChromeSystem, isChromeDefault, isDefaultSystem, hasDefault, + browserCount, systemCount); + } + + @Override + protected void onPostExecute(DefaultInfo defaultInfo) { + flushCallbacks(sTestInfo == null ? defaultInfo : sTestInfo.get()); + } + + @Override + protected void onCancelled() { + flushCallbacks(null); + } + + private void flushCallbacks(DefaultInfo info) { + for (Callback<DefaultInfo> callback : mObservers) callback.onResult(info); + mObservers.clear(); + } + } + + private static boolean isSamePackage(Context context, ResolveInfo info) { + return TextUtils.equals( + context.getPackageName(), info.activityInfo.applicationInfo.packageName); + } + + private static boolean isSystemPackage(ResolveInfo info) { + return (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationController.java b/chrome/android/java/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationController.java index db28e8027..7b6908a3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationController.java
@@ -15,7 +15,7 @@ import org.chromium.base.Callback; import org.chromium.chrome.R; -import org.chromium.chrome.browser.DefaultBrowserInfo; +import org.chromium.chrome.browser.DefaultBrowserInfo2; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.notifications.NotificationUmaTracker; import org.chromium.chrome.browser.notifications.NotificationUmaTracker.SystemNotificationType; @@ -79,8 +79,8 @@ } @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - protected void getDefaultBrowserInfo(Callback<DefaultBrowserInfo.DefaultInfo> callback) { - DefaultBrowserInfo.getDefaultBrowserInfo(callback); + protected void getDefaultBrowserInfo(Callback<DefaultBrowserInfo2.DefaultInfo> callback) { + DefaultBrowserInfo2.getDefaultBrowserInfo(callback); } private boolean showNotification(String feature) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/OWNERS b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/OWNERS index 4e1c69c..33e91e60 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/OWNERS +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/OWNERS
@@ -1 +1 @@ -file://chrome/browser/ui/android/appmenu/OWNERS +file://chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/OWNERS
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java index cf4f1fcf..44df2a0 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java
@@ -25,6 +25,7 @@ import org.junit.runner.RunWith; import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.Feature; import org.chromium.chrome.R; import org.chromium.chrome.browser.ChromeActivity; @@ -61,6 +62,7 @@ ContentSwitches.HOST_RESOLVER_RULES + "=MAP * 127.0.0.1"}) @Features. EnableFeatures(ContentSettingsFeatureList.IMPROVED_COOKIE_CONTROLS_FOR_THIRD_PARTY_COOKIE_BLOCKING) +@DisabledTest(message = "crbug.com/1112985") public class PageInfoViewTest { @Rule public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerIntegrationTest.java index 52e2e0d..2b0cd07 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerIntegrationTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerIntegrationTest.java
@@ -37,9 +37,10 @@ import org.chromium.base.FeatureList; import org.chromium.base.test.util.CallbackHelper; import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.UrlUtils; import org.chromium.chrome.R; -import org.chromium.chrome.browser.DefaultBrowserInfo; +import org.chromium.chrome.browser.DefaultBrowserInfo2; import org.chromium.chrome.browser.app.reengagement.ReengagementActivity; import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule; import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils; @@ -92,7 +93,7 @@ @After public void tearDown() { TrackerFactory.setTrackerForTests(null); - DefaultBrowserInfo.clearDefaultInfoForTests(); + DefaultBrowserInfo2.clearDefaultInfoForTests(); FeatureList.resetTestCanUseDefaultsForTesting(); FeatureList.setTestFeatures(null); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) closeReengagementNotifications(); @@ -101,7 +102,7 @@ @Test @MediumTest public void testReengagementNotificationSent() { - DefaultBrowserInfo.setDefaultInfoForTests( + DefaultBrowserInfo2.setDefaultInfoForTests( createDefaultInfo(/* passesPrecondition = */ true)); doReturn(true).when(mTracker).shouldTriggerHelpUI( FeatureConstants.CHROME_REENGAGEMENT_NOTIFICATION_1_FEATURE); @@ -121,7 +122,7 @@ @Test @MediumTest public void testReengagementDifferentNotificationSent() { - DefaultBrowserInfo.setDefaultInfoForTests( + DefaultBrowserInfo2.setDefaultInfoForTests( createDefaultInfo(/* passesPrecondition = */ true)); doReturn(true).when(mTracker).shouldTriggerHelpUI( FeatureConstants.CHROME_REENGAGEMENT_NOTIFICATION_2_FEATURE); @@ -141,7 +142,7 @@ @Test @MediumTest public void testReengagementNotificationNotSentDueToIPH() { - DefaultBrowserInfo.setDefaultInfoForTests( + DefaultBrowserInfo2.setDefaultInfoForTests( createDefaultInfo(/* passesPrecondition = */ true)); mCustomTabActivityTestRule.startCustomTabActivityWithIntent( CustomTabsTestUtils.createMinimalCustomTabIntent( @@ -165,7 +166,7 @@ @Test @MediumTest public void testReengagementNotificationNotSentDueToPreconditions() { - DefaultBrowserInfo.setDefaultInfoForTests( + DefaultBrowserInfo2.setDefaultInfoForTests( createDefaultInfo(/* passesPrecondition = */ false)); mCustomTabActivityTestRule.startCustomTabActivityWithIntent( CustomTabsTestUtils.createMinimalCustomTabIntent( @@ -189,7 +190,7 @@ @Test @MediumTest public void testReengagementNotificationNotSentDueToUnavailablePreconditions() { - DefaultBrowserInfo.setDefaultInfoForTests(null); + DefaultBrowserInfo2.setDefaultInfoForTests(null); mCustomTabActivityTestRule.startCustomTabActivityWithIntent( CustomTabsTestUtils.createMinimalCustomTabIntent( InstrumentationRegistry.getTargetContext(), @@ -228,6 +229,7 @@ @Test @SmallTest + @DisabledTest(message = "crbug.com/1112519 - Disabled while safety guard is in place.") public void testEngagementTrackedWhenDisabled() { setReengagementNotificationEnabled(false); mTabbedActivityTestRule.startMainActivityFromLauncher(); @@ -246,7 +248,7 @@ @MediumTest public void testEngagementNotificationNotSentDueToDisabled() { setReengagementNotificationEnabled(false); - DefaultBrowserInfo.setDefaultInfoForTests( + DefaultBrowserInfo2.setDefaultInfoForTests( createDefaultInfo(/* passesPrecondition = */ true)); mCustomTabActivityTestRule.startCustomTabActivityWithIntent( CustomTabsTestUtils.createMinimalCustomTabIntent( @@ -361,9 +363,9 @@ ReengagementNotificationController.NOTIFICATION_ID); } - private DefaultBrowserInfo.DefaultInfo createDefaultInfo(boolean passesPrecondition) { + private DefaultBrowserInfo2.DefaultInfo createDefaultInfo(boolean passesPrecondition) { int browserCount = passesPrecondition ? 2 : 1; - return new DefaultBrowserInfo.DefaultInfo(/* isChromeSystem = */ true, + return new DefaultBrowserInfo2.DefaultInfo(/* isChromeSystem = */ true, /* isChromeDefault = */ true, /* isDefaultSystem = */ true, /* hasDefault = */ true, browserCount, /* systemCount = */ 0);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/OWNERS b/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/OWNERS new file mode 100644 index 0000000..33e91e60 --- /dev/null +++ b/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/OWNERS
@@ -0,0 +1 @@ +file://chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/OWNERS
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerTest.java index 52dfba61..058e8b2a 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerTest.java
@@ -36,7 +36,7 @@ import org.chromium.base.metrics.test.ShadowRecordHistogram; import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.chrome.R; -import org.chromium.chrome.browser.DefaultBrowserInfo; +import org.chromium.chrome.browser.DefaultBrowserInfo2; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.notifications.NotificationUmaTracker; import org.chromium.components.feature_engagement.FeatureConstants; @@ -59,15 +59,15 @@ private class TestingReengagementNotificationController extends ReengagementNotificationController { - private DefaultBrowserInfo.DefaultInfo mInfo; + private DefaultBrowserInfo2.DefaultInfo mInfo; - TestingReengagementNotificationController(DefaultBrowserInfo.DefaultInfo info) { + TestingReengagementNotificationController(DefaultBrowserInfo2.DefaultInfo info) { super(mContext, mTracker, Activity.class); mInfo = info; } @Override - protected void getDefaultBrowserInfo(Callback<DefaultBrowserInfo.DefaultInfo> callback) { + protected void getDefaultBrowserInfo(Callback<DefaultBrowserInfo2.DefaultInfo> callback) { new Handler().post(() -> callback.onResult(mInfo)); } } @@ -269,9 +269,9 @@ return null; } - private DefaultBrowserInfo.DefaultInfo createDefaultInfo(boolean passesPrecondition) { + private DefaultBrowserInfo2.DefaultInfo createDefaultInfo(boolean passesPrecondition) { int browserCount = passesPrecondition ? 2 : 1; - return new DefaultBrowserInfo.DefaultInfo(/* isChromeSystem = */ true, + return new DefaultBrowserInfo2.DefaultInfo(/* isChromeSystem = */ true, /* isChromeDefault = */ true, /* isDefaultSystem = */ true, /* hasDefault = */ true, browserCount, /* systemCount = */ 0);
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 58bc8f5..4ebfb5e1 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -3176,6 +3176,10 @@ flag_descriptions::kAndroidPictureInPictureAPIName, flag_descriptions::kAndroidPictureInPictureAPIDescription, kOsAndroid, FEATURE_VALUE_TYPE(media::kPictureInPictureAPI)}, + {"reengagement-notification", + flag_descriptions::kReengagementNotificationName, + flag_descriptions::kReengagementNotificationDescription, kOsAndroid, + FEATURE_VALUE_TYPE(chrome::android::kReengagementNotification)}, #endif // OS_ANDROID {"disallow-doc-written-script-loads", flag_descriptions::kDisallowDocWrittenScriptsUiName, @@ -5102,6 +5106,10 @@ flag_descriptions::kDisplayAlignmentAssistanceName, flag_descriptions::kDisplayAlignmentAssistanceDescription, kOsCrOS, FEATURE_VALUE_TYPE(ash::features::kDisplayAlignAssist)}, + + {"print-save-to-drive", flag_descriptions::kPrintSaveToDriveName, + flag_descriptions::kPrintSaveToDriveDescription, kOsCrOS, + FEATURE_VALUE_TYPE(chromeos::features::kPrintSaveToDrive)}, #endif // OS_CHROMEOS {"autofill-off-no-server-data",
diff --git a/chrome/browser/android/vr/arcore_device/ar_image_transport.cc b/chrome/browser/android/vr/arcore_device/ar_image_transport.cc index f607441a..e22108c 100644 --- a/chrome/browser/android/vr/arcore_device/ar_image_transport.cc +++ b/chrome/browser/android/vr/arcore_device/ar_image_transport.cc
@@ -172,7 +172,8 @@ buffer->mailbox_holder = mailbox_bridge_->CreateSharedImage( buffer->gmb.get(), gfx::ColorSpace(), shared_image_usage); DVLOG(2) << ": CreateSharedImage, mailbox=" - << buffer->mailbox_holder.mailbox.ToDebugString(); + << buffer->mailbox_holder.mailbox.ToDebugString() << ", SyncToken=" + << buffer->mailbox_holder.sync_token.ToDebugString(); auto img = base::MakeRefCounted<gl::GLImageAHardwareBuffer>(size); @@ -230,6 +231,8 @@ // that it's transitioned through "processing" and "rendering" states back // to "animating". DCHECK(shared_buffer->mailbox_holder.sync_token.HasData()); + DVLOG(2) << ": SyncToken=" + << shared_buffer->mailbox_holder.sync_token.ToDebugString(); return shared_buffer->mailbox_holder; }
diff --git a/chrome/browser/autofill/autofill_interactive_uitest.cc b/chrome/browser/autofill/autofill_interactive_uitest.cc index ac87aa55..dfd89cc6 100644 --- a/chrome/browser/autofill/autofill_interactive_uitest.cc +++ b/chrome/browser/autofill/autofill_interactive_uitest.cc
@@ -393,7 +393,8 @@ ASSERT_TRUE(content::ExecuteScriptAndExtractString( GetWebContents(), "window.domAutomationController.send(" - " document.getElementById('" + field_name + "').value);", + " document.getElementById('" + + field_name + "').value);", &value)); EXPECT_EQ(expected_value, value) << "for field " << field_name; } @@ -416,7 +417,7 @@ GetWebContents(), "window.domAutomationController.send(" " document.defaultView.getComputedStyle(document.getElementById('" + - field_name + "')).backgroundColor);", + field_name + "')).backgroundColor);", color)); } @@ -1256,6 +1257,35 @@ EXPECT_TRUE(test_delegate()->Wait()); } +// Makes sure that clicking a field while there is no enough height in the +// content area for at least one suggestion, won't show the autofill popup. This +// is a regression test for crbug.com/1108181 +IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest, + DontAutofillShowPopupWhenNoEnoughHeightInContentArea) { + // This firstname field starts at y=-100px and has a height of 5120px. There + // is no enough space to show at least one row of the autofill popup and hence + // the autofill shouldn't be shown. + static const char kTestFormWithLargeInputField[] = + "<form action=\"http://www.example.com/\" method=\"POST\">" + "<label for=\"firstname\">First name:</label>" + " <input type=\"text\" id=\"firstname\" style=\"position:fixed; " + "top:-100px;height:5120px\"><br>" + "<label for=\"lastname\">Last name:</label>" + " <input type=\"text\" id=\"lastname\"><br>" + "<label for=\"city\">City:</label>" + " <input type=\"text\" id=\"city\"><br>" + "</form>"; + + CreateTestProfile(); + SetTestUrlResponse(kTestFormWithLargeInputField); + + ui_test_utils::NavigateToURL(browser(), GetTestUrl()); + + FocusFirstNameField(); + SendKeyToPage(GetWebContents(), ui::DomKey::ARROW_DOWN); + ASSERT_NO_FATAL_FAILURE(MakeSurePopupDoesntAppear()); +} + // Test that a field is still autofillable after the previously autofilled // value is deleted. IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest, OnDeleteValueAfterAutofill) { @@ -2156,7 +2186,7 @@ PopulateForm("NAME_FIRST"); ASSERT_TRUE(content::ExecuteScript( - GetWebContents(), "document.getElementById('testform').reset()")); + GetWebContents(), "document.getElementById('testform').reset()")); PopulateForm("NAME_FIRST"); @@ -3379,7 +3409,7 @@ // The fields that were initially filled and not reset should still be filled. ExpectFieldValue("firstname", ""); // That field value was reset dynamically. ExpectFieldValue("address1", "4120 Freidrich Lane"); - ExpectFieldValue("state", "CA"); // Default value. + ExpectFieldValue("state", "CA"); // Default value. ExpectFieldValue("city", "Austin"); ExpectFieldValue("company", company_name_enabled_ ? "Initech" : ""); ExpectFieldValue("email", "red.swingline@initech.com");
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc index e2afbb6c..3074a26 100644 --- a/chrome/browser/chrome_browser_interface_binders.cc +++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -110,6 +110,7 @@ #include "chrome/browser/media/kaleidoscope/kaleidoscope_ui.h" #include "chrome/browser/media/kaleidoscope/mojom/kaleidoscope.mojom.h" #include "chrome/browser/payments/payment_request_factory.h" +#include "chrome/browser/promo_browser_command/promo_browser_command.mojom.h" #include "chrome/browser/speech/speech_recognition_service.h" #include "chrome/browser/speech/speech_recognition_service_factory.h" #include "chrome/browser/ui/webui/downloads/downloads.mojom.h" @@ -524,6 +525,9 @@ RegisterWebUIControllerInterfaceBinder< new_tab_page::mojom::PageHandlerFactory, NewTabPageUI>(map); + RegisterWebUIControllerInterfaceBinder< + promo_browser_command::mojom::CommandHandler, NewTabPageUI>(map); + RegisterWebUIControllerInterfaceBinder<media_feeds::mojom::MediaFeedsStore, MediaFeedsUI>(map);
diff --git a/chrome/browser/chromeos/arc/session/arc_session_manager.cc b/chrome/browser/chromeos/arc/session/arc_session_manager.cc index 53bb6ca..1c777ab 100644 --- a/chrome/browser/chromeos/arc/session/arc_session_manager.cc +++ b/chrome/browser/chromeos/arc/session/arc_session_manager.cc
@@ -327,7 +327,7 @@ SIGN_IN_CLOUD_PROVISION_FLOW_ACCOUNT_MISSING_ERROR; case mojom::CloudProvisionFlowError::ERROR_ACCOUNT_NOT_READY: - case mojom::CloudProvisionFlowError::ERROR_ACCOUNT_NOT_WHITELISTED: + case mojom::CloudProvisionFlowError::ERROR_ACCOUNT_NOT_ALLOWLISTED: case mojom::CloudProvisionFlowError::ERROR_DPC_SUPPORT: case mojom::CloudProvisionFlowError::ERROR_ENTERPRISE_INVALID: return ArcSupportHost::Error::
diff --git a/chrome/browser/chromeos/arc/test/test_arc_session_manager.cc b/chrome/browser/chromeos/arc/test/test_arc_session_manager.cc index 1eb6a00..a8f42fa2 100644 --- a/chrome/browser/chromeos/arc/test/test_arc_session_manager.cc +++ b/chrome/browser/chromeos/arc/test/test_arc_session_manager.cc
@@ -22,7 +22,8 @@ // Create empty prop files so ArcSessionManager's property expansion code // works like production. for (const char* filename : - {"default.prop", "build.prop", "vendor_build.prop"}) { + {"default.prop", "build.prop", "vendor_build.prop", + "system_ext_build.prop", "product_build.prop", "odm_build.prop"}) { if (base::WriteFile(source_dir->Append(filename), "", 1) != 1) return false; }
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_sharesheet.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_sharesheet.cc index 8605bd8..03096797 100644 --- a/chrome/browser/chromeos/extensions/file_manager/private_api_sharesheet.cc +++ b/chrome/browser/chromeos/extensions/file_manager/private_api_sharesheet.cc
@@ -13,6 +13,7 @@ #include "chrome/common/extensions/api/file_manager_private.h" #include "chrome/common/extensions/api/file_manager_private_internal.h" #include "components/services/app_service/public/cpp/intent_util.h" +#include "content/public/browser/web_contents.h" #include "extensions/browser/api/file_handlers/directory_util.h" #include "extensions/browser/api/file_handlers/mime_util.h" #include "storage/browser/file_system/file_system_context.h" @@ -68,11 +69,18 @@ void FileManagerPrivateInternalSharesheetHasTargetsFunction:: OnMimeTypesCollected(std::unique_ptr<std::vector<std::string>> mime_types) { - auto* sharesheet_service = + sharesheet::SharesheetService* sharesheet_service = sharesheet::SharesheetServiceFactory::GetForProfile( chrome_details_.GetProfile()); - bool result; + bool result = false; + + if (!sharesheet_service) { + LOG(ERROR) << "Couldn't get Sharesheet Service for profile"; + Respond(ArgumentList(extensions::api::file_manager_private_internal:: + SharesheetHasTargets::Results::Create(result))); + } + result = sharesheet_service->HasShareTargets( apps_util::CreateShareIntentFromFiles(urls_, *mime_types)); @@ -80,4 +88,66 @@ SharesheetHasTargets::Results::Create(result))); } +FileManagerPrivateInternalInvokeSharesheetFunction:: + FileManagerPrivateInternalInvokeSharesheetFunction() + : chrome_details_(this) {} + +FileManagerPrivateInternalInvokeSharesheetFunction:: + ~FileManagerPrivateInternalInvokeSharesheetFunction() = default; + +ExtensionFunction::ResponseAction +FileManagerPrivateInternalInvokeSharesheetFunction::Run() { + using extensions::api::file_manager_private_internal::InvokeSharesheet:: + Params; + const std::unique_ptr<Params> params(Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params); + + if (params->urls.empty()) + return RespondNow(Error("No URLs provided")); + + const scoped_refptr<storage::FileSystemContext> file_system_context = + file_manager::util::GetFileSystemContextForRenderFrameHost( + chrome_details_.GetProfile(), render_frame_host()); + + std::vector<storage::FileSystemURL> file_system_urls; + // Collect all the URLs, convert them to GURLs, and crack all the urls into + // file paths. + for (size_t i = 0; i < params->urls.size(); ++i) { + const GURL url(params->urls[i]); + storage::FileSystemURL file_system_url(file_system_context->CrackURL(url)); + if (!chromeos::FileSystemBackend::CanHandleURL(file_system_url)) + continue; + urls_.push_back(url); + file_system_urls.push_back(file_system_url); + } + + mime_type_collector_ = + std::make_unique<app_file_handler_util::MimeTypeCollector>( + chrome_details_.GetProfile()); + mime_type_collector_->CollectForURLs( + file_system_urls, + base::BindOnce(&FileManagerPrivateInternalInvokeSharesheetFunction:: + OnMimeTypesCollected, + this)); + + return RespondLater(); +} + +void FileManagerPrivateInternalInvokeSharesheetFunction::OnMimeTypesCollected( + std::unique_ptr<std::vector<std::string>> mime_types) { + // On button press show sharesheet bubble. + auto* profile = chrome_details_.GetProfile(); + sharesheet::SharesheetService* sharesheet_service = + sharesheet::SharesheetServiceFactory::GetForProfile(profile); + if (!sharesheet_service) { + Respond(Error("Cannot find sharesheet service")); + return; + } + sharesheet_service->ShowBubble( + GetSenderWebContents(), + apps_util::CreateShareIntentFromFiles(urls_, *mime_types)); + + Respond(NoArguments()); +} + } // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_sharesheet.h b/chrome/browser/chromeos/extensions/file_manager/private_api_sharesheet.h index d4dd6c1..4611706c 100644 --- a/chrome/browser/chromeos/extensions/file_manager/private_api_sharesheet.h +++ b/chrome/browser/chromeos/extensions/file_manager/private_api_sharesheet.h
@@ -47,6 +47,31 @@ const ChromeExtensionFunctionDetails chrome_details_; }; +// Implements the chrome.fileManagerPrivateInternal.invokeSharesheet method. +class FileManagerPrivateInternalInvokeSharesheetFunction + : public LoggedExtensionFunction { + public: + FileManagerPrivateInternalInvokeSharesheetFunction(); + + DECLARE_EXTENSION_FUNCTION("fileManagerPrivateInternal.invokeSharesheet", + FILEMANAGERPRIVATEINTERNAL_INVOKESHARESHEET) + + protected: + ~FileManagerPrivateInternalInvokeSharesheetFunction() override; + + // ExtensionFunction overrides. + ResponseAction Run() override; + + private: + void OnMimeTypesCollected( + std::unique_ptr<std::vector<std::string>> mime_types); + + std::unique_ptr<app_file_handler_util::MimeTypeCollector> + mime_type_collector_; + std::vector<GURL> urls_; + const ChromeExtensionFunctionDetails chrome_details_; +}; + } // namespace extensions #endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_SHARESHEET_H_
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc index e4be506..878d0ba 100644 --- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc +++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -920,6 +920,7 @@ TestCase("recentsCrostiniMounted"), TestCase("recentsDownloadsAndDrive"), TestCase("recentsDownloadsAndDriveWithOverlap"), + TestCase("recentsNested"), TestCase("recentAudioDownloads").EnableUnifiedMediaView(), TestCase("recentAudioDownloadsAndDrive").EnableUnifiedMediaView(), TestCase("recentImagesDownloads").EnableUnifiedMediaView(),
diff --git a/chrome/browser/chromeos/web_applications/terminal_source.cc b/chrome/browser/chromeos/web_applications/terminal_source.cc index 8420679..76aaa2bf 100644 --- a/chrome/browser/chromeos/web_applications/terminal_source.cc +++ b/chrome/browser/chromeos/web_applications/terminal_source.cc
@@ -97,12 +97,12 @@ auto* webui_allowlist = WebUIAllowlist::GetOrCreate(profile); const url::Origin terminal_origin = url::Origin::Create(GURL(source)); CHECK(!terminal_origin.opaque()); - webui_allowlist->RegisterAutoGrantedPermission( - terminal_origin, ContentSettingsType::NOTIFICATIONS); - webui_allowlist->RegisterAutoGrantedPermission( - terminal_origin, ContentSettingsType::CLIPBOARD_READ_WRITE); - webui_allowlist->RegisterAutoGrantedPermission(terminal_origin, - ContentSettingsType::COOKIES); + for (auto permission : + {ContentSettingsType::JAVASCRIPT, ContentSettingsType::NOTIFICATIONS, + ContentSettingsType::CLIPBOARD_READ_WRITE, ContentSettingsType::COOKIES, + ContentSettingsType::IMAGES, ContentSettingsType::SOUND}) { + webui_allowlist->RegisterAutoGrantedPermission(terminal_origin, permission); + } } TerminalSource::~TerminalSource() = default;
diff --git a/chrome/browser/data_saver/lite_video_browsertest.cc b/chrome/browser/data_saver/lite_video_browsertest.cc index 3dece8ee..098391c2 100644 --- a/chrome/browser/data_saver/lite_video_browsertest.cc +++ b/chrome/browser/data_saver/lite_video_browsertest.cc
@@ -8,7 +8,11 @@ #include "base/test/scoped_feature_list.h" #include "build/build_config.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/lite_video/lite_video_features.h" +#include "chrome/browser/lite_video/lite_video_hint.h" +#include "chrome/browser/lite_video/lite_video_navigation_metrics.h" #include "chrome/browser/lite_video/lite_video_switches.h" +#include "chrome/browser/lite_video/lite_video_user_blocklist.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" @@ -16,6 +20,7 @@ #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" #include "components/metrics/content/subprocess_metrics_provider.h" +#include "components/ukm/test_ukm_recorder.h" #include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test.h" @@ -23,6 +28,8 @@ #include "media/base/media_switches.h" #include "media/base/test_data_util.h" #include "net/test/embedded_test_server/embedded_test_server.h" +#include "services/metrics/public/cpp/ukm_builders.h" +#include "services/metrics/public/cpp/ukm_source.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" #include "url/url_util.h" @@ -123,15 +130,18 @@ std::string query = media::GetURLQueryString(query_params); content::TitleWatcher title_watcher( browser()->tab_strip_model()->GetActiveWebContents(), expected_title); - EXPECT_TRUE(ui_test_utils::NavigateToURL( - browser(), http_server_.GetURL("/" + html_page + "?" + query))); + media_url_ = http_server_.GetURL("/" + html_page + "?" + query); + EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), media_url_)); EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); } const base::HistogramTester& histogram_tester() { return histogram_tester_; } + GURL media_url() { return media_url_; } + private: bool enable_lite_mode_; // Whether LiteMode is enabled. + GURL media_url_; base::test::ScopedFeatureList scoped_feature_list_; net::EmbeddedTestServer http_server_; base::HistogramTester histogram_tester_; @@ -150,6 +160,7 @@ IN_PROC_BROWSER_TEST_F(LiteVideoBrowserTest, DISABLE_ON_WIN_MAC_CHROMEOS(SimplePlayback)) { + ukm::TestAutoSetUkmRecorder ukm_recorder; TestMSEPlayback("bear-vp9.webm", "2000", "2000", false); RetryForHistogramUntilCountReached(histogram_tester(), @@ -161,6 +172,26 @@ // made and the hint is available. RetryForHistogramUntilCountReached(histogram_tester(), "LiteVideo.URLLoader.ThrottleLatency", 1); + + // Close the tab to flush the UKM metrics. + browser()->tab_strip_model()->GetActiveWebContents()->Close(); + auto entries = + ukm_recorder.GetEntriesByName(ukm::builders::LiteVideo::kEntryName); + ASSERT_EQ(1u, entries.size()); + auto* entry = entries[0]; + ukm_recorder.ExpectEntrySourceHasUrl(entry, media_url()); + ukm_recorder.ExpectEntryMetric( + entry, ukm::builders::LiteVideo::kThrottlingStartDecisionName, + static_cast<int>(lite_video::LiteVideoDecision::kAllowed)); + // Blocklist reason is unknown due to force overriding the decision logic + // for testing. + ukm_recorder.ExpectEntryMetric( + entry, ukm::builders::LiteVideo::kBlocklistReasonName, + static_cast<int>(lite_video::LiteVideoBlocklistReason::kUnknown)); + ukm_recorder.ExpectEntryMetric( + entry, ukm::builders::LiteVideo::kThrottlingResultName, + static_cast<int>( + lite_video::LiteVideoThrottleResult::kThrottledWithoutStop)); } class LiteVideoWithLiteModeDisabledBrowserTest : public LiteVideoBrowserTest { @@ -172,6 +203,7 @@ IN_PROC_BROWSER_TEST_F(LiteVideoWithLiteModeDisabledBrowserTest, VideoThrottleDisabled) { + ukm::TestAutoSetUkmRecorder ukm_recorder; TestMSEPlayback("bear-vp9.webm", "2000", "2000", false); RetryForHistogramUntilCountReached(histogram_tester(), @@ -179,10 +211,17 @@ histogram_tester().ExpectTotalCount("LiteVideo.HintAgent.HasHint", 0); histogram_tester().ExpectTotalCount("LiteVideo.URLLoader.ThrottleLatency", 0); + + // Close the tab to flush the UKM metrics. + browser()->tab_strip_model()->GetActiveWebContents()->Close(); + auto entries = + ukm_recorder.GetEntriesByName(ukm::builders::LiteVideo::kEntryName); + ASSERT_EQ(0u, entries.size()); } IN_PROC_BROWSER_TEST_F(LiteVideoBrowserTest, MSEPlaybackStalledDueToBufferUnderflow) { + ukm::TestAutoSetUkmRecorder ukm_recorder; TestMSEPlayback("bear-vp9.webm", "2700", "500", false); RetryForHistogramUntilCountReached(histogram_tester(), @@ -197,10 +236,31 @@ EXPECT_GE(1U, histogram_tester() .GetAllSamples("LiteVideo.HintsAgent.StopThrottling") .size()); + // Close the tab to flush the UKM metrics. + browser()->tab_strip_model()->GetActiveWebContents()->Close(); + + auto entries = + ukm_recorder.GetEntriesByName(ukm::builders::LiteVideo::kEntryName); + ASSERT_EQ(1u, entries.size()); + auto* entry = entries[0]; + ukm_recorder.ExpectEntrySourceHasUrl(entry, media_url()); + ukm_recorder.ExpectEntryMetric( + entry, ukm::builders::LiteVideo::kThrottlingStartDecisionName, + static_cast<int>(lite_video::LiteVideoDecision::kAllowed)); + // Blocklist reason is unknown due to force overriding the decision logic + // for testing. + ukm_recorder.ExpectEntryMetric( + entry, ukm::builders::LiteVideo::kBlocklistReasonName, + static_cast<int>(lite_video::LiteVideoBlocklistReason::kUnknown)); + ukm_recorder.ExpectEntryMetric( + entry, ukm::builders::LiteVideo::kThrottlingResultName, + static_cast<int>( + lite_video::LiteVideoThrottleResult::kThrottleStoppedOnRebuffer)); } IN_PROC_BROWSER_TEST_F(LiteVideoBrowserTest, MSEPlaybackStalledDueToBufferUnderflow_WithSubframe) { + ukm::TestAutoSetUkmRecorder ukm_recorder; TestMSEPlayback("bear-vp9.webm", "2700", "500", true); RetryForHistogramUntilCountReached(histogram_tester(), @@ -217,6 +277,27 @@ EXPECT_GE(2U, histogram_tester() .GetAllSamples("LiteVideo.HintsAgent.StopThrottling") .size()); + // Close the tab to flush the UKM metrics. + browser()->tab_strip_model()->GetActiveWebContents()->Close(); + + auto entries = + ukm_recorder.GetEntriesByName(ukm::builders::LiteVideo::kEntryName); + // Only 1 UKM entry logged, tied to the mainframe navigation. + ASSERT_EQ(1u, entries.size()); + auto* entry = entries[0]; + ukm_recorder.ExpectEntrySourceHasUrl(entry, media_url()); + ukm_recorder.ExpectEntryMetric( + entry, ukm::builders::LiteVideo::kThrottlingStartDecisionName, + static_cast<int>(lite_video::LiteVideoDecision::kAllowed)); + // Blocklist reason is unknown due to force overriding the decision logic + // for testing. + ukm_recorder.ExpectEntryMetric( + entry, ukm::builders::LiteVideo::kBlocklistReasonName, + static_cast<int>(lite_video::LiteVideoBlocklistReason::kUnknown)); + ukm_recorder.ExpectEntryMetric( + entry, ukm::builders::LiteVideo::kThrottlingResultName, + static_cast<int>( + lite_video::LiteVideoThrottleResult::kThrottleStoppedOnRebuffer)); } class LiteVideoAndLiteModeDisabledBrowserTest : public LiteVideoBrowserTest { @@ -228,6 +309,7 @@ IN_PROC_BROWSER_TEST_F(LiteVideoAndLiteModeDisabledBrowserTest, VideoThrottleDisabled) { + ukm::TestAutoSetUkmRecorder ukm_recorder; TestMSEPlayback("bear-vp9.webm", "2000", "2000", false); RetryForHistogramUntilCountReached(histogram_tester(), @@ -235,6 +317,11 @@ histogram_tester().ExpectTotalCount("LiteVideo.HintAgent.HasHint", 0); histogram_tester().ExpectTotalCount("LiteVideo.URLLoader.ThrottleLatency", 0); + // Close the tab to flush the UKM metrics. + browser()->tab_strip_model()->GetActiveWebContents()->Close(); + auto entries = + ukm_recorder.GetEntriesByName(ukm::builders::LiteVideo::kEntryName); + ASSERT_EQ(0u, entries.size()); } } // namespace
diff --git a/chrome/browser/extensions/api/debugger/debugger_api.cc b/chrome/browser/extensions/api/debugger/debugger_api.cc index 73456ea..f957e78 100644 --- a/chrome/browser/extensions/api/debugger/debugger_api.cc +++ b/chrome/browser/extensions/api/debugger/debugger_api.cc
@@ -416,12 +416,9 @@ *debuggee_.tab_id, browser_context(), include_incognito_information(), &web_contents); if (result && web_contents) { - // TODO(rdevlin.cronin) This should definitely be GetLastCommittedURL(). - GURL url = web_contents->GetVisibleURL(); - if (!ExtensionCanAttachToURL( - *extension(), url, Profile::FromBrowserContext(browser_context()), - error)) { + *extension(), web_contents->GetLastCommittedURL(), + Profile::FromBrowserContext(browser_context()), error)) { return false; }
diff --git a/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc b/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc index 24e38b2..3f87fc8e 100644 --- a/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc +++ b/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc
@@ -78,12 +78,16 @@ NativeMessagingLazyApiTest, ::testing::Values(ContextType::kServiceWorker)); -IN_PROC_BROWSER_TEST_P(NativeMessagingLazyApiTest, NativeMessagingBasic) { +// Flaky test: http://crbug.com/1111536 +IN_PROC_BROWSER_TEST_P(NativeMessagingLazyApiTest, + DISABLED_NativeMessagingBasic) { ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestHost(false)); ASSERT_TRUE(RunLazyTest("native_messaging_lazy")) << message_; } -IN_PROC_BROWSER_TEST_P(NativeMessagingLazyApiTest, UserLevelNativeMessaging) { +// Flaky test: http://crbug.com/1111337 +IN_PROC_BROWSER_TEST_P(NativeMessagingLazyApiTest, + DISABLED_UserLevelNativeMessaging) { ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestHost(true)); ASSERT_TRUE(RunLazyTest("native_messaging_lazy")) << message_; }
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc index 584ae172..005f8e0df 100644 --- a/chrome/browser/extensions/api/tabs/tabs_api.cc +++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -1596,6 +1596,9 @@ return RespondNow(NoArguments()); } +TabsRemoveFunction::TabsRemoveFunction() = default; +TabsRemoveFunction::~TabsRemoveFunction() = default; + ExtensionFunction::ResponseAction TabsRemoveFunction::Run() { std::unique_ptr<tabs::Remove::Params> params( tabs::Remove::Params::Create(*args_)); @@ -1613,7 +1616,16 @@ if (!RemoveTab(*params->tab_ids.as_integer, &error)) return RespondNow(Error(std::move(error))); } - return RespondNow(NoArguments()); + triggered_all_tab_removals_ = true; + DCHECK(!did_respond()); + // WebContentsDestroyed will return the response in most cases, except when + // the last tab closed immediately (it won't return a response because + // |triggered_all_tab_removals_| will still be false). In this case we should + // return the response from here. + if (remaining_tabs_count_ == 0) { + return RespondNow(NoArguments()); + } + return RespondLater(); } bool TabsRemoveFunction::RemoveTab(int tab_id, std::string* error) { @@ -1629,6 +1641,17 @@ *error = tabs_constants::kTabStripNotEditableError; return false; } + // The tab might not immediately close after calling Close() below, so we + // should wait until WebContentsDestroyed is called before responding. + web_contents_destroyed_observers_.push_back( + std::make_unique<WebContentsDestroyedObserver>(this, contents)); + // Ensure that we're going to keep this class alive until + // |remaining_tabs_count| reaches zero. This relies on WebContents::Close() + // always (eventually) resulting in a WebContentsDestroyed() call; otherwise, + // this function will never respond and may leak. + AddRef(); + remaining_tabs_count_++; + // There's a chance that the tab is being dragged, or we're in some other // nested event loop. This code path ensures that the tab is safely closed // under such circumstances, whereas |TabStripModel::CloseWebContentsAt()| @@ -1637,6 +1660,40 @@ return true; } +void TabsRemoveFunction::TabDestroyed() { + DCHECK_GT(remaining_tabs_count_, 0); + // One of the tabs we wanted to remove had been destroyed. + remaining_tabs_count_--; + // If we've triggered all the tab removals we need, and this is the last tab + // we're waiting for and we haven't sent a response (it's possible that we've + // responded earlier in case of errors, etc.), send a response. + if (triggered_all_tab_removals_ && remaining_tabs_count_ == 0 && + !did_respond()) { + Respond(NoArguments()); + } + Release(); +} + +class TabsRemoveFunction::WebContentsDestroyedObserver + : public content::WebContentsObserver { + public: + WebContentsDestroyedObserver(extensions::TabsRemoveFunction* owner, + content::WebContents* watched_contents) + : content::WebContentsObserver(watched_contents), owner_(owner) {} + + ~WebContentsDestroyedObserver() override = default; + WebContentsDestroyedObserver(const WebContentsDestroyedObserver&) = delete; + WebContentsDestroyedObserver& operator=(const WebContentsDestroyedObserver&) = + delete; + + // WebContentsObserver + void WebContentsDestroyed() override { owner_->TabDestroyed(); } + + private: + // Guaranteed to outlive this object. + extensions::TabsRemoveFunction* owner_; +}; + TabsCaptureVisibleTabFunction::TabsCaptureVisibleTabFunction() : chrome_details_(this) { }
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.h b/chrome/browser/extensions/api/tabs/tabs_api.h index ed3aa53..a3df97cb 100644 --- a/chrome/browser/extensions/api/tabs/tabs_api.h +++ b/chrome/browser/extensions/api/tabs/tabs_api.h
@@ -24,7 +24,6 @@ class GURL; class SkBitmap; class TabStripModel; - namespace content { class WebContents; } @@ -175,9 +174,20 @@ DECLARE_EXTENSION_FUNCTION("tabs.reload", TABS_RELOAD) }; class TabsRemoveFunction : public ExtensionFunction { - ~TabsRemoveFunction() override {} + public: + TabsRemoveFunction(); + void TabDestroyed(); + + private: + class WebContentsDestroyedObserver; + ~TabsRemoveFunction() override; ResponseAction Run() override; bool RemoveTab(int tab_id, std::string* error); + + int remaining_tabs_count_ = 0; + bool triggered_all_tab_removals_ = false; + std::vector<std::unique_ptr<WebContentsDestroyedObserver>> + web_contents_destroyed_observers_; DECLARE_EXTENSION_FUNCTION("tabs.remove", TABS_REMOVE) }; class TabsDetectLanguageFunction
diff --git a/chrome/browser/extensions/extension_tabs_apitest.cc b/chrome/browser/extensions/extension_tabs_apitest.cc index 0da92c4..f5d6979 100644 --- a/chrome/browser/extensions/extension_tabs_apitest.cc +++ b/chrome/browser/extensions/extension_tabs_apitest.cc
@@ -119,6 +119,15 @@ ASSERT_TRUE(RunExtensionSubtest("tabs/basics", "opener.html")) << message_; } +IN_PROC_BROWSER_TEST_F(ExtensionApiTabTest, TabRemove) { + ASSERT_TRUE(RunExtensionSubtest("tabs/basics", "remove.html")) << message_; +} + +IN_PROC_BROWSER_TEST_F(ExtensionApiTabTest, TabRemoveMultiple) { + ASSERT_TRUE(RunExtensionSubtest("tabs/basics", "remove-multiple.html")) + << message_; +} + IN_PROC_BROWSER_TEST_F(ExtensionApiTabTest, TabGetCurrent) { ASSERT_TRUE(RunExtensionTest("tabs/get_current")) << message_; }
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 211318c6..5711b2ec 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -52,7 +52,7 @@ { "name": "align-font-display-auto-lcp", "owners": [ "xiaochengh" ], - "expiry_milestone": 85 + "expiry_milestone": 87 }, { "name": "allow-disable-mouse-acceleration", @@ -2838,6 +2838,11 @@ "expiry_milestone": 87 }, { + "name": "legacy-tls-interstitial", + "owners": [ "cthomp" ], + "expiry_milestone": 92 + }, + { "name": "list-all-display-modes", "owners": [ "//ui/display/OWNERS" ], // This flag is used for debugging and development purposes to list all @@ -3588,6 +3593,11 @@ "expiry_milestone": 88 }, { + "name": "print-save-to-drive", + "owners": [ "gavinwill", "cros-peripherals@google.com" ], + "expiry_milestone": 90 + }, + { "name": "print-with-reduced-rasterization", "owners": [ "thestig" ], "expiry_milestone": 89 @@ -3698,6 +3708,11 @@ "expiry_milestone": 87 }, { + "name": "reengagement-notification", + "owners": [ "dtrainor", "xingliu" ], + "expiry_milestone": 90 + }, + { "name": "related-searches", "owners": [ "ayman", "donnd" ], "expiry_milestone": 85
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index feb2a418..7c8cc58 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -1848,6 +1848,10 @@ "Enables the print management app that allows Chrome OS users to view " "and manage their native print jobs."; +const char kPrintSaveToDriveName[] = "Print Save to Drive locally"; +const char kPrintSaveToDriveDescription[] = + "Modifies Print Preview Save to Drive to use locally mounted Drive"; + const char kPrivacyElevatedAndroidName[] = "Elevate Privacy in Settings on Android"; const char kPrivacyElevatedAndroidDescription[] = @@ -2957,6 +2961,12 @@ "Enables showing UI which allows for easy reverting of the decision to " "never save passwords on a certain webiste"; +const char kReengagementNotificationName[] = + "Enable re-engagement notifications"; +const char kReengagementNotificationDescription[] = + "Enables Chrome to use the in-product help system to decide when " + "to show re-engagement notifications."; + const char kRelatedSearchesName[] = "Enables an experiment for Related Searches on Android"; const char kRelatedSearchesDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 900d203..03e8ff1 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -1069,6 +1069,9 @@ extern const char kPrintJobManagementAppName[]; extern const char kPrintJobManagementAppDescription[]; +extern const char kPrintSaveToDriveName[]; +extern const char kPrintSaveToDriveDescription[]; + extern const char kPrivacyElevatedAndroidName[]; extern const char kPrivacyElevatedAndroidDescription[]; @@ -1704,6 +1707,9 @@ extern const char kRecoverFromNeverSaveAndroidName[]; extern const char kRecoverFromNeverSaveAndroidDescription[]; +extern const char kReengagementNotificationName[]; +extern const char kReengagementNotificationDescription[]; + extern const char kRelatedSearchesName[]; extern const char kRelatedSearchesDescription[];
diff --git a/chrome/browser/lite_video/lite_video_decider.cc b/chrome/browser/lite_video/lite_video_decider.cc index 0a3fd95..7c16124 100644 --- a/chrome/browser/lite_video/lite_video_decider.cc +++ b/chrome/browser/lite_video/lite_video_decider.cc
@@ -171,8 +171,14 @@ return base::nullopt; // The navigation will have the LiteVideo optimization triggered so - // update the navigation blocklist. + // update the blocklist. user_blocklist_->AddNavigationToBlocklist(navigation_handle, false); + + navigation_handle->IsInMainFrame() + ? DidMediaRebuffer(navigation_handle->GetURL(), base::nullopt, false) + : DidMediaRebuffer( + navigation_handle->GetWebContents()->GetLastCommittedURL(), + navigation_handle->GetURL(), false); return hint; } @@ -210,4 +216,12 @@ LOCAL_HISTOGRAM_BOOLEAN("LiteVideo.UserBlocklist.ClearBlocklist", true); } +void LiteVideoDecider::DidMediaRebuffer(const GURL& mainframe_url, + base::Optional<GURL> subframe_url, + bool opt_out) { + if (user_blocklist_) + user_blocklist_->AddRebufferToBlocklist(mainframe_url, subframe_url, + opt_out); +} + } // namespace lite_video
diff --git a/chrome/browser/lite_video/lite_video_decider.h b/chrome/browser/lite_video/lite_video_decider.h index 1f4f49a..faf01ca 100644 --- a/chrome/browser/lite_video/lite_video_decider.h +++ b/chrome/browser/lite_video/lite_video_decider.h
@@ -70,6 +70,12 @@ void ClearBlocklist(const base::Time& delete_begin, const base::Time& delete_end); + // Update |user_blocklist_| that a rebuffer event consided an opt-out on the + // mainframe and subframe URLs occurred. + void DidMediaRebuffer(const GURL& mainframe_url, + base::Optional<GURL> subframe_url, + bool opt_out); + private: // The hint cache that holds LiteVideoHints that specify the parameters // for throttling media requests for that navigation.
diff --git a/chrome/browser/lite_video/lite_video_keyed_service_browsertest.cc b/chrome/browser/lite_video/lite_video_keyed_service_browsertest.cc index 215367c..02a7970 100644 --- a/chrome/browser/lite_video/lite_video_keyed_service_browsertest.cc +++ b/chrome/browser/lite_video/lite_video_keyed_service_browsertest.cc
@@ -246,6 +246,10 @@ ukm_recorder.ExpectEntryMetric( entry, ukm::builders::LiteVideo::kBlocklistReasonName, static_cast<int>(lite_video::LiteVideoBlocklistReason::kAllowed)); + ukm_recorder.ExpectEntryMetric( + entry, ukm::builders::LiteVideo::kThrottlingResultName, + static_cast<int>( + lite_video::LiteVideoThrottleResult::kThrottledWithoutStop)); } IN_PROC_BROWSER_TEST_F(LiteVideoKeyedServiceBrowserTest, @@ -288,6 +292,10 @@ ukm_recorder.ExpectEntryMetric( entry, ukm::builders::LiteVideo::kBlocklistReasonName, static_cast<int>(lite_video::LiteVideoBlocklistReason::kAllowed)); + ukm_recorder.ExpectEntryMetric( + entry, ukm::builders::LiteVideo::kThrottlingResultName, + static_cast<int>( + lite_video::LiteVideoThrottleResult::kThrottledWithoutStop)); } IN_PROC_BROWSER_TEST_F(LiteVideoKeyedServiceBrowserTest, @@ -347,6 +355,10 @@ entry, ukm::builders::LiteVideo::kBlocklistReasonName, static_cast<int>( lite_video::LiteVideoBlocklistReason::kNavigationReload)); + ukm_recorder.ExpectEntryMetric( + entry, ukm::builders::LiteVideo::kThrottlingResultName, + static_cast<int>( + lite_video::LiteVideoThrottleResult::kThrottledWithoutStop)); entry = entries[1]; ukm_recorder.ExpectEntrySourceHasUrl(entry, url); @@ -357,6 +369,10 @@ entry, ukm::builders::LiteVideo::kBlocklistReasonName, static_cast<int>( lite_video::LiteVideoBlocklistReason::kNavigationBlocklisted)); + ukm_recorder.ExpectEntryMetric( + entry, ukm::builders::LiteVideo::kThrottlingResultName, + static_cast<int>( + lite_video::LiteVideoThrottleResult::kThrottledWithoutStop)); } IN_PROC_BROWSER_TEST_F(LiteVideoKeyedServiceBrowserTest, @@ -416,6 +432,10 @@ entry, ukm::builders::LiteVideo::kBlocklistReasonName, static_cast<int>( lite_video::LiteVideoBlocklistReason::kNavigationForwardBack)); + ukm_recorder.ExpectEntryMetric( + entry, ukm::builders::LiteVideo::kThrottlingResultName, + static_cast<int>( + lite_video::LiteVideoThrottleResult::kThrottledWithoutStop)); entry = entries[1]; ukm_recorder.ExpectEntrySourceHasUrl(entry, url); @@ -426,6 +446,10 @@ entry, ukm::builders::LiteVideo::kBlocklistReasonName, static_cast<int>( lite_video::LiteVideoBlocklistReason::kNavigationBlocklisted)); + ukm_recorder.ExpectEntryMetric( + entry, ukm::builders::LiteVideo::kThrottlingResultName, + static_cast<int>( + lite_video::LiteVideoThrottleResult::kThrottledWithoutStop)); } IN_PROC_BROWSER_TEST_F(LiteVideoKeyedServiceBrowserTest, @@ -483,6 +507,10 @@ ukm_recorder.ExpectEntryMetric( entry, ukm::builders::LiteVideo::kBlocklistReasonName, static_cast<int>(lite_video::LiteVideoBlocklistReason::kAllowed)); + ukm_recorder.ExpectEntryMetric( + entry, ukm::builders::LiteVideo::kThrottlingResultName, + static_cast<int>( + lite_video::LiteVideoThrottleResult::kThrottledWithoutStop)); } } @@ -669,6 +697,10 @@ ukm_recorder.ExpectEntryMetric( entry, ukm::builders::LiteVideo::kBlocklistReasonName, static_cast<int>(lite_video::LiteVideoBlocklistReason::kAllowed)); + ukm_recorder.ExpectEntryMetric( + entry, ukm::builders::LiteVideo::kThrottlingResultName, + static_cast<int>( + lite_video::LiteVideoThrottleResult::kThrottledWithoutStop)); } class LiteVideoKeyedServiceCoinflipBrowserTest
diff --git a/chrome/browser/lite_video/lite_video_navigation_metrics.cc b/chrome/browser/lite_video/lite_video_navigation_metrics.cc index 4c8da07..09add76 100644 --- a/chrome/browser/lite_video/lite_video_navigation_metrics.cc +++ b/chrome/browser/lite_video/lite_video_navigation_metrics.cc
@@ -9,15 +9,18 @@ LiteVideoNavigationMetrics::LiteVideoNavigationMetrics( int64_t nav_id, LiteVideoDecision decision, - LiteVideoBlocklistReason blocklist_reason) + LiteVideoBlocklistReason blocklist_reason, + LiteVideoThrottleResult throttle_result) : nav_id_(nav_id), decision_(decision), - blocklist_reason_(blocklist_reason) {} + blocklist_reason_(blocklist_reason), + throttle_result_(throttle_result) {} LiteVideoNavigationMetrics::~LiteVideoNavigationMetrics() = default; -LiteVideoBlocklistReason LiteVideoNavigationMetrics::blocklist_reason() const { - return blocklist_reason_; +void LiteVideoNavigationMetrics::SetThrottleResult( + LiteVideoThrottleResult throttle_result) { + throttle_result_ = throttle_result; } } // namespace lite_video
diff --git a/chrome/browser/lite_video/lite_video_navigation_metrics.h b/chrome/browser/lite_video/lite_video_navigation_metrics.h index dd3962e..d9bf552 100644 --- a/chrome/browser/lite_video/lite_video_navigation_metrics.h +++ b/chrome/browser/lite_video/lite_video_navigation_metrics.h
@@ -25,21 +25,44 @@ kMaxValue = kHoldback, }; +// The result of throttling on a navigation. +// This should be kept in sync with LiteVideoThrottleResult in enums.xml. +enum class LiteVideoThrottleResult { + kUnknown, + // LiteVideos were enabled to throttle media requests on the navigation + // and they were not stopped due to rebuffering events. + kThrottledWithoutStop, + // LiteVideos were enabled to throttle media requests on the navigation + // but they were stopped due to rebuffering events. + kThrottleStoppedOnRebuffer, + + // Insert new values before this line. + kMaxValue = kThrottleStoppedOnRebuffer, +}; + class LiteVideoNavigationMetrics { public: LiteVideoNavigationMetrics(int64_t nav_id, LiteVideoDecision decision, - LiteVideoBlocklistReason blocklist_reason); + LiteVideoBlocklistReason blocklist_reason, + LiteVideoThrottleResult throttle_result); ~LiteVideoNavigationMetrics(); int64_t nav_id() const { return nav_id_; } LiteVideoDecision decision() const { return decision_; } - LiteVideoBlocklistReason blocklist_reason() const; + LiteVideoBlocklistReason blocklist_reason() const { + return blocklist_reason_; + } + LiteVideoThrottleResult throttle_result() const { return throttle_result_; } + + // Update the throttling result of the current navigation. + void SetThrottleResult(LiteVideoThrottleResult throttle_result); private: int64_t nav_id_; LiteVideoDecision decision_; LiteVideoBlocklistReason blocklist_reason_; + LiteVideoThrottleResult throttle_result_; }; } // namespace lite_video
diff --git a/chrome/browser/lite_video/lite_video_observer.cc b/chrome/browser/lite_video/lite_video_observer.cc index 978fb5b..c790692 100644 --- a/chrome/browser/lite_video/lite_video_observer.cc +++ b/chrome/browser/lite_video/lite_video_observer.cc
@@ -12,6 +12,7 @@ #include "chrome/browser/lite_video/lite_video_hint.h" #include "chrome/browser/lite_video/lite_video_keyed_service.h" #include "chrome/browser/lite_video/lite_video_keyed_service_factory.h" +#include "chrome/browser/lite_video/lite_video_navigation_metrics.h" #include "chrome/browser/lite_video/lite_video_switches.h" #include "chrome/browser/lite_video/lite_video_user_blocklist.h" #include "chrome/browser/lite_video/lite_video_util.h" @@ -91,7 +92,8 @@ if (navigation_handle->IsInMainFrame()) { FlushUKMMetrics(); nav_metrics_ = lite_video::LiteVideoNavigationMetrics( - navigation_handle->GetNavigationId(), decision, blocklist_reason); + navigation_handle->GetNavigationId(), decision, blocklist_reason, + lite_video::LiteVideoThrottleResult::kThrottledWithoutStop); } LOCAL_HISTOGRAM_BOOLEAN("LiteVideo.Navigation.HasHint", hint ? true : false); @@ -140,6 +142,7 @@ ukm::builders::LiteVideo builder(ukm_source_id); builder.SetThrottlingStartDecision(static_cast<int>(nav_metrics_->decision())) .SetBlocklistReason(static_cast<int>(nav_metrics_->blocklist_reason())) + .SetThrottlingResult(static_cast<int>(nav_metrics_->throttle_result())) .Record(ukm::UkmRecorder::Get()); nav_metrics_.reset(); } @@ -171,12 +174,26 @@ &loading_hints_agent); loading_hints_agent->StopThrottlingMediaRequests(); } + // Only consider a rebuffer event related to LiteVideos if they + // were allowed on current navigation. + if (!nav_metrics_ || + nav_metrics_->decision() != lite_video::LiteVideoDecision::kAllowed) { + return; + } - // TODO(crbug/1101563 Update the user blocklist. This needs additional - // work to operate on local state mapping the current render frame id - // to the navigation's origin. + nav_metrics_->SetThrottleResult( + lite_video::LiteVideoThrottleResult::kThrottleStoppedOnRebuffer); - // TODO(crbug/1097792): Flush a UKM event for this render frame host. + if (!lite_video_decider_) + return; + + // Determine if the rebuffer happened in the mainframe. + render_frame_host->GetMainFrame() == render_frame_host + ? lite_video_decider_->DidMediaRebuffer( + render_frame_host->GetLastCommittedURL(), base::nullopt, true) + : lite_video_decider_->DidMediaRebuffer( + render_frame_host->GetMainFrame()->GetLastCommittedURL(), + render_frame_host->GetLastCommittedURL(), true); } WEB_CONTENTS_USER_DATA_KEY_IMPL(LiteVideoObserver)
diff --git a/chrome/browser/lite_video/lite_video_user_blocklist.cc b/chrome/browser/lite_video/lite_video_user_blocklist.cc index 7368e0a..afc9f2a 100644 --- a/chrome/browser/lite_video/lite_video_user_blocklist.cc +++ b/chrome/browser/lite_video/lite_video_user_blocklist.cc
@@ -139,4 +139,17 @@ static_cast<int>(LiteVideoBlocklistType::kNavigationBlocklist)); } +void LiteVideoUserBlocklist::AddRebufferToBlocklist( + const GURL& mainframe_url, + base::Optional<GURL> subframe_url, + bool opt_out) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + base::Optional<std::string> rebuffer_key = + GetRebufferBlocklistKey(mainframe_url, subframe_url); + if (rebuffer_key) { + AddEntry(*rebuffer_key, opt_out, + static_cast<int>(LiteVideoBlocklistType::kRebufferBlocklist)); + } +} + } // namespace lite_video
diff --git a/chrome/browser/lite_video/lite_video_user_blocklist.h b/chrome/browser/lite_video/lite_video_user_blocklist.h index bfdd7bb..14de369 100644 --- a/chrome/browser/lite_video/lite_video_user_blocklist.h +++ b/chrome/browser/lite_video/lite_video_user_blocklist.h
@@ -86,6 +86,12 @@ void AddNavigationToBlocklist(content::NavigationHandle* navigation_handle, bool opt_out); + // Update the entry within the RebufferBlocklistType for the + // mainframe and subframe urls based on whether it was an opt-out or not. + void AddRebufferToBlocklist(const GURL& mainframe_url, + base::Optional<GURL> subframe_url, + bool opt_out); + protected: // OptOutBlocklist: bool ShouldUseSessionPolicy(base::TimeDelta* duration,
diff --git a/chrome/browser/lite_video/lite_video_user_blocklist_unittest.cc b/chrome/browser/lite_video/lite_video_user_blocklist_unittest.cc index 4b2fd53..d1d6748 100644 --- a/chrome/browser/lite_video/lite_video_user_blocklist_unittest.cc +++ b/chrome/browser/lite_video/lite_video_user_blocklist_unittest.cc
@@ -182,7 +182,7 @@ MainframeNavigationBlocklistedByRebufferBlocklist) { ConfigBlocklistParamsForTesting(); GURL url("https://test.com"); - SeedBlocklist(url.host() + "_", LiteVideoBlocklistType::kRebufferBlocklist); + blocklist()->AddRebufferToBlocklist(url, base::nullopt, true); EXPECT_EQ(CheckBlocklistForMainframeNavigation(url), LiteVideoBlocklistReason::kRebufferingBlocklisted); } @@ -199,8 +199,7 @@ ConfigBlocklistParamsForTesting(); GURL mainframe_url("https://test.com"); GURL subframe_url("https://subframe.com"); - SeedBlocklist(mainframe_url.host() + "_" + subframe_url.host(), - LiteVideoBlocklistType::kRebufferBlocklist); + blocklist()->AddRebufferToBlocklist(mainframe_url, subframe_url, true); EXPECT_EQ(CheckBlocklistForSubframeNavigation(mainframe_url, subframe_url), LiteVideoBlocklistReason::kRebufferingBlocklisted); }
diff --git a/chrome/browser/nearby_sharing/nearby_process_manager_unittest.cc b/chrome/browser/nearby_sharing/nearby_process_manager_unittest.cc index 7151481..847cd96 100644 --- a/chrome/browser/nearby_sharing/nearby_process_manager_unittest.cc +++ b/chrome/browser/nearby_sharing/nearby_process_manager_unittest.cc
@@ -47,7 +47,7 @@ // sharing::mojom::Sharing: void CreateSharingWebRtcConnection( - mojo::PendingRemote<sharing::mojom::SignallingSender>, + mojo::PendingRemote<sharing::mojom::SignalingSender>, mojo::PendingReceiver<sharing::mojom::SignallingReceiver>, mojo::PendingRemote<sharing::mojom::SharingWebRtcConnectionDelegate>, mojo::PendingReceiver<sharing::mojom::SharingWebRtcConnection>,
diff --git a/chrome/browser/optimization_guide/hints_fetcher_browsertest.cc b/chrome/browser/optimization_guide/hints_fetcher_browsertest.cc index 76ae61a..683d759 100644 --- a/chrome/browser/optimization_guide/hints_fetcher_browsertest.cc +++ b/chrome/browser/optimization_guide/hints_fetcher_browsertest.cc
@@ -751,18 +751,6 @@ 1); histogram_tester->ExpectTotalCount( "OptimizationGuide.HintsFetcher.GetHintsRequest.HintCount", 0); - - LoadHintsForUrl(https_url()); - - ui_test_utils::NavigateToURL(browser(), https_url()); - - // Verifies that no Fetched Hint was added to the store, only the - // Component hint is loaded. - histogram_tester->ExpectUniqueSample( - "OptimizationGuide.HintCache.HintType.Loaded", - static_cast<int>(optimization_guide::OptimizationGuideStore:: - StoreEntryType::kComponentHint), - 1); } IN_PROC_BROWSER_TEST_F(
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc index 7493218..f7da3b77 100644 --- a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc +++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
@@ -238,11 +238,14 @@ profile_(profile), pref_service_(pref_service), hint_cache_(std::make_unique<optimization_guide::HintCache>( - std::make_unique<optimization_guide::OptimizationGuideStore>( - database_provider, - profile_path.AddExtensionASCII( - optimization_guide::kOptimizationGuideHintStore), - background_task_runner_))), + optimization_guide::features::ShouldPersistHintsToDisk() + ? std::make_unique<optimization_guide::OptimizationGuideStore>( + database_provider, + profile_path.AddExtensionASCII( + optimization_guide::kOptimizationGuideHintStore), + background_task_runner_) + : nullptr, + optimization_guide::features::MaxHostKeyedHintCacheSize())), page_navigation_hints_fetchers_( optimization_guide::features::MaxConcurrentPageNavigationFetches()), hints_fetcher_factory_( @@ -359,6 +362,11 @@ config->optimization_blacklists(), registered_optimization_types); + // TODO(crbug/1112500): Figure out what to do with component hints if there + // isn't a persistent store. Right now, it doesn't really matter since there + // aren't hints sent down via the component, but we need to figure out + // threading since these hints are now stored in memory prior to being + // persisted. if (update_data) { bool did_process_hints = hint_cache_->ProcessAndCacheHints( config->mutable_hints(), update_data.get()); @@ -679,6 +687,14 @@ DCHECK_CURRENTLY_ON(content::BrowserThread::UI); LOCAL_HISTOGRAM_BOOLEAN("OptimizationGuide.FetchedHints.Stored", true); + if (!optimization_guide::features::ShouldPersistHintsToDisk()) { + // If we aren't persisting hints to disk, there's no point in purging + // hints from disk or starting a new fetch since at this point we should + // just be fetching everything on page navigation and only storing + // in-memory. + return; + } + hint_cache_->PurgeExpiredFetchedHints(); top_hosts_hints_fetch_timer_.Stop();
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc index 01ed3e69..03339c8 100644 --- a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc +++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
@@ -266,7 +266,11 @@ class OptimizationGuideHintsManagerTest : public optimization_guide::ProtoDatabaseProviderTestBase { public: - OptimizationGuideHintsManagerTest() = default; + OptimizationGuideHintsManagerTest() { + scoped_feature_list_.InitAndEnableFeatureWithParameters( + optimization_guide::features::kOptimizationHints, + {{"max_host_keyed_hint_cache_size", "1"}}); + } ~OptimizationGuideHintsManagerTest() override = default; void SetUp() override { @@ -364,6 +368,14 @@ optimization_guide::proto::PreviewsMetadata* default_opt_metadata = default_opt->mutable_previews_metadata(); default_opt_metadata->set_inflation_percent(1234); + // Add another hint so somedomain.org hint is not in-memory initially. + optimization_guide::proto::Hint* hint2 = config.add_hints(); + hint2->set_key("somedomain2.org"); + hint2->set_key_representation(optimization_guide::proto::HOST); + hint2->set_version("someversion"); + optimization_guide::proto::Optimization* opt = + hint2->add_whitelisted_optimizations(); + opt->set_optimization_type(optimization_guide::proto::NOSCRIPT); ProcessHints(config, version); } @@ -438,6 +450,7 @@ content::BrowserTaskEnvironment task_environment_{ base::test::TaskEnvironment::MainThreadType::UI, base::test::TaskEnvironment::TimeSource::MOCK_TIME}; + base::test::ScopedFeatureList scoped_feature_list_; TestingProfile testing_profile_; std::unique_ptr<content::TestWebContentsFactory> web_contents_factory_; std::unique_ptr<OptimizationGuideHintsManager> hints_manager_;
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc index 30ef51f..01ae1f8 100644 --- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc +++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
@@ -23,6 +23,7 @@ #include "components/optimization_guide/optimization_guide_decider.h" #include "components/optimization_guide/optimization_guide_features.h" #include "components/optimization_guide/optimization_guide_service.h" +#include "components/optimization_guide/proto/models.pb.h" #include "components/optimization_guide/top_host_provider.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" @@ -68,6 +69,25 @@ } } +// Logs |optimization_target_decision| for |optimization_target| in a histogram +// and passes the corresponding OptimizationGuideDecision to |callback|. +void LogOptimizationTargetDecisionAndPassOptimizationGuideDecision( + optimization_guide::proto::OptimizationTarget optimization_target, + optimization_guide::OptimizationGuideTargetDecisionCallback callback, + optimization_guide::OptimizationTargetDecision + optimization_target_decision) { + base::UmaHistogramExactLinear( + "OptimizationGuide.TargetDecision." + + GetStringNameForOptimizationTarget(optimization_target), + static_cast<int>(optimization_target_decision), + static_cast<int>( + optimization_guide::OptimizationTargetDecision::kMaxValue)); + + std::move(callback).Run( + GetOptimizationGuideDecisionFromOptimizationTargetDecision( + optimization_target_decision)); +} + } // namespace OptimizationGuideKeyedService::OptimizationGuideKeyedService( @@ -177,18 +197,11 @@ return; } - optimization_guide::OptimizationTargetDecision optimization_target_decision = - prediction_manager_->ShouldTargetNavigation( - navigation_handle, optimization_target, client_model_feature_values); - base::UmaHistogramExactLinear( - "OptimizationGuide.TargetDecision." + - GetStringNameForOptimizationTarget(optimization_target), - static_cast<int>(optimization_target_decision), - static_cast<int>( - optimization_guide::OptimizationTargetDecision::kMaxValue)); - std::move(callback).Run( - GetOptimizationGuideDecisionFromOptimizationTargetDecision( - optimization_target_decision)); + prediction_manager_->ShouldTargetNavigationAsync( + navigation_handle, optimization_target, client_model_feature_values, + base::BindOnce( + &LogOptimizationTargetDecisionAndPassOptimizationGuideDecision, + optimization_target, std::move(callback))); } void OptimizationGuideKeyedService::RegisterOptimizationTypes(
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h index 978654b0..dad095f7 100644 --- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h +++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
@@ -36,7 +36,7 @@ class OptimizationGuideService; class TopHostProvider; class PredictionManager; -class PredictionManagerBrowserTest; +class PredictionManagerBrowserTestBase; } // namespace optimization_guide class GURL; @@ -95,7 +95,7 @@ friend class OptimizationGuideKeyedServiceBrowserTest; friend class OptimizationGuideWebContentsObserver; friend class ProfileManager; - friend class optimization_guide::PredictionManagerBrowserTest; + friend class optimization_guide::PredictionManagerBrowserTestBase; friend class optimization_guide::android::OptimizationGuideBridge; // Initializes the service. |optimization_guide_service| is the
diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager.cc b/chrome/browser/optimization_guide/prediction/prediction_manager.cc index c5ea5d3a..e042c99 100644 --- a/chrome/browser/optimization_guide/prediction/prediction_manager.cc +++ b/chrome/browser/optimization_guide/prediction/prediction_manager.cc
@@ -164,6 +164,36 @@ namespace optimization_guide { +struct PredictionDecisionParams { + PredictionDecisionParams( + base::WeakPtr<OptimizationGuideNavigationData> navigation_data, + proto::OptimizationTarget optimization_target, + OptimizationTargetDecisionCallback callback, + int64_t version, + base::TimeTicks model_evaluation_start_time) + : navigation_data(navigation_data), + optimization_target(optimization_target), + callback(std::move(callback)), + version(version), + model_evaluation_start_time(model_evaluation_start_time) {} + + ~PredictionDecisionParams() = default; + + PredictionDecisionParams(const PredictionDecisionParams&) = delete; + PredictionDecisionParams& operator=(const PredictionDecisionParams&) = delete; + + // Will store relevant prediction results, if not null. + base::WeakPtr<OptimizationGuideNavigationData> navigation_data; + // Target of the prediction. + proto::OptimizationTarget optimization_target; + // Callback to be invoked once a OptimizationTargetDecision is made. + OptimizationTargetDecisionCallback callback; + // Model version. + int64_t version; + // Time when the model evaluation is initiated. + base::TimeTicks model_evaluation_start_time; +}; + PredictionManager::PredictionManager( const std::vector<optimization_guide::proto::OptimizationTarget>& optimization_targets_at_initialization, @@ -483,6 +513,147 @@ return target_decision; } +void PredictionManager::ShouldTargetNavigationAsync( + content::NavigationHandle* navigation_handle, + proto::OptimizationTarget optimization_target, + const base::flat_map<proto::ClientModelFeature, float>& + override_client_model_feature_values, + OptimizationTargetDecisionCallback callback) { + SEQUENCE_CHECKER(sequence_checker_); + DCHECK(navigation_handle->GetURL().SchemeIsHTTPOrHTTPS()); + + OptimizationGuideNavigationData* navigation_data = + OptimizationGuideNavigationData::GetFromNavigationHandle( + navigation_handle); + if (navigation_data) { + base::Optional<optimization_guide::OptimizationTargetDecision> + optimization_target_decision = + navigation_data->GetDecisionForOptimizationTarget( + optimization_target); + if (optimization_target_decision.has_value() && + ShouldUseCurrentOptimizationTargetDecision( + *optimization_target_decision)) { + std::move(callback).Run(*optimization_target_decision); + return; + } + } + + if (!registered_optimization_targets_.contains(optimization_target)) { + std::move(callback).Run(OptimizationTargetDecision::kUnknown); + return; + } + + // Use the synchronous code path if ML Service is not enabled. + if (!features::ShouldUseMLServiceForPrediction()) { + std::move(callback).Run( + ShouldTargetNavigation(navigation_handle, optimization_target, + override_client_model_feature_values)); + return; + } + + ScopedPredictionManagerModelStatusRecorder model_status_recorder( + optimization_target); + auto it = + optimization_target_remote_model_predictor_map_.find(optimization_target); + if (it == optimization_target_remote_model_predictor_map_.end()) { + if (store_is_ready_ && model_and_features_store_) { + OptimizationGuideStore::EntryKey model_entry_key; + if (model_and_features_store_->FindPredictionModelEntryKey( + optimization_target, &model_entry_key)) { + model_status_recorder.set_status( + PredictionManagerModelStatus::kStoreAvailableModelNotLoaded); + } else { + model_status_recorder.set_status( + PredictionManagerModelStatus::kStoreAvailableNoModelForTarget); + } + } else { + model_status_recorder.set_status( + PredictionManagerModelStatus::kStoreUnavailableModelUnknown); + } + std::move(callback).Run( + OptimizationTargetDecision::kModelNotAvailableOnClient); + return; + } + + RemoteDecisionTreePredictor* predictor = it->second.get(); + + if (!predictor->Get() || !predictor->IsConnected()) { + // Connection to remote model is no longer valid. + model_status_recorder.set_status( + optimization_guide::PredictionManagerModelStatus:: + kStoreAvailableModelNotLoaded); + optimization_target_remote_model_predictor_map_.erase(it); + std::move(callback).Run( + OptimizationTargetDecision::kModelNotAvailableOnClient); + return; + } + + model_status_recorder.set_status( + PredictionManagerModelStatus::kModelAvailable); + + base::flat_map<std::string, float> feature_map = + BuildFeatureMap(navigation_handle, predictor->model_features(), + override_client_model_feature_values); + + predictor->Get()->Predict( + feature_map, + base::BindOnce(&PredictionManager::OnModelEvaluated, + ui_weak_ptr_factory_.GetWeakPtr(), + std::make_unique<PredictionDecisionParams>( + navigation_data->GetWeakPtr(), optimization_target, + std::move(callback), predictor->version(), + base::TimeTicks::Now()))); +} + +void PredictionManager::OnModelEvaluated( + std::unique_ptr<PredictionDecisionParams> params, + machine_learning::mojom::DecisionTreePredictionResult result, + double prediction_score) { + SEQUENCE_CHECKER(sequence_checker_); + + if (result != + machine_learning::mojom::DecisionTreePredictionResult::kUnknown) { + UmaHistogramTimes( + "OptimizationGuide.PredictionModelEvaluationLatency." + + GetStringNameForOptimizationTarget(params->optimization_target), + base::TimeTicks::Now() - params->model_evaluation_start_time); + } + + if (params->navigation_data) { + params->navigation_data->SetModelVersionForOptimizationTarget( + params->optimization_target, params->version); + params->navigation_data->SetModelPredictionScoreForOptimizationTarget( + params->optimization_target, prediction_score); + } + + if (optimization_guide::features:: + ShouldOverrideOptimizationTargetDecisionForMetricsPurposes( + params->optimization_target)) { + std::move(params->callback) + .Run(optimization_guide::OptimizationTargetDecision:: + kModelPredictionHoldback); + return; + } + + optimization_guide::OptimizationTargetDecision target_decision; + switch (result) { + case machine_learning::mojom::DecisionTreePredictionResult::kTrue: + target_decision = + optimization_guide::OptimizationTargetDecision::kPageLoadMatches; + break; + case machine_learning::mojom::DecisionTreePredictionResult::kFalse: + target_decision = + optimization_guide::OptimizationTargetDecision::kPageLoadDoesNotMatch; + break; + case machine_learning::mojom::DecisionTreePredictionResult::kUnknown: + target_decision = + optimization_guide::OptimizationTargetDecision::kUnknown; + break; + } + + std::move(params->callback).Run(target_decision); +} + void PredictionManager::OnEffectiveConnectionTypeChanged( net::EffectiveConnectionType effective_connection_type) { SEQUENCE_CHECKER(sequence_checker_); @@ -497,6 +668,16 @@ return nullptr; } +RemoteDecisionTreePredictor* +PredictionManager::GetRemoteDecisionTreePredictorForTesting( + proto::OptimizationTarget optimization_target) const { + auto it = + optimization_target_remote_model_predictor_map_.find(optimization_target); + if (it != optimization_target_remote_model_predictor_map_.end()) + return it->second.get(); + return nullptr; +} + const HostModelFeaturesMRUCache* PredictionManager::GetHostModelFeaturesForTesting() const { return &host_model_features_cache_; @@ -853,10 +1034,11 @@ auto* service_connection = machine_learning::ServiceConnection::GetInstance(); + auto pending_receiver = predictor_handle->BindNewPipeAndPassReceiver(); + std::string model_string = model->SerializeAsString(); service_connection->LoadDecisionTreeModel( - machine_learning::mojom::DecisionTreeModelSpec::New( - model->SerializeAsString()), - predictor_handle->BindNewPipeAndPassReceiver(), + machine_learning::mojom::DecisionTreeModelSpec::New(model_string), + std::move(pending_receiver), base::BindOnce(&PredictionManager::OnPredictionModelSentToMLService, ui_weak_ptr_factory_.GetWeakPtr(), std::move(callback), std::move(model), std::move(predictor_handle))); @@ -870,6 +1052,7 @@ std::unique_ptr<proto::PredictionModel> model, std::unique_ptr<RemoteDecisionTreePredictor> predictor_handle, machine_learning::mojom::LoadModelResult result) { + SEQUENCE_CHECKER(sequence_checker_); proto::OptimizationTarget target = model->model_info().optimization_target(); ScopedPredictionModelConstructionAndValidationRecorder prediction_model_recorder(target);
diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager.h b/chrome/browser/optimization_guide/prediction/prediction_manager.h index 31f5c30b..4095549e 100644 --- a/chrome/browser/optimization_guide/prediction/prediction_manager.h +++ b/chrome/browser/optimization_guide/prediction/prediction_manager.h
@@ -54,6 +54,10 @@ class TopHostProvider; class RemoteDecisionTreePredictor; +// Parameters to be passed to PredictionManager::OnModelEvaluated for post +// processing after the model prediction decision and score are obtained. +struct PredictionDecisionParams; + using HostModelFeaturesMRUCache = base::HashingMRUCache<std::string, base::flat_map<std::string, float>>; @@ -98,7 +102,7 @@ // Determine if the navigation matches the criteria for // |optimization_target|. Return kUnknown if a PredictionModel for the // optimization target is not registered and kModelNotAvailableOnClient if the - // if model for the optimization target is not currently on the client. + // model for the optimization target is not currently on the client. // If the model for the optimization target requires a client model feature // that is present in |override_client_model_feature_values|, the value from // |override_client_model_feature_values| will be used. The client will @@ -112,6 +116,22 @@ const base::flat_map<proto::ClientModelFeature, float>& override_client_model_feature_values); + // Invokes |callback| with the decision for whether the navigation matches the + // criteria for |optimization_target|. Passes kUnknown if a PredictionModel + // for the optimization target is not registered + // and kModelNotAvailableOnClient if the model for the optimization target is + // not currently on the client. + // + // Values provided in |client_model_feature_values| will be used over any + // values for features required by the model that may be calculated by the + // Optimization Guide. + void ShouldTargetNavigationAsync( + content::NavigationHandle* navigation_handle, + proto::OptimizationTarget optimization_target, + const base::flat_map<proto::ClientModelFeature, float>& + override_client_model_feature_values, + OptimizationTargetDecisionCallback callback); + // Update |session_fcp_| and |previous_fcp_| with |fcp|. void UpdateFCPSessionStatistics(base::TimeDelta fcp); @@ -161,6 +181,11 @@ PredictionModel* GetPredictionModelForTesting( proto::OptimizationTarget optimization_target) const; + // Return the remote model predictor handle for the optimization target used + // by this PredictionManager for testing. + RemoteDecisionTreePredictor* GetRemoteDecisionTreePredictorForTesting( + proto::OptimizationTarget optimization_target) const; + // Return the host model features for all hosts used by this // PredictionManager for testing. const HostModelFeaturesMRUCache* GetHostModelFeaturesForTesting() const; @@ -308,6 +333,14 @@ bool ProcessAndStoreHostModelFeatures( const proto::HostModelFeatures& host_model_features); + // Callback to be passed to the ML Service via the predictor handle and to + // retrieve |result| and |prediction_score|. Performs post processing using + // information passed via |params|. + void OnModelEvaluated( + std::unique_ptr<PredictionDecisionParams> params, + machine_learning::mojom::DecisionTreePredictionResult result, + double prediction_score); + // Return the time when a prediction model and host model features fetch was // last attempted. base::Time GetLastFetchAttemptTime() const;
diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc b/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc index 6b660f19..8bfe3553 100644 --- a/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc +++ b/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
@@ -191,7 +191,7 @@ class OptimizationGuideConsumerWebContentsObserver : public content::WebContentsObserver { public: - OptimizationGuideConsumerWebContentsObserver( + explicit OptimizationGuideConsumerWebContentsObserver( content::WebContents* web_contents) : content::WebContentsObserver(web_contents) {} ~OptimizationGuideConsumerWebContentsObserver() override = default; @@ -260,39 +260,27 @@ namespace optimization_guide { -class PredictionManagerBrowserTest - : public InProcessBrowserTest, - public ::testing::WithParamInterface<bool> { +// Abstract base class for browser testing Prediction Manager. +// Actual class fixtures should implement InitializeFeatureList to set up +// features used in tests. +class PredictionManagerBrowserTestBase : public InProcessBrowserTest { public: - PredictionManagerBrowserTest() : using_ml_service_(GetParam()) {} - ~PredictionManagerBrowserTest() override = default; + PredictionManagerBrowserTestBase() = default; + ~PredictionManagerBrowserTestBase() override = default; - PredictionManagerBrowserTest(const PredictionManagerBrowserTest&) = delete; - PredictionManagerBrowserTest& operator=(const PredictionManagerBrowserTest&) = + PredictionManagerBrowserTestBase(const PredictionManagerBrowserTestBase&) = delete; + PredictionManagerBrowserTestBase& operator=( + const PredictionManagerBrowserTestBase&) = delete; void SetUp() override { - if (using_ml_service_) { - scoped_feature_list_.InitWithFeatures( - {optimization_guide::features::kOptimizationHints, - optimization_guide::features::kRemoteOptimizationGuideFetching, - optimization_guide::features::kOptimizationTargetPrediction, - optimization_guide::features:: - kOptimizationTargetPredictionUsingMLService}, - {}); - } else { - scoped_feature_list_.InitWithFeatures( - {optimization_guide::features::kOptimizationHints, - optimization_guide::features::kRemoteOptimizationGuideFetching, - optimization_guide::features::kOptimizationTargetPrediction}, - {}); - } + InitializeFeatureList(); models_server_ = std::make_unique<net::EmbeddedTestServer>( net::EmbeddedTestServer::TYPE_HTTPS); models_server_->ServeFilesFromSourceDirectory("chrome/test/data/previews"); models_server_->RegisterRequestHandler(base::BindRepeating( - &PredictionManagerBrowserTest::HandleGetModelsRequest, + &PredictionManagerBrowserTestBase::HandleGetModelsRequest, base::Unretained(this))); ASSERT_TRUE(models_server_->Start()); @@ -302,16 +290,16 @@ void SetUpOnMainThread() override { content::NetworkConnectionChangeSimulator().SetConnectionType( network::mojom::ConnectionType::CONNECTION_2G); - https_server_.reset( - new net::EmbeddedTestServer(net::EmbeddedTestServer::TYPE_HTTPS)); + https_server_ = std::make_unique<net::EmbeddedTestServer>( + net::EmbeddedTestServer::TYPE_HTTPS); https_server_->ServeFilesFromSourceDirectory(GetChromeTestDataDir()); ASSERT_TRUE(https_server_->Start()); https_url_with_content_ = https_server_->GetURL("/english_page.html"); https_url_without_content_ = https_server_->GetURL("/empty.html"); // Set up an OptimizationGuideKeyedService consumer. - consumer_.reset(new OptimizationGuideConsumerWebContentsObserver( - browser()->tab_strip_model()->GetActiveWebContents())); + consumer_ = std::make_unique<OptimizationGuideConsumerWebContentsObserver>( + browser()->tab_strip_model()->GetActiveWebContents()); InProcessBrowserTest::SetUpOnMainThread(); } @@ -387,6 +375,11 @@ GURL https_url_without_content() { return https_url_without_content_; } protected: + // Virtualize for testing different feature configurations. + virtual void InitializeFeatureList() = 0; + + base::test::ScopedFeatureList scoped_feature_list_; + // Feature that the model server should return in response to // GetModelsRequest. proto::ClientModelFeature client_model_feature_ = @@ -428,13 +421,48 @@ GURL https_url_with_content_, https_url_without_content_; std::unique_ptr<net::EmbeddedTestServer> https_server_; std::unique_ptr<net::EmbeddedTestServer> models_server_; - base::test::ScopedFeatureList scoped_feature_list_; PredictionModelsFetcherRemoteResponseType response_type_ = PredictionModelsFetcherRemoteResponseType:: kSuccessfulWithModelsAndFeatures; std::unique_ptr<OptimizationGuideConsumerWebContentsObserver> consumer_; }; +// Parametrized on whether the ML Service path is enabled. +class PredictionManagerBrowserTest + : public PredictionManagerBrowserTestBase, + public ::testing::WithParamInterface<bool> { + public: + PredictionManagerBrowserTest() : using_ml_service_(GetParam()) {} + ~PredictionManagerBrowserTest() override = default; + + PredictionManagerBrowserTest(const PredictionManagerBrowserTest&) = delete; + PredictionManagerBrowserTest& operator=(const PredictionManagerBrowserTest&) = + delete; + + bool using_ml_service() const { return using_ml_service_; } + + private: + void InitializeFeatureList() override { + if (using_ml_service_) { + scoped_feature_list_.InitWithFeatures( + {optimization_guide::features::kOptimizationHints, + optimization_guide::features::kRemoteOptimizationGuideFetching, + optimization_guide::features::kOptimizationTargetPrediction, + optimization_guide::features:: + kOptimizationTargetPredictionUsingMLService}, + {}); + } else { + scoped_feature_list_.InitWithFeatures( + {optimization_guide::features::kOptimizationHints, + optimization_guide::features::kRemoteOptimizationGuideFetching, + optimization_guide::features::kOptimizationTargetPrediction}, + {}); + } + } + + bool using_ml_service_ = false; +}; + #if defined(OS_WIN) || defined(OS_MAC) || defined(OS_CHROMEOS) #define DISABLE_ON_WIN_MAC_CHROMEOS(x) DISABLED_##x #else @@ -443,7 +471,8 @@ INSTANTIATE_TEST_SUITE_P(UsingMLService, PredictionManagerBrowserTest, - ::testing::Bool()); + ::testing::Bool(), + ::testing::PrintToStringParamName()); IN_PROC_BROWSER_TEST_P( PredictionManagerBrowserTest, @@ -596,14 +625,6 @@ IN_PROC_BROWSER_TEST_P( PredictionManagerBrowserTest, DISABLE_ON_WIN_MAC_CHROMEOS(HostModelFeaturesClearedOnHistoryClear)) { - if (using_ml_service()) { - // Skipped for the ML Service path because ShouldTargetNavigation has not - // been migrated. - // TODO(crbug/1099371): Enable this after adding ML Service integration to - // ShouldTargetNavigation. - GTEST_SKIP(); - } - base::HistogramTester histogram_tester; MLServiceProcessObserver ml_service_observer; @@ -661,19 +682,17 @@ } }; -// Disabled for the ML Service path because ShouldTargetNavigation has not -// been migrated. -// TODO(crbug/1099371): Enable this after adding ML Service integration to -// ShouldTargetNavigation. INSTANTIATE_TEST_SUITE_P(UsingMLService, PredictionManagerBrowserSameOriginTest, - ::testing::Values(false)); + ::testing::Bool(), + ::testing::PrintToStringParamName()); // Regression test for https://crbug.com/1037945. Tests that the origin of the // previous navigation is computed correctly. IN_PROC_BROWSER_TEST_P(PredictionManagerBrowserSameOriginTest, DISABLE_ON_WIN_MAC_CHROMEOS(IsSameOriginNavigation)) { base::HistogramTester histogram_tester; + MLServiceProcessObserver ml_service_observer; RegisterWithKeyedService(); @@ -691,6 +710,12 @@ &histogram_tester, "OptimizationGuide.PredictionManager.PredictionModelsStored", 1); + RetryForHistogramUntilCountReached( + &histogram_tester, + "OptimizationGuide.PredictionModelLoadedVersion.PainfulPageLoad", 1); + + EXPECT_EQ(ml_service_observer.IsLaunched(), using_ml_service()); + SetCallbackOnConsumer(base::DoNothing()); ui_test_utils::NavigateToURL(browser(), https_url_with_content()); RetryForHistogramUntilCountReached( @@ -725,6 +750,7 @@ PredictionManagerBrowserSameOriginTest, DISABLE_ON_WIN_MAC_CHROMEOS(ShouldTargetNavigationAsync)) { base::HistogramTester histogram_tester; + MLServiceProcessObserver ml_service_observer; RegisterWithKeyedService(); @@ -742,6 +768,12 @@ &histogram_tester, "OptimizationGuide.PredictionManager.PredictionModelsStored", 1); + RetryForHistogramUntilCountReached( + &histogram_tester, + "OptimizationGuide.PredictionModelLoadedVersion.PainfulPageLoad", 1); + + EXPECT_EQ(ml_service_observer.IsLaunched(), using_ml_service()); + std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>(); SetCallbackOnConsumer(base::BindOnce( [](base::RunLoop* run_loop, @@ -759,4 +791,119 @@ run_loop->Run(); } +class PredictionManagerUsingMLServiceBrowserSameOriginTest + : public PredictionManagerBrowserSameOriginTest {}; + +// Only instantiate with ML Service enabled. +INSTANTIATE_TEST_SUITE_P(UsingMLService, + PredictionManagerUsingMLServiceBrowserSameOriginTest, + ::testing::Values(true), + ::testing::PrintToStringParamName()); + +IN_PROC_BROWSER_TEST_P( + PredictionManagerUsingMLServiceBrowserSameOriginTest, + DISABLE_ON_WIN_MAC_CHROMEOS( + ShouldTargetNavigationAsyncWithServiceDisconnection)) { + base::HistogramTester histogram_tester; + MLServiceProcessObserver ml_service_observer; + + RegisterWithKeyedService(); + + // Wait until histograms have been updated before performing checks for + // correct behavior based on the response. + RetryForHistogramUntilCountReached( + &histogram_tester, + "OptimizationGuide.PredictionModelFetcher.GetModelsResponse.Status", 1); + + RetryForHistogramUntilCountReached( + &histogram_tester, + "OptimizationGuide.PredictionManager.HostModelFeaturesStored", 1); + + RetryForHistogramUntilCountReached( + &histogram_tester, + "OptimizationGuide.PredictionManager.PredictionModelsStored", 1); + + RetryForHistogramUntilCountReached( + &histogram_tester, + "OptimizationGuide.PredictionModelLoadedVersion.PainfulPageLoad", 1); + + EXPECT_TRUE(ml_service_observer.IsLaunched()); + + // Force termination of the service: model predictors will become invalid. + machine_learning::ServiceConnection::GetInstance()->ResetServiceForTesting(); + + SetCallbackOnConsumer(base::BindOnce( + [](OptimizationGuideConsumerWebContentsObserver* consumer, + optimization_guide::OptimizationGuideDecision decision) { + EXPECT_EQ(decision, + optimization_guide::OptimizationGuideDecision::kUnknown); + }, + consumer())); + + ui_test_utils::NavigateToURL(browser(), https_url_with_content()); +} + +class PredictionManagerUsingMLServiceMetricsOnlyBrowserTest + : public PredictionManagerBrowserTestBase { + public: + PredictionManagerUsingMLServiceMetricsOnlyBrowserTest() = default; + ~PredictionManagerUsingMLServiceMetricsOnlyBrowserTest() override = default; + + private: + void InitializeFeatureList() override { + scoped_feature_list_.InitWithFeaturesAndParameters( + {{optimization_guide::features::kOptimizationHints, {}}, + {optimization_guide::features::kRemoteOptimizationGuideFetching, {}}, + {optimization_guide::features::kOptimizationTargetPrediction, + {{"painful_page_load_metrics_only", "true"}}}, + {optimization_guide::features:: + kOptimizationTargetPredictionUsingMLService, + {}}}, + {}); + } +}; + +IN_PROC_BROWSER_TEST_F( + PredictionManagerUsingMLServiceMetricsOnlyBrowserTest, + DISABLE_ON_WIN_MAC_CHROMEOS(ShouldTargetNavigationAsync)) { + base::HistogramTester histogram_tester; + MLServiceProcessObserver ml_service_observer; + + EXPECT_TRUE( + features::ShouldOverrideOptimizationTargetDecisionForMetricsPurposes( + proto::OptimizationTarget::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); + + RegisterWithKeyedService(); + + // Wait until histograms have been updated before performing checks for + // correct behavior based on the response. + RetryForHistogramUntilCountReached( + &histogram_tester, + "OptimizationGuide.PredictionModelFetcher.GetModelsResponse.Status", 1); + + RetryForHistogramUntilCountReached( + &histogram_tester, + "OptimizationGuide.PredictionManager.HostModelFeaturesStored", 1); + + RetryForHistogramUntilCountReached( + &histogram_tester, + "OptimizationGuide.PredictionManager.PredictionModelsStored", 1); + + RetryForHistogramUntilCountReached( + &histogram_tester, + "OptimizationGuide.PredictionModelLoadedVersion.PainfulPageLoad", 1); + + EXPECT_TRUE(ml_service_observer.IsLaunched()); + + SetCallbackOnConsumer(base::BindOnce( + [](OptimizationGuideConsumerWebContentsObserver* consumer, + optimization_guide::OptimizationGuideDecision decision) { + EXPECT_EQ(decision, + optimization_guide::OptimizationGuideDecision::kFalse); + }, + consumer())); + + ui_test_utils::NavigateToURL(browser(), https_url_with_content()); +} + } // namespace optimization_guide
diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager_unittest.cc b/chrome/browser/optimization_guide/prediction/prediction_manager_unittest.cc index ee03b9d..14c7275d1 100644 --- a/chrome/browser/optimization_guide/prediction/prediction_manager_unittest.cc +++ b/chrome/browser/optimization_guide/prediction/prediction_manager_unittest.cc
@@ -18,6 +18,9 @@ #include "chrome/browser/optimization_guide/optimization_guide_util.h" #include "chrome/browser/optimization_guide/optimization_guide_web_contents_observer.h" #include "chrome/browser/optimization_guide/prediction/prediction_model_fetcher.h" +#include "chrome/browser/optimization_guide/prediction/remote_decision_tree_predictor.h" +#include "chrome/services/machine_learning/public/cpp/test_support/fake_service_connection.h" +#include "chrome/services/machine_learning/public/mojom/decision_tree.mojom.h" #include "chrome/test/base/testing_profile.h" #include "components/leveldb_proto/testing/fake_db.h" #include "components/optimization_guide/optimization_guide_features.h" @@ -320,6 +323,7 @@ bool WasHostModelFeaturesLoaded() const { return host_model_features_loaded_; } + private: base::OnceClosure init_callback_; base::OnceClosure update_host_models_callback_; @@ -369,6 +373,7 @@ using PredictionManager::GetHostModelFeaturesForHost; using PredictionManager::GetHostModelFeaturesForTesting; using PredictionManager::GetPredictionModelForTesting; + using PredictionManager::GetRemoteDecisionTreePredictorForTesting; std::unique_ptr<OptimizationGuideStore> CreateModelAndHostModelFeaturesStore() { @@ -396,12 +401,14 @@ }; class PredictionManagerTest - : public optimization_guide::ProtoDatabaseProviderTestBase, - public testing::WithParamInterface<proto::ClientModelFeature> { + : public optimization_guide::ProtoDatabaseProviderTestBase { public: PredictionManagerTest() = default; ~PredictionManagerTest() override = default; + PredictionManagerTest(const PredictionManagerTest&) = delete; + PredictionManagerTest& operator=(const PredictionManagerTest&) = delete; + void SetUp() override { optimization_guide::ProtoDatabaseProviderTestBase::SetUp(); web_contents_factory_ = std::make_unique<content::TestWebContentsFactory>(); @@ -464,14 +471,6 @@ return navigation_handle; } - bool IsSameOriginNavigationFeature() { - return GetParam() == proto::CLIENT_MODEL_FEATURE_SAME_ORIGIN_NAVIGATION; - } - - bool IsUnknownFeature() { - return GetParam() == proto::CLIENT_MODEL_FEATURE_UNKNOWN; - } - void TearDown() override { optimization_guide::ProtoDatabaseProviderTestBase::TearDown(); } @@ -533,8 +532,6 @@ TestingProfile testing_profile_; std::unique_ptr<TestingPrefServiceSimple> pref_service_; std::unique_ptr<content::TestWebContentsFactory> web_contents_factory_; - - DISALLOW_COPY_AND_ASSIGN(PredictionManagerTest); }; // No support for Mac, Windows or ChromeOS. @@ -544,6 +541,64 @@ #define DISABLE_ON_WIN_MAC_CHROMEOS(x) x #endif +class PredictionManagerMLServiceTest + : public PredictionManagerTest, + public testing::WithParamInterface<bool> { + public: + PredictionManagerMLServiceTest() = default; + ~PredictionManagerMLServiceTest() override = default; + + PredictionManagerMLServiceTest(const PredictionManagerMLServiceTest&) = + delete; + PredictionManagerMLServiceTest& operator=( + const PredictionManagerMLServiceTest&) = delete; + + void SetUp() override { + service_connection_ = + std::make_unique<machine_learning::testing::FakeServiceConnection>(); + service_connection_->SetAsyncModeForTesting(false); + + PredictionManagerTest::SetUp(); + } + + void TearDown() override { + PredictionManagerTest::TearDown(); + service_connection_.reset(); + } + + bool UsingMLService() const { return GetParam(); } + + void SetLoadModelResult(machine_learning::mojom::LoadModelResult result) { + if (UsingMLService()) + service_connection_->SetLoadModelResult(result); + } + + void SetDecisionTreePredictionResult( + machine_learning::mojom::DecisionTreePredictionResult result, + double score) { + if (UsingMLService()) + service_connection_->SetDecisionTreePredictionResult(result, score); + } + + void RunScheduledCalls() { + if (UsingMLService()) + service_connection_->RunScheduledCalls(); + } + + void ResetMLService() { + if (UsingMLService()) + service_connection_->ResetServiceForTesting(); + } + + protected: + std::unique_ptr<machine_learning::testing::FakeServiceConnection> + service_connection_; +}; + +INSTANTIATE_TEST_SUITE_P(UsingMLService, + PredictionManagerMLServiceTest, + ::testing::Bool()); + TEST_F(PredictionManagerTest, OptimizationTargetProvidedAtInitializationIsRegistered) { CreatePredictionManager( @@ -552,7 +607,18 @@ EXPECT_FALSE(prediction_manager()->registered_optimization_targets().empty()); } -TEST_F(PredictionManagerTest, OptimizationTargetNotRegisteredForNavigation) { +TEST_P(PredictionManagerMLServiceTest, + OptimizationTargetNotRegisteredForNavigation) { + base::test::ScopedFeatureList scoped_feature_list; + if (UsingMLService()) { + scoped_feature_list.InitWithFeatures( + {optimization_guide::features:: + kOptimizationTargetPredictionUsingMLService}, + {}); + + SetLoadModelResult(machine_learning::mojom::LoadModelResult::kOk); + } + base::HistogramTester histogram_tester; std::unique_ptr<content::MockNavigationHandle> navigation_handle = CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver( @@ -571,10 +637,22 @@ EXPECT_TRUE(prediction_model_fetcher()->models_fetched()); - EXPECT_EQ( - OptimizationTargetDecision::kUnknown, - prediction_manager()->ShouldTargetNavigation( - navigation_handle.get(), proto::OPTIMIZATION_TARGET_UNKNOWN, {})); + if (UsingMLService()) { + prediction_manager()->ShouldTargetNavigationAsync( + navigation_handle.get(), proto::OPTIMIZATION_TARGET_UNKNOWN, {}, + base::BindOnce([](OptimizationTargetDecision decision) { + EXPECT_EQ(OptimizationTargetDecision::kUnknown, decision); + })); + + // Flush the Service connection pipe. + RunUntilIdle(); + } else { + EXPECT_EQ( + OptimizationTargetDecision::kUnknown, + prediction_manager()->ShouldTargetNavigation( + navigation_handle.get(), proto::OPTIMIZATION_TARGET_UNKNOWN, {})); + } + // OptimizationGuideNavData should not be populated. OptimizationGuideNavigationData* nav_data = OptimizationGuideNavigationData::GetFromNavigationHandle( @@ -599,19 +677,40 @@ 0); } -TEST_F(PredictionManagerTest, +TEST_P(PredictionManagerMLServiceTest, DISABLE_ON_WIN_MAC_CHROMEOS( NoPredictionModelForRegisteredOptimizationTarget)) { + base::test::ScopedFeatureList scoped_feature_list; + if (UsingMLService()) { + scoped_feature_list.InitWithFeatures( + {optimization_guide::features:: + kOptimizationTargetPredictionUsingMLService}, + {}); + } + base::HistogramTester histogram_tester; std::unique_ptr<content::MockNavigationHandle> navigation_handle = CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver( GURL("https://foo.com")); CreatePredictionManager({proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD}); - EXPECT_EQ(OptimizationTargetDecision::kModelNotAvailableOnClient, - prediction_manager()->ShouldTargetNavigation( - navigation_handle.get(), - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); + + if (UsingMLService()) { + prediction_manager()->ShouldTargetNavigationAsync( + navigation_handle.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, + {}, base::BindOnce([](OptimizationTargetDecision decision) { + EXPECT_EQ(OptimizationTargetDecision::kModelNotAvailableOnClient, + decision); + })); + + // Flush the Service connection pipe. + RunUntilIdle(); + } else { + EXPECT_EQ(OptimizationTargetDecision::kModelNotAvailableOnClient, + prediction_manager()->ShouldTargetNavigation( + navigation_handle.get(), + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); + } // OptimizationGuideNavData should not be populated. OptimizationGuideNavigationData* nav_data = @@ -635,7 +734,17 @@ 0); } -TEST_F(PredictionManagerTest, EvaluatePredictionModel) { +TEST_P(PredictionManagerMLServiceTest, EvaluatePredictionModel) { + base::test::ScopedFeatureList scoped_feature_list; + if (UsingMLService()) { + scoped_feature_list.InitWithFeatures( + {optimization_guide::features:: + kOptimizationTargetPredictionUsingMLService}, + {}); + + SetLoadModelResult(machine_learning::mojom::LoadModelResult::kOk); + } + base::HistogramTester histogram_tester; std::unique_ptr<content::MockNavigationHandle> navigation_handle = CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver( @@ -652,17 +761,31 @@ SetStoreInitialized(); EXPECT_TRUE(prediction_model_fetcher()->models_fetched()); - EXPECT_EQ(OptimizationTargetDecision::kPageLoadMatches, - prediction_manager()->ShouldTargetNavigation( - navigation_handle.get(), - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); + if (UsingMLService()) { + SetDecisionTreePredictionResult( + machine_learning::mojom::DecisionTreePredictionResult::kTrue, + /* score */ 0.6); - TestPredictionModel* test_prediction_model = - static_cast<TestPredictionModel*>( - prediction_manager()->GetPredictionModelForTesting( - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); - EXPECT_TRUE(test_prediction_model); - EXPECT_TRUE(test_prediction_model->WasModelEvaluated()); + prediction_manager()->ShouldTargetNavigationAsync( + navigation_handle.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, + {}, base::BindOnce([](OptimizationTargetDecision decision) { + EXPECT_EQ(OptimizationTargetDecision::kPageLoadMatches, decision); + })); + + // Flush the Service connection pipe. + RunUntilIdle(); + } else { + EXPECT_EQ(OptimizationTargetDecision::kPageLoadMatches, + prediction_manager()->ShouldTargetNavigation( + navigation_handle.get(), + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); + TestPredictionModel* test_prediction_model = + static_cast<TestPredictionModel*>( + prediction_manager()->GetPredictionModelForTesting( + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); + EXPECT_TRUE(test_prediction_model); + EXPECT_TRUE(test_prediction_model->WasModelEvaluated()); + } histogram_tester.ExpectTotalCount( "OptimizationGuide.PredictionModelEvaluationLatency." + @@ -688,7 +811,18 @@ "OptimizationGuide.PredictionModelValidationLatency", 1); } -TEST_F(PredictionManagerTest, UpdatePredictionModelsWithInvalidModel) { +TEST_P(PredictionManagerMLServiceTest, UpdatePredictionModelsWithInvalidModel) { + base::test::ScopedFeatureList scoped_feature_list; + if (UsingMLService()) { + scoped_feature_list.InitWithFeatures( + {optimization_guide::features:: + kOptimizationTargetPredictionUsingMLService}, + {}); + + SetLoadModelResult( + machine_learning::mojom::LoadModelResult::kLoadModelError); + } + base::HistogramTester histogram_tester; CreatePredictionManager({}); prediction_manager()->SetPredictionModelFetcherForTesting( @@ -718,7 +852,17 @@ "OptimizationGuide.PredictionModelLoadedVersion.PainfulPageLoad", 0); } -TEST_F(PredictionManagerTest, UpdateModelWithSameVersion) { +TEST_P(PredictionManagerMLServiceTest, UpdateModelWithSameVersion) { + base::test::ScopedFeatureList scoped_feature_list; + if (UsingMLService()) { + scoped_feature_list.InitWithFeatures( + {optimization_guide::features:: + kOptimizationTargetPredictionUsingMLService}, + {}); + + SetLoadModelResult(machine_learning::mojom::LoadModelResult::kOk); + } + base::HistogramTester histogram_tester; CreatePredictionManager({}); prediction_manager()->SetPredictionModelFetcherForTesting( @@ -748,19 +892,36 @@ prediction_manager()->UpdatePredictionModelsForTesting( get_models_response.get()); - TestPredictionModel* stored_prediction_model = - static_cast<TestPredictionModel*>( - prediction_manager()->GetPredictionModelForTesting( - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); - EXPECT_TRUE(stored_prediction_model); - EXPECT_EQ(3, stored_prediction_model->GetVersion()); - + if (UsingMLService()) { + RemoteDecisionTreePredictor* stored_predictor_handle = + prediction_manager()->GetRemoteDecisionTreePredictorForTesting( + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD); + EXPECT_TRUE(stored_predictor_handle); + EXPECT_EQ(3, stored_predictor_handle->version()); + } else { + TestPredictionModel* stored_prediction_model = + static_cast<TestPredictionModel*>( + prediction_manager()->GetPredictionModelForTesting( + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); + EXPECT_TRUE(stored_prediction_model); + EXPECT_EQ(3, stored_prediction_model->GetVersion()); + } histogram_tester.ExpectBucketCount("OptimizationGuide.IsPredictionModelValid", true, 2); } -TEST_F(PredictionManagerTest, +TEST_P(PredictionManagerMLServiceTest, EvaluatePredictionModelUsesDecisionFromPostiveEvalIfModelWasEvaluated) { + base::test::ScopedFeatureList scoped_feature_list; + if (UsingMLService()) { + scoped_feature_list.InitWithFeatures( + {optimization_guide::features:: + kOptimizationTargetPredictionUsingMLService}, + {}); + + SetLoadModelResult(machine_learning::mojom::LoadModelResult::kOk); + } + std::unique_ptr<content::MockNavigationHandle> navigation_handle = CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver( GURL("https://foo.com")); @@ -784,23 +945,54 @@ proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, OptimizationTargetDecision::kPageLoadMatches); - TestPredictionModel* test_prediction_model = - static_cast<TestPredictionModel*>( - prediction_manager()->GetPredictionModelForTesting( - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); - EXPECT_TRUE(test_prediction_model); + if (UsingMLService()) { + RemoteDecisionTreePredictor* predictor_handle = + prediction_manager()->GetRemoteDecisionTreePredictorForTesting( + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD); + EXPECT_TRUE(predictor_handle); - // Make sure the cached decision is returned and that the model was not - // evaluated. - EXPECT_EQ(OptimizationTargetDecision::kPageLoadMatches, - prediction_manager()->ShouldTargetNavigation( - navigation_handle.get(), - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); - EXPECT_FALSE(test_prediction_model->WasModelEvaluated()); + // Set ML prediction result to False to ensure the actual model is not run. + SetDecisionTreePredictionResult( + machine_learning::mojom::DecisionTreePredictionResult::kUnknown, + /* score */ 0.0); + + prediction_manager()->ShouldTargetNavigationAsync( + navigation_handle.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, + {}, base::BindOnce([](OptimizationTargetDecision decision) { + EXPECT_EQ(OptimizationTargetDecision::kPageLoadMatches, decision); + })); + + // Flush the Service connection pipe. + RunUntilIdle(); + } else { + TestPredictionModel* test_prediction_model = + static_cast<TestPredictionModel*>( + prediction_manager()->GetPredictionModelForTesting( + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); + EXPECT_TRUE(test_prediction_model); + + // Make sure the cached decision is returned and that the model was not + // evaluated. + EXPECT_EQ(OptimizationTargetDecision::kPageLoadMatches, + prediction_manager()->ShouldTargetNavigation( + navigation_handle.get(), + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); + EXPECT_FALSE(test_prediction_model->WasModelEvaluated()); + } } -TEST_F(PredictionManagerTest, +TEST_P(PredictionManagerMLServiceTest, EvaluatePredictionModelUsesDecisionFromNegativeEvalIfModelWasEvaluated) { + base::test::ScopedFeatureList scoped_feature_list; + if (UsingMLService()) { + scoped_feature_list.InitWithFeatures( + {optimization_guide::features:: + kOptimizationTargetPredictionUsingMLService}, + {}); + + SetLoadModelResult(machine_learning::mojom::LoadModelResult::kOk); + } + base::HistogramTester histogram_tester; std::unique_ptr<content::MockNavigationHandle> navigation_handle = CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver( @@ -825,25 +1017,58 @@ proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, OptimizationTargetDecision::kPageLoadDoesNotMatch); - TestPredictionModel* test_prediction_model = - static_cast<TestPredictionModel*>( - prediction_manager()->GetPredictionModelForTesting( - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); - EXPECT_TRUE(test_prediction_model); + if (UsingMLService()) { + RemoteDecisionTreePredictor* predictor_handle = + prediction_manager()->GetRemoteDecisionTreePredictorForTesting( + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD); + EXPECT_TRUE(predictor_handle); - // Make sure the previous decision is reused and that the model was not - // evaluated. - EXPECT_EQ(OptimizationTargetDecision::kPageLoadDoesNotMatch, - prediction_manager()->ShouldTargetNavigation( - navigation_handle.get(), - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); - EXPECT_FALSE(test_prediction_model->WasModelEvaluated()); + // Set ML prediction result to Unknown to ensure the actual model is not + // run. + SetDecisionTreePredictionResult( + machine_learning::mojom::DecisionTreePredictionResult::kUnknown, + /* score */ 0.0); + + prediction_manager()->ShouldTargetNavigationAsync( + navigation_handle.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, + {}, base::BindOnce([](OptimizationTargetDecision decision) { + EXPECT_EQ(OptimizationTargetDecision::kPageLoadDoesNotMatch, + decision); + })); + + // Flush the Service connection pipe. + RunUntilIdle(); + } else { + TestPredictionModel* test_prediction_model = + static_cast<TestPredictionModel*>( + prediction_manager()->GetPredictionModelForTesting( + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); + EXPECT_TRUE(test_prediction_model); + + // Make sure the previous decision is reused and that the model was not + // evaluated. + EXPECT_EQ(OptimizationTargetDecision::kPageLoadDoesNotMatch, + prediction_manager()->ShouldTargetNavigation( + navigation_handle.get(), + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); + EXPECT_FALSE(test_prediction_model->WasModelEvaluated()); + } histogram_tester.ExpectTotalCount( "OptimizationGuide.ShouldTargetNavigation.PredictionModelStatus", 0); } -TEST_F(PredictionManagerTest, +TEST_P(PredictionManagerMLServiceTest, EvaluatePredictionModelUsesDecisionFromHoldbackEvalIfModelWasEvaluated) { + base::test::ScopedFeatureList scoped_feature_list; + if (UsingMLService()) { + scoped_feature_list.InitWithFeatures( + {optimization_guide::features:: + kOptimizationTargetPredictionUsingMLService}, + {}); + + SetLoadModelResult(machine_learning::mojom::LoadModelResult::kOk); + } + std::unique_ptr<content::MockNavigationHandle> navigation_handle = CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver( GURL("https://foo.com")); @@ -867,22 +1092,56 @@ proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, OptimizationTargetDecision::kModelPredictionHoldback); - TestPredictionModel* test_prediction_model = - static_cast<TestPredictionModel*>( - prediction_manager()->GetPredictionModelForTesting( - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); - EXPECT_TRUE(test_prediction_model); + if (UsingMLService()) { + RemoteDecisionTreePredictor* predictor_handle = + prediction_manager()->GetRemoteDecisionTreePredictorForTesting( + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD); + EXPECT_TRUE(predictor_handle); - // Make sure the cached decision is returned and that the model was not - // evaluated. - EXPECT_EQ(OptimizationTargetDecision::kModelPredictionHoldback, - prediction_manager()->ShouldTargetNavigation( - navigation_handle.get(), - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); - EXPECT_FALSE(test_prediction_model->WasModelEvaluated()); + // Set ML prediction result to Unknown to ensure the actual model is not + // run. + SetDecisionTreePredictionResult( + machine_learning::mojom::DecisionTreePredictionResult::kUnknown, + /* score */ 0.0); + + prediction_manager()->ShouldTargetNavigationAsync( + navigation_handle.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, + {}, base::BindOnce([](OptimizationTargetDecision decision) { + EXPECT_EQ(OptimizationTargetDecision::kModelPredictionHoldback, + decision); + })); + + // Flush the Service connection pipe. + RunUntilIdle(); + } else { + TestPredictionModel* test_prediction_model = + static_cast<TestPredictionModel*>( + prediction_manager()->GetPredictionModelForTesting( + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); + EXPECT_TRUE(test_prediction_model); + + // Make sure the cached decision is returned and that the model was not + // evaluated. + EXPECT_EQ(OptimizationTargetDecision::kModelPredictionHoldback, + prediction_manager()->ShouldTargetNavigation( + navigation_handle.get(), + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); + EXPECT_FALSE(test_prediction_model->WasModelEvaluated()); + } } -TEST_F(PredictionManagerTest, EvaluatePredictionModelPopulatesNavData) { +TEST_P(PredictionManagerMLServiceTest, + EvaluatePredictionModelPopulatesNavData) { + base::test::ScopedFeatureList scoped_feature_list; + if (UsingMLService()) { + scoped_feature_list.InitWithFeatures( + {optimization_guide::features:: + kOptimizationTargetPredictionUsingMLService}, + {}); + + SetLoadModelResult(machine_learning::mojom::LoadModelResult::kOk); + } + base::HistogramTester histogram_tester; std::unique_ptr<content::MockNavigationHandle> navigation_handle = CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver( @@ -913,19 +1172,36 @@ proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, OptimizationTargetDecision::kModelNotAvailableOnClient); - // Make sure model gets evaluated despite there already being a decision in - // the navigation data. - EXPECT_EQ(OptimizationTargetDecision::kPageLoadMatches, - prediction_manager()->ShouldTargetNavigation( - navigation_handle.get(), - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); + if (UsingMLService()) { + // Set ML prediction result to True to ensure the actual model is evaluated + // despite there already being a decision in the navigation data. + SetDecisionTreePredictionResult( + machine_learning::mojom::DecisionTreePredictionResult::kTrue, + /* score */ 0.6); - TestPredictionModel* test_prediction_model = - static_cast<TestPredictionModel*>( - prediction_manager()->GetPredictionModelForTesting( - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); - EXPECT_TRUE(test_prediction_model); - EXPECT_TRUE(test_prediction_model->WasModelEvaluated()); + prediction_manager()->ShouldTargetNavigationAsync( + navigation_handle.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, + {}, base::BindOnce([](OptimizationTargetDecision decision) { + EXPECT_EQ(OptimizationTargetDecision::kPageLoadMatches, decision); + })); + + // Flush the Service connection pipe. + RunUntilIdle(); + } else { + // Make sure model gets evaluated despite there already being a decision in + // the navigation data. + EXPECT_EQ(OptimizationTargetDecision::kPageLoadMatches, + prediction_manager()->ShouldTargetNavigation( + navigation_handle.get(), + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); + + TestPredictionModel* test_prediction_model = + static_cast<TestPredictionModel*>( + prediction_manager()->GetPredictionModelForTesting( + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); + EXPECT_TRUE(test_prediction_model); + EXPECT_TRUE(test_prediction_model->WasModelEvaluated()); + } EXPECT_EQ(2, *nav_data->GetModelVersionForOptimizationTarget( proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); @@ -933,15 +1209,27 @@ proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); } -TEST_F(PredictionManagerTest, +TEST_P(PredictionManagerMLServiceTest, EvaluatePredictionModelPopulatesNavDataEvenWithHoldback) { - base::HistogramTester histogram_tester; base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitWithFeaturesAndParameters( - {base::test::ScopedFeatureList::FeatureAndParams( - features::kOptimizationTargetPrediction, - {{"painful_page_load_metrics_only", "true"}})}, - {}); + if (UsingMLService()) { + scoped_feature_list.InitWithFeaturesAndParameters( + {{features::kOptimizationTargetPrediction, + {{"painful_page_load_metrics_only", "true"}}}, + {optimization_guide::features:: + kOptimizationTargetPredictionUsingMLService, + {}}}, + {}); + + SetLoadModelResult(machine_learning::mojom::LoadModelResult::kOk); + } else { + scoped_feature_list.InitWithFeaturesAndParameters( + {{features::kOptimizationTargetPrediction, + {{"painful_page_load_metrics_only", "true"}}}}, + {}); + } + + base::HistogramTester histogram_tester; std::unique_ptr<content::MockNavigationHandle> navigation_handle = CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver( @@ -960,17 +1248,34 @@ EXPECT_TRUE(prediction_model_fetcher()->models_fetched()); models_and_features_store()->RunUpdateHostModelFeaturesCallback(); - EXPECT_EQ(OptimizationTargetDecision::kModelPredictionHoldback, - prediction_manager()->ShouldTargetNavigation( - navigation_handle.get(), - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); + if (UsingMLService()) { + // Set ML prediction result to True to ensure the actual model is evaluated. + SetDecisionTreePredictionResult( + machine_learning::mojom::DecisionTreePredictionResult::kTrue, + /* score */ 0.6); - TestPredictionModel* test_prediction_model = - static_cast<TestPredictionModel*>( - prediction_manager()->GetPredictionModelForTesting( - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); - EXPECT_TRUE(test_prediction_model); - EXPECT_TRUE(test_prediction_model->WasModelEvaluated()); + prediction_manager()->ShouldTargetNavigationAsync( + navigation_handle.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, + {}, base::BindOnce([](OptimizationTargetDecision decision) { + EXPECT_EQ(OptimizationTargetDecision::kModelPredictionHoldback, + decision); + })); + + // Flush the Service connection pipe. + RunUntilIdle(); + } else { + EXPECT_EQ(OptimizationTargetDecision::kModelPredictionHoldback, + prediction_manager()->ShouldTargetNavigation( + navigation_handle.get(), + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); + + TestPredictionModel* test_prediction_model = + static_cast<TestPredictionModel*>( + prediction_manager()->GetPredictionModelForTesting( + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); + EXPECT_TRUE(test_prediction_model); + EXPECT_TRUE(test_prediction_model->WasModelEvaluated()); + } OptimizationGuideNavigationData* nav_data = OptimizationGuideNavigationData::GetFromNavigationHandle( @@ -991,7 +1296,16 @@ PredictionManagerModelStatus::kModelAvailable, 1); } -TEST_F(PredictionManagerTest, ShouldTargetNavigationStoreAvailableNoModel) { +TEST_P(PredictionManagerMLServiceTest, + ShouldTargetNavigationStoreAvailableNoModel) { + base::test::ScopedFeatureList scoped_feature_list; + if (UsingMLService()) { + scoped_feature_list.InitWithFeatures( + {optimization_guide::features:: + kOptimizationTargetPredictionUsingMLService}, + {}); + } + base::HistogramTester histogram_tester; std::unique_ptr<content::MockNavigationHandle> navigation_handle = CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver( @@ -1005,14 +1319,26 @@ prediction_manager()->RegisterOptimizationTargets( {proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD}); - SetStoreInitialized(/*load_models=*/false, - /*load_host_model_features=*/true, - /*have_models_in_store=)*/ false); + SetStoreInitialized(/* load_models= */ false, + /* load_host_model_features= */ true, + /* have_models_in_store= */ false); - EXPECT_EQ(OptimizationTargetDecision::kModelNotAvailableOnClient, - prediction_manager()->ShouldTargetNavigation( - navigation_handle.get(), - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); + if (UsingMLService()) { + prediction_manager()->ShouldTargetNavigationAsync( + navigation_handle.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, + {}, base::BindOnce([](OptimizationTargetDecision decision) { + EXPECT_EQ(OptimizationTargetDecision::kModelNotAvailableOnClient, + decision); + })); + + // Flush the Service connection pipe. + RunUntilIdle(); + } else { + EXPECT_EQ(OptimizationTargetDecision::kModelNotAvailableOnClient, + prediction_manager()->ShouldTargetNavigation( + navigation_handle.get(), + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); + } histogram_tester.ExpectBucketCount( "OptimizationGuide.ShouldTargetNavigation.PredictionModelStatus", @@ -1025,8 +1351,15 @@ PredictionManagerModelStatus::kStoreAvailableNoModelForTarget, 1); } -TEST_F(PredictionManagerTest, +TEST_P(PredictionManagerMLServiceTest, ShouldTargetNavigationStoreAvailableModelNotLoaded) { + base::test::ScopedFeatureList scoped_feature_list; + if (UsingMLService()) { + scoped_feature_list.InitWithFeatures( + {optimization_guide::features:: + kOptimizationTargetPredictionUsingMLService}, + {}); + } base::HistogramTester histogram_tester; std::unique_ptr<content::MockNavigationHandle> navigation_handle = CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver( @@ -1040,14 +1373,26 @@ prediction_manager()->RegisterOptimizationTargets( {proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD}); - SetStoreInitialized(/*load_models=*/false, - /*load_host_model_features=*/true, - /*have_models_in_store=)*/ true); + SetStoreInitialized(/* load_models= */ false, + /* load_host_model_features= */ true, + /* have_models_in_store= */ true); - EXPECT_EQ(OptimizationTargetDecision::kModelNotAvailableOnClient, - prediction_manager()->ShouldTargetNavigation( - navigation_handle.get(), - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); + if (UsingMLService()) { + prediction_manager()->ShouldTargetNavigationAsync( + navigation_handle.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, + {}, base::BindOnce([](OptimizationTargetDecision decision) { + EXPECT_EQ(OptimizationTargetDecision::kModelNotAvailableOnClient, + decision); + })); + + // Flush the Service connection pipe. + RunUntilIdle(); + } else { + EXPECT_EQ(OptimizationTargetDecision::kModelNotAvailableOnClient, + prediction_manager()->ShouldTargetNavigation( + navigation_handle.get(), + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); + } histogram_tester.ExpectBucketCount( "OptimizationGuide.ShouldTargetNavigation.PredictionModelStatus", @@ -1063,9 +1408,16 @@ "OptimizationGuide.PredictionModelLoadedVersion.PainfulPageLoad", 0); } -TEST_F(PredictionManagerTest, +TEST_P(PredictionManagerMLServiceTest, DISABLE_ON_WIN_MAC_CHROMEOS( ShouldTargetNavigationStoreUnavailableModelUnknown)) { + base::test::ScopedFeatureList scoped_feature_list; + if (UsingMLService()) { + scoped_feature_list.InitWithFeatures( + {optimization_guide::features:: + kOptimizationTargetPredictionUsingMLService}, + {}); + } base::HistogramTester histogram_tester; std::unique_ptr<content::MockNavigationHandle> navigation_handle = CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver( @@ -1079,10 +1431,22 @@ prediction_manager()->RegisterOptimizationTargets( {proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD}); - EXPECT_EQ(OptimizationTargetDecision::kModelNotAvailableOnClient, - prediction_manager()->ShouldTargetNavigation( - navigation_handle.get(), - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); + if (UsingMLService()) { + prediction_manager()->ShouldTargetNavigationAsync( + navigation_handle.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, + {}, base::BindOnce([](OptimizationTargetDecision decision) { + EXPECT_EQ(OptimizationTargetDecision::kModelNotAvailableOnClient, + decision); + })); + + // Flush the Service connection pipe. + RunUntilIdle(); + } else { + EXPECT_EQ(OptimizationTargetDecision::kModelNotAvailableOnClient, + prediction_manager()->ShouldTargetNavigation( + navigation_handle.get(), + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); + } histogram_tester.ExpectBucketCount( "OptimizationGuide.ShouldTargetNavigation.PredictionModelStatus", @@ -1095,7 +1459,16 @@ PredictionManagerModelStatus::kStoreUnavailableModelUnknown, 1); } -TEST_F(PredictionManagerTest, UpdateModelForUnregisteredTarget) { +TEST_P(PredictionManagerMLServiceTest, UpdateModelForUnregisteredTarget) { + base::test::ScopedFeatureList scoped_feature_list; + if (UsingMLService()) { + scoped_feature_list.InitWithFeatures( + {optimization_guide::features:: + kOptimizationTargetPredictionUsingMLService}, + {}); + SetLoadModelResult(machine_learning::mojom::LoadModelResult::kOk); + } + base::HistogramTester histogram_tester; CreatePredictionManager({}); prediction_manager()->SetPredictionModelFetcherForTesting( @@ -1114,11 +1487,19 @@ prediction_manager()->UpdatePredictionModelsForTesting( get_models_response.get()); - TestPredictionModel* test_prediction_model = - static_cast<TestPredictionModel*>( - prediction_manager()->GetPredictionModelForTesting( - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); - EXPECT_FALSE(test_prediction_model); + if (UsingMLService()) { + RemoteDecisionTreePredictor* predictor = + prediction_manager()->GetRemoteDecisionTreePredictorForTesting( + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD); + EXPECT_FALSE(predictor); + } else { + TestPredictionModel* test_prediction_model = + static_cast<TestPredictionModel*>( + prediction_manager()->GetPredictionModelForTesting( + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); + EXPECT_FALSE(test_prediction_model); + } + histogram_tester.ExpectTotalCount( "OptimizationGuide.PredictionManager.PredictionModelsStored", 1); histogram_tester.ExpectTotalCount( @@ -1127,9 +1508,17 @@ "OptimizationGuide.PredictionModelLoadedVersion.PainfulPageLoad", 0); } -TEST_F( - PredictionManagerTest, +TEST_P( + PredictionManagerMLServiceTest, DISABLE_ON_WIN_MAC_CHROMEOS(UpdateModelWithUnsupportedOptimizationTarget)) { + base::test::ScopedFeatureList scoped_feature_list; + if (UsingMLService()) { + scoped_feature_list.InitWithFeatures( + {optimization_guide::features:: + kOptimizationTargetPredictionUsingMLService}, + {}); + } + std::unique_ptr<content::MockNavigationHandle> navigation_handle = CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver( GURL("https://foo.com")); @@ -1153,19 +1542,108 @@ prediction_manager()->UpdatePredictionModelsForTesting( get_models_response.get()); - EXPECT_EQ(OptimizationTargetDecision::kModelNotAvailableOnClient, - prediction_manager()->ShouldTargetNavigation( - navigation_handle.get(), - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); + if (UsingMLService()) { + prediction_manager()->ShouldTargetNavigationAsync( + navigation_handle.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, + {}, base::BindOnce([](OptimizationTargetDecision decision) { + EXPECT_EQ(OptimizationTargetDecision::kModelNotAvailableOnClient, + decision); + })); - TestPredictionModel* test_prediction_model = - static_cast<TestPredictionModel*>( - prediction_manager()->GetPredictionModelForTesting( - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); - EXPECT_FALSE(test_prediction_model); + // Flush the Service connection pipe. + RunUntilIdle(); + + RemoteDecisionTreePredictor* predictor = + prediction_manager()->GetRemoteDecisionTreePredictorForTesting( + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD); + EXPECT_FALSE(predictor); + } else { + EXPECT_EQ(OptimizationTargetDecision::kModelNotAvailableOnClient, + prediction_manager()->ShouldTargetNavigation( + navigation_handle.get(), + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {})); + + TestPredictionModel* test_prediction_model = + static_cast<TestPredictionModel*>( + prediction_manager()->GetPredictionModelForTesting( + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)); + EXPECT_FALSE(test_prediction_model); + } EXPECT_FALSE(models_and_features_store()->WasModelLoaded()); } +class PredictionManagerMLServiceEnabledTest + : public PredictionManagerMLServiceTest { + public: + void SetUp() override { + PredictionManagerMLServiceTest::SetUp(); + service_connection_->SetAsyncModeForTesting(true); + } +}; + +INSTANTIATE_TEST_SUITE_P(MLServiceEnabled, + PredictionManagerMLServiceEnabledTest, + ::testing::Values(true)); + +TEST_P(PredictionManagerMLServiceEnabledTest, + ServiceDisconnectedAtModelEvaluation) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatures( + {optimization_guide::features:: + kOptimizationTargetPredictionUsingMLService}, + {}); + + base::HistogramTester histogram_tester; + std::unique_ptr<content::MockNavigationHandle> navigation_handle = + CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver( + GURL("https://foo.com")); + + CreatePredictionManager({}); + // The model will be loaded from the store. + prediction_manager()->SetPredictionModelFetcherForTesting( + BuildTestPredictionModelFetcher( + PredictionModelFetcherEndState::kFetchSuccessWithEmptyResponse)); + + prediction_manager()->RegisterOptimizationTargets( + {proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD}); + SetStoreInitialized(); + EXPECT_TRUE(prediction_model_fetcher()->models_fetched()); + + SetLoadModelResult(machine_learning::mojom::LoadModelResult::kOk); + RunScheduledCalls(); + + // Reset the service to cause disconnection. + ResetMLService(); + + // Still sets the evaluation result + SetDecisionTreePredictionResult( + machine_learning::mojom::DecisionTreePredictionResult::kTrue, + /* score */ 0.6); + + prediction_manager()->ShouldTargetNavigationAsync( + navigation_handle.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, {}, + base::BindOnce([](OptimizationTargetDecision decision) { + EXPECT_EQ(OptimizationTargetDecision::kModelNotAvailableOnClient, + decision); + })); + + // Flush the Service connection pipe. + RunUntilIdle(); + RunScheduledCalls(); + + histogram_tester.ExpectTotalCount( + "OptimizationGuide.PredictionModelEvaluationLatency." + + GetStringNameForOptimizationTarget( + optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD), + 0); + + histogram_tester.ExpectUniqueSample( + "OptimizationGuide.IsPredictionModelValid." + + GetStringNameForOptimizationTarget( + optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD), + true, 1); +} + TEST_F(PredictionManagerTest, HasHostModelFeaturesForHost) { base::HistogramTester histogram_tester; @@ -1237,7 +1715,6 @@ } TEST_F(PredictionManagerTest, UpdateHostModelFeaturesMissingHost) { - CreatePredictionManager({}); prediction_manager()->SetPredictionModelFetcherForTesting( BuildTestPredictionModelFetcher( @@ -1262,7 +1739,6 @@ } TEST_F(PredictionManagerTest, UpdateHostModelFeaturesNoFeature) { - CreatePredictionManager({}); prediction_manager()->SetPredictionModelFetcherForTesting( BuildTestPredictionModelFetcher( @@ -1286,7 +1762,6 @@ } TEST_F(PredictionManagerTest, UpdateHostModelFeaturesNoFeatureName) { - CreatePredictionManager({}); prediction_manager()->SetPredictionModelFetcherForTesting( BuildTestPredictionModelFetcher( @@ -1338,7 +1813,6 @@ } TEST_F(PredictionManagerTest, UpdateHostModelFeaturesIntValue) { - CreatePredictionManager({}); prediction_manager()->SetPredictionModelFetcherForTesting( BuildTestPredictionModelFetcher( @@ -1462,7 +1936,25 @@ EXPECT_EQ(6.0, (*host_model_features)["host_feat_added"]); } -TEST_P(PredictionManagerTest, ClientFeature) { +class PredictionManagerClientFeatureTest + : public PredictionManagerTest, + public testing::WithParamInterface<proto::ClientModelFeature> { + public: + bool IsSameOriginNavigationFeature() { + return GetParam() == proto::CLIENT_MODEL_FEATURE_SAME_ORIGIN_NAVIGATION; + } + + bool IsUnknownFeature() { + return GetParam() == proto::CLIENT_MODEL_FEATURE_UNKNOWN; + } +}; + +INSTANTIATE_TEST_SUITE_P(ClientFeature, + PredictionManagerClientFeatureTest, + testing::Range(proto::ClientModelFeature_MIN, + proto::ClientModelFeature_MAX)); + +TEST_P(PredictionManagerClientFeatureTest, ClientFeature) { base::HistogramTester histogram_tester; std::unique_ptr<content::MockNavigationHandle> navigation_handle = CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver( @@ -1513,11 +2005,6 @@ } } -INSTANTIATE_TEST_SUITE_P(ClientFeature, - PredictionManagerTest, - testing::Range(proto::ClientModelFeature_MIN, - proto::ClientModelFeature_MAX)); - TEST_F(PredictionManagerTest, PreviousSessionStatisticsUsed) { base::HistogramTester histogram_tester; GURL previous_url = GURL("https://foo.com");
diff --git a/chrome/browser/optimization_guide/prediction/remote_decision_tree_predictor.cc b/chrome/browser/optimization_guide/prediction/remote_decision_tree_predictor.cc index a60ac5c..86778eca 100644 --- a/chrome/browser/optimization_guide/prediction/remote_decision_tree_predictor.cc +++ b/chrome/browser/optimization_guide/prediction/remote_decision_tree_predictor.cc
@@ -43,6 +43,14 @@ return remote_.get(); } +bool RemoteDecisionTreePredictor::IsConnected() const { + return remote_.is_connected(); +} + +void RemoteDecisionTreePredictor::FlushForTesting() { + remote_.FlushForTesting(); +} + mojo::PendingReceiver<machine_learning::mojom::DecisionTreePredictor> RemoteDecisionTreePredictor::BindNewPipeAndPassReceiver() { return remote_.BindNewPipeAndPassReceiver();
diff --git a/chrome/browser/optimization_guide/prediction/remote_decision_tree_predictor.h b/chrome/browser/optimization_guide/prediction/remote_decision_tree_predictor.h index 6487c1d..44a9286 100644 --- a/chrome/browser/optimization_guide/prediction/remote_decision_tree_predictor.h +++ b/chrome/browser/optimization_guide/prediction/remote_decision_tree_predictor.h
@@ -32,6 +32,12 @@ // receiver. Returns nullptr if |remote_| is unbound. machine_learning::mojom::DecisionTreePredictorProxy* Get() const; + // Whether |remote_| is connected. + bool IsConnected() const; + + // Flushes |remote_| for testing purpose. + void FlushForTesting(); + // Calls the |BindNewPipeAndPassReceiver| method of the |remote_|. Must only // be called on a bound |remote_|. mojo::PendingReceiver<machine_learning::mojom::DecisionTreePredictor>
diff --git a/chrome/browser/optimization_guide/prediction/remote_decision_tree_predictor_unittest.cc b/chrome/browser/optimization_guide/prediction/remote_decision_tree_predictor_unittest.cc index bad16db..f1c5eba 100644 --- a/chrome/browser/optimization_guide/prediction/remote_decision_tree_predictor_unittest.cc +++ b/chrome/browser/optimization_guide/prediction/remote_decision_tree_predictor_unittest.cc
@@ -55,6 +55,12 @@ auto pending_receiver = predictor.BindNewPipeAndPassReceiver(); EXPECT_TRUE(predictor.Get()); EXPECT_TRUE(pending_receiver); + EXPECT_TRUE(predictor.IsConnected()); + + pending_receiver.reset(); + predictor.FlushForTesting(); + EXPECT_TRUE(predictor.Get()); + EXPECT_FALSE(predictor.IsConnected()); } } // namespace optimization_guide
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn index 2ad95bd7..150c221 100644 --- a/chrome/browser/resources/BUILD.gn +++ b/chrome/browser/resources/BUILD.gn
@@ -260,6 +260,7 @@ } deps = [ + "//chrome/browser/promo_browser_command:mojo_bindings_js", "//chrome/browser/ui/webui/new_tab_page:mojo_bindings_js", "//skia/public/mojom:mojom_js", ]
diff --git a/chrome/browser/resources/nearby_internals/contact_tab.js b/chrome/browser/resources/nearby_internals/contact_tab.js index 40943b0..5e4b298 100644 --- a/chrome/browser/resources/nearby_internals/contact_tab.js +++ b/chrome/browser/resources/nearby_internals/contact_tab.js
@@ -86,6 +86,6 @@ */ onContactUpdateAdded_(contacts) { contacts.unshift('contactList_'); - this.push.apply(this, contacts); + this.unshift.apply(this, contacts); }, });
diff --git a/chrome/browser/resources/nearby_internals/http_message_object.html b/chrome/browser/resources/nearby_internals/http_message_object.html index 0167286..d2879dc 100644 --- a/chrome/browser/resources/nearby_internals/http_message_object.html +++ b/chrome/browser/resources/nearby_internals/http_message_object.html
@@ -4,9 +4,7 @@ } #item { - border-left: var(--standard-border); - border-right: var(--standard-border); - border-top: var(--standard-border); + border: var(--standard-border); } #header { @@ -48,7 +46,7 @@ [[rpcToString_(item.rpc)]]:[[directionToString_(item.direction)]] </span> <div id="flex"></div> - <span id="time">[[item.time]]</span> + <span id="time">[[formatTime_(item.time)]]</span> <cr-expand-button id="expandContent" class="cr-row" expanded="{{contentExpanded_}}"> </cr-expand-button>
diff --git a/chrome/browser/resources/nearby_internals/http_message_object.js b/chrome/browser/resources/nearby_internals/http_message_object.js index 58e7d99..4e7ba40 100644 --- a/chrome/browser/resources/nearby_internals/http_message_object.js +++ b/chrome/browser/resources/nearby_internals/http_message_object.js
@@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'chrome://resources/cr_elements/cr_expand_button/cr_expand_button.m.js'; +import 'chrome://resources/polymer/v3_0/iron-collapse/iron-collapse.js'; + import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {Direction, Rpc} from './types.js'; @@ -79,4 +82,15 @@ break; } }, + + /** + * Sets the string representation of time. + * @private + * @param {number} time + * @return + */ + formatTime_(time) { + const d = new Date(time); + return d.toLocaleTimeString(); + }, });
diff --git a/chrome/browser/resources/nearby_internals/http_tab.html b/chrome/browser/resources/nearby_internals/http_tab.html index 6d9c3f3b..561db62 100644 --- a/chrome/browser/resources/nearby_internals/http_tab.html +++ b/chrome/browser/resources/nearby_internals/http_tab.html
@@ -3,10 +3,6 @@ --standard-border: 1px solid black; } - #http-list:last-child { - border-bottom: var(--standard-border); - } - #clearButton { float: right; } @@ -25,10 +21,10 @@ Clear Messages </cr-button> -<iron-list items="[[httpMessageList_]]" as="http-message" id="http-list" +<dom-repeat items="[[httpMessageList_]]" as="http-message" hidden="[[!httpMessageList_.length]]"> <template> <http-message-object item="[[http-message]]"> </http-message-object> </template> -</iron-list> +</dom-repeat>
diff --git a/chrome/browser/resources/nearby_internals/http_tab.js b/chrome/browser/resources/nearby_internals/http_tab.js index 74d02d5..c40a949b 100644 --- a/chrome/browser/resources/nearby_internals/http_tab.js +++ b/chrome/browser/resources/nearby_internals/http_tab.js
@@ -104,6 +104,6 @@ */ parseAndAddMessages_(messages) { messages.unshift('httpMessageList_'); - this.push.apply(this, messages); + this.unshift.apply(this, messages); }, });
diff --git a/chrome/browser/resources/nearby_internals/types.js b/chrome/browser/resources/nearby_internals/types.js index e61b65a..384e10e 100644 --- a/chrome/browser/resources/nearby_internals/types.js +++ b/chrome/browser/resources/nearby_internals/types.js
@@ -53,7 +53,7 @@ * The HTTP request/response object, sent by NearbyInternalsHttpHandler * chrome/browser/ui/webui/nearby_internals/nearby_internals_http_handler.cc. * @typedef {{body: string, - * time: string, + * time: number, * rpc: !Rpc, * direction: !Direction}} */
diff --git a/chrome/browser/resources/new_tab_page/BUILD.gn b/chrome/browser/resources/new_tab_page/BUILD.gn index ba975ca..298e94fd 100644 --- a/chrome/browser/resources/new_tab_page/BUILD.gn +++ b/chrome/browser/resources/new_tab_page/BUILD.gn
@@ -23,6 +23,7 @@ ":module_registry", ":modules", ":one_google_bar_api", + ":promo_browser_command_proxy", ":realbox", ":realbox_button", ":realbox_dropdown", @@ -51,12 +52,20 @@ ":modules", ":most_visited", ":one_google_bar_api", + ":promo_browser_command_proxy", "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", "//ui/webui/resources/js:event_tracker.m", "//ui/webui/resources/js:load_time_data.m", ] } +js_library("promo_browser_command_proxy") { + deps = [ + "//chrome/browser/promo_browser_command:mojo_bindings_js_library_for_compile", + "//ui/webui/resources/js:cr.m", + ] +} + js_library("most_visited") { deps = [ ":browser_proxy", @@ -271,6 +280,7 @@ source = "new_tab_page_resources.grd" deps = [ + "//chrome/browser/promo_browser_command:mojo_bindings_js", "//chrome/browser/resources/new_tab_page:web_components", "//chrome/browser/ui/webui/new_tab_page:mojo_bindings_js", "//skia/public/mojom:mojom_js", @@ -296,6 +306,7 @@ deps = [ ":unoptimized_resources" ] excludes = [ + "../../promo_browser_command/promo_browser_command.mojom-lite.js", "../../ui/webui/new_tab_page/new_tab_page.mojom-lite.js", "../../../common/search/omnibox.mojom-lite.js", "../../../../skia/public/mojom/skcolor.mojom-lite.js", @@ -322,6 +333,7 @@ "chrome://resources/mojo/url/mojom/url.mojom-lite.js", "new_tab_page.mojom-lite.js", "omnibox.mojom-lite.js", + "promo_browser_command.mojom-lite.js", ] } }
diff --git a/chrome/browser/resources/new_tab_page/app.js b/chrome/browser/resources/new_tab_page/app.js index 227da8f..4c50b3c 100644 --- a/chrome/browser/resources/new_tab_page/app.js +++ b/chrome/browser/resources/new_tab_page/app.js
@@ -24,8 +24,17 @@ import {BackgroundSelection, BackgroundSelectionType} from './customize_dialog.js'; import {registry} from './modules/modules.js'; import {oneGoogleBarApi} from './one_google_bar_api.js'; +import {PromoBrowserCommandProxy} from './promo_browser_command_proxy.js'; import {$$, hexColorToSkColor, skColorToRgba} from './utils.js'; +/** + * @typedef {{ + * commandId: promoBrowserCommand.mojom.Command<number>, + * clickInfo: !promoBrowserCommand.mojom.ClickInfo + * }} + */ +let CommandData; + class AppElement extends PolymerElement { static get is() { return 'ntp-app'; @@ -227,7 +236,9 @@ performance.measure('theme-set'); this.theme_ = theme; }); - this.eventTracker_.add(window, 'message', ({data}) => { + this.eventTracker_.add(window, 'message', (event) => { + /** @type {!Object} */ + const data = event.data; // Something in OneGoogleBar is sending a message that is received here. // Need to ignore it. if (typeof data !== 'object') { @@ -235,9 +246,9 @@ } if ('frameType' in data) { if (data.frameType === 'promo') { - this.handlePromoMessage_(data); + this.handlePromoMessage_(event); } else if (data.frameType === 'one-google-bar') { - this.handleOneGoogleBarMessage_(data); + this.handleOneGoogleBarMessage_(event); } } }); @@ -640,6 +651,32 @@ } /** + * Sends the command and the accompanying mouse click info received from the + * promo of the given source and origin to the browser. Relays the execution + * status response back to the source promo frame. |commandSource| and + * |commandOrigin| are used only to send the execution status response back to + * the source promo frame and should not be used for anything else. + * @param {!CommandData} commandData Command and mouse click info. + * @param {Window} commandSource Source promo frame. + * @param {string} commandOrigin Origin of the source promo frame. + * @private + */ + executePromoBrowserCommand_(commandData, commandSource, commandOrigin) { + // Make sure we don't send unsupported commands to the browser. + /** @type {!promoBrowserCommand.mojom.Command} */ + const commandId = Object.values(promoBrowserCommand.mojom.Command) + .includes(commandData.commandId) ? + commandData.commandId : + promoBrowserCommand.mojom.Command.kUnknownCommand; + + PromoBrowserCommandProxy.getInstance() + .handler.executeCommand(commandId, commandData.clickInfo) + .then(({commandExecuted}) => { + commandSource.postMessage(commandExecuted, commandOrigin); + }); + } + + /** * Handles messages from the OneGoogleBar iframe. The messages that are * handled include show bar on load and overlay updates. * @@ -649,10 +686,12 @@ * When modal overlays are enabled, activate/deactivate controls if the * OneGoogleBar is layered on top of #content with a backdrop. This would * happen when OneGoogleBar has an overlay open. - * @param {!Object} data + * @param {!MessageEvent} event * @private */ - handleOneGoogleBarMessage_(data) { + handleOneGoogleBarMessage_(event) { + /** @type {!Object} */ + const data = event.data; if (data.messageType === 'loaded') { if (!this.oneGoogleBarModalOverlaysEnabled_) { const oneGoogleBar = $$(this, '#oneGoogleBar'); @@ -683,6 +722,9 @@ } else if (data.messageType === 'deactivate') { this.$.oneGoogleBarOverlayBackdrop.toggleAttribute('show', false); $$(this, '#oneGoogleBar').style.zIndex = '0'; + } else if (data.messageType === 'execute-browser-command') { + this.executePromoBrowserCommand_( + /** @type CommandData */ (data), event.source, event.origin); } } @@ -690,10 +732,12 @@ * Handle messages from promo iframe. This shows the promo on load and sets * up the show/hide logic (in case there is an overlap with most-visited * tiles). - * @param {!Object} data + * @param {!MessageEvent} event * @private */ - handlePromoMessage_(data) { + handlePromoMessage_(event) { + /** @type {!Object} */ + const data = event.data; if (data.messageType === 'loaded') { this.promoLoaded_ = true; const onResize = () => { @@ -706,6 +750,9 @@ this.pageHandler_.onPromoRendered(BrowserProxy.getInstance().now()); } else if (data.messageType === 'link-clicked') { this.pageHandler_.onPromoLinkClicked(); + } else if (data.messageType === 'execute-browser-command') { + this.executePromoBrowserCommand_( + /** @type CommandData */ (data), event.source, event.origin); } }
diff --git a/chrome/browser/resources/new_tab_page/new_tab_page.js b/chrome/browser/resources/new_tab_page/new_tab_page.js index ad1f3bc..ac41a8d 100644 --- a/chrome/browser/resources/new_tab_page/new_tab_page.js +++ b/chrome/browser/resources/new_tab_page/new_tab_page.js
@@ -17,5 +17,6 @@ export {BackgroundSelectionType} from './customize_dialog.js'; export {dummyDescriptor} from './modules/dummy/module.js'; export {ModuleRegistry} from './modules/module_registry.js'; +export {PromoBrowserCommandProxy} from './promo_browser_command_proxy.js'; export {NO_SUGGESTION_GROUP_ID} from './realbox_dropdown.js'; export {$$, createScrollBorders, decodeString16, hexColorToSkColor, mojoString16, skColorToRgba} from './utils.js';
diff --git a/chrome/browser/resources/new_tab_page/new_tab_page_resources_common.grdp b/chrome/browser/resources/new_tab_page/new_tab_page_resources_common.grdp index 1b52ac2..8d0effd 100644 --- a/chrome/browser/resources/new_tab_page/new_tab_page_resources_common.grdp +++ b/chrome/browser/resources/new_tab_page/new_tab_page_resources_common.grdp
@@ -6,6 +6,9 @@ <include name="IDR_NEW_TAB_PAGE_OMNIBOX_MOJO_LITE_JS" file="${root_gen_dir}/chrome/common/search/omnibox.mojom-lite.js" use_base_dir="false" type="BINDATA" /> + <include name="IDR_NEW_TAB_PAGE_PROMO_BROWSER_COMMAND_MOJO_LITE_JS" + file="${root_gen_dir}/chrome/browser/promo_browser_command/promo_browser_command.mojom-lite.js" + use_base_dir="false" type="BINDATA" /> <include name="IDR_NEW_TAB_PAGE_ACCOUNT_CIRCLE_SVG" file="icons/account_circle.svg" type="BINDATA" /> <include name="IDR_NEW_TAB_PAGE_BRUSH_ICON_SVG" @@ -64,4 +67,6 @@ file="untrusted/background_image.js" type="BINDATA" /> <include name="IDR_NEW_TAB_PAGE_ONE_GOOGLE_BAR_API_JS" file="one_google_bar_api.js" type="BINDATA" compress="false" /> + <include name="IDR_NEW_TAB_PAGE_PROMO_BROWSER_COMMAND_PROXY_JS" + file="promo_browser_command_proxy.js" type="BINDATA" compress="false" /> </grit-part>
diff --git a/chrome/browser/resources/new_tab_page/promo_browser_command_proxy.js b/chrome/browser/resources/new_tab_page/promo_browser_command_proxy.js new file mode 100644 index 0000000..2abad05 --- /dev/null +++ b/chrome/browser/resources/new_tab_page/promo_browser_command_proxy.js
@@ -0,0 +1,22 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import './promo_browser_command.mojom-lite.js'; + +import {addSingletonGetter} from 'chrome://resources/js/cr.m.js'; + +/** + * @fileoverview This file provides a class that exposes the Mojo handler + * interface used for sending the NTP promos browser commands to the browser and + * receiving the browser response. + */ + +export class PromoBrowserCommandProxy { + constructor() { + /** @type {!promoBrowserCommand.mojom.CommandHandlerRemote} */ + this.handler = promoBrowserCommand.mojom.CommandHandler.getRemote(); + } +} + +addSingletonGetter(PromoBrowserCommandProxy);
diff --git a/chrome/browser/resources/new_tab_page/untrusted/promo.js b/chrome/browser/resources/new_tab_page/untrusted/promo.js index 454df0f2..2faa373 100644 --- a/chrome/browser/resources/new_tab_page/untrusted/promo.js +++ b/chrome/browser/resources/new_tab_page/untrusted/promo.js
@@ -16,7 +16,27 @@ if (el.target !== '_blank') { el.target = '_top'; } - el.addEventListener('click', () => { + el.addEventListener('click', (event) => { + const browserCommandFound = + event.target.getAttribute('href').match(/^command:([1-9][0-9]*)$/); + if (browserCommandFound) { + event.preventDefault(); // Prevent navigation attempt. + window.parent.postMessage( + { + frameType: 'promo', + messageType: 'execute-browser-command', + commandId: parseInt(browserCommandFound[1], 10), + clickInfo: { + middleButton: event.button === 1, + altKey: event.altKey, + ctrlKey: event.ctrlKey, + metaKey: event.metaKey, + shiftKey: event.shiftKey + } + }, + 'chrome://new-tab-page'); + } + window.parent.postMessage( {frameType: 'promo', messageType: 'link-clicked'}, 'chrome://new-tab-page');
diff --git a/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html b/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html index a10ab1e0..1f99140 100644 --- a/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html +++ b/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html
@@ -5,8 +5,8 @@ #waiting-spinner { flex-shrink: 0; - height: 20px; - width: 20px; + height: 2.0em; + width: 2.0em; } /* Apply a fixed height to the <svg> tag inside #powered-by-logo.
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn index 814ccfa..6f3187a 100644 --- a/chrome/browser/resources/settings/chromeos/BUILD.gn +++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -253,7 +253,8 @@ "bluetooth_page:closure_compile_module", #"crostini_page:closure_compile_module", - #"date_time_page:closure_compile_module", + "date_time_page:closure_compile_module", + #"device_page:closure_compile_module", #"google_assistant_page:closure_compile_module", #"internet_page:closure_compile_module",
diff --git a/chrome/browser/resources/settings/chromeos/date_time_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/date_time_page/BUILD.gn index 3bfa5759..e6e792803 100644 --- a/chrome/browser/resources/settings/chromeos/date_time_page/BUILD.gn +++ b/chrome/browser/resources/settings/chromeos/date_time_page/BUILD.gn
@@ -3,6 +3,7 @@ # found in the LICENSE file. import("//third_party/closure_compiler/compile_js.gni") +import("../os_settings.gni") js_type_check("closure_compile") { deps = [ @@ -39,7 +40,6 @@ js_library("timezone_selector") { deps = [ - ":date_time_types", "../../controls:settings_dropdown_menu", "../../prefs:prefs_behavior", "//ui/webui/resources/js:cr", @@ -57,17 +57,18 @@ ] } -# TODO: Uncomment as the Polymer3 migration makes progress. -#js_type_check("closure_compile_module") { -# is_polymer3 = true -# deps = [ -# ":date_time_page.m", -# ":date_time_types.m", -# ":timezone_browser_proxy.m", -# ":timezone_selector.m", -# ":timezone_subpage.m" -# ] -#} +js_type_check("closure_compile_module") { + is_polymer3 = true + deps = [ + # ":date_time_page.m", + # ":date_time_types.m", + + # ":timezone_browser_proxy.m", + ":timezone_selector.m", + + # ":timezone_subpage.m" + ] +} js_library("date_time_page.m") { sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/date_time_page/date_time_page.m.js" ] @@ -96,7 +97,11 @@ js_library("timezone_selector.m") { sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/date_time_page/timezone_selector.m.js" ] deps = [ - # TODO: Fill those in. + "../../controls:settings_dropdown_menu.m", + "../../prefs:prefs_behavior.m", + "../../prefs:prefs_types.m", + "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", + "//ui/webui/resources/js:load_time_data.m", ] extra_deps = [ ":timezone_selector_module" ] } @@ -137,6 +142,9 @@ js_file = "timezone_selector.js" html_file = "timezone_selector.html" html_type = "dom-module" + migrated_imports = os_settings_migrated_imports + namespace_rewrites = os_settings_namespace_rewrites + auto_imports = os_settings_auto_imports } polymer_modulizer("timezone_subpage") {
diff --git a/chrome/browser/resources/settings/chromeos/date_time_page/date_time_types.js b/chrome/browser/resources/settings/chromeos/date_time_page/date_time_types.js index 87b2d761..c6e060c4 100644 --- a/chrome/browser/resources/settings/chromeos/date_time_page/date_time_types.js +++ b/chrome/browser/resources/settings/chromeos/date_time_page/date_time_types.js
@@ -4,17 +4,6 @@ cr.define('settings', function() { /** - * Describes the effective policy restriction on time zone automatic - * detection. - * @enum {number} - */ - const TimeZoneAutoDetectPolicyRestriction = { - NONE: 0, - FORCED_ON: 1, - FORCED_OFF: 2, - }; - - /** * Describes values of * prefs.generated.resolve_timezone_by_geolocation_method_short. Must be kept * in sync with TimeZoneResolverManager::TimeZoneResolveMethod enum. @@ -28,5 +17,5 @@ }; // #cr_define_end - return {TimeZoneAutoDetectPolicyRestriction, TimeZoneAutoDetectMethod}; + return {TimeZoneAutoDetectMethod}; });
diff --git a/chrome/browser/resources/settings/chromeos/date_time_page/timezone_selector.html b/chrome/browser/resources/settings/chromeos/date_time_page/timezone_selector.html index e48bc3a0..3dc2133 100644 --- a/chrome/browser/resources/settings/chromeos/date_time_page/timezone_selector.html +++ b/chrome/browser/resources/settings/chromeos/date_time_page/timezone_selector.html
@@ -1,11 +1,12 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="../../controls/settings_dropdown_menu.html"> <link rel="import" href="../../i18n_setup.html"> <link rel="import" href="../../prefs/prefs_behavior.html"> +<link rel="import" href="../../prefs/prefs_types.html"> <link rel="import" href="../../settings_shared_css.html"> -<link rel="import" href="date_time_types.html"> <dom-module id="timezone-selector"> <template>
diff --git a/chrome/browser/resources/settings/chromeos/date_time_page/timezone_selector.js b/chrome/browser/resources/settings/chromeos/date_time_page/timezone_selector.js index 6a8bbe1..09a74deb 100644 --- a/chrome/browser/resources/settings/chromeos/date_time_page/timezone_selector.js +++ b/chrome/browser/resources/settings/chromeos/date_time_page/timezone_selector.js
@@ -5,13 +5,11 @@ /** * @fileoverview 'timezone-selector' is the time zone selector dropdown. */ -(function() { -'use strict'; Polymer({ is: 'timezone-selector', - behaviors: [I18nBehavior, PrefsBehavior], + behaviors: [PrefsBehavior], properties: { /** @@ -179,4 +177,3 @@ prefResolveOnOffValue; }, }); -})();
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.gni b/chrome/browser/resources/settings/chromeos/os_settings.gni index 2350de3..0b07244 100644 --- a/chrome/browser/resources/settings/chromeos/os_settings.gni +++ b/chrome/browser/resources/settings/chromeos/os_settings.gni
@@ -7,44 +7,43 @@ import("//ui/webui/resources/cr_elements/chromeos/os_cr_elements.gni") import("../settings.gni") -os_settings_namespace_rewrites = - settings_namespace_rewrites + cr_components_chromeos_namespace_rewrites + - cr_elements_chromeos_namespace_rewrites + - [ - "parental_controls.ParentalControlsBrowserProxy|ParentalControlsBrowserProxy", - "settings.AccountManagerBrowserProxy|AccountManagerBrowserProxy", - "settings.AmbientModeBrowserProxy|AmbientModeBrowserProxy", - "settings.ChangePictureBrowserProxy|ChangePictureBrowserProxy", - "settings.DefaultImage|DefaultImage", - "settings.KerberosAccountsBrowserProxy|KerberosAccountsBrowserProxy", - "settings.KerberosErrorType|KerberosErrorType", - "settings.KerberosConfigErrorCode|KerberosConfigErrorCode", - "settings.MultiDeviceBrowserProxy|MultiDeviceBrowserProxy", - "settings.MultiDeviceSettingsMode|MultiDeviceSettingsMode", - "settings.MultiDeviceFeature|MultiDeviceFeature", - "settings.MultiDeviceFeatureState|MultiDeviceFeatureState", - "settings.MultiDevicePageContentData|MultiDevicePageContentData", - "settings.LanguagesMetricsProxy|LanguagesMetricsProxy", - "settings.LanguagesPageInteraction|LanguagesPageInteraction", - "settings.OsResetBrowserProxy|OsResetBrowserProxy", - "settings.RouteObserverBehavior|RouteObserverBehavior", - "settings.Route|Route", - "settings.routes|routes", - "settings.recordSettingChange|recordSettingChange", - "settings.SmartLockSignInEnabledState|SmartLockSignInEnabledState", - "settings.WallpaperBrowserProxy|WallpaperBrowserProxy", - "settings.FingerprintBrowserProxy|FingerprintBrowserProxy", - "settings.FingerprintResultType|FingerprintResultType", - "settings.recordLockScreenProgress|recordLockScreenProgress", - "settings.OsSyncBrowserProxy|OsSyncBrowserProxy", - "settings.FingerprintInfo|FingerprintInfo", - "settings.FingerprintSetupStep|FingerprintSetupStep", - "settings.FingerprintAttempt|FingerprintAttempt", - "settings.FingerprintScan|FingerprintScan", - "settings.KerberosAccount|KerberosAccount", - "settings.OsSyncPrefs|OsSyncPrefs", - "settings.ValidateKerberosConfigResult|ValidateKerberosConfigResult", - ] +os_settings_namespace_rewrites = settings_namespace_rewrites + + cr_components_chromeos_namespace_rewrites + + cr_elements_chromeos_namespace_rewrites + [ + "parental_controls.ParentalControlsBrowserProxy|ParentalControlsBrowserProxy", + "settings.AccountManagerBrowserProxy|AccountManagerBrowserProxy", + "settings.AmbientModeBrowserProxy|AmbientModeBrowserProxy", + "settings.ChangePictureBrowserProxy|ChangePictureBrowserProxy", + "settings.DefaultImage|DefaultImage", + "settings.KerberosAccountsBrowserProxy|KerberosAccountsBrowserProxy", + "settings.KerberosErrorType|KerberosErrorType", + "settings.KerberosConfigErrorCode|KerberosConfigErrorCode", + "settings.MultiDeviceBrowserProxy|MultiDeviceBrowserProxy", + "settings.MultiDeviceSettingsMode|MultiDeviceSettingsMode", + "settings.MultiDeviceFeature|MultiDeviceFeature", + "settings.MultiDeviceFeatureState|MultiDeviceFeatureState", + "settings.MultiDevicePageContentData|MultiDevicePageContentData", + "settings.LanguagesMetricsProxy|LanguagesMetricsProxy", + "settings.LanguagesPageInteraction|LanguagesPageInteraction", + "settings.OsResetBrowserProxy|OsResetBrowserProxy", + "settings.RouteObserverBehavior|RouteObserverBehavior", + "settings.Route|Route", + "settings.routes|routes", + "settings.recordSettingChange|recordSettingChange", + "settings.SmartLockSignInEnabledState|SmartLockSignInEnabledState", + "settings.WallpaperBrowserProxy|WallpaperBrowserProxy", + "settings.FingerprintBrowserProxy|FingerprintBrowserProxy", + "settings.FingerprintResultType|FingerprintResultType", + "settings.recordLockScreenProgress|recordLockScreenProgress", + "settings.OsSyncBrowserProxy|OsSyncBrowserProxy", + "settings.FingerprintInfo|FingerprintInfo", + "settings.FingerprintSetupStep|FingerprintSetupStep", + "settings.FingerprintAttempt|FingerprintAttempt", + "settings.FingerprintScan|FingerprintScan", + "settings.KerberosAccount|KerberosAccount", + "settings.OsSyncPrefs|OsSyncPrefs", + "settings.ValidateKerberosConfigResult|ValidateKerberosConfigResult", + ] os_settings_auto_imports = settings_auto_imports + cr_components_chromeos_auto_imports + @@ -68,6 +67,7 @@ "chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_browser_proxy.html|WallpaperBrowserProxy,WallpaperBrowserProxyImpl", "chrome/browser/resources/settings/chromeos/parental_controls_page/parental_controls_browser_proxy.html|ParentalControlsBrowserProxy,ParentalControlsBrowserProxyImpl", "chrome/browser/resources/settings/chromeos/route_origin_behavior.html|RouteOriginBehaviorImpl,RouteOriginBehavior", + "chrome/browser/resources/settings/controls/settings_dropdown_menu.html|DropdownMenuOptionList", "chrome/browser/resources/settings/lifetime_browser_proxy.html|LifetimeBrowserProxy,LifetimeBrowserProxyImpl", "chrome/browser/resources/settings/people_page/account_manager_browser_proxy.html|AccountManagerBrowserProxy,AccountManagerBrowserProxyImpl,Account", "chrome/browser/resources/settings/people_page/profile_info_browser_proxy.html|ProfileInfoBrowserProxyImpl,ProfileInfoBrowserProxy,ProfileInfo",
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.js b/chrome/browser/resources/settings/chromeos/os_settings.js index 9804189..070cb54 100644 --- a/chrome/browser/resources/settings/chromeos/os_settings.js +++ b/chrome/browser/resources/settings/chromeos/os_settings.js
@@ -19,6 +19,7 @@ import './parental_controls_page/parental_controls_page.m.js'; import './os_people_page/os_people_page.m.js'; import './os_privacy_page/os_privacy_page.m.js'; +import './date_time_page/timezone_selector.m.js'; export {LifetimeBrowserProxy, LifetimeBrowserProxyImpl} from '../lifetime_browser_proxy.m.js'; export {dataUsageStringToEnum, NearbyShareDataUsage} from '../nearby_share_page/types.m.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_resources_v3.grdp b/chrome/browser/resources/settings/chromeos/os_settings_resources_v3.grdp index 8d1583645..c020c33 100644 --- a/chrome/browser/resources/settings/chromeos/os_settings_resources_v3.grdp +++ b/chrome/browser/resources/settings/chromeos/os_settings_resources_v3.grdp
@@ -62,6 +62,11 @@ use_base_dir="false" compress="false" type="BINDATA" /> + <include name="IDR_OS_SETTINGS_SETTINGS_DROPDOWN_MENU_M_JS" + file="${root_gen_dir}/chrome/browser/resources/settings/controls/settings_dropdown_menu.m.js" + use_base_dir="false" + compress="false" + type="BINDATA" /> <include name="IDR_OS_SETTINGS_PEOPLE_PAGE_ACCOUNT_MANAGER_BROWSER_PROXY_M_JS" file="${root_gen_dir}/chrome/browser/resources/settings/people_page/account_manager_browser_proxy.m.js" use_base_dir="false" @@ -455,4 +460,14 @@ file="chromeos/os_settings.js" compress="false" type="BINDATA" /> + <include name="IDR_OS_SETTINGS_SETTINGS_DATE_TIME_PAGE_DATE_TIME_TYPES_M_JS" + file="${root_gen_dir}/chrome/browser/resources/settings/chromeos/date_time_page/date_time_types.m.js" + use_base_dir="false" + compress="false" + type="BINDATA" /> + <include name="IDR_OS_SETTINGS_SETTINGS_DATE_TIME_PAGE_TIMEZONE_SELECTOR_M_JS" + file="${root_gen_dir}/chrome/browser/resources/settings/chromeos/date_time_page/timezone_selector.m.js" + use_base_dir="false" + compress="false" + type="BINDATA" /> </grit-part>
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc index 11a7030..3327ac76 100644 --- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc +++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
@@ -303,7 +303,7 @@ request->deep_scanning_request()); } else { WebUIInfoSingleton::GetInstance()->AddToDeepScanRequests( - request->content_analysis_request()); + request->tab_url(), request->content_analysis_request()); } // |request| might have been deleted by the call to Start() in tests, so don't @@ -471,7 +471,7 @@ // We add the request here in case we never actually uploaded anything, so it // wasn't added in OnGetRequestData WebUIInfoSingleton::GetInstance()->AddToDeepScanRequests( - request->content_analysis_request()); + request->tab_url(), request->content_analysis_request()); WebUIInfoSingleton::GetInstance()->AddToDeepScanResponses( active_tokens_[request], ResultToString(result), response); @@ -608,6 +608,16 @@ BinaryUploadService::Request::~Request() = default; +void BinaryUploadService::Request::set_tab_url(const GURL& tab_url) { + DCHECK(!use_legacy_proto_); + tab_url_ = tab_url; +} + +const GURL& BinaryUploadService::Request::tab_url() const { + DCHECK(!use_legacy_proto_); + return tab_url_; +} + void BinaryUploadService::Request::set_request_dlp_scan( DlpDeepScanningClientRequest dlp_request) { DCHECK(use_legacy_proto_);
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h index 09e5a2d..e0eb274e 100644 --- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h +++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
@@ -148,6 +148,9 @@ bool use_legacy_proto() const { return use_legacy_proto_; } + void set_tab_url(const GURL& tab_url); + const GURL& tab_url() const; + // Methods for modifying the DeepScanningClientRequest. void set_request_dlp_scan(DlpDeepScanningClientRequest dlp_request); void set_request_malware_scan( @@ -198,6 +201,8 @@ ContentAnalysisCallback content_analysis_callback_; GURL url_; + // The URL of the page that initially triggered the scan. + GURL tab_url_; }; // Upload the given file contents for deep scanning if the browser is
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc index 098f465..4c7bb9b 100644 --- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc +++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
@@ -786,6 +786,7 @@ request->set_analysis_connector(connector); request->set_email(GetProfileEmail(profile)); request->set_url(data_.url.spec()); + request->set_tab_url(data_.url); for (const std::string& tag : data_.settings.tags) request->add_tag(tag); }
diff --git a/chrome/browser/safe_browsing/download_protection/deep_scanning_request.cc b/chrome/browser/safe_browsing/download_protection/deep_scanning_request.cc index a140bbf..cbbd33b 100644 --- a/chrome/browser/safe_browsing/download_protection/deep_scanning_request.cc +++ b/chrome/browser/safe_browsing/download_protection/deep_scanning_request.cc
@@ -351,6 +351,9 @@ if (item_->GetURL().is_valid()) request->set_url(item_->GetURL().spec()); + if (item_->GetTabUrl().is_valid()) + request->set_tab_url(item_->GetTabUrl()); + for (const std::string& tag : analysis_settings_.tags) request->add_tag(tag); }
diff --git a/chrome/browser/sharesheet/sharesheet_service.cc b/chrome/browser/sharesheet/sharesheet_service.cc index c185a9b..72884e4f 100644 --- a/chrome/browser/sharesheet/sharesheet_service.cc +++ b/chrome/browser/sharesheet/sharesheet_service.cc
@@ -42,24 +42,19 @@ auto sharesheet_service_delegate = std::make_unique<SharesheetServiceDelegate>( delegate_counter_++, std::move(bubble_anchor_view), this); + ShowBubbleWithDelegate(std::move(sharesheet_service_delegate), + std::move(intent)); +} - std::vector<TargetInfo> targets; - auto& actions = sharesheet_action_cache_->GetShareActions(); - auto iter = actions.begin(); - while (iter != actions.end()) { - targets.emplace(targets.begin(), TargetType::kAction, - (*iter)->GetActionIcon(), (*iter)->GetActionName(), - (*iter)->GetActionName()); - ++iter; - } - - std::vector<apps::AppIdAndActivityName> app_id_and_activities = - app_service_proxy_->GetAppsForIntent(intent); - LoadAppIcons(std::move(app_id_and_activities), std::move(targets), 0, - base::BindOnce(&SharesheetService::OnAppIconsLoaded, - weak_factory_.GetWeakPtr(), - std::move(sharesheet_service_delegate), - std::move(intent))); +void SharesheetService::ShowBubble(content::WebContents* web_contents, + apps::mojom::IntentPtr intent) { + DCHECK(intent->action == apps_util::kIntentActionSend || + intent->action == apps_util::kIntentActionSendMultiple); + auto sharesheet_service_delegate = + std::make_unique<SharesheetServiceDelegate>(delegate_counter_++, + web_contents, this); + ShowBubbleWithDelegate(std::move(sharesheet_service_delegate), + std::move(intent)); } // Cleanup delegate when bubble closes. @@ -121,7 +116,7 @@ return nullptr; } -bool SharesheetService::HasShareTargets(apps::mojom::IntentPtr intent) { +bool SharesheetService::HasShareTargets(const apps::mojom::IntentPtr& intent) { auto& actions = sharesheet_action_cache_->GetShareActions(); std::vector<apps::AppIdAndActivityName> app_id_and_activities = app_service_proxy_->GetAppsForIntent(intent); @@ -175,8 +170,28 @@ apps::mojom::IntentPtr intent, std::vector<TargetInfo> targets) { delegate->ShowBubble(std::move(targets), std::move(intent)); - active_delegates_.push_back(std::move(delegate)); } +void SharesheetService::ShowBubbleWithDelegate( + std::unique_ptr<SharesheetServiceDelegate> delegate, + apps::mojom::IntentPtr intent) { + std::vector<TargetInfo> targets; + auto& actions = sharesheet_action_cache_->GetShareActions(); + auto iter = actions.begin(); + while (iter != actions.end()) { + targets.emplace(targets.begin(), TargetType::kAction, + (*iter)->GetActionIcon(), (*iter)->GetActionName(), + (*iter)->GetActionName()); + ++iter; + } + + std::vector<apps::AppIdAndActivityName> app_id_and_activities = + app_service_proxy_->GetAppsForIntent(intent); + LoadAppIcons(std::move(app_id_and_activities), std::move(targets), 0, + base::BindOnce(&SharesheetService::OnAppIconsLoaded, + weak_factory_.GetWeakPtr(), std::move(delegate), + std::move(intent))); +} + } // namespace sharesheet
diff --git a/chrome/browser/sharesheet/sharesheet_service.h b/chrome/browser/sharesheet/sharesheet_service.h index 829ac36..d4abbba 100644 --- a/chrome/browser/sharesheet/sharesheet_service.h +++ b/chrome/browser/sharesheet/sharesheet_service.h
@@ -27,6 +27,10 @@ class View; } +namespace content { +class WebContents; +} + namespace sharesheet { class SharesheetServiceDelegate; @@ -41,8 +45,13 @@ SharesheetService(const SharesheetService&) = delete; SharesheetService& operator=(const SharesheetService&) = delete; + // Displays the dialog (aka bubble) for sharing content (or files) with + // other applications and targets. |intent| contains the list of the + // files/content to be shared. void ShowBubble(views::View* bubble_anchor_view, apps::mojom::IntentPtr intent); + void ShowBubble(content::WebContents* web_contents, + apps::mojom::IntentPtr intent); void OnBubbleClosed(uint32_t id, const base::string16& active_action); void OnTargetSelected(uint32_t delegate_id, const base::string16& target_name, @@ -50,7 +59,7 @@ apps::mojom::IntentPtr intent, views::View* share_action_view); SharesheetServiceDelegate* GetDelegate(uint32_t delegate_id); - bool HasShareTargets(apps::mojom::IntentPtr intent); + bool HasShareTargets(const apps::mojom::IntentPtr& intent); private: using SharesheetServiceIconLoaderCallback = @@ -73,6 +82,10 @@ apps::mojom::IntentPtr intent, std::vector<TargetInfo> targets); + void ShowBubbleWithDelegate( + std::unique_ptr<SharesheetServiceDelegate> delegate, + apps::mojom::IntentPtr intent); + uint32_t delegate_counter_ = 0; std::unique_ptr<SharesheetActionCache> sharesheet_action_cache_; apps::AppServiceProxy* app_service_proxy_;
diff --git a/chrome/browser/sharesheet/sharesheet_service_delegate.cc b/chrome/browser/sharesheet/sharesheet_service_delegate.cc index be53dd7..1d7a34a 100644 --- a/chrome/browser/sharesheet/sharesheet_service_delegate.cc +++ b/chrome/browser/sharesheet/sharesheet_service_delegate.cc
@@ -11,6 +11,7 @@ #include "chrome/browser/sharesheet/sharesheet_service.h" #include "chrome/browser/sharesheet/sharesheet_service_factory.h" #include "chrome/browser/ui/views/sharesheet_bubble_view.h" +#include "content/public/browser/web_contents.h" #include "ui/views/view.h" namespace sharesheet { @@ -23,6 +24,14 @@ sharesheet_bubble_view_( std::make_unique<SharesheetBubbleView>(bubble_anchor_view, this)), sharesheet_service_(sharesheet_service) {} +SharesheetServiceDelegate::SharesheetServiceDelegate( + uint32_t id, + content::WebContents* web_contents, + SharesheetService* sharesheet_service) + : id_(id), + sharesheet_bubble_view_( + std::make_unique<SharesheetBubbleView>(web_contents, this)), + sharesheet_service_(sharesheet_service) {} SharesheetServiceDelegate::~SharesheetServiceDelegate() = default;
diff --git a/chrome/browser/sharesheet/sharesheet_service_delegate.h b/chrome/browser/sharesheet/sharesheet_service_delegate.h index a4b86c1..7715c11 100644 --- a/chrome/browser/sharesheet/sharesheet_service_delegate.h +++ b/chrome/browser/sharesheet/sharesheet_service_delegate.h
@@ -18,6 +18,10 @@ class View; } +namespace content { +class WebContents; +} + namespace sharesheet { class SharesheetService; @@ -26,9 +30,12 @@ // business logic in the sharesheet. class SharesheetServiceDelegate : public SharesheetController { public: - explicit SharesheetServiceDelegate(uint32_t id, - views::View* bubble_anchor_view, - SharesheetService* sharesheet_service); + SharesheetServiceDelegate(uint32_t id, + views::View* bubble_anchor_view, + SharesheetService* sharesheet_service); + SharesheetServiceDelegate(uint32_t id, + content::WebContents* web_contents, + SharesheetService* sharesheet_service); ~SharesheetServiceDelegate() override; SharesheetServiceDelegate(const SharesheetServiceDelegate&) = delete; SharesheetServiceDelegate& operator=(const SharesheetServiceDelegate&) =
diff --git a/chrome/browser/sharing/webrtc/sharing_service_host.cc b/chrome/browser/sharing/webrtc/sharing_service_host.cc index abee3a43..aed679f8 100644 --- a/chrome/browser/sharing/webrtc/sharing_service_host.cc +++ b/chrome/browser/sharing/webrtc/sharing_service_host.cc
@@ -148,7 +148,7 @@ } // namespace struct SharingWebRtcMojoPipes { - MojoPipe<sharing::mojom::SignallingSender> signalling_sender; + MojoPipe<sharing::mojom::SignalingSender> signaling_sender; MojoPipe<sharing::mojom::SignallingReceiver> signalling_receiver; MojoPipe<sharing::mojom::SharingWebRtcConnectionDelegate> delegate; MojoPipe<sharing::mojom::SharingWebRtcConnection> connection; @@ -266,7 +266,7 @@ auto pipes = std::make_unique<SharingWebRtcMojoPipes>(); auto signalling_host = std::make_unique<WebRtcSignallingHostFCM>( - std::move(pipes->signalling_sender.receiver), + std::move(pipes->signaling_sender.receiver), std::move(pipes->signalling_receiver.remote), message_sender_, CreateDeviceInfo(device_guid, fcm_configuration, device_source_)); @@ -312,7 +312,7 @@ } sharing_utility_service_->CreateSharingWebRtcConnection( - std::move(pipes->signalling_sender.remote), + std::move(pipes->signaling_sender.remote), std::move(pipes->signalling_receiver.receiver), std::move(pipes->delegate.remote), std::move(pipes->connection.receiver), std::move(pipes->socket_manager.remote),
diff --git a/chrome/browser/sharing/webrtc/sharing_service_host_unittest.cc b/chrome/browser/sharing/webrtc/sharing_service_host_unittest.cc index 01c5a73f..be23cc3 100644 --- a/chrome/browser/sharing/webrtc/sharing_service_host_unittest.cc +++ b/chrome/browser/sharing/webrtc/sharing_service_host_unittest.cc
@@ -38,7 +38,7 @@ // sharing::mojom::Sharing: void CreateSharingWebRtcConnection( - mojo::PendingRemote<sharing::mojom::SignallingSender> signalling_sender, + mojo::PendingRemote<sharing::mojom::SignalingSender> signaling_sender, mojo::PendingReceiver<sharing::mojom::SignallingReceiver> signalling_receiver, mojo::PendingRemote<sharing::mojom::SharingWebRtcConnectionDelegate>
diff --git a/chrome/browser/sharing/webrtc/sharing_webrtc_connection_host_unittest.cc b/chrome/browser/sharing/webrtc/sharing_webrtc_connection_host_unittest.cc index 05f0cf1..f29611d 100644 --- a/chrome/browser/sharing/webrtc/sharing_webrtc_connection_host_unittest.cc +++ b/chrome/browser/sharing/webrtc/sharing_webrtc_connection_host_unittest.cc
@@ -52,7 +52,7 @@ public: MockSignallingHost() : WebRtcSignallingHostFCM( - mojo::PendingReceiver<sharing::mojom::SignallingSender>(), + mojo::PendingReceiver<sharing::mojom::SignalingSender>(), mojo::PendingRemote<sharing::mojom::SignallingReceiver>(), /*message_sender=*/nullptr, CreateFakeDeviceInfo("id", "name")) {}
diff --git a/chrome/browser/sharing/webrtc/webrtc_signalling_host_fcm.cc b/chrome/browser/sharing/webrtc/webrtc_signalling_host_fcm.cc index 2667fe5..b790dbb 100644 --- a/chrome/browser/sharing/webrtc/webrtc_signalling_host_fcm.cc +++ b/chrome/browser/sharing/webrtc/webrtc_signalling_host_fcm.cc
@@ -12,13 +12,13 @@ #include "chrome/browser/sharing/sharing_send_message_result.h" WebRtcSignallingHostFCM::WebRtcSignallingHostFCM( - mojo::PendingReceiver<sharing::mojom::SignallingSender> signalling_sender, + mojo::PendingReceiver<sharing::mojom::SignalingSender> signaling_sender, mojo::PendingRemote<sharing::mojom::SignallingReceiver> signalling_receiver, SharingMessageSender* message_sender, std::unique_ptr<syncer::DeviceInfo> device_info) : message_sender_(message_sender), device_info_(std::move(device_info)), - signalling_sender_(this, std::move(signalling_sender)), + signaling_sender_(this, std::move(signaling_sender)), signalling_receiver_(std::move(signalling_receiver)) { DCHECK(device_info_); }
diff --git a/chrome/browser/sharing/webrtc/webrtc_signalling_host_fcm.h b/chrome/browser/sharing/webrtc/webrtc_signalling_host_fcm.h index 6af4852..b1693d3 100644 --- a/chrome/browser/sharing/webrtc/webrtc_signalling_host_fcm.h +++ b/chrome/browser/sharing/webrtc/webrtc_signalling_host_fcm.h
@@ -19,12 +19,12 @@ #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote.h" -// Handles the signalling part of a WebRTC connection via FCM. The signalling +// Handles the signaling part of a WebRTC connection via FCM. The signaling // messages are sent via the |message_sender| to |device_info| through FCM. -class WebRtcSignallingHostFCM : public sharing::mojom::SignallingSender { +class WebRtcSignallingHostFCM : public sharing::mojom::SignalingSender { public: WebRtcSignallingHostFCM( - mojo::PendingReceiver<sharing::mojom::SignallingSender> signalling_sender, + mojo::PendingReceiver<sharing::mojom::SignalingSender> signaling_sender, mojo::PendingRemote<sharing::mojom::SignallingReceiver> signalling_receiver, SharingMessageSender* message_sender, @@ -33,7 +33,7 @@ WebRtcSignallingHostFCM& operator=(const WebRtcSignallingHostFCM&) = delete; ~WebRtcSignallingHostFCM() override; - // sharing::mojom::SignallingSender: + // sharing::mojom::SignalingSender: void SendOffer(const std::string& offer, SendOfferCallback callback) override; void SendIceCandidates( std::vector<sharing::mojom::IceCandidatePtr> ice_candidates) override; @@ -53,7 +53,7 @@ SharingMessageSender* message_sender_; std::unique_ptr<syncer::DeviceInfo> device_info_; - mojo::Receiver<sharing::mojom::SignallingSender> signalling_sender_; + mojo::Receiver<sharing::mojom::SignalingSender> signaling_sender_; mojo::Remote<sharing::mojom::SignallingReceiver> signalling_receiver_; base::WeakPtrFactory<WebRtcSignallingHostFCM> weak_ptr_factory_{this};
diff --git a/chrome/browser/sharing/webrtc/webrtc_signalling_host_fcm_unittest.cc b/chrome/browser/sharing/webrtc/webrtc_signalling_host_fcm_unittest.cc index 0d472849..e0ce580 100644 --- a/chrome/browser/sharing/webrtc/webrtc_signalling_host_fcm_unittest.cc +++ b/chrome/browser/sharing/webrtc/webrtc_signalling_host_fcm_unittest.cc
@@ -29,7 +29,7 @@ MOCK_METHOD1(OnIceCandidatesReceived, void(std::vector<sharing::mojom::IceCandidatePtr>)); - mojo::Remote<sharing::mojom::SignallingSender> signalling_sender; + mojo::Remote<sharing::mojom::SignalingSender> signaling_sender; mojo::Receiver<sharing::mojom::SignallingReceiver> signalling_receiver{this}; }; @@ -73,7 +73,7 @@ MockSharingMessageSender message_sender_; MockSignallingService signalling_service_; WebRtcSignallingHostFCM signalling_host_{ - signalling_service_.signalling_sender.BindNewPipeAndPassReceiver(), + signalling_service_.signaling_sender.BindNewPipeAndPassReceiver(), signalling_service_.signalling_receiver.BindNewPipeAndPassRemote(), &message_sender_, CreateFakeDeviceInfo("id", "name")};
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 38980e3..4a9044dd 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -1488,7 +1488,10 @@ "//chrome/app/vector_icons", "//chrome/browser:theme_properties", "//chrome/browser/media/router", + "//chrome/browser/nearby_sharing/certificates", + "//chrome/browser/nearby_sharing/client", "//chrome/browser/nearby_sharing/contacts", + "//chrome/browser/nearby_sharing/local_device_data", "//chrome/browser/nearby_sharing/logging", "//chrome/browser/nearby_sharing/logging:util", "//chrome/browser/nearby_sharing/proto",
diff --git a/chrome/browser/ui/android/appmenu/OWNERS b/chrome/browser/ui/android/appmenu/OWNERS index 3feaf49..8e0277c4 100644 --- a/chrome/browser/ui/android/appmenu/OWNERS +++ b/chrome/browser/ui/android/appmenu/OWNERS
@@ -1,3 +1,5 @@ twellington@chromium.org -# COMPONENT: UI>Browser>Mobile +# COMPONENT: UI>Browser>Mobile>AppMenu +# TEAM: chrome-android-app@chromium.org +# OS: Android
diff --git a/chrome/browser/ui/ash/clipboard_history_browsertest.cc b/chrome/browser/ui/ash/clipboard_history_browsertest.cc index 676b80c..d49ed18c 100644 --- a/chrome/browser/ui/ash/clipboard_history_browsertest.cc +++ b/chrome/browser/ui/ash/clipboard_history_browsertest.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include <list> +#include <memory> #include "ash/clipboard/clipboard_history.h" #include "ash/clipboard/clipboard_history_controller.h" @@ -17,9 +18,22 @@ #include "ui/base/clipboard/scoped_clipboard_writer.h" #include "ui/events/test/event_generator.h" #include "ui/views/controls/menu/menu_config.h" +#include "ui/views/controls/textfield/textfield.h" +#include "ui/views/widget/widget.h" namespace { +std::unique_ptr<views::Widget> CreateTestWidget() { + auto widget = std::make_unique<views::Widget>(); + + views::Widget::InitParams params; + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; + widget->Init(std::move(params)); + + return widget; +} + void SetClipboardText(const std::string& text) { ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste) .WriteText(base::ASCIIToUTF16(text)); @@ -35,12 +49,11 @@ } const std::list<ui::ClipboardData>& GetClipboardData() { - return GetClipboardHistoryController()->clipboard_history()->GetItems(); + return GetClipboardHistoryController()->history()->GetItems(); } -gfx::Rect GetClipboardHistoryMenuScreenBounds() { - return GetClipboardHistoryController() - ->GetClipboardHistoryMenuBoundsForTest(); +gfx::Rect GetClipboardHistoryMenuBoundsInScreen() { + return GetClipboardHistoryController()->GetMenuBoundsInScreenForTest(); } class ClipboardTestHelper { @@ -55,11 +68,9 @@ ui::test::EventGenerator* event_generator() { return event_generator_.get(); } - void ShowContextMenuViaAccelerator() { - event_generator_->PressKey(ui::KeyboardCode::VKEY_COMMAND, ui::EF_NONE); - event_generator_->PressKey(ui::KeyboardCode::VKEY_V, ui::EF_COMMAND_DOWN); - event_generator_->ReleaseKey(ui::KeyboardCode::VKEY_V, ui::EF_COMMAND_DOWN); - event_generator_->ReleaseKey(ui::KeyboardCode::VKEY_COMMAND, ui::EF_NONE); + void PressAndRelease(ui::KeyboardCode key, int modifiers) { + event_generator_->PressKey(key, modifiers); + event_generator_->ReleaseKey(key, modifiers); } private: @@ -87,8 +98,12 @@ } protected: + void PressAndRelease(ui::KeyboardCode key, int modifiers = ui::EF_NONE) { + test_helper_->PressAndRelease(key, modifiers); + } + void ShowContextMenuViaAccelerator() { - test_helper_->ShowContextMenuViaAccelerator(); + PressAndRelease(ui::KeyboardCode::VKEY_V, ui::EF_COMMAND_DOWN); } void SetUpOnMainThread() override { @@ -183,9 +198,83 @@ // Verifies that the menu is anchored at the cursor's location. ASSERT_TRUE(GetClipboardHistoryController()->IsMenuShowing()); - const gfx::Point menu_origin = GetClipboardHistoryMenuScreenBounds().origin(); + const gfx::Point menu_origin = + GetClipboardHistoryMenuBoundsInScreen().origin(); EXPECT_EQ(mouse_location.x() + views::MenuConfig::instance().touchable_anchor_offset, menu_origin.x()); EXPECT_EQ(mouse_location.y(), menu_origin.y()); } + +IN_PROC_BROWSER_TEST_F(ClipboardHistoryWithMultiProfileBrowserTest, + ShouldPasteHistoryViaKeyboard) { + LoginUser(account_id1_); + CloseAllBrowsers(); + + // Create a widget containing a single, focusable textfield. + auto widget = CreateTestWidget(); + auto* textfield = + widget->SetContentsView(std::make_unique<views::Textfield>()); + textfield->SetAccessibleName(base::UTF8ToUTF16("Textfield")); + textfield->SetFocusBehavior(views::View::FocusBehavior::ALWAYS); + + // Show the widget. + widget->SetBounds(gfx::Rect(0, 0, 100, 100)); + widget->Show(); + EXPECT_TRUE(widget->IsActive()); + + // Focus the textfield and confirm initial state. + textfield->RequestFocus(); + EXPECT_TRUE(textfield->HasFocus()); + EXPECT_TRUE(textfield->GetText().empty()); + + // Write some things to the clipboard. + SetClipboardText("A"); + SetClipboardText("B"); + SetClipboardText("C"); + + // Verify we can paste the first history item via the ENTER key. + PressAndRelease(ui::KeyboardCode::VKEY_V, ui::EF_COMMAND_DOWN); + EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing()); + PressAndRelease(ui::KeyboardCode::VKEY_DOWN); + PressAndRelease(ui::KeyboardCode::VKEY_RETURN); + EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing()); + EXPECT_EQ("C", base::UTF16ToUTF8(textfield->GetText())); + + textfield->SetText(base::string16()); + EXPECT_TRUE(textfield->GetText().empty()); + + // Verify we can paste the first history item via the COMMAND+V shortcut. + PressAndRelease(ui::KeyboardCode::VKEY_V, ui::EF_COMMAND_DOWN); + EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing()); + PressAndRelease(ui::KeyboardCode::VKEY_DOWN); + PressAndRelease(ui::KeyboardCode::VKEY_V, ui::EF_COMMAND_DOWN); + EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing()); + EXPECT_EQ("C", base::UTF16ToUTF8(textfield->GetText())); + + textfield->SetText(base::string16()); + EXPECT_TRUE(textfield->GetText().empty()); + + // Verify we can paste the last history item via the ENTER key. + PressAndRelease(ui::KeyboardCode::VKEY_V, ui::EF_COMMAND_DOWN); + EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing()); + PressAndRelease(ui::KeyboardCode::VKEY_DOWN); + PressAndRelease(ui::KeyboardCode::VKEY_DOWN); + PressAndRelease(ui::KeyboardCode::VKEY_DOWN); + PressAndRelease(ui::KeyboardCode::VKEY_RETURN); + EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing()); + EXPECT_EQ("A", base::UTF16ToUTF8(textfield->GetText())); + + textfield->SetText(base::string16()); + EXPECT_TRUE(textfield->GetText().empty()); + + // Verify we can paste the last history item via the COMMAND+V shortcut. + PressAndRelease(ui::KeyboardCode::VKEY_V, ui::EF_COMMAND_DOWN); + EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing()); + PressAndRelease(ui::KeyboardCode::VKEY_DOWN); + PressAndRelease(ui::KeyboardCode::VKEY_DOWN); + PressAndRelease(ui::KeyboardCode::VKEY_DOWN); + PressAndRelease(ui::KeyboardCode::VKEY_V, ui::EF_COMMAND_DOWN); + EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing()); + EXPECT_EQ("A", base::UTF16ToUTF8(textfield->GetText())); +}
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc b/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc index 2a75d79..cb0c7e0 100644 --- a/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc +++ b/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc
@@ -17,10 +17,11 @@ #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/views/autofill/autofill_popup_view_utils.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" +#include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/browser/ui/views/frame/contents_web_view.h" #include "components/strings/grit/components_strings.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/platform/ax_platform_node.h" -#include "ui/base/buildflags.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/color_palette.h" #include "ui/native_theme/native_theme.h" @@ -117,7 +118,11 @@ } GetWidget()->GetRootView()->SetBorder(CreateBorder()); - DoUpdateBoundsAndRedrawPopup(); + bool enough_height = DoUpdateBoundsAndRedrawPopup(); + // If there is insufficient height, DoUpdateBoundsAndRedrawPopup() hides and + // thus deletes |this|. Hence, there is nothing else to do. + if (!enough_height) + return; GetWidget()->Show(); // Showing the widget can change native focus (which would result in an @@ -218,25 +223,31 @@ } gfx::Rect AutofillPopupBaseView::GetWindowBounds() const { -// The call to FindBrowserWithWindow will fail on Android, so we use -// platform-specific calls. -#if defined(OS_ANDROID) - return delegate()->container_view()->GetWindowAndroid()->bounds(); -#else views::Widget* widget = views::Widget::GetTopLevelWidgetForNativeView( delegate()->container_view()); if (widget) return widget->GetWindowBoundsInScreen(); - // If the browser is null, simply return an empty rect. The most common reason + // If the widget is null, simply return an empty rect. The most common reason // to end up here is that the NativeView has been destroyed externally, which // can happen at any time. This happens fairly commonly on Windows (e.g., at // shutdown) in particular. return gfx::Rect(); -#endif } -void AutofillPopupBaseView::DoUpdateBoundsAndRedrawPopup() { +gfx::Rect AutofillPopupBaseView::GetContentAreaBounds() const { + content::WebContents* web_contents = delegate()->GetWebContents(); + if (web_contents) + return web_contents->GetContainerBounds(); + + // If the |web_contents| is null, simply return an empty rect. The most common + // reason to end up here is that the |web_contents| has been destroyed + // externally, which can happen at any time. This happens fairly commonly on + // Windows (e.g., at shutdown) in particular. + return gfx::Rect(); +} + +bool AutofillPopupBaseView::DoUpdateBoundsAndRedrawPopup() { gfx::Size preferred_size = GetPreferredSize(); // When a bubble border is shown, the contents area (inside the shadow) is @@ -244,6 +255,16 @@ gfx::Rect element_bounds = gfx::ToEnclosingRect(delegate()->element_bounds()); element_bounds.Inset(/*horizontal=*/0, /*vertical=*/-kElementBorderPadding); + // At least one row of the popup should be shown in the bounds of the content + // area so that the user notices the presence of the popup. + int item_height = + children().size() > 0 ? children()[0]->GetPreferredSize().height() : 0; + if (!HasEnoughHeightForOneRow(item_height, GetContentAreaBounds(), + element_bounds)) { + HideController(PopupHidingReason::kInsufficientSpace); + return false; + } + gfx::Rect popup_bounds = CalculatePopupBounds( preferred_size, GetWindowBounds(), element_bounds, delegate()->IsRTL()); // Account for the scroll view's border so that the content has enough space. @@ -253,6 +274,7 @@ Layout(); UpdateClipPath(); SchedulePaint(); + return true; } void AutofillPopupBaseView::OnNativeFocusChanged(gfx::NativeView focused_now) {
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_base_view.h b/chrome/browser/ui/views/autofill/autofill_popup_base_view.h index 9d8116f..5876a6c4 100644 --- a/chrome/browser/ui/views/autofill/autofill_popup_base_view.h +++ b/chrome/browser/ui/views/autofill/autofill_popup_base_view.h
@@ -70,8 +70,13 @@ // Returns the bounds of the containing window in screen space. gfx::Rect GetWindowBounds() const; - // Update size of popup and paint (virtual for testing). - virtual void DoUpdateBoundsAndRedrawPopup(); + // Returns the bounds of the content area in screen space. + gfx::Rect GetContentAreaBounds() const; + + // Update size of popup and paint. If there is insufficient height to draw the + // popup, it hides and thus deletes |this| and returns false. (virtual for + // testing). + virtual bool DoUpdateBoundsAndRedrawPopup(); const AutofillPopupViewDelegate* delegate() const { return delegate_; }
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc index e75759e..4a9504e 100644 --- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc +++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
@@ -1211,7 +1211,7 @@ return width; } -void AutofillPopupViewNativeViews::DoUpdateBoundsAndRedrawPopup() { +bool AutofillPopupViewNativeViews::DoUpdateBoundsAndRedrawPopup() { gfx::Size preferred_size = CalculatePreferredSize(); gfx::Rect popup_bounds; @@ -1226,6 +1226,16 @@ // look too close to the element. element_bounds.Inset(/*horizontal=*/0, /*vertical=*/-kElementBorderPadding); + int item_height = + body_container_->children().size() > 0 + ? body_container_->children()[0]->GetPreferredSize().height() + : 0; + if (!HasEnoughHeightForOneRow(item_height, GetContentAreaBounds(), + element_bounds)) { + controller_->Hide(PopupHidingReason::kInsufficientSpace); + return false; + } + CalculatePopupYAndHeight(preferred_size.height(), window_bounds, element_bounds, &popup_bounds); @@ -1253,6 +1263,7 @@ UpdateClipPath(); SchedulePaint(); + return true; } // static
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h index 2a77c71..7176240 100644 --- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h +++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h
@@ -109,7 +109,7 @@ int AdjustWidth(int width) const; // AutofillPopupBaseView: - void DoUpdateBoundsAndRedrawPopup() override; + bool DoUpdateBoundsAndRedrawPopup() override; // Controller for this view. AutofillPopupController* controller_ = nullptr;
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_utils.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_utils.cc index 2011853..e95fe36 100644 --- a/chrome/browser/ui/views/autofill/autofill_popup_view_utils.cc +++ b/chrome/browser/ui/views/autofill/autofill_popup_view_utils.cc
@@ -86,3 +86,17 @@ return popup_bounds; } + +bool HasEnoughHeightForOneRow(int item_height, + const gfx::Rect& content_area_bounds, + const gfx::Rect& element_bounds) { + // Ensure that at least one row of the popup can be displayed within the + // bounds of the content area so that the user notices the presence of the + // popup. + bool enough_space_for_one_item_in_content_area_above_element = + element_bounds.y() - content_area_bounds.y() >= item_height; + bool enough_space_for_one_item_in_content_area_below_element = + content_area_bounds.bottom() - element_bounds.bottom() >= item_height; + return enough_space_for_one_item_in_content_area_above_element || + enough_space_for_one_item_in_content_area_below_element; +}
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_utils.h b/chrome/browser/ui/views/autofill/autofill_popup_view_utils.h index 88af63ce..990d67c 100644 --- a/chrome/browser/ui/views/autofill/autofill_popup_view_utils.h +++ b/chrome/browser/ui/views/autofill/autofill_popup_view_utils.h
@@ -33,4 +33,10 @@ const gfx::Rect& element_bounds, bool is_rtl); +// Returns whether there is enough height within |content_area_bounds| above or +// below |element_bounds| to display |item_height|. +bool HasEnoughHeightForOneRow(int item_height, + const gfx::Rect& content_area_bounds, + const gfx::Rect& element_bounds); + #endif // CHROME_BROWSER_UI_VIEWS_AUTOFILL_AUTOFILL_POPUP_VIEW_UTILS_H_
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_utils_unittest.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_utils_unittest.cc index 9fd284990..78fa8a09 100644 --- a/chrome/browser/ui/views/autofill/autofill_popup_view_utils_unittest.cc +++ b/chrome/browser/ui/views/autofill/autofill_popup_view_utils_unittest.cc
@@ -93,3 +93,29 @@ << "Popup bounds failed to match for rtl test " << i; } } + +TEST(AutofillPopupViewUtilsTest, NotEnoughHeightForAnItem) { + // In this test, each row of the popup has a height of 8 pixels, and there is + // no enough height in the content area to show one row. + // + // |---------------------| ---> y = 5 + // | Window | + // | |-----------------| | ---> y = 7 + // | | Content Area | | + // | | |-------------| | | ---> y = 8 + // | | | Element | | | + // |-|-|-------------|-|-| ---> y = 15 + + constexpr int item_height = 8; + constexpr int window_y = 5; + constexpr int x = 10; + constexpr int width = 5; + constexpr int height = 10; + + gfx::Rect content_area_bounds(x, window_y + 2, width, height - 2); + gfx::Rect element_bounds(x, window_y + 3, width, height - 3); + + bool enough_height_for_item = HasEnoughHeightForOneRow( + item_height, content_area_bounds, element_bounds); + EXPECT_FALSE(enough_height_for_item); +}
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc index a8b8b143..ccf243a 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
@@ -123,8 +123,7 @@ suggestion_view_->OnMatchUpdate(this, match_); keyword_view_->OnMatchUpdate(this, match_); - suggestion_tab_switch_button_->SetVisible( - match.ShouldShowTabMatchButtonInlineInResultView()); + suggestion_tab_switch_button_->SetVisible(ShouldShowTabMatchButtonInline()); UpdateRemoveSuggestionVisibility(); suggestion_view_->content()->SetText(match_.contents, match_.contents_class); @@ -242,7 +241,7 @@ // TODO(orinj): Eventually the deep digging in this class should get // replaced with a single local point of access to all selection state. ShowKeyword(popup_contents_view_->model()->selection().state == - OmniboxPopupModel::KEYWORD); + OmniboxPopupModel::KEYWORD_MODE); } else { ShowKeyword(false); } @@ -336,7 +335,7 @@ button_size.height()); } - if (match_.ShouldShowTabMatchButtonInlineInResultView()) { + if (ShouldShowTabMatchButtonInline()) { suggestion_tab_switch_button_->ProvideWidthHint(suggestion_width); const gfx::Size ts_button_size = suggestion_tab_switch_button_->GetPreferredSize(); @@ -516,6 +515,13 @@ ApplyThemeAndRefreshIcons(); } +bool OmniboxResultView::ShouldShowTabMatchButtonInline() { + return !OmniboxFieldTrial::IsSuggestionButtonRowEnabled() && + popup_contents_view_->model()->IsControlPresentOnMatch( + OmniboxPopupModel::Selection( + model_index_, OmniboxPopupModel::FOCUSED_BUTTON_TAB_SWITCH)); +} + void OmniboxResultView::UpdateRemoveSuggestionVisibility() { bool old_visibility = remove_suggestion_button_->GetVisible(); bool new_visibility =
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.h b/chrome/browser/ui/views/omnibox/omnibox_result_view.h index e09c1ba..5458d56 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_result_view.h +++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.h
@@ -115,6 +115,13 @@ // controls that are only visible on row hover. void UpdateHoverState(); + // This returns true if the match has a matching tab and will use a + // switch-to-tab button inline in Result View. It returns false, for + // example, when the switch button is not shown because a keyword match is + // taking precedence or when Suggestion Button Row is enabled, as the + // Switch-to-tab button will appear in the button row. + bool ShouldShowTabMatchButtonInline(); + // Sets the visibility of the |remove_suggestion_button_| based on the current // state. void UpdateRemoveSuggestionVisibility();
diff --git a/chrome/browser/ui/views/sharesheet_bubble_view.cc b/chrome/browser/ui/views/sharesheet_bubble_view.cc index d8a1912..7905e7f2 100644 --- a/chrome/browser/ui/views/sharesheet_bubble_view.cc +++ b/chrome/browser/ui/views/sharesheet_bubble_view.cc
@@ -10,8 +10,10 @@ #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/sharesheet/sharesheet_service_delegate.h" -#include "chrome/browser/ui/views/chrome_layout_provider.h" +#include "content/public/browser/web_contents.h" #include "third_party/skia/include/core/SkColor.h" +#include "ui/gfx/color_palette.h" +#include "ui/gfx/font_list.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/image/image_skia.h" @@ -23,16 +25,27 @@ #include "ui/views/controls/label.h" #include "ui/views/controls/styled_label.h" #include "ui/views/layout/box_layout.h" +#include "ui/views/layout/grid_layout.h" #include "ui/views/widget/widget.h" namespace { -constexpr int kButtonSize = 64; +// Sizes are in px. +constexpr int kButtonWidth = 92; +constexpr int kButtonHeight = 104; +constexpr int kButtonLineHeight = 20; +constexpr int kButtonPadding = 8; + constexpr int kCornerRadius = 12; -constexpr int kMaxTargetRowSize = 4; +constexpr int kMaxTargetsPerRow = 4; +constexpr int kBubbleWidth = 416; constexpr int kSpacing = 24; +constexpr int kTitleLineHeight = 24; constexpr char kTitle[] = "Share"; +constexpr SkColor kShareTitleColor = gfx::kGoogleGrey900; +constexpr SkColor kShareTargetTitleColor = gfx::kGoogleGrey700; + enum { COLUMN_SET_ID_TITLE, COLUMN_SET_ID_TARGETS }; } // namespace @@ -46,10 +59,13 @@ const base::string16& display_name, const gfx::ImageSkia* icon) : Button(listener) { - SetLayoutManager(std::make_unique<views::BoxLayout>( - views::BoxLayout::Orientation::kVertical, gfx::Insets(), - ChromeLayoutProvider::Get()->GetDistanceMetric( - views::DISTANCE_RELATED_CONTROL_VERTICAL))); + auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>( + views::BoxLayout::Orientation::kVertical, gfx::Insets(kButtonPadding), + kButtonPadding, true)); + layout->set_main_axis_alignment( + views::BoxLayout::MainAxisAlignment::kStart); + layout->set_cross_axis_alignment( + views::BoxLayout::CrossAxisAlignment::kCenter); auto* image = AddChildView(std::make_unique<views::ImageView>()); image->set_can_process_events_within_subtree(false); @@ -59,9 +75,13 @@ } auto* label = AddChildView(std::make_unique<views::Label>(display_name)); + label->SetFontList(gfx::FontList("Roboto, Medium, 14px")); + label->SetLineHeight(kButtonLineHeight); label->SetBackgroundColor(SK_ColorTRANSPARENT); - label->SetHandlesTooltips(false); - label->SetMultiLine(true); + label->SetEnabledColor(kShareTargetTitleColor); + label->SetHandlesTooltips(true); + label->SetTooltipText(display_name); + label->SetMultiLine(false); label->SetAutoColorReadabilityEnabled(false); label->SetHorizontalAlignment(gfx::ALIGN_CENTER); @@ -71,8 +91,9 @@ ShareSheetTargetButton(const ShareSheetTargetButton&) = delete; ShareSheetTargetButton& operator=(const ShareSheetTargetButton&) = delete; + // Button is 76px width x 88px height + 8px padding along all sides. gfx::Size CalculatePreferredSize() const override { - return gfx::Size(kButtonSize, kButtonSize); + return gfx::Size(kButtonWidth, kButtonHeight); } }; @@ -80,30 +101,18 @@ views::View* anchor_view, sharesheet::SharesheetServiceDelegate* delegate) : delegate_(delegate) { - SetButtons(ui::DIALOG_BUTTON_NONE); - SetAnchorView(anchor_view); + CreateBubble(); +} - SetLayoutManager(std::make_unique<views::BoxLayout>( - views::BoxLayout::Orientation::kVertical, gfx::Insets(), - ChromeLayoutProvider::Get()->GetDistanceMetric( - views::DISTANCE_RELATED_CONTROL_VERTICAL))); - - auto root_view = std::make_unique<views::View>(); - root_view->SetLayoutManager(std::make_unique<views::BoxLayout>( - views::BoxLayout::Orientation::kVertical, gfx::Insets(), - ChromeLayoutProvider::Get()->GetDistanceMetric( - views::DISTANCE_RELATED_CONTROL_VERTICAL))); - root_view_ = AddChildView(std::move(root_view)); - - auto main_view = std::make_unique<views::View>(); - main_view_ = root_view_->AddChildView(std::move(main_view)); - - auto share_action_view = std::make_unique<views::View>(); - share_action_view->SetLayoutManager(std::make_unique<views::BoxLayout>( - views::BoxLayout::Orientation::kVertical, gfx::Insets(), 0, true)); - share_action_view_ = root_view_->AddChildView(std::move(share_action_view)); - share_action_view_->SetVisible(false); +SharesheetBubbleView::SharesheetBubbleView( + content::WebContents* web_contents, + sharesheet::SharesheetServiceDelegate* delegate) + : delegate_(delegate) { + // TODO(crbug.com/1097623): Make the bubble located in the center of the + // invoke window. + set_parent_window(web_contents->GetNativeView()); + CreateBubble(); } SharesheetBubbleView::~SharesheetBubbleView() = default; @@ -119,44 +128,33 @@ // Set up columnsets views::ColumnSet* cs = main_layout->AddColumnSet(COLUMN_SET_ID_TITLE); cs->AddColumn(/* h_align */ views::GridLayout::FILL, - /* v_align */ views::GridLayout::CENTER, - /* resize_percent */ 1.0, + /* v_align */ views::GridLayout::LEADING, + /* resize_percent */ 0, views::GridLayout::ColumnSize::kUsePreferred, /* fixed_width */ 0, /*min_width*/ 0); views::ColumnSet* cs_buttons = main_layout->AddColumnSet(COLUMN_SET_ID_TARGETS); - cs_buttons->AddPaddingColumn(/* resize_percent */ 1, kSpacing); - cs_buttons->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, - 1.0, views::GridLayout::ColumnSize::kFixed, kButtonSize, - 0); - cs_buttons->AddPaddingColumn(/* resize_percent */ 1, kSpacing); - cs_buttons->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, - 1.0, views::GridLayout::ColumnSize::kFixed, kButtonSize, - 0); - cs_buttons->AddPaddingColumn(/* resize_percent */ 1, kSpacing); - cs_buttons->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, - 1.0, views::GridLayout::ColumnSize::kFixed, kButtonSize, - 0); - cs_buttons->AddPaddingColumn(/* resize_percent */ 1, kSpacing); - cs_buttons->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, - 1.0, views::GridLayout::ColumnSize::kFixed, kButtonSize, - 0); - cs_buttons->AddPaddingColumn(/* resize_percent */ 1, kSpacing); + for (int i = 0; i < kMaxTargetsPerRow; i++) { + cs_buttons->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, + 0, views::GridLayout::ColumnSize::kFixed, + kButtonWidth, 0); + } // Add Title label - main_layout->AddPaddingRow(views::GridLayout::kFixedSize, kSpacing); main_layout->StartRow(views::GridLayout::kFixedSize, COLUMN_SET_ID_TITLE, - /* height */ kSpacing); + kTitleLineHeight); auto* title = main_layout->AddView(std::make_unique<views::Label>( - base::UTF8ToUTF16(base::StringPiece(kTitle)), - views::style::CONTEXT_DIALOG_TITLE, views::style::STYLE_PRIMARY)); - title->SetHorizontalAlignment(gfx::ALIGN_CENTER); + base::UTF8ToUTF16(base::StringPiece(kTitle)))); + title->SetFontList(gfx::FontList("GoogleSans, Medium, 24px")); + title->SetLineHeight(kTitleLineHeight); + title->SetEnabledColor(kShareTitleColor); + title->SetHorizontalAlignment(gfx::ALIGN_LEFT); // Add Targets size_t i = 0; for (const auto& target : targets_) { - if (i % kMaxTargetRowSize == 0) { + if (i % kMaxTargetsPerRow == 0) { main_layout->AddPaddingRow(views::GridLayout::kFixedSize, kSpacing); main_layout->StartRow(views::GridLayout::kFixedSize, COLUMN_SET_ID_TARGETS); @@ -212,9 +210,27 @@ } gfx::Size SharesheetBubbleView::CalculatePreferredSize() const { - const int width = ChromeLayoutProvider::Get()->GetDistanceMetric( - DISTANCE_BUBBLE_PREFERRED_WIDTH) - - margins().width(); - gfx::Size size = gfx::Size(width, GetHeightForWidth(width)); - return size; + return gfx::Size(kBubbleWidth, GetHeightForWidth(kBubbleWidth)); +} + +void SharesheetBubbleView::CreateBubble() { + SetButtons(ui::DIALOG_BUTTON_NONE); + + SetLayoutManager(std::make_unique<views::BoxLayout>( + views::BoxLayout::Orientation::kVertical)); + + auto root_view = std::make_unique<views::View>(); + root_view->SetLayoutManager(std::make_unique<views::BoxLayout>( + views::BoxLayout::Orientation::kVertical, gfx::Insets(kSpacing), 0, + true)); + root_view_ = AddChildView(std::move(root_view)); + + auto main_view = std::make_unique<views::View>(); + main_view_ = root_view_->AddChildView(std::move(main_view)); + + auto share_action_view = std::make_unique<views::View>(); + share_action_view->SetLayoutManager(std::make_unique<views::BoxLayout>( + views::BoxLayout::Orientation::kVertical, gfx::Insets(), 0, true)); + share_action_view_ = root_view_->AddChildView(std::move(share_action_view)); + share_action_view_->SetVisible(false); }
diff --git a/chrome/browser/ui/views/sharesheet_bubble_view.h b/chrome/browser/ui/views/sharesheet_bubble_view.h index 53739be..a3f629f0 100644 --- a/chrome/browser/ui/views/sharesheet_bubble_view.h +++ b/chrome/browser/ui/views/sharesheet_bubble_view.h
@@ -16,6 +16,10 @@ class SharesheetServiceDelegate; } +namespace content { +class WebContents; +} + class SharesheetBubbleView : public views::BubbleDialogDelegateView, public views::ButtonListener { public: @@ -23,6 +27,8 @@ SharesheetBubbleView(views::View* anchor_view, sharesheet::SharesheetServiceDelegate* delegate); + SharesheetBubbleView(content::WebContents* web_contents, + sharesheet::SharesheetServiceDelegate* delegate); SharesheetBubbleView(const SharesheetBubbleView&) = delete; SharesheetBubbleView& operator=(const SharesheetBubbleView&) = delete; ~SharesheetBubbleView() override; @@ -44,6 +50,7 @@ void OnWidgetDestroyed(views::Widget* widget) override; private: + void CreateBubble(); // Owns this class. sharesheet::SharesheetServiceDelegate* delegate_; std::vector<TargetInfo> targets_;
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc index 1cefa66..914a788 100644 --- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc +++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -651,6 +651,14 @@ observer.Wait(); } + // Helper method to click the first tab. Used to ensure no additional widgets + // are in focus. For example, the tab editor bubble is automatically opened + // upon creating a new group. + void EnsureFocusToTabStrip(TabStrip* tab_strip) { + ASSERT_TRUE(PressInput(GetCenterInScreenCoordinates(tab_strip->tab_at(0)))); + ASSERT_TRUE(ReleaseInput()); + } + Browser* browser() const { return InProcessBrowserTest::browser(); } private: @@ -841,6 +849,7 @@ tab_groups::TabGroupId group1 = model->AddToNewGroup({0}); model->AddToNewGroup({2}); StopAnimating(tab_strip); + EnsureFocusToTabStrip(tab_strip); // Dragging the tab in the first index toward the tab in the zero-th index // switches the tabs and adds the dragged tab to the group. @@ -899,6 +908,7 @@ tab_groups::TabGroupId group1 = model->AddToNewGroup({3}); model->AddToNewGroup({1}); StopAnimating(tab_strip); + EnsureFocusToTabStrip(tab_strip); // Dragging the tab in the second index to the tab in the third index switches // the tabs and adds the dragged tab to the group. @@ -951,6 +961,7 @@ model->AddToNewGroup({2}); tab_groups::TabGroupId group4 = model->AddToNewGroup({3}); StopAnimating(tab_strip); + EnsureFocusToTabStrip(tab_strip); // Click the first tab and select third tab so both first and third tabs are // selected. @@ -1005,6 +1016,7 @@ tab_groups::TabGroupId group3 = model->AddToNewGroup({2}); model->AddToNewGroup({3}); StopAnimating(tab_strip); + EnsureFocusToTabStrip(tab_strip); // Click the second tab and select fourth tab so both second and fourth tabs // are selected. @@ -1117,6 +1129,7 @@ AddTabsAndResetBrowser(browser(), 3); tab_groups::TabGroupId group = model->AddToNewGroup({1, 2}); StopAnimating(tab_strip); + EnsureFocusToTabStrip(tab_strip); ASSERT_EQ(4, model->count()); ASSERT_EQ(2u, group_model->GetTabGroup(group)->ListTabs().size()); @@ -2130,6 +2143,7 @@ AddTabsAndResetBrowser(browser(), 3); tab_groups::TabGroupId group = model->AddToNewGroup({0, 1}); StopAnimating(tab_strip); + EnsureFocusToTabStrip(tab_strip); TabGroupHeader* group_header = tab_strip->group_header(group); EXPECT_FALSE(group_header->dragging()); @@ -2160,6 +2174,7 @@ AddTabsAndResetBrowser(browser(), 3); tab_groups::TabGroupId group = model->AddToNewGroup({2, 3}); StopAnimating(tab_strip); + EnsureFocusToTabStrip(tab_strip); TabGroupHeader* group_header = tab_strip->group_header(group); EXPECT_FALSE(group_header->dragging()); @@ -2217,14 +2232,13 @@ AddTabsAndResetBrowser(browser(), 1); tab_groups::TabGroupId group = model->AddToNewGroup({0}); StopAnimating(tab_strip); + EnsureFocusToTabStrip(tab_strip); DragGroupAndNotify(tab_strip, base::BindOnce(&PressEscapeWhileDetachedHeaderStep2, this), group); - TabGroupHeader* group_header = - GetTabStripForBrowser(browser())->group_header(group); - EXPECT_FALSE(group_header->dragging()); + EXPECT_FALSE(tab_strip->group_header(group)->dragging()); ASSERT_FALSE(tab_strip->GetDragContext()->IsDragSessionActive()); ASSERT_FALSE(TabDragController::IsActive()); @@ -2330,6 +2344,8 @@ AddTabsAndResetBrowser(browser(), 1); tab_groups::TabGroupId group = model->AddToNewGroup({0}); EXPECT_FALSE(model->IsGroupCollapsed(group)); + EnsureFocusToTabStrip(tab_strip); + tab_strip->controller()->ToggleTabGroupCollapsedState(group); StopAnimating(tab_strip); EXPECT_TRUE(model->IsGroupCollapsed(group));
diff --git a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view_browsertest.cc b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view_browsertest.cc index 17cd3c1..fac137c 100644 --- a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view_browsertest.cc +++ b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view_browsertest.cc
@@ -29,18 +29,6 @@ BrowserView* browser_view = static_cast<BrowserView*>(browser()->window()); TabGroupHeader* header = browser_view->tabstrip()->group_header(group); ASSERT_NE(nullptr, header); - ASSERT_FALSE(header->editor_bubble_tracker_.is_open()); - - ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED, gfx::PointF(), - gfx::PointF(), base::TimeTicks(), 0, 0); - header->OnMousePressed(pressed_event); - - ASSERT_FALSE(header->editor_bubble_tracker_.is_open()); - - ui::MouseEvent released_event(ui::ET_MOUSE_RELEASED, gfx::PointF(), - gfx::PointF(), base::TimeTicks(), 0, 0); - header->OnMouseReleased(released_event); - ASSERT_TRUE(header->editor_bubble_tracker_.is_open()); }
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc index 4a208dd..460b2537 100644 --- a/chrome/browser/ui/views/tabs/tab_strip.cc +++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -1341,6 +1341,13 @@ auto group_view = std::make_unique<TabGroupViews>(this, group); layout_helper_->InsertGroupHeader(group, group_view->header()); group_views_[group] = std::move(group_view); + + // The context menu relys on a Browser object which is not provided in + // TabStripTest. + if (this->controller()->GetBrowser()) { + group_views_[group]->header()->ShowContextMenuForViewImpl( + this, gfx::Point(), ui::MENU_SOURCE_NONE); + } } void TabStrip::OnGroupContentsChanged(const tab_groups::TabGroupId& group) {
diff --git a/chrome/browser/ui/webui/nearby_internals/nearby_internals_http_handler.cc b/chrome/browser/ui/webui/nearby_internals/nearby_internals_http_handler.cc index 1dc65012..c2d6bd2 100644 --- a/chrome/browser/ui/webui/nearby_internals/nearby_internals_http_handler.cc +++ b/chrome/browser/ui/webui/nearby_internals/nearby_internals_http_handler.cc
@@ -5,7 +5,17 @@ #include "chrome/browser/ui/webui/nearby_internals/nearby_internals_http_handler.h" #include "base/bind.h" +#include "base/json/json_writer.h" +#include "base/time/time.h" #include "base/values.h" +#include "chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager.h" +#include "chrome/browser/nearby_sharing/client/nearby_share_http_notifier.h" +#include "chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager.h" +#include "chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager.h" +#include "chrome/browser/nearby_sharing/logging/logging.h" +#include "chrome/browser/nearby_sharing/logging/proto_to_dictionary_conversion.h" +#include "chrome/browser/nearby_sharing/nearby_sharing_service.h" +#include "chrome/browser/nearby_sharing/nearby_sharing_service_factory.h" namespace { @@ -24,9 +34,44 @@ kResponse = 1 }; +std::string FormatAsJSON(const base::Value& value) { + std::string json; + base::JSONWriter::WriteWithOptions( + value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json); + return json; +} + +base::Value GetJavascriptTimestamp() { + return base::Value(base::Time::Now().ToJsTimeIgnoringNull()); +} + +// FireWebUIListener message to notify the JavaScript of HTTP message addition. +const char kHttpMessageAdded[] = "http-message-added"; + +// Keys in the JSON representation of a Http Message +const char kHttpMessageBodyKey[] = "body"; +const char kHttpMessageTimeKey[] = "time"; +const char kHttpMessageRpcKey[] = "rpc"; +const char kHttpMessageDirectionKey[] = "direction"; + +// Converts a RPC request/response to a raw dictionary value used as a +// JSON argument to JavaScript functions. +base::Value HttpMessageToDictionary(const base::Value& message, + Direction dir, + Rpc rpc) { + base::Value dictionary(base::Value::Type::DICTIONARY); + dictionary.SetStringKey(kHttpMessageBodyKey, FormatAsJSON(message)); + dictionary.SetKey(kHttpMessageTimeKey, GetJavascriptTimestamp()); + dictionary.SetIntKey(kHttpMessageRpcKey, static_cast<int>(rpc)); + dictionary.SetIntKey(kHttpMessageDirectionKey, static_cast<int>(dir)); + return dictionary; +} + } // namespace -NearbyInternalsHttpHandler::NearbyInternalsHttpHandler() = default; +NearbyInternalsHttpHandler::NearbyInternalsHttpHandler( + content::BrowserContext* context) + : context_(context) {} NearbyInternalsHttpHandler::~NearbyInternalsHttpHandler() = default; @@ -49,9 +94,19 @@ base::Unretained(this))); } -void NearbyInternalsHttpHandler::OnJavascriptAllowed() {} +void NearbyInternalsHttpHandler::OnJavascriptAllowed() { + NearbySharingService* service_ = + NearbySharingServiceFactory::GetForBrowserContext(context_); + if (service_) { + observer_.Add(service_->GetHttpNotifier()); + } else { + NS_LOG(ERROR) << "No NearbyShareService instance to call."; + } +} -void NearbyInternalsHttpHandler::OnJavascriptDisallowed() {} +void NearbyInternalsHttpHandler::OnJavascriptDisallowed() { + observer_.RemoveAll(); +} void NearbyInternalsHttpHandler::InitializeContents( const base::ListValue* args) { @@ -59,19 +114,96 @@ } void NearbyInternalsHttpHandler::UpdateDevice(const base::ListValue* args) { - // TODO(julietlevesque): Add functionality for Update Device call, which - // responds to javascript callback from chrome://nearby-internals HTTP - // Messages tab. + NearbySharingService* service_ = + NearbySharingServiceFactory::GetForBrowserContext(context_); + if (service_) { + service_->GetLocalDeviceDataManager()->DownloadDeviceData(); + } else { + NS_LOG(ERROR) << "No NearbyShareService instance to call."; + } } + void NearbyInternalsHttpHandler::ListPublicCertificates( const base::ListValue* args) { - // TODO(julietlevesque): Add functionality for List Public Certificates call, - // which responds to javascript callback from chrome://nearby-internals HTTP - // Messages tab. + NearbySharingService* service_ = + NearbySharingServiceFactory::GetForBrowserContext(context_); + if (service_) { + service_->GetCertificateManager()->DownloadPublicCertificates(); + } else { + NS_LOG(ERROR) << "No NearbyShareService instance to call."; + } } + void NearbyInternalsHttpHandler::ListContactPeople( const base::ListValue* args) { - // TODO(julietlevesque): Add functionality for List ContactPeople call, which - // responds to javascript callback from chrome://nearby-internals HTTP - // Messages tab. + NearbySharingService* service_ = + NearbySharingServiceFactory::GetForBrowserContext(context_); + if (service_) { + service_->GetContactManager()->DownloadContacts( + /*only_download_if_contacts_changed=*/false); + } else { + NS_LOG(ERROR) << "No NearbyShareService instance to call."; + } +} + +void NearbyInternalsHttpHandler::OnUpdateDeviceRequest( + const nearbyshare::proto::UpdateDeviceRequest& request) { + FireWebUIListener( + kHttpMessageAdded, + HttpMessageToDictionary(UpdateDeviceRequestToReadableDictionary(request), + Direction::kRequest, Rpc::kDevice)); +} + +void NearbyInternalsHttpHandler::OnUpdateDeviceResponse( + const nearbyshare::proto::UpdateDeviceResponse& response) { + FireWebUIListener(kHttpMessageAdded, + HttpMessageToDictionary( + UpdateDeviceResponseToReadableDictionary(response), + Direction::kResponse, Rpc::kDevice)); +} + +void NearbyInternalsHttpHandler::OnListContactPeopleRequest( + const nearbyshare::proto::ListContactPeopleRequest& request) { + FireWebUIListener(kHttpMessageAdded, + HttpMessageToDictionary( + ListContactPeopleRequestToReadableDictionary(request), + Direction::kRequest, Rpc::kContact)); +} + +void NearbyInternalsHttpHandler::OnListContactPeopleResponse( + const nearbyshare::proto::ListContactPeopleResponse& response) { + FireWebUIListener(kHttpMessageAdded, + HttpMessageToDictionary( + ListContactPeopleResponseToReadableDictionary(response), + Direction::kResponse, Rpc::kContact)); +} + +void NearbyInternalsHttpHandler::OnListPublicCertificatesRequest( + const nearbyshare::proto::ListPublicCertificatesRequest& request) { + FireWebUIListener( + kHttpMessageAdded, + HttpMessageToDictionary( + ListPublicCertificatesRequestToReadableDictionary(request), + Direction::kRequest, Rpc::kCertificate)); +} + +void NearbyInternalsHttpHandler::OnListPublicCertificatesResponse( + const nearbyshare::proto::ListPublicCertificatesResponse& response) { + FireWebUIListener( + kHttpMessageAdded, + HttpMessageToDictionary( + ListPublicCertificatesResponseToReadableDictionary(response), + Direction::kResponse, Rpc::kCertificate)); +} + +void NearbyInternalsHttpHandler::OnCheckContactsReachabilityRequest( + const nearbyshare::proto::CheckContactsReachabilityRequest& request) { + // No FireWebUIListener() because internal debug page does not support + // CheckContactsReachabilityRequest. +} + +void NearbyInternalsHttpHandler::OnCheckContactsReachabilityResponse( + const nearbyshare::proto::CheckContactsReachabilityResponse& response) { + // No FireWebUIListener() because internal debug page does not support + // CheckContactsReachabilityResponse. }
diff --git a/chrome/browser/ui/webui/nearby_internals/nearby_internals_http_handler.h b/chrome/browser/ui/webui/nearby_internals/nearby_internals_http_handler.h index a791f9e3..d892a937 100644 --- a/chrome/browser/ui/webui/nearby_internals/nearby_internals_http_handler.h +++ b/chrome/browser/ui/webui/nearby_internals/nearby_internals_http_handler.h
@@ -6,27 +6,59 @@ #define CHROME_BROWSER_UI_WEBUI_NEARBY_INTERNALS_NEARBY_INTERNALS_HTTP_HANDLER_H_ #include "base/memory/weak_ptr.h" +#include "base/scoped_observer.h" +#include "chrome/browser/nearby_sharing/client/nearby_share_http_notifier.h" +#include "chrome/browser/nearby_sharing/proto/certificate_rpc.pb.h" +#include "chrome/browser/nearby_sharing/proto/contact_rpc.pb.h" +#include "chrome/browser/nearby_sharing/proto/device_rpc.pb.h" #include "content/public/browser/web_ui_message_handler.h" namespace base { class ListValue; } // namespace base +namespace content { +class BrowserContext; +} // namespace content + // WebUIMessageHandler for HTTP Messages to pass messages to the // chrome://nearby-internals HTTP tab. -class NearbyInternalsHttpHandler : public content::WebUIMessageHandler { +class NearbyInternalsHttpHandler : public content::WebUIMessageHandler, + public NearbyShareHttpNotifier::Observer { public: - NearbyInternalsHttpHandler(); + explicit NearbyInternalsHttpHandler(content::BrowserContext* context); NearbyInternalsHttpHandler(const NearbyInternalsHttpHandler&) = delete; NearbyInternalsHttpHandler& operator=(const NearbyInternalsHttpHandler&) = delete; ~NearbyInternalsHttpHandler() override; - // content::WebUIMessageHandler + // content::WebUIMessageHandler: void RegisterMessages() override; void OnJavascriptAllowed() override; void OnJavascriptDisallowed() override; + // NearbyShareHttpNotifier::Observer: + void OnUpdateDeviceRequest( + const nearbyshare::proto::UpdateDeviceRequest& request) override; + void OnUpdateDeviceResponse( + const nearbyshare::proto::UpdateDeviceResponse& response) override; + void OnListContactPeopleRequest( + const nearbyshare::proto::ListContactPeopleRequest& request) override; + void OnListContactPeopleResponse( + const nearbyshare::proto::ListContactPeopleResponse& response) override; + void OnListPublicCertificatesRequest( + const nearbyshare::proto::ListPublicCertificatesRequest& request) + override; + void OnListPublicCertificatesResponse( + const nearbyshare::proto::ListPublicCertificatesResponse& response) + override; + void OnCheckContactsReachabilityRequest( + const nearbyshare::proto::CheckContactsReachabilityRequest& request) + override; + void OnCheckContactsReachabilityResponse( + const nearbyshare::proto::CheckContactsReachabilityResponse& response) + override; + private: // Message handler callback that initializes JavaScript. void InitializeContents(const base::ListValue* args); @@ -40,6 +72,9 @@ // Message handler callback that calls List Contacts RPC. void ListContactPeople(const base::ListValue* args); + content::BrowserContext* const context_; + ScopedObserver<NearbyShareHttpNotifier, NearbyShareHttpNotifier::Observer> + observer_{this}; base::WeakPtrFactory<NearbyInternalsHttpHandler> weak_ptr_factory_{this}; };
diff --git a/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui.cc b/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui.cc index af4a9b4..9b36cd5 100644 --- a/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui.cc +++ b/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui.cc
@@ -48,9 +48,10 @@ web_ui->GetWebContents()->GetBrowserContext(); web_ui->AddMessageHandler(std::make_unique<NearbyInternalsLogsHandler>()); - web_ui->AddMessageHandler(std::make_unique<NearbyInternalsHttpHandler>()); web_ui->AddMessageHandler( std::make_unique<NearbyInternalsContactHandler>(context)); + web_ui->AddMessageHandler( + std::make_unique<NearbyInternalsHttpHandler>(context)); } NearbyInternalsUI::~NearbyInternalsUI() = default;
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc index bd7d211..bd3ef6b9 100644 --- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc +++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -16,6 +16,7 @@ #include "chrome/browser/ui/search/omnibox_mojo_utils.h" #include "chrome/browser/ui/webui/favicon_source.h" #include "chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h" +#include "chrome/browser/ui/webui/new_tab_page/promo_browser_command/promo_browser_command_handler.h" #include "chrome/browser/ui/webui/new_tab_page/untrusted_source.h" #include "chrome/browser/ui/webui/theme_source.h" #include "chrome/browser/ui/webui/webui_util.h" @@ -199,6 +200,8 @@ IDR_NEW_TAB_PAGE_MOJO_LITE_JS); source->AddResourcePath("omnibox.mojom-lite.js", IDR_NEW_TAB_PAGE_OMNIBOX_MOJO_LITE_JS); + source->AddResourcePath("promo_browser_command.mojom-lite.js", + IDR_NEW_TAB_PAGE_PROMO_BROWSER_COMMAND_MOJO_LITE_JS); #if BUILDFLAG(OPTIMIZE_WEBUI) source->AddResourcePath("new_tab_page.js", IDR_NEW_TAB_PAGE_NEW_TAB_PAGE_JS); #endif // BUILDFLAG(OPTIMIZE_WEBUI) @@ -280,6 +283,13 @@ page_factory_receiver_.Bind(std::move(pending_receiver)); } +void NewTabPageUI::BindInterface( + mojo::PendingReceiver<promo_browser_command::mojom::CommandHandler> + pending_page_handler) { + promo_browser_command_handler_ = std::make_unique<PromoBrowserCommandHandler>( + std::move(pending_page_handler), profile_); +} + void NewTabPageUI::CreatePageHandler( mojo::PendingRemote<new_tab_page::mojom::Page> pending_page, mojo::PendingReceiver<new_tab_page::mojom::PageHandler>
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h index 5cbe503..e37d8c27 100644 --- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h +++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
@@ -6,6 +6,7 @@ #define CHROME_BROWSER_UI_WEBUI_NEW_TAB_PAGE_NEW_TAB_PAGE_UI_H_ #include "base/macros.h" +#include "chrome/browser/promo_browser_command/promo_browser_command.mojom-forward.h" #include "chrome/browser/search/instant_service_observer.h" #include "chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom.h" #include "content/public/browser/web_contents_observer.h" @@ -14,6 +15,7 @@ #include "mojo/public/cpp/bindings/receiver.h" #include "ui/webui/mojo_web_ui_controller.h" +class PromoBrowserCommandHandler; namespace content { class NavigationHandle; class WebContents; @@ -40,6 +42,13 @@ mojo::PendingReceiver<new_tab_page::mojom::PageHandlerFactory> pending_receiver); + // Instantiates the implementor of the + // promo_browser_command::mojom::CommandHandler mojo interface passing the + // pending receiver that will be internally bound. + void BindInterface( + mojo::PendingReceiver<promo_browser_command::mojom::CommandHandler> + pending_receiver); + private: // new_tab_page::mojom::PageHandlerFactory: void CreatePageHandler( @@ -63,6 +72,7 @@ std::unique_ptr<NewTabPageHandler> page_handler_; mojo::Receiver<new_tab_page::mojom::PageHandlerFactory> page_factory_receiver_; + std::unique_ptr<PromoBrowserCommandHandler> promo_browser_command_handler_; Profile* profile_; InstantService* instant_service_; content::WebContents* web_contents_;
diff --git a/chrome/browser/web_applications/components/web_app_run_on_os_login.cc b/chrome/browser/web_applications/components/web_app_run_on_os_login.cc index b18f20c7..f50b0fb 100644 --- a/chrome/browser/web_applications/components/web_app_run_on_os_login.cc +++ b/chrome/browser/web_applications/components/web_app_run_on_os_login.cc
@@ -27,7 +27,6 @@ FROM_HERE, base::BindOnce(std::move(callback), run_on_os_login_registered)); } - } // namespace namespace internals { @@ -41,8 +40,10 @@ // TODO(crbug.com/897302): This boilerplate function is used for platforms // other than Windows, currently the feature is only supported in Windows. -void UnregisterRunOnOsLogin(const base::FilePath& profile_path, - const base::string16& shortcut_title) {} +bool UnregisterRunOnOsLogin(const base::FilePath& profile_path, + const base::string16& shortcut_title) { + return true; +} #endif } // namespace internals @@ -56,4 +57,16 @@ std::move(shortcut_info)); } +void ScheduleUnregisterRunOnOsLogin(const base::FilePath& profile_path, + const base::string16& shortcut_title, + UnregisterRunOnOsLoginCallback callback) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + internals::GetShortcutIOTaskRunner()->PostTaskAndReplyWithResult( + FROM_HERE, + base::BindOnce(&internals::UnregisterRunOnOsLogin, profile_path, + shortcut_title), + std::move(callback)); +} + } // namespace web_app
diff --git a/chrome/browser/web_applications/components/web_app_run_on_os_login.h b/chrome/browser/web_applications/components/web_app_run_on_os_login.h index e91425a..53f5221 100644 --- a/chrome/browser/web_applications/components/web_app_run_on_os_login.h +++ b/chrome/browser/web_applications/components/web_app_run_on_os_login.h
@@ -25,6 +25,10 @@ // registered. using RegisterRunOnOsLoginCallback = base::OnceCallback<void(bool success)>; +// Callback made when UnregisterRunOnOslogin has finished indicating whether or +// not it was successfully unregistered +using UnregisterRunOnOsLoginCallback = base::OnceCallback<void(bool success)>; + namespace internals { // Registers the app with the OS to run on OS login. Platform specific @@ -35,7 +39,7 @@ // Unregisters the app with the OS from running on startup. Platform specific // implementations are required for this. // See web_app_shortcut_win.cc for Windows. -void UnregisterRunOnOsLogin(const base::FilePath& profile_path, +bool UnregisterRunOnOsLogin(const base::FilePath& profile_path, const base::string16& shortcut_title); } // namespace internals @@ -46,6 +50,9 @@ void ScheduleRegisterRunOnOsLogin(std::unique_ptr<ShortcutInfo> shortcut_info, RegisterRunOnOsLoginCallback callback); +void ScheduleUnregisterRunOnOsLogin(const base::FilePath& profile_path, + const base::string16& shortcut_title, + UnregisterRunOnOsLoginCallback callback); } // namespace web_app #endif // CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_RUN_ON_OS_LOGIN_H_
diff --git a/chrome/browser/web_applications/components/web_app_run_on_os_login_win.cc b/chrome/browser/web_applications/components/web_app_run_on_os_login_win.cc index 71cbfe4..69a52c7 100644 --- a/chrome/browser/web_applications/components/web_app_run_on_os_login_win.cc +++ b/chrome/browser/web_applications/components/web_app_run_on_os_login_win.cc
@@ -24,22 +24,24 @@ SHORTCUT_CREATION_BY_USER, shortcut_info); } -void UnregisterRunOnOsLogin(const base::FilePath& profile_path, +bool UnregisterRunOnOsLogin(const base::FilePath& profile_path, const base::string16& shortcut_title) { web_app::ShortcutLocations all_shortcut_locations; all_shortcut_locations.in_startup = true; std::vector<base::FilePath> all_paths = GetShortcutPaths(all_shortcut_locations); - + bool result = true; // Only Startup folder is the expected path to be returned in all_paths. for (const auto& path : all_paths) { // Find all app's shortcuts in Startup folder to delete. std::vector<base::FilePath> shortcut_files = FindAppShortcutsByProfileAndTitle(path, profile_path, shortcut_title); for (const auto& shortcut_file : shortcut_files) { - base::DeleteFile(shortcut_file); + if (!base::DeleteFile(shortcut_file)) + result = false; } } + return result; } } // namespace internals
diff --git a/chrome/browser/web_applications/components/web_app_shortcut.cc b/chrome/browser/web_applications/components/web_app_shortcut.cc index 6befe144..bf53d71 100644 --- a/chrome/browser/web_applications/components/web_app_shortcut.cc +++ b/chrome/browser/web_applications/components/web_app_shortcut.cc
@@ -71,6 +71,16 @@ FROM_HERE, base::BindOnce(std::move(callback), shortcut_created)); } +void DeletePlatformShortcutsAndPostCallback( + const base::FilePath& shortcut_data_path, + CreateShortcutsCallback callback, + const ShortcutInfo& shortcut_info) { + bool shortcut_deleted = + internals::DeletePlatformShortcuts(shortcut_data_path, shortcut_info); + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), shortcut_deleted)); +} + } // namespace ShortcutInfo::ShortcutInfo() = default; @@ -166,6 +176,17 @@ std::move(shortcut_info)); } +void ScheduleDeletePlatformShortcuts( + const base::FilePath& shortcut_data_path, + std::unique_ptr<ShortcutInfo> shortcut_info, + DeleteShortcutsCallback callback) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + PostShortcutIOTask(base::BindOnce(&DeletePlatformShortcutsAndPostCallback, + shortcut_data_path, std::move(callback)), + std::move(shortcut_info)); +} + void PostShortcutIOTaskAndReply( base::OnceCallback<void(const ShortcutInfo&)> task, std::unique_ptr<ShortcutInfo> shortcut_info,
diff --git a/chrome/browser/web_applications/components/web_app_shortcut.h b/chrome/browser/web_applications/components/web_app_shortcut.h index 74693de..5235fe6 100644 --- a/chrome/browser/web_applications/components/web_app_shortcut.h +++ b/chrome/browser/web_applications/components/web_app_shortcut.h
@@ -123,6 +123,10 @@ // platform shortcuts indicating whether or not they were successfully // created. using CreateShortcutsCallback = base::OnceCallback<void(bool shortcut_created)>; +// Callback made when DeletePlatformShortcuts has finished trying to delete the +// platform shortcuts indicating whether or not they were successfully +// deleted. +using DeleteShortcutsCallback = base::OnceCallback<void(bool shortcut_deleted)>; // Returns an array of desired icon sizes (in px) to be contained in an app OS // shortcut, sorted in ascending order (biggest desired icon size is last). @@ -155,10 +159,15 @@ std::unique_ptr<ShortcutInfo> shortcut_info, CreateShortcutsCallback callback); +void ScheduleDeletePlatformShortcuts( + const base::FilePath& shortcut_data_path, + std::unique_ptr<ShortcutInfo> shortcut_info, + DeleteShortcutsCallback callback); + // Delete all the shortcuts we have added for this extension. This is the // platform specific implementation of the DeleteAllShortcuts function, and // is executed on the FILE thread. -void DeletePlatformShortcuts(const base::FilePath& shortcut_data_path, +bool DeletePlatformShortcuts(const base::FilePath& shortcut_data_path, const ShortcutInfo& shortcut_info); // Delete the multi-profile (non-profile_scoped) shortcuts for the specified
diff --git a/chrome/browser/web_applications/components/web_app_shortcut_chromeos.cc b/chrome/browser/web_applications/components/web_app_shortcut_chromeos.cc index 620b12a..e79a248 100644 --- a/chrome/browser/web_applications/components/web_app_shortcut_chromeos.cc +++ b/chrome/browser/web_applications/components/web_app_shortcut_chromeos.cc
@@ -15,8 +15,10 @@ return true; } -void DeletePlatformShortcuts(const base::FilePath& web_app_path, - const ShortcutInfo& shortcut_info) {} +bool DeletePlatformShortcuts(const base::FilePath& web_app_path, + const ShortcutInfo& shortcut_info) { + return true; +} void UpdatePlatformShortcuts(const base::FilePath& web_app_path, const base::string16& old_app_title,
diff --git a/chrome/browser/web_applications/components/web_app_shortcut_linux.cc b/chrome/browser/web_applications/components/web_app_shortcut_linux.cc index c12b00f..9b3ea69 100644 --- a/chrome/browser/web_applications/components/web_app_shortcut_linux.cc +++ b/chrome/browser/web_applications/components/web_app_shortcut_linux.cc
@@ -281,13 +281,15 @@ return base::FilePath(filename.append(".desktop")); } -void DeleteShortcutOnDesktop(const base::FilePath& shortcut_filename) { +bool DeleteShortcutOnDesktop(const base::FilePath& shortcut_filename) { base::FilePath desktop_path; + bool result = false; if (base::PathService::Get(base::DIR_USER_DESKTOP, &desktop_path)) - base::DeleteFile(desktop_path.Append(shortcut_filename)); + result = base::DeleteFile(desktop_path.Append(shortcut_filename)); + return result; } -void DeleteShortcutInApplicationsMenu( +bool DeleteShortcutInApplicationsMenu( const base::FilePath& shortcut_filename, const base::FilePath& directory_filename) { std::vector<std::string> argv; @@ -306,7 +308,7 @@ argv.push_back(directory_filename.value()); argv.push_back(shortcut_filename.value()); int exit_code; - shell_integration_linux::LaunchXdgUtility(argv, &exit_code); + return shell_integration_linux::LaunchXdgUtility(argv, &exit_code); } bool CreateDesktopShortcut(const ShortcutInfo& shortcut_info, @@ -447,7 +449,7 @@ return locations; } -void DeleteDesktopShortcuts(const base::FilePath& profile_path, +bool DeleteDesktopShortcuts(const base::FilePath& profile_path, const std::string& extension_id) { base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::BlockingType::MAY_BLOCK); @@ -456,21 +458,22 @@ GetAppShortcutFilename(profile_path, extension_id); DCHECK(!shortcut_filename.empty()); - DeleteShortcutOnDesktop(shortcut_filename); + bool deleted_from_desktop = DeleteShortcutOnDesktop(shortcut_filename); // Delete shortcuts from |kDirectoryFilename|. // Note that it is possible that shortcuts were not created in the Chrome Apps // directory. It doesn't matter: this will still delete the shortcut even if // it isn't in the directory. - DeleteShortcutInApplicationsMenu(shortcut_filename, - base::FilePath(kDirectoryFilename)); + bool deleted_from_application_menu = DeleteShortcutInApplicationsMenu( + shortcut_filename, base::FilePath(kDirectoryFilename)); + return (deleted_from_desktop && deleted_from_application_menu); } -void DeleteAllDesktopShortcuts(const base::FilePath& profile_path) { +bool DeleteAllDesktopShortcuts(const base::FilePath& profile_path) { base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::BlockingType::MAY_BLOCK); std::unique_ptr<base::Environment> env(base::Environment::Create()); - + bool result = true; // Delete shortcuts from Desktop. base::FilePath desktop_path; if (base::PathService::Get(base::DIR_USER_DESKTOP, &desktop_path)) { @@ -478,7 +481,8 @@ shell_integration_linux::GetExistingProfileShortcutFilenames( profile_path, desktop_path); for (const auto& shortcut : shortcut_filenames_desktop) { - DeleteShortcutOnDesktop(shortcut); + if (!DeleteShortcutOnDesktop(shortcut)) + result = false; } } @@ -490,8 +494,12 @@ shell_integration_linux::GetExistingProfileShortcutFilenames( profile_path, applications_menu); for (const auto& menu : shortcut_filenames_app_menu) { - DeleteShortcutInApplicationsMenu(menu, base::FilePath(kDirectoryFilename)); + if (!DeleteShortcutInApplicationsMenu(menu, + base::FilePath(kDirectoryFilename))) { + result = false; + } } + return result; } namespace internals { @@ -509,12 +517,13 @@ #endif } -void DeletePlatformShortcuts(const base::FilePath& web_app_path, +bool DeletePlatformShortcuts(const base::FilePath& web_app_path, const ShortcutInfo& shortcut_info) { #if !defined(OS_CHROMEOS) - DeleteDesktopShortcuts(shortcut_info.profile_path, - shortcut_info.extension_id); + return DeleteDesktopShortcuts(shortcut_info.profile_path, + shortcut_info.extension_id); #endif + return true; } void UpdatePlatformShortcuts(const base::FilePath& web_app_path,
diff --git a/chrome/browser/web_applications/components/web_app_shortcut_linux.h b/chrome/browser/web_applications/components/web_app_shortcut_linux.h index a9fa20a3..c6e5844 100644 --- a/chrome/browser/web_applications/components/web_app_shortcut_linux.h +++ b/chrome/browser/web_applications/components/web_app_shortcut_linux.h
@@ -49,13 +49,14 @@ const base::FilePath& desktop_path); // Delete any desktop shortcuts on desktop or in the application menu that have -// been added for the extension with |extension_id| in |profile_path|. -void DeleteDesktopShortcuts(const base::FilePath& profile_path, +// been added for the extension with |extension_id| in |profile_path|. Returns +// true on successful deletion. +bool DeleteDesktopShortcuts(const base::FilePath& profile_path, const std::string& extension_id); // Delete any desktop shortcuts on desktop or in the application menu that have -// for the profile in |profile_path|. -void DeleteAllDesktopShortcuts(const base::FilePath& profile_path); +// for the profile in |profile_path|. Returns true on successful deletion. +bool DeleteAllDesktopShortcuts(const base::FilePath& profile_path); } // namespace web_app
diff --git a/chrome/browser/web_applications/components/web_app_shortcut_mac.mm b/chrome/browser/web_applications/components/web_app_shortcut_mac.mm index 696bcd7..8bba0ceb 100644 --- a/chrome/browser/web_applications/components/web_app_shortcut_mac.mm +++ b/chrome/browser/web_applications/components/web_app_shortcut_mac.mm
@@ -1328,15 +1328,19 @@ return shortcut_creator.CreateShortcuts(creation_reason, creation_locations); } -void DeletePlatformShortcuts(const base::FilePath& app_data_path, +bool DeletePlatformShortcuts(const base::FilePath& app_data_path, const ShortcutInfo& shortcut_info) { base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::BlockingType::MAY_BLOCK); const std::string bundle_id = GetBundleIdentifier(shortcut_info.extension_id, shortcut_info.profile_path); auto bundle_infos = SearchForBundlesById(bundle_id); - for (const auto& bundle_info : bundle_infos) - base::DeletePathRecursively(bundle_info.bundle_path()); + bool result = true; + for (const auto& bundle_info : bundle_infos) { + if (!base::DeletePathRecursively(bundle_info.bundle_path())) + result = false; + } + return result; } void DeleteMultiProfileShortcutsForApp(const std::string& app_id) {
diff --git a/chrome/browser/web_applications/components/web_app_shortcut_win.cc b/chrome/browser/web_applications/components/web_app_shortcut_win.cc index 08df698..bb6b775c 100644 --- a/chrome/browser/web_applications/components/web_app_shortcut_win.cc +++ b/chrome/browser/web_applications/components/web_app_shortcut_win.cc
@@ -229,12 +229,14 @@ // for this app were found (and deleted). This will delete duplicate shortcuts, // but only return each path once, even if it contained multiple deleted // shortcuts. Both of these may be NULL. -void GetShortcutLocationsAndDeleteShortcuts( +bool GetShortcutLocationsAndDeleteShortcuts( const base::FilePath& web_app_path, const base::FilePath& profile_path, const base::string16& title, bool* was_pinned_to_taskbar, std::vector<base::FilePath>* shortcut_paths) { + bool result = true; + // Get all possible locations for shortcuts. web_app::ShortcutLocations all_shortcut_locations; all_shortcut_locations.in_quick_launch_bar = true; @@ -275,9 +277,11 @@ // Any shortcut could have been pinned, either by chrome or the user, so // they are all unpinned. base::win::UnpinShortcutFromTaskbar(*j); - base::DeleteFile(*j); + if (base::DeleteFile(*j)) + result = false; } } + return result; } void CreateIconAndSetRelaunchDetails( @@ -472,11 +476,11 @@ CheckAndSaveIcon(icon_file, shortcut_info.favicon, true); } -void DeletePlatformShortcuts(const base::FilePath& web_app_path, +bool DeletePlatformShortcuts(const base::FilePath& web_app_path, const ShortcutInfo& shortcut_info) { - GetShortcutLocationsAndDeleteShortcuts(web_app_path, - shortcut_info.profile_path, - shortcut_info.title, NULL, NULL); + bool result = GetShortcutLocationsAndDeleteShortcuts( + web_app_path, shortcut_info.profile_path, shortcut_info.title, nullptr, + nullptr); // If there are no more shortcuts in the Chrome Apps subdirectory, remove it. base::FilePath chrome_apps_dir; @@ -488,7 +492,9 @@ } // Delete downloaded shortcut icons for the web app. - web_app::internals::DeleteShortcutsMenuIcons(web_app_path); + if (!web_app::internals::DeleteShortcutsMenuIcons(web_app_path)) + result = false; + return result; } void DeleteAllShortcutsForProfile(const base::FilePath& profile_path) {
diff --git a/chrome/browser/web_applications/components/web_app_shortcuts_menu.cc b/chrome/browser/web_applications/components/web_app_shortcuts_menu.cc index d62687a4a..3733d29b 100644 --- a/chrome/browser/web_applications/components/web_app_shortcuts_menu.cc +++ b/chrome/browser/web_applications/components/web_app_shortcuts_menu.cc
@@ -25,10 +25,12 @@ DCHECK(ShouldRegisterShortcutsMenuWithOs()); } -void UnregisterShortcutsMenuWithOs(const AppId& app_id, +bool UnregisterShortcutsMenuWithOs(const AppId& app_id, const base::FilePath& profile_path) { NOTIMPLEMENTED(); DCHECK(ShouldRegisterShortcutsMenuWithOs()); + + return true; } #endif // !defined(OS_WIN)
diff --git a/chrome/browser/web_applications/components/web_app_shortcuts_menu.h b/chrome/browser/web_applications/components/web_app_shortcuts_menu.h index 69a5062..4af43b6 100644 --- a/chrome/browser/web_applications/components/web_app_shortcuts_menu.h +++ b/chrome/browser/web_applications/components/web_app_shortcuts_menu.h
@@ -30,8 +30,8 @@ const ShortcutsMenuIconsBitmaps& shortcuts_menu_icons_bitmaps); // Deletes the ShortcutsMenu from the OS. This should be called during the -// uninstallation process. -void UnregisterShortcutsMenuWithOs(const AppId& app_id, +// uninstallation process. Returns true on successful deletion. +bool UnregisterShortcutsMenuWithOs(const AppId& app_id, const base::FilePath& profile_path); } // namespace web_app
diff --git a/chrome/browser/web_applications/components/web_app_shortcuts_menu_win.cc b/chrome/browser/web_applications/components/web_app_shortcuts_menu_win.cc index 3dfc97fa..4c54093 100644 --- a/chrome/browser/web_applications/components/web_app_shortcuts_menu_win.cc +++ b/chrome/browser/web_applications/components/web_app_shortcuts_menu_win.cc
@@ -211,22 +211,23 @@ shortcuts_menu_icons_bitmaps)); } -void UnregisterShortcutsMenuWithOs(const AppId& app_id, +bool UnregisterShortcutsMenuWithOs(const AppId& app_id, const base::FilePath& profile_path) { if (!JumpListUpdater::DeleteJumpList( GenerateAppUserModelId(profile_path, app_id))) { RecordUnregistration(UnregistrationResult::kFailedToDeleteJumpList); - return; + return false; } RecordUnregistration(UnregistrationResult::kSuccess); + return true; } namespace internals { -void DeleteShortcutsMenuIcons(const base::FilePath& shortcut_data_dir) { +bool DeleteShortcutsMenuIcons(const base::FilePath& shortcut_data_dir) { base::FilePath shortcuts_menu_icons_path = GetShortcutsMenuIconsDirectory(shortcut_data_dir); - base::DeletePathRecursively(shortcuts_menu_icons_path); + return base::DeletePathRecursively(shortcuts_menu_icons_path); } } // namespace internals
diff --git a/chrome/browser/web_applications/components/web_app_shortcuts_menu_win.h b/chrome/browser/web_applications/components/web_app_shortcuts_menu_win.h index e7dd94c2..b369716e 100644 --- a/chrome/browser/web_applications/components/web_app_shortcuts_menu_win.h +++ b/chrome/browser/web_applications/components/web_app_shortcuts_menu_win.h
@@ -13,7 +13,7 @@ // Deletes all .ico shortcuts menu icons that were written to disk at PWA // install time. Call this when PWA is uninstalled on Windows. -void DeleteShortcutsMenuIcons(const base::FilePath& web_app_path); +bool DeleteShortcutsMenuIcons(const base::FilePath& web_app_path); } // namespace internals
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc b/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc index 6fb7f5a..c472edfb 100644 --- a/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc +++ b/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc
@@ -81,7 +81,7 @@ NotifyWebAppUninstalled(extension->id()); web_app::WebAppProviderBase::GetProviderBase(profile()) ->os_integration_manager() - .UninstallOsHooks(extension->id()); + .UninstallOsHooks(extension->id(), base::DoNothing()); bookmark_app_being_observed_ = nullptr; } @@ -104,7 +104,7 @@ NotifyWebAppProfileWillBeDeleted(extension->id()); web_app::WebAppProviderBase::GetProviderBase(profile()) ->os_integration_manager() - .UninstallOsHooks(extension->id()); + .UninstallOsHooks(extension->id(), base::DoNothing()); } bookmark_app_being_observed_ = nullptr;
diff --git a/chrome/browser/web_applications/extensions/web_app_extension_shortcut.cc b/chrome/browser/web_applications/extensions/web_app_extension_shortcut.cc index b2275a4..cea641d 100644 --- a/chrome/browser/web_applications/extensions/web_app_extension_shortcut.cc +++ b/chrome/browser/web_applications/extensions/web_app_extension_shortcut.cc
@@ -268,9 +268,8 @@ ShortcutInfoForExtensionAndProfile(app, profile)); base::FilePath shortcut_data_dir = internals::GetShortcutDataDir(*shortcut_info); - internals::PostShortcutIOTask( - base::BindOnce(&internals::DeletePlatformShortcuts, shortcut_data_dir), - std::move(shortcut_info)); + internals::ScheduleDeletePlatformShortcuts( + shortcut_data_dir, std::move(shortcut_info), base::DoNothing()); } void UpdateAllShortcuts(const base::string16& old_app_title,
diff --git a/chrome/browser/web_applications/os_integration_manager.cc b/chrome/browser/web_applications/os_integration_manager.cc index d4a4a61..4e057f2 100644 --- a/chrome/browser/web_applications/os_integration_manager.cc +++ b/chrome/browser/web_applications/os_integration_manager.cc
@@ -30,11 +30,11 @@ explicit OsHooksBarrierInfo(InstallOsHooksCallback done_callback) : done_callback_(std::move(done_callback)) {} - void Run(OsHookType::Type os_hook, bool created) { + void Run(OsHookType::Type os_hook, bool completed) { DCHECK(!os_hooks_called_[os_hook]); os_hooks_called_[os_hook] = true; - os_hooks_results_[os_hook] = created; + os_hooks_results_[os_hook] = completed; if (os_hooks_called_.all()) { std::move(done_callback_).Run(os_hooks_results_); @@ -103,7 +103,7 @@ // callback for every type. Developers should double check that Run is // called for every OsHookType::Type. If there is any missing type, the // InstallOsHooksCallback will not get run. - base::RepeatingCallback<void(OsHookType::Type os_hook, bool created)> + base::RepeatingCallback<void(OsHookType::Type os_hook, bool completed)> barrier = base::BindRepeating( &OsHooksBarrierInfo::Run, base::Owned(new OsHooksBarrierInfo(std::move(callback)))); @@ -127,12 +127,24 @@ } } -void OsIntegrationManager::UninstallOsHooks(const AppId& app_id) { +void OsIntegrationManager::UninstallOsHooks(const AppId& app_id, + UninstallOsHooksCallback callback) { + DCHECK(shortcut_manager_); + if (suppress_os_hooks_for_testing_) return; - if (ShouldRegisterShortcutsMenuWithOs()) - UnregisterShortcutsMenuWithOs(app_id, profile_->GetPath()); + base::RepeatingCallback<void(OsHookType::Type os_hook, bool completed)> + barrier = base::BindRepeating( + &OsHooksBarrierInfo::Run, + base::Owned(new OsHooksBarrierInfo(std::move(callback)))); + + if (ShouldRegisterShortcutsMenuWithOs()) { + barrier.Run(OsHookType::kShortcutsMenu, + UnregisterShortcutsMenuWithOs(app_id, profile_->GetPath())); + } else { + barrier.Run(OsHookType::kShortcutsMenu, /*completed=*/true); + } std::unique_ptr<ShortcutInfo> shortcut_info = shortcut_manager_->BuildShortcutInfo(app_id); @@ -140,17 +152,19 @@ internals::GetShortcutDataDir(*shortcut_info); if (base::FeatureList::IsEnabled(features::kDesktopPWAsRunOnOsLogin)) { - internals::GetShortcutIOTaskRunner()->PostTask( - FROM_HERE, - base::BindOnce(&internals::UnregisterRunOnOsLogin, - shortcut_info->profile_path, shortcut_info->title)); + ScheduleUnregisterRunOnOsLogin( + shortcut_info->profile_path, shortcut_info->title, + base::BindOnce(barrier, OsHookType::kRunOnOsLogin)); } - internals::PostShortcutIOTask( - base::BindOnce(&internals::DeletePlatformShortcuts, shortcut_data_dir), - std::move(shortcut_info)); + internals::ScheduleDeletePlatformShortcuts( + shortcut_data_dir, std::move(shortcut_info), + base::BindOnce(barrier, OsHookType::kShortcuts)); + // TODO(https://crbug.com/1108109) we should return the result of file handler + // unregistration and record errors during unregistration. file_handler_manager_->DisableAndUnregisterOsFileHandlers(app_id); + barrier.Run(OsHookType::kFileHandlers, /*completed=*/true); DeleteSharedAppShims(app_id); } @@ -169,12 +183,12 @@ DCHECK(file_handler_manager_); DCHECK(ui_manager_); - barrier_callback.Run(OsHookType::kShortcuts, true); + barrier_callback.Run(OsHookType::kShortcuts, /*completed=*/true); // TODO(crbug.com/1087219): callback should be run after all hooks are // deployed, need to refactor filehandler to allow this. file_handler_manager_->EnableAndRegisterOsFileHandlers(app_id); - barrier_callback.Run(OsHookType::kFileHandlers, true); + barrier_callback.Run(OsHookType::kFileHandlers, /*completed=*/true); if (options.add_to_quick_launch_bar && ui_manager_->CanAddAppToQuickLaunchBar()) { @@ -187,13 +201,13 @@ web_app_info->shortcuts_menu_icons_bitmaps); // TODO(https://crbug.com/1098471): fix RegisterShortcutsMenuWithOs to // take callback. - barrier_callback.Run(OsHookType::kShortcutsMenu, true); + barrier_callback.Run(OsHookType::kShortcutsMenu, /*completed=*/true); } else { shortcut_manager_->ReadAllShortcutsMenuIconsAndRegisterShortcutsMenu( app_id, base::BindOnce(barrier_callback, OsHookType::kShortcutsMenu)); } } else { - barrier_callback.Run(OsHookType::kShortcutsMenu, false); + barrier_callback.Run(OsHookType::kShortcutsMenu, /*completed=*/false); } if (base::FeatureList::IsEnabled(features::kDesktopPWAsRunOnOsLogin) &&
diff --git a/chrome/browser/web_applications/os_integration_manager.h b/chrome/browser/web_applications/os_integration_manager.h index 4a77c83a..aee7dac 100644 --- a/chrome/browser/web_applications/os_integration_manager.h +++ b/chrome/browser/web_applications/os_integration_manager.h
@@ -34,11 +34,14 @@ bool run_on_os_login = false; }; -// Callback made when InstallOsHooks has finished trying to deploy all -// needed OS hooks. +// Callback made after InstallOsHooks is finished. using InstallOsHooksCallback = base::OnceCallback<void(OsHooksResults os_hooks_info)>; +// Callback made after UninstallOsHooks is finished. +using UninstallOsHooksCallback = + base::OnceCallback<void(OsHooksResults os_hooks_info)>; + // OsIntegrationManager is responsible of creating/updating/deleting // all OS hooks during Web App lifecycle. // It contains individual OS integration managers and takes @@ -67,7 +70,8 @@ // Uninstall all OS hooks for the web app. // TODO(https://crbug.com/1108109) we should record uninstall result and allow // callback. virtual for testing - virtual void UninstallOsHooks(const AppId& app_id); + virtual void UninstallOsHooks(const AppId& app_id, + UninstallOsHooksCallback callback); void SuppressOsHooksForTesting();
diff --git a/chrome/browser/web_applications/test/test_os_integration_manager.cc b/chrome/browser/web_applications/test/test_os_integration_manager.cc index 0bdefbf..45bb01d 100644 --- a/chrome/browser/web_applications/test/test_os_integration_manager.cc +++ b/chrome/browser/web_applications/test/test_os_integration_manager.cc
@@ -57,7 +57,9 @@ base::BindOnce(std::move(callback), std::move(os_hooks_results))); } -void TestOsIntegrationManager::UninstallOsHooks(const AppId& app_id) { +void TestOsIntegrationManager::UninstallOsHooks( + const AppId& app_id, + UninstallOsHooksCallback callback) { NOTIMPLEMENTED(); }
diff --git a/chrome/browser/web_applications/test/test_os_integration_manager.h b/chrome/browser/web_applications/test/test_os_integration_manager.h index fe3706c..96b08af 100644 --- a/chrome/browser/web_applications/test/test_os_integration_manager.h +++ b/chrome/browser/web_applications/test/test_os_integration_manager.h
@@ -23,7 +23,8 @@ InstallOsHooksCallback callback, std::unique_ptr<WebApplicationInfo> web_app_info, InstallOsHooksOptions options) override; - void UninstallOsHooks(const AppId& app_id) override; + void UninstallOsHooks(const AppId& app_id, + UninstallOsHooksCallback callback) override; size_t num_create_shortcuts_calls() const { return num_create_shortcuts_calls_;
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc index e44a6775..24bf0b0 100644 --- a/chrome/browser/web_applications/web_app_install_finalizer.cc +++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
@@ -384,7 +384,7 @@ registrar().NotifyWebAppUninstalled(app_id); WebAppProviderBase::GetProviderBase(profile_) ->os_integration_manager() - .UninstallOsHooks(app_id); + .UninstallOsHooks(app_id, base::DoNothing()); ScopedRegistryUpdate update(registry_controller().AsWebAppSyncBridge()); update->DeleteApp(app_id);
diff --git a/chrome/browser/web_applications/web_app_registrar.cc b/chrome/browser/web_applications/web_app_registrar.cc index 2b77a2b..237d86e 100644 --- a/chrome/browser/web_applications/web_app_registrar.cc +++ b/chrome/browser/web_applications/web_app_registrar.cc
@@ -191,7 +191,7 @@ NotifyWebAppProfileWillBeDeleted(app.app_id()); WebAppProviderBase::GetProviderBase(profile()) ->os_integration_manager() - .UninstallOsHooks(app.app_id()); + .UninstallOsHooks(app.app_id(), base::DoNothing()); } // We can't do registry_.clear() here because it makes in-memory registry // diverged from the sync server registry and from the on-disk registry
diff --git a/chrome/browser/web_applications/web_app_sync_bridge.cc b/chrome/browser/web_applications/web_app_sync_bridge.cc index 47187ab..4d6f347 100644 --- a/chrome/browser/web_applications/web_app_sync_bridge.cc +++ b/chrome/browser/web_applications/web_app_sync_bridge.cc
@@ -513,7 +513,7 @@ registrar_->NotifyWebAppUninstalled(app_id); WebAppProviderBase::GetProviderBase(profile()) ->os_integration_manager() - .UninstallOsHooks(app_id); + .UninstallOsHooks(app_id, base::DoNothing()); } std::vector<WebApp*> apps_to_install;
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index 5b90f8583..df2b786 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-master-1596549454-7f993c1fb49036aa3a3eb3f7c44ae7cd093767bd.profdata +chrome-win32-master-1596571199-429c5d3274b1adcffd4054f095f3cc2a5ada36c8.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index d1d7bc19..e8bf390 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-master-1596542250-4b9669504bdebb7cf76d0d2b9c7cc833bb153daa.profdata +chrome-win64-master-1596562698-892fa89488e0e796ec09ee348ea5782de7379276.profdata
diff --git a/chrome/common/extensions/api/_features.md b/chrome/common/extensions/api/_features.md index c15412d8..520a1875 100644 --- a/chrome/common/extensions/api/_features.md +++ b/chrome/common/extensions/api/_features.md
@@ -312,8 +312,8 @@ The `platforms` property specifies the properties the feature should be available on. -The accepted values are lists of strings from `chromeos`, `mac`, `linux`, and -`win`. +The accepted values are lists of strings from `chromeos`, `mac`, 'lacros', +`linux`, and `win`. ### session\_types
diff --git a/chrome/common/extensions/api/enterprise_platform_keys.idl b/chrome/common/extensions/api/enterprise_platform_keys.idl index 39c98d6f..14a0de2f 100644 --- a/chrome/common/extensions/api/enterprise_platform_keys.idl +++ b/chrome/common/extensions/api/enterprise_platform_keys.idl
@@ -7,7 +7,7 @@ // certificates will be managed by the platform and can be used for TLS // authentication, network access or by other extension through // $(ref:platformKeys chrome.platformKeys). -[platforms = ("chromeos")] +[platforms = ("chromeos", "lacros")] namespace enterprise.platformKeys { [nocompile, noinline_doc] dictionary Token { // Uniquely identifies this <code>Token</code>.
diff --git a/chrome/common/extensions/api/file_manager_private.idl b/chrome/common/extensions/api/file_manager_private.idl index c57fdcc..a170272 100644 --- a/chrome/common/extensions/api/file_manager_private.idl +++ b/chrome/common/extensions/api/file_manager_private.idl
@@ -1218,10 +1218,19 @@ // Return true if sharesheet contains share targets for entries. // |entries| Array of selected entries - // |callback| + // |callback| is called with error in case of failure and with no arguments + // if successfully launched the Sharesheet dialog, but before user has + // finished the sharing. [nocompile] static void sharesheetHasTargets([instanceof=Entry] object[] entries, BooleanCallback callback); + + // Invoke Sharesheet for selected files. + // |entries| Array of selected entries. + // |callback| + [nocompile] + static void invokeSharesheet([instanceof=Entry] object[] entries, + SimpleCallback callback); }; interface Events {
diff --git a/chrome/common/extensions/api/file_manager_private_internal.idl b/chrome/common/extensions/api/file_manager_private_internal.idl index 8394d8e..7cbd331 100644 --- a/chrome/common/extensions/api/file_manager_private_internal.idl +++ b/chrome/common/extensions/api/file_manager_private_internal.idl
@@ -110,5 +110,6 @@ boolean cropToSquare, GetThumbnailCallback callback); static void sharesheetHasTargets(DOMString[] urls, BooleanCallback callback); + static void invokeSharesheet(DOMString[] urls, SimpleCallback callback); }; };
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base.cc b/chrome/credential_provider/gaiacp/gaia_credential_base.cc index cba85d8..81e29dd 100644 --- a/chrome/credential_provider/gaiacp/gaia_credential_base.cc +++ b/chrome/credential_provider/gaiacp/gaia_credential_base.cc
@@ -2455,8 +2455,9 @@ } base::string16 sid = OLE2CW(user_sid_); - if (UserPoliciesManager::Get()->GetTimeDeltaSinceLastPolicyFetch(sid) > - kMaxTimeDeltaSinceLastUserPolicyRefresh) { + if (UserPoliciesManager::Get()->CloudPoliciesEnabled() && + UserPoliciesManager::Get()->GetTimeDeltaSinceLastPolicyFetch(sid) > + kMaxTimeDeltaSinceLastUserPolicyRefresh) { // TODO(crbug.com/976744) Use downscoped token here. base::string16 access_token = GetDictString(*properties, kKeyAccessToken); HRESULT hr = UserPoliciesManager::Get()->FetchAndStoreCloudUserPolicies(
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc b/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc index 390a6eb..49e19e0 100644 --- a/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc +++ b/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc
@@ -3233,14 +3233,17 @@ // http server. // 2. bool true: Policies were fetched recently and don't need refreshing. // false: Policies were never fetched or are very old. +// 3. bool : Whether cloud policies feature is enabled. class GcpGaiaCredentialBaseFetchCloudPoliciesTest : public GcpGaiaCredentialBaseTest, - public ::testing::WithParamInterface<std::tuple<bool, bool>> {}; + public ::testing::WithParamInterface<std::tuple<bool, bool, bool>> {}; TEST_P(GcpGaiaCredentialBaseFetchCloudPoliciesTest, FetchAndStore) { bool fail_fetch_policies = std::get<0>(GetParam()); bool policy_refreshed_recently = std::get<1>(GetParam()); + bool cloud_policies_enabled = std::get<2>(GetParam()); + FakeUserPoliciesManager fake_user_policies_manager(cloud_policies_enabled); GoogleMdmEnrolledStatusForTesting force_success(true); // Create a fake user associated to a gaia id. @@ -3251,30 +3254,32 @@ base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), &sid_str)); base::string16 sid = OLE2W(sid_str); - base::string16 fetch_time_millis = L"0"; - if (policy_refreshed_recently) { - fetch_time_millis = base::NumberToString16( - base::Time::Now().ToDeltaSinceWindowsEpoch().InMilliseconds()); + if (cloud_policies_enabled) { + base::string16 fetch_time_millis = L"0"; + if (policy_refreshed_recently) { + fetch_time_millis = base::NumberToString16( + base::Time::Now().ToDeltaSinceWindowsEpoch().InMilliseconds()); + } + ASSERT_EQ(S_OK, SetUserProperty(sid, L"last_policy_refresh_time", + fetch_time_millis)); + + std::string expected_response; + if (fail_fetch_policies) { + expected_response = "Invalid json response"; + } else { + UserPolicies policies; + base::Value policies_value = policies.ToValue(); + base::JSONWriter::Write(policies_value, &expected_response); + } + + fake_http_url_fetcher_factory()->SetFakeResponse( + UserPoliciesManager::Get()->GetGcpwServiceUserPoliciesUrl(sid), + FakeWinHttpUrlFetcher::Headers(), expected_response); } - ASSERT_EQ(S_OK, SetUserProperty(sid, L"last_policy_refresh_time", - fetch_time_millis)); // Change token response to an valid one. SetDefaultTokenHandleResponse(kDefaultValidTokenHandleResponse); - std::string expected_response; - if (fail_fetch_policies) { - expected_response = "Invalid json response"; - } else { - UserPolicies policies; - base::Value policies_value = policies.ToValue(); - base::JSONWriter::Write(policies_value, &expected_response); - } - - fake_http_url_fetcher_factory()->SetFakeResponse( - UserPoliciesManager::Get()->GetGcpwServiceUserPoliciesUrl(sid), - FakeWinHttpUrlFetcher::Headers(), expected_response); - // Create provider and start logon. Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; @@ -3288,12 +3293,18 @@ base::TimeDelta time_since_last_fetch = UserPoliciesManager::Get()->GetTimeDeltaSinceLastPolicyFetch(sid); + if (cloud_policies_enabled && !policy_refreshed_recently) { + ASSERT_EQ(1, fake_user_policies_manager.GetNumTimesFetchAndStoreCalled()); + } else { + ASSERT_EQ(0, fake_user_policies_manager.GetNumTimesFetchAndStoreCalled()); + } + // Expected number of HTTP calls when not fetching user policies since upload // device details is always called. const size_t base_num_http_requests = 1; const size_t requests_created = fake_http_url_fetcher_factory()->requests_created(); - if (policy_refreshed_recently) { + if (!cloud_policies_enabled || policy_refreshed_recently) { // No new requests for fetching policies. ASSERT_EQ(base_num_http_requests, requests_created); } else { @@ -3321,6 +3332,7 @@ INSTANTIATE_TEST_SUITE_P(All, GcpGaiaCredentialBaseFetchCloudPoliciesTest, ::testing::Combine(::testing::Bool(), + ::testing::Bool(), ::testing::Bool())); } // namespace testing
diff --git a/chrome/credential_provider/gaiacp/user_policies_manager.h b/chrome/credential_provider/gaiacp/user_policies_manager.h index 16cbd66..3021199 100644 --- a/chrome/credential_provider/gaiacp/user_policies_manager.h +++ b/chrome/credential_provider/gaiacp/user_policies_manager.h
@@ -25,8 +25,9 @@ // Fetch the policies for the user from GCPW backend with |sid| using // |access_token| for authentication and authorization and saves it in file // storage replacing any previously fetched versions. - HRESULT FetchAndStoreCloudUserPolicies(const base::string16& sid, - const std::string& access_token); + virtual HRESULT FetchAndStoreCloudUserPolicies( + const base::string16& sid, + const std::string& access_token); // Return the elapsed time delta since the last time the policies were // successfully fetched for the user with |sid|.
diff --git a/chrome/credential_provider/test/gcp_fakes.cc b/chrome/credential_provider/test/gcp_fakes.cc index cafb3764..5113a1d 100644 --- a/chrome/credential_provider/test/gcp_fakes.cc +++ b/chrome/credential_provider/test/gcp_fakes.cc
@@ -1166,6 +1166,15 @@ *GetInstanceStorage() = original_manager_; } +HRESULT FakeUserPoliciesManager::FetchAndStoreCloudUserPolicies( + const base::string16& sid, + const std::string& access_token) { + ++num_times_fetch_called_; + fetch_status_ = + original_manager_->FetchAndStoreCloudUserPolicies(sid, access_token); + return fetch_status_; +} + void FakeUserPoliciesManager::SetUserPolicies(const base::string16& sid, const UserPolicies& policies) { user_policies_[sid] = policies; @@ -1181,6 +1190,10 @@ return false; } +int FakeUserPoliciesManager::GetNumTimesFetchAndStoreCalled() const { + return num_times_fetch_called_; +} + /////////////////////////////////////////////////////////////////////////////// FakeDevicePoliciesManager::FakeDevicePoliciesManager(
diff --git a/chrome/credential_provider/test/gcp_fakes.h b/chrome/credential_provider/test/gcp_fakes.h index 6f5e4aef..c4ccbafd 100644 --- a/chrome/credential_provider/test/gcp_fakes.h +++ b/chrome/credential_provider/test/gcp_fakes.h
@@ -595,15 +595,24 @@ explicit FakeUserPoliciesManager(bool cloud_policies_enabled); ~FakeUserPoliciesManager() override; + HRESULT FetchAndStoreCloudUserPolicies( + const base::string16& sid, + const std::string& access_token) override; + // Specify the policy to use for a user. void SetUserPolicies(const base::string16& sid, const UserPolicies& policies); bool GetUserPolicies(const base::string16& sid, UserPolicies* policies) override; + // Returns the number of times FetchAndStoreCloudUserPolicies method was + // called. + int GetNumTimesFetchAndStoreCalled() const; + private: UserPoliciesManager* original_manager_ = nullptr; std::map<base::string16, UserPolicies> user_policies_; + int num_times_fetch_called_ = 0; }; ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/installer/setup/install_worker.cc b/chrome/installer/setup/install_worker.cc index 7ad0077..c4b7b71 100644 --- a/chrome/installer/setup/install_worker.cc +++ b/chrome/installer/setup/install_worker.cc
@@ -395,7 +395,7 @@ install_static::GetElevationServiceDisplayName(), base::CommandLine(elevation_service_path), install_static::GetClientStateKeyPath(), - install_static::GetElevatorClsid(), install_static::GetElevatorIid()); + {install_static::GetElevatorClsid()}, {install_static::GetElevatorIid()}); install_service_work_item->set_best_effort(true); list->AddWorkItem(install_service_work_item); }
diff --git a/chrome/installer/setup/uninstall.cc b/chrome/installer/setup/uninstall.cc index 56e29e5..8d3b5f3 100644 --- a/chrome/installer/setup/uninstall.cc +++ b/chrome/installer/setup/uninstall.cc
@@ -679,8 +679,8 @@ if (!InstallServiceWorkItem::DeleteService( install_static::GetElevationServiceName(), install_static::GetClientStateKeyPath(), - install_static::GetElevatorClsid(), - install_static::GetElevatorIid())) { + {install_static::GetElevatorClsid()}, + {install_static::GetElevatorIid()})) { LOG(WARNING) << "Failed to delete " << install_static::GetElevationServiceName(); }
diff --git a/chrome/installer/util/install_service_work_item.cc b/chrome/installer/util/install_service_work_item.cc index ffee605a..8f9e70a 100644 --- a/chrome/installer/util/install_service_work_item.cc +++ b/chrome/installer/util/install_service_work_item.cc
@@ -14,14 +14,14 @@ const base::string16& display_name, const base::CommandLine& service_cmd_line, const base::string16& registry_path, - const GUID& clsid, - const GUID& iid) + const std::vector<GUID>& clsids, + const std::vector<GUID>& iids) : impl_(std::make_unique<InstallServiceWorkItemImpl>(service_name, display_name, service_cmd_line, registry_path, - clsid, - iid)) {} + clsids, + iids)) {} InstallServiceWorkItem::~InstallServiceWorkItem() = default; @@ -36,12 +36,12 @@ // static bool InstallServiceWorkItem::DeleteService(const base::string16& service_name, const base::string16& registry_path, - const GUID& clsid, - const GUID& iid) { + const std::vector<GUID>& clsids, + const std::vector<GUID>& iids) { return InstallServiceWorkItemImpl( service_name, base::string16(), base::CommandLine(base::CommandLine::NO_PROGRAM), registry_path, - clsid, iid) + clsids, iids) .DeleteServiceImpl(); }
diff --git a/chrome/installer/util/install_service_work_item.h b/chrome/installer/util/install_service_work_item.h index 5a2fe889..b3726ab5 100644 --- a/chrome/installer/util/install_service_work_item.h +++ b/chrome/installer/util/install_service_work_item.h
@@ -13,6 +13,7 @@ #define CHROME_INSTALLER_UTIL_INSTALL_SERVICE_WORK_ITEM_H_ #include <memory> +#include <vector> #include "base/macros.h" #include "base/strings/string16.h" @@ -47,25 +48,22 @@ // persist a versioned service name. An example |registry_path| is // "Software\ProductFoo". // - // |clsid| is the CLSID and AppId to register. - // If COM CLSID/AppId registration is not required, pass in GUID_NULL for - // |clsid|. - // |iid| is the Interface and Typelib to register. - // If COM Interface/Typelib registration is not required, pass in - // GUID_NULL for |iid|. + // If COM CLSID/AppId registration is required, |clsids| should contain the + // CLSIDs and AppIds to register. If COM Interface/Typelib registration is + // required, |iids| should contain the Interfaces and Typelibs to register. InstallServiceWorkItem(const base::string16& service_name, const base::string16& display_name, const base::CommandLine& service_cmd_line, const base::string16& registry_path, - const GUID& clsid, - const GUID& iid); + const std::vector<GUID>& clsids, + const std::vector<GUID>& iids); ~InstallServiceWorkItem() override; static bool DeleteService(const base::string16& service_name, const base::string16& registry_path, - const GUID& clsid, - const GUID& iid); + const std::vector<GUID>& clsids, + const std::vector<GUID>& iids); private: friend class InstallServiceWorkItemTest;
diff --git a/chrome/installer/util/install_service_work_item_impl.cc b/chrome/installer/util/install_service_work_item_impl.cc index c37faaf..9c1fd44 100644 --- a/chrome/installer/util/install_service_work_item_impl.cc +++ b/chrome/installer/util/install_service_work_item_impl.cc
@@ -147,15 +147,15 @@ const base::string16& display_name, const base::CommandLine& service_cmd_line, const base::string16& registry_path, - const GUID& clsid, - const GUID& iid) + const std::vector<GUID>& clsids, + const std::vector<GUID>& iids) : com_registration_work_items_(WorkItem::CreateWorkItemList()), service_name_(service_name), display_name_(display_name), service_cmd_line_(service_cmd_line), registry_path_(registry_path), - clsid_(clsid), - iid_(iid), + clsids_(clsids), + iids_(iids), rollback_existing_service_(false), rollback_new_service_(false), original_service_still_exists_(false) {} @@ -232,15 +232,15 @@ } bool InstallServiceWorkItemImpl::DoComRegistration() { - if (clsid_ != GUID_NULL) { - const base::string16 clsid_reg_path = GetComClsidRegistryPath(clsid_); - const base::string16 appid_reg_path = GetComAppidRegistryPath(clsid_); + for (const auto& clsid : clsids_) { + const base::string16 clsid_reg_path = GetComClsidRegistryPath(clsid); + const base::string16 appid_reg_path = GetComAppidRegistryPath(clsid); com_registration_work_items_->AddCreateRegKeyWorkItem( HKEY_LOCAL_MACHINE, clsid_reg_path, WorkItem::kWow64Default); com_registration_work_items_->AddSetRegValueWorkItem( HKEY_LOCAL_MACHINE, clsid_reg_path, WorkItem::kWow64Default, L"AppID", - base::win::WStringFromGUID(clsid_), true); + base::win::WStringFromGUID(clsid), true); com_registration_work_items_->AddCreateRegKeyWorkItem( HKEY_LOCAL_MACHINE, appid_reg_path, WorkItem::kWow64Default); com_registration_work_items_->AddSetRegValueWorkItem( @@ -248,9 +248,9 @@ L"LocalService", GetCurrentServiceName(), true); } - if (iid_ != GUID_NULL) { - const base::string16 iid_reg_path = GetComIidRegistryPath(iid_); - const base::string16 typelib_reg_path = GetComTypeLibRegistryPath(iid_); + for (const auto& iid : iids_) { + const base::string16 iid_reg_path = GetComIidRegistryPath(iid); + const base::string16 typelib_reg_path = GetComTypeLibRegistryPath(iid); // Registering the Ole Automation marshaler with the CLSID // {00020424-0000-0000-C000-000000000046} as the proxy/stub for the @@ -269,7 +269,7 @@ WorkItem::kWow64Default); com_registration_work_items_->AddSetRegValueWorkItem( HKEY_LOCAL_MACHINE, iid_reg_path + L"\\TypeLib", - WorkItem::kWow64Default, L"", base::win::WStringFromGUID(iid_), true); + WorkItem::kWow64Default, L"", base::win::WStringFromGUID(iid), true); com_registration_work_items_->AddSetRegValueWorkItem( HKEY_LOCAL_MACHINE, iid_reg_path + L"\\TypeLib", WorkItem::kWow64Default, L"Version", L"1.0", true); @@ -359,20 +359,22 @@ bool InstallServiceWorkItemImpl::DeleteServiceImpl() { // Uninstall the elevation service. - if (clsid_ != GUID_NULL) { + for (const auto& clsid : clsids_) { for (const auto& reg_path : - {GetComClsidRegistryPath(clsid_), GetComAppidRegistryPath(clsid_)}) { + {GetComClsidRegistryPath(clsid), GetComAppidRegistryPath(clsid)}) { InstallUtil::DeleteRegistryKey(HKEY_LOCAL_MACHINE, reg_path, WorkItem::kWow64Default); } } - if (iid_ != GUID_NULL) { + + for (const auto& iid : iids_) { for (const auto& reg_path : - {GetComIidRegistryPath(iid_), GetComTypeLibRegistryPath(iid_)}) { + {GetComIidRegistryPath(iid), GetComTypeLibRegistryPath(iid)}) { InstallUtil::DeleteRegistryKey(HKEY_LOCAL_MACHINE, reg_path, WorkItem::kWow64Default); } } + scm_.Set(::OpenSCManager(nullptr, nullptr, SC_MANAGER_CONNECT)); if (!scm_.IsValid()) { DPLOG(ERROR) << "::OpenSCManager Failed";
diff --git a/chrome/installer/util/install_service_work_item_impl.h b/chrome/installer/util/install_service_work_item_impl.h index d4125191..8ff5626 100644 --- a/chrome/installer/util/install_service_work_item_impl.h +++ b/chrome/installer/util/install_service_work_item_impl.h
@@ -54,8 +54,8 @@ const base::string16& display_name, const base::CommandLine& service_cmd_line, const base::string16& registry_path, - const GUID& clsid, - const GUID& iid); + const std::vector<GUID>& clsids, + const std::vector<GUID>& iids); ~InstallServiceWorkItemImpl(); @@ -170,13 +170,12 @@ // to the 32-bit view of the registry. const base::string16 registry_path_; - // If COM CLSID/AppId registration is required, |clsid| would contain a valid - // CLSID. - const GUID clsid_; + // If COM CLSID/AppId registration is required, |clsids_| would be populated. + const std::vector<GUID> clsids_; - // If COM Interface/Typelib registration is required, |iid| would contain a - // valid IID. - const GUID iid_; + // If COM Interface/Typelib registration is required, |iids_| would be + // populated. + const std::vector<GUID> iids_; ScopedScHandle scm_; ScopedScHandle service_;
diff --git a/chrome/installer/util/install_service_work_item_unittest.cc b/chrome/installer/util/install_service_work_item_unittest.cc index 5736557b..0bd18db 100644 --- a/chrome/installer/util/install_service_work_item_unittest.cc +++ b/chrome/installer/util/install_service_work_item_unittest.cc
@@ -36,6 +36,8 @@ 0x9c33, 0x4a09, {0x9b, 0x3a, 0x3b, 0x88, 0xd, 0xf6, 0x44, 0x40}}; +const std::vector<GUID> kClsids = {kClsid}; + constexpr base::char16 kClsidRegPath[] = L"Software\\Classes\\CLSID\\{76EDE292-9C33-4A09-9B3A-3B880DF64440}"; constexpr base::char16 kAppidRegPath[] = @@ -46,6 +48,8 @@ 0xa94a, 0x4c0a, {0x93, 0xc7, 0x81, 0x33, 0x5, 0x26, 0xac, 0x7b}}; +const std::vector<GUID> kIids = {kIid}; + #define IID_REGISTRY_PATH \ L"Software\\Classes\\Interface\\{0F9A0C1C-A94A-4C0A-93C7-81330526AC7B}" constexpr base::char16 kIidPSRegPath[] = @@ -116,7 +120,7 @@ auto item = std::make_unique<InstallServiceWorkItem>( kServiceName, kServiceDisplayName, base::CommandLine(base::FilePath(kServiceProgramPath)), kProductRegPath, - kClsid, kIid); + kClsids, kIids); ASSERT_TRUE(item->Do()); EXPECT_TRUE(GetImpl(item.get())->OpenService()); @@ -177,21 +181,21 @@ auto item = std::make_unique<InstallServiceWorkItem>( kServiceName, kServiceDisplayName, base::CommandLine(base::FilePath(kServiceProgramPath)), kProductRegPath, - kClsid, kIid); + kClsids, kIids); ASSERT_TRUE(item->Do()); EXPECT_TRUE(GetImpl(item.get())->OpenService()); EXPECT_TRUE(IsServiceCorrectlyConfigured(item.get())); EXPECT_TRUE(InstallServiceWorkItem::DeleteService( - kServiceName, kProductRegPath, kClsid, kIid)); + kServiceName, kProductRegPath, kClsids, kIids)); } TEST_F(InstallServiceWorkItemTest, Do_UpgradeNoChanges) { auto item = std::make_unique<InstallServiceWorkItem>( kServiceName, kServiceDisplayName, base::CommandLine(base::FilePath(kServiceProgramPath)), kProductRegPath, - kClsid, kIid); + kClsids, kIids); ASSERT_TRUE(item->Do()); EXPECT_TRUE(IsServiceCorrectlyConfigured(item.get())); @@ -200,7 +204,7 @@ auto item_upgrade = std::make_unique<InstallServiceWorkItem>( kServiceName, kServiceDisplayName, base::CommandLine(base::FilePath(kServiceProgramPath)), kProductRegPath, - kClsid, kIid); + kClsids, kIids); EXPECT_TRUE(item_upgrade->Do()); item_upgrade->Rollback(); @@ -213,7 +217,7 @@ auto item = std::make_unique<InstallServiceWorkItem>( kServiceName, kServiceDisplayName, base::CommandLine(base::FilePath(kServiceProgramPath)), kProductRegPath, - kClsid, kIid); + kClsids, kIids); ASSERT_TRUE(item->Do()); EXPECT_TRUE(IsServiceCorrectlyConfigured(item.get())); @@ -222,7 +226,7 @@ auto item_upgrade = std::make_unique<InstallServiceWorkItem>( kServiceName, kServiceDisplayName, base::CommandLine::FromString(L"NewCmd.exe arg1 arg2"), kProductRegPath, - kClsid, kIid); + kClsids, kIids); EXPECT_TRUE(item_upgrade->Do()); item_upgrade->Rollback(); @@ -238,7 +242,7 @@ auto item = std::make_unique<InstallServiceWorkItem>( kServiceName, kServiceDisplayName, base::CommandLine(base::FilePath(kServiceProgramPath)), kProductRegPath, - kClsid, kIid); + kClsids, kIids); EXPECT_STREQ(kServiceName, GetImpl(item.get())->GetCurrentServiceName().c_str());
diff --git a/chrome/renderer/resources/extensions/file_manager_private_custom_bindings.js b/chrome/renderer/resources/extensions/file_manager_private_custom_bindings.js index 3e1d23f..69f1602 100644 --- a/chrome/renderer/resources/extensions/file_manager_private_custom_bindings.js +++ b/chrome/renderer/resources/extensions/file_manager_private_custom_bindings.js
@@ -267,6 +267,14 @@ }); fileManagerPrivateInternal.sharesheetHasTargets(urls, callback); }); + + apiFunctions.setHandleRequest( + 'invokeSharesheet', function(entries, callback) { + var urls = entries.map(function(entry) { + return getEntryURL(entry); + }); + fileManagerPrivateInternal.invokeSharesheet(urls, callback); + }); }); bindingUtil.registerEventArgumentMassager(
diff --git a/chrome/services/machine_learning/BUILD.gn b/chrome/services/machine_learning/BUILD.gn index 897ca2fc..fefc19f 100644 --- a/chrome/services/machine_learning/BUILD.gn +++ b/chrome/services/machine_learning/BUILD.gn
@@ -48,6 +48,7 @@ "public/cpp/decision_tree/decision_tree_prediction_model_unittest.cc", "public/cpp/decision_tree/prediction_model_unittest.cc", "public/cpp/decision_tree_model_unittest.cc", + "public/cpp/test_support/fake_service_connection_unittest.cc", ] if (build_with_tflite_lib) {
diff --git a/chrome/services/machine_learning/public/cpp/BUILD.gn b/chrome/services/machine_learning/public/cpp/BUILD.gn index b9b5a25..cba969eb 100644 --- a/chrome/services/machine_learning/public/cpp/BUILD.gn +++ b/chrome/services/machine_learning/public/cpp/BUILD.gn
@@ -39,11 +39,18 @@ } source_set("test_support") { - public = [ "test_support/machine_learning_test_utils.h" ] + public = [ + "test_support/fake_service_connection.h", + "test_support/machine_learning_test_utils.h", + ] - sources = [ "test_support/machine_learning_test_utils.cc" ] + sources = [ + "test_support/fake_service_connection.cc", + "test_support/machine_learning_test_utils.cc", + ] public_deps = [ + ":cpp", "//base", "//chrome/services/machine_learning/public/mojom", "//components/optimization_guide",
diff --git a/chrome/services/machine_learning/public/cpp/test_support/fake_service_connection.cc b/chrome/services/machine_learning/public/cpp/test_support/fake_service_connection.cc new file mode 100644 index 0000000..67e9ef0 --- /dev/null +++ b/chrome/services/machine_learning/public/cpp/test_support/fake_service_connection.cc
@@ -0,0 +1,137 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/services/machine_learning/public/cpp/test_support/fake_service_connection.h" + +#include <string> +#include <utility> +#include "base/bind.h" +#include "base/callback_forward.h" +#include "base/containers/flat_map.h" +#include "chrome/services/machine_learning/public/mojom/decision_tree.mojom.h" +#include "chrome/services/machine_learning/public/mojom/machine_learning_service.mojom.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" + +namespace machine_learning { +namespace testing { + +FakeServiceConnection::FakeServiceConnection() { + ServiceConnection::SetServiceConnectionForTesting(this); +} + +FakeServiceConnection::~FakeServiceConnection() { + FakeServiceConnection::SetServiceConnectionForTesting(nullptr); +} + +void FakeServiceConnection::ScheduleCall(base::OnceClosure callback) { + if (!is_async_) { + std::move(callback).Run(); + } + + pending_calls_.emplace_back(std::move(callback)); +} + +void FakeServiceConnection::RunScheduledCalls() { + for (auto& call : pending_calls_) { + std::move(call).Run(); + } + + pending_calls_.clear(); +} + +void FakeServiceConnection::SetLoadModelResult(mojom::LoadModelResult result) { + load_model_result_ = result; +} + +void FakeServiceConnection::SetDecisionTreePredictionResult( + mojom::DecisionTreePredictionResult result, + double prediction_score) { + if (result == mojom::DecisionTreePredictionResult::kUnknown) + prediction_score = 0.0; + + decision_tree_prediction_result_ = result; + decision_tree_prediction_score_ = prediction_score; +} + +void FakeServiceConnection::SetAsyncModeForTesting(bool is_async) { + is_async_ = is_async; +} + +bool FakeServiceConnection::is_service_running() const { + return is_service_running_; +} + +mojom::MachineLearningService* FakeServiceConnection::GetService() { + if (!is_service_running_) { + is_service_running_ = true; + } + + return this; +} + +void FakeServiceConnection::ResetServiceForTesting() { + if (is_service_running_) { + is_service_running_ = false; + decision_tree_receivers_.Clear(); + pending_calls_.clear(); + load_model_result_ = mojom::LoadModelResult::kLoadModelError; + decision_tree_prediction_result_ = + mojom::DecisionTreePredictionResult::kUnknown; + decision_tree_prediction_score_ = 0.0; + } +} + +void FakeServiceConnection::LoadDecisionTreeModel( + mojom::DecisionTreeModelSpecPtr spec, + mojo::PendingReceiver<mojom::DecisionTreePredictor> receiver, + LoadDecisionTreeCallback callback) { + GetService(); + LoadDecisionTree(std::move(spec), std::move(receiver), std::move(callback)); +} + +void FakeServiceConnection::LoadDecisionTree( + mojom::DecisionTreeModelSpecPtr spec, + mojo::PendingReceiver<mojom::DecisionTreePredictor> receiver, + LoadDecisionTreeCallback callback) { + if (!is_service_running_) + return; + + ScheduleCall(base::BindOnce(&FakeServiceConnection::HandleLoadDecisionTree, + base::Unretained(this), std::move(receiver), + std::move(callback))); +} + +void FakeServiceConnection::HandleLoadDecisionTree( + mojo::PendingReceiver<mojom::DecisionTreePredictor> receiver, + LoadDecisionTreeCallback callback) { + if (!is_service_running_) + return; + + if (load_model_result_ == mojom::LoadModelResult::kOk) + decision_tree_receivers_.Add(this, std::move(receiver)); + + std::move(callback).Run(load_model_result_); +} + +void FakeServiceConnection::Predict( + const base::flat_map<std::string, float>& model_features, + PredictCallback callback) { + if (!is_service_running_) + return; + + ScheduleCall(base::BindOnce(&FakeServiceConnection::HandleDecisionTreePredict, + base::Unretained(this), std::move(callback))); +} + +void FakeServiceConnection::HandleDecisionTreePredict( + PredictCallback callback) { + if (!is_service_running_) + return; + + std::move(callback).Run(decision_tree_prediction_result_, + decision_tree_prediction_score_); +} + +} // namespace testing +} // namespace machine_learning
diff --git a/chrome/services/machine_learning/public/cpp/test_support/fake_service_connection.h b/chrome/services/machine_learning/public/cpp/test_support/fake_service_connection.h new file mode 100644 index 0000000..b4896c1c --- /dev/null +++ b/chrome/services/machine_learning/public/cpp/test_support/fake_service_connection.h
@@ -0,0 +1,100 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_SERVICES_MACHINE_LEARNING_PUBLIC_CPP_TEST_SUPPORT_FAKE_SERVICE_CONNECTION_H_ +#define CHROME_SERVICES_MACHINE_LEARNING_PUBLIC_CPP_TEST_SUPPORT_FAKE_SERVICE_CONNECTION_H_ + +#include <string> +#include <vector> + +#include "base/callback_forward.h" +#include "chrome/services/machine_learning/public/cpp/service_connection.h" +#include "chrome/services/machine_learning/public/mojom/decision_tree.mojom.h" +#include "chrome/services/machine_learning/public/mojom/machine_learning_service.mojom.h" +#include "mojo/public/cpp/bindings/receiver_set.h" + +namespace machine_learning { +namespace testing { + +// Fake implementation of machine_learning::ServiceConnection. +// - Replaces the actual ServiceConnection singleton on initialization. +// - Handles LoadDecisionTreeModel by returning the value specified by a +// previous call to SetLoadModelResults. Binds to itself if result is set to +// kOk. +// - Handles DecisionTreePredictor::Predict by returning the values specified by +// a previous call to SetDecisionTreePredictionResult. +class FakeServiceConnection : public ServiceConnection, + public mojom::MachineLearningService, + public mojom::DecisionTreePredictor { + public: + FakeServiceConnection(); + ~FakeServiceConnection() override; + + FakeServiceConnection(const FakeServiceConnection&) = delete; + FakeServiceConnection& operator=(const FakeServiceConnection&) = delete; + + // Runs all scheduled callbacks and removes them from schedule. + void RunScheduledCalls(); + + // Sets the return value for model loading. + void SetLoadModelResult(mojom::LoadModelResult result); + + // Sets the return value for decision tree prediction. + void SetDecisionTreePredictionResult( + mojom::DecisionTreePredictionResult result, + double prediction_score); + + // Whether the service is running. + bool is_service_running() const; + + // ServiceConnection implementations. + mojom::MachineLearningService* GetService() override; + void ResetServiceForTesting() override; + void LoadDecisionTreeModel( + mojom::DecisionTreeModelSpecPtr spec, + mojo::PendingReceiver<mojom::DecisionTreePredictor> receiver, + mojom::MachineLearningService::LoadDecisionTreeCallback callback) + override; + + // Sets whether calls are handled async for testing purposes. + void SetAsyncModeForTesting(bool is_async); + + private: + // Store |callback| to be run when RunScheduledCalls is invoked. + void ScheduleCall(base::OnceClosure callback); + + // mojom::MachineLearningService implementations. + void LoadDecisionTree( + mojom::DecisionTreeModelSpecPtr spec, + mojo::PendingReceiver<mojom::DecisionTreePredictor> receiver, + LoadDecisionTreeCallback callback) override; + + // Callback used to handle LoadDecisionTree calls. + void HandleLoadDecisionTree( + mojo::PendingReceiver<mojom::DecisionTreePredictor> receiver, + LoadDecisionTreeCallback callback); + + // mojom::DecisionTreePredictor implementations. + void Predict(const base::flat_map<std::string, float>& model_features, + PredictCallback callback) override; + + // Callback used to handle Predict calls. + void HandleDecisionTreePredict(PredictCallback callback); + + bool is_service_running_ = false; + bool is_async_ = true; + + mojo::ReceiverSet<mojom::DecisionTreePredictor> decision_tree_receivers_; + std::vector<base::OnceClosure> pending_calls_; + mojom::LoadModelResult load_model_result_ = + mojom::LoadModelResult::kLoadModelError; + mojom::DecisionTreePredictionResult decision_tree_prediction_result_ = + mojom::DecisionTreePredictionResult::kUnknown; + double decision_tree_prediction_score_ = 0.0; +}; + +} // namespace testing +} // namespace machine_learning + +#endif // CHROME_SERVICES_MACHINE_LEARNING_PUBLIC_CPP_TEST_SUPPORT_FAKE_SERVICE_CONNECTION_H_
diff --git a/chrome/services/machine_learning/public/cpp/test_support/fake_service_connection_unittest.cc b/chrome/services/machine_learning/public/cpp/test_support/fake_service_connection_unittest.cc new file mode 100644 index 0000000..6f9e6ce8 --- /dev/null +++ b/chrome/services/machine_learning/public/cpp/test_support/fake_service_connection_unittest.cc
@@ -0,0 +1,133 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/services/machine_learning/public/cpp/test_support/fake_service_connection.h" + +#include <memory> +#include <utility> + +#include "base/bind.h" +#include "base/run_loop.h" +#include "base/test/task_environment.h" +#include "chrome/services/machine_learning/machine_learning_service.h" +#include "chrome/services/machine_learning/public/cpp/test_support/machine_learning_test_utils.h" +#include "chrome/services/machine_learning/public/mojom/decision_tree.mojom-shared.h" +#include "chrome/services/machine_learning/public/mojom/decision_tree.mojom.h" +#include "chrome/services/machine_learning/public/mojom/machine_learning_service.mojom-shared.h" +#include "chrome/services/machine_learning/public/mojom/machine_learning_service.mojom.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace machine_learning { + +TEST(FakeServiceConnectionTest, MultipleLaunchesReusesSameService) { + testing::FakeServiceConnection service_connection; + + ASSERT_EQ(&service_connection, ServiceConnection::GetInstance()); + + auto* service_ptr1 = service_connection.GetService(); + EXPECT_TRUE(service_ptr1); + EXPECT_TRUE(service_connection.is_service_running()); + + auto* service_ptr2 = service_connection.GetService(); + EXPECT_EQ(service_ptr1, service_ptr2); + + service_connection.ResetServiceForTesting(); + EXPECT_FALSE(service_connection.is_service_running()); +} + +TEST(FakeServiceConnectionTest, LoadInvalidDecisionTree) { + base::test::SingleThreadTaskEnvironment task_environment; + testing::FakeServiceConnection service_connection; + + mojo::Remote<mojom::DecisionTreePredictor> predictor; + mojom::LoadModelResult result = mojom::LoadModelResult::kLoadModelError; + + service_connection.LoadDecisionTreeModel( + mojom::DecisionTreeModelSpec::New("some model string"), + predictor.BindNewPipeAndPassReceiver(), + base::BindOnce([](mojom::LoadModelResult* p_result, + mojom::LoadModelResult result) { *p_result = result; }, + &result)); + + service_connection.SetLoadModelResult( + mojom::LoadModelResult::kModelSpecError); + service_connection.RunScheduledCalls(); + + EXPECT_TRUE(service_connection.is_service_running()); + EXPECT_EQ(mojom::LoadModelResult::kModelSpecError, result); + + predictor.FlushForTesting(); + // Invalid models doesn't lead to a predictor connection. + EXPECT_FALSE(predictor.is_connected()); +} + +TEST(FakeServiceConnectionTest, LoadValidDecisionTreeAndPredict) { + base::test::SingleThreadTaskEnvironment task_environment; + testing::FakeServiceConnection service_connection; + + // Making an actual model for consistency even though we are mocking the + // result. + auto model_proto = testing::GetModelProtoForPredictionResult( + mojom::DecisionTreePredictionResult::kTrue); + + mojo::Remote<mojom::DecisionTreePredictor> predictor; + mojom::LoadModelResult result = mojom::LoadModelResult::kLoadModelError; + + service_connection.LoadDecisionTreeModel( + mojom::DecisionTreeModelSpec::New(model_proto->SerializeAsString()), + predictor.BindNewPipeAndPassReceiver(), + base::BindOnce([](mojom::LoadModelResult* p_result, + mojom::LoadModelResult result) { *p_result = result; }, + &result)); + + service_connection.SetLoadModelResult(mojom::LoadModelResult::kOk); + service_connection.RunScheduledCalls(); + + EXPECT_TRUE(service_connection.is_service_running()); + EXPECT_EQ(mojom::LoadModelResult::kOk, result); + + predictor.FlushForTesting(); + // Valid models leads to a predictor connection. + EXPECT_TRUE(predictor.is_connected()); + + // Normal prediction call. + const mojom::DecisionTreePredictionResult prediction_result_expected = + mojom::DecisionTreePredictionResult::kTrue; + const double prediction_score_expected = 2.33; + + predictor->Predict( + {}, base::BindOnce( + [](mojom::DecisionTreePredictionResult result_expected, + double score_expected, + mojom::DecisionTreePredictionResult result, double score) { + EXPECT_EQ(result_expected, result); + EXPECT_EQ(score_expected, score); + }, + prediction_result_expected, prediction_score_expected)); + predictor.FlushForTesting(); + + service_connection.SetDecisionTreePredictionResult(prediction_result_expected, + prediction_score_expected); + service_connection.RunScheduledCalls(); + + // Predictor launches a call but the service got disconnected. + predictor->Predict( + {}, base::BindOnce( + [](mojom::DecisionTreePredictionResult result, double score) { + // Callback should not run. + FAIL(); + })); + predictor.FlushForTesting(); + + service_connection.ResetServiceForTesting(); + service_connection.RunScheduledCalls(); + + predictor.FlushForTesting(); + EXPECT_FALSE(predictor.is_connected()); +} + +} // namespace machine_learning
diff --git a/chrome/services/sharing/public/mojom/sharing.mojom b/chrome/services/sharing/public/mojom/sharing.mojom index e704b64..2f54ad1 100644 --- a/chrome/services/sharing/public/mojom/sharing.mojom +++ b/chrome/services/sharing/public/mojom/sharing.mojom
@@ -14,8 +14,8 @@ // and is used by the browser process to offload unsafe protocol parsing. interface Sharing { // Creates a new Sharing connection via WebRTC. - // Signalling of WebRTC is done OOB by the browser using the - // |signalling_sender| and |signalling_receiver| interfaces. + // Signaling of WebRTC is done OOB by the browser using the |signaling_sender| + // and |signalling_receiver| interfaces. // WebRTC messages to and from the browser process are handled via the // |delegate| and |connection| interfaces. // All network communication is handled by the |socket_manager| and @@ -23,7 +23,7 @@ // |ice_servers| contains the list of ICE servers to be used to establish a // connection. CreateSharingWebRtcConnection( - pending_remote<SignallingSender> signalling_sender, + pending_remote<SignalingSender> signaling_sender, pending_receiver<SignallingReceiver> signalling_receiver, pending_remote<SharingWebRtcConnectionDelegate> delegate, pending_receiver<SharingWebRtcConnection> connection,
diff --git a/chrome/services/sharing/public/mojom/webrtc.mojom b/chrome/services/sharing/public/mojom/webrtc.mojom index 1124521d..2330119 100644 --- a/chrome/services/sharing/public/mojom/webrtc.mojom +++ b/chrome/services/sharing/public/mojom/webrtc.mojom
@@ -7,7 +7,7 @@ import "url/mojom/url.mojom"; // Represents an ICE candidate as defined in RFC 5245. These will be exchanged -// OOB via the SignallingSender and SignallingReceiver interfaces. +// OOB via the SignalingSender and SignallingReceiver interfaces. struct IceCandidate { // Represents the remote address to connect to as defined in RFC 5245 # 15.1. string candidate; @@ -42,9 +42,9 @@ GetIceServers() => (array<IceServer> ice_servers); }; -// Signalling sender interface used to exchange offer / answer pairs and a list +// Signaling sender interface used to exchange offer / answer pairs and a list // of ICE candidates OOB. Implemented in the browser process. -interface SignallingSender { +interface SignalingSender { // Sends an offer to the remote expecting an answer in response. The content // of both is in SDP format. SendOffer(string offer) => (string answer);
diff --git a/chrome/services/sharing/sharing_impl.cc b/chrome/services/sharing/sharing_impl.cc index 21cdae8..98274014 100644 --- a/chrome/services/sharing/sharing_impl.cc +++ b/chrome/services/sharing/sharing_impl.cc
@@ -23,7 +23,7 @@ SharingImpl::~SharingImpl() = default; void SharingImpl::CreateSharingWebRtcConnection( - mojo::PendingRemote<mojom::SignallingSender> signalling_sender, + mojo::PendingRemote<mojom::SignalingSender> signaling_sender, mojo::PendingReceiver<mojom::SignallingReceiver> signalling_receiver, mojo::PendingRemote<mojom::SharingWebRtcConnectionDelegate> delegate, mojo::PendingReceiver<mojom::SharingWebRtcConnection> connection, @@ -36,7 +36,7 @@ // base::Unretained is safe as the |peer_connection| is owned by |this|. auto sharing_connection = std::make_unique<SharingWebRtcConnection>( webrtc_peer_connection_factory_.get(), std::move(ice_servers), - std::move(signalling_sender), std::move(signalling_receiver), + std::move(signaling_sender), std::move(signalling_receiver), std::move(delegate), std::move(connection), std::move(socket_manager), std::move(mdns_responder), base::BindOnce(&SharingImpl::SharingWebRtcConnectionDisconnected,
diff --git a/chrome/services/sharing/sharing_impl.h b/chrome/services/sharing/sharing_impl.h index e148347..17764182f 100644 --- a/chrome/services/sharing/sharing_impl.h +++ b/chrome/services/sharing/sharing_impl.h
@@ -53,7 +53,7 @@ // mojom::Sharing: void CreateSharingWebRtcConnection( - mojo::PendingRemote<mojom::SignallingSender> signalling_sender, + mojo::PendingRemote<mojom::SignalingSender> signaling_sender, mojo::PendingReceiver<mojom::SignallingReceiver> signalling_receiver, mojo::PendingRemote<mojom::SharingWebRtcConnectionDelegate> delegate, mojo::PendingReceiver<mojom::SharingWebRtcConnection> connection,
diff --git a/chrome/services/sharing/sharing_impl_unittest.cc b/chrome/services/sharing/sharing_impl_unittest.cc index 4b2db20b..0632638 100644 --- a/chrome/services/sharing/sharing_impl_unittest.cc +++ b/chrome/services/sharing/sharing_impl_unittest.cc
@@ -41,7 +41,7 @@ std::unique_ptr<MockSharingConnectionHost> CreateWebRtcConnection() { auto connection = std::make_unique<MockSharingConnectionHost>(); service_->CreateSharingWebRtcConnection( - connection->signalling_sender.BindNewPipeAndPassRemote(), + connection->signaling_sender.BindNewPipeAndPassRemote(), connection->signalling_receiver.BindNewPipeAndPassReceiver(), connection->delegate.BindNewPipeAndPassRemote(), connection->connection.BindNewPipeAndPassReceiver(),
diff --git a/chrome/services/sharing/webrtc/sharing_webrtc_connection.cc b/chrome/services/sharing/webrtc/sharing_webrtc_connection.cc index 7140265..080402c8 100644 --- a/chrome/services/sharing/webrtc/sharing_webrtc_connection.cc +++ b/chrome/services/sharing/webrtc/sharing_webrtc_connection.cc
@@ -149,7 +149,7 @@ SharingWebRtcConnection::SharingWebRtcConnection( webrtc::PeerConnectionFactoryInterface* connection_factory, const std::vector<mojom::IceServerPtr>& ice_servers, - mojo::PendingRemote<mojom::SignallingSender> signalling_sender, + mojo::PendingRemote<mojom::SignalingSender> signaling_sender, mojo::PendingReceiver<mojom::SignallingReceiver> signalling_receiver, mojo::PendingRemote<mojom::SharingWebRtcConnectionDelegate> delegate, mojo::PendingReceiver<mojom::SharingWebRtcConnection> connection, @@ -157,7 +157,7 @@ mojo::PendingRemote<network::mojom::MdnsResponder> mdns_responder, base::OnceCallback<void(SharingWebRtcConnection*)> on_disconnect) : signalling_receiver_(this, std::move(signalling_receiver)), - signalling_sender_(std::move(signalling_sender)), + signaling_sender_(std::move(signaling_sender)), connection_(this, std::move(connection)), delegate_(std::move(delegate)), p2p_socket_manager_(std::move(socket_manager)), @@ -175,7 +175,7 @@ rtc_config.servers.push_back(ice_turn_server); } - signalling_sender_.set_disconnect_handler( + signaling_sender_.set_disconnect_handler( base::BindOnce(&SharingWebRtcConnection::CloseConnection, weak_ptr_factory_.GetWeakPtr())); delegate_.set_disconnect_handler( @@ -559,7 +559,7 @@ timing_recorder_.LogEvent(WebRtcTimingEvent::kOfferCreated); - signalling_sender_->SendOffer( + signaling_sender_->SendOffer( sdp, base::BindOnce(&SharingWebRtcConnection::OnAnswerReceived, weak_ptr_factory_.GetWeakPtr())); } @@ -641,7 +641,7 @@ queued_messages_.pop(); } - signalling_sender_.reset(); + signaling_sender_.reset(); delegate_.reset(); // Close DataChannel if necessary. @@ -718,7 +718,7 @@ } if (!local_ice_candidates_.empty()) { - signalling_sender_->SendIceCandidates(std::move(local_ice_candidates_)); + signaling_sender_->SendIceCandidates(std::move(local_ice_candidates_)); local_ice_candidates_.clear(); } }
diff --git a/chrome/services/sharing/webrtc/sharing_webrtc_connection.h b/chrome/services/sharing/webrtc/sharing_webrtc_connection.h index 748011c..16de8e5 100644 --- a/chrome/services/sharing/webrtc/sharing_webrtc_connection.h +++ b/chrome/services/sharing/webrtc/sharing_webrtc_connection.h
@@ -34,7 +34,7 @@ class IpcPacketSocketFactory; -// Manages a WebRTC PeerConnection. Signalling is handled via the passed sender +// Manages a WebRTC PeerConnection. Signaling is handled via the passed sender // and receiver. All network communication is handled by the network service via // the passed P2PSocketManager and MdnsResponder pipes. All methods of this // class are called on the same thread and the PeerConnectionFactory is setup to @@ -47,7 +47,7 @@ SharingWebRtcConnection( webrtc::PeerConnectionFactoryInterface* connection_factory, const std::vector<mojom::IceServerPtr>& ice_servers, - mojo::PendingRemote<mojom::SignallingSender> signalling_sender, + mojo::PendingRemote<mojom::SignalingSender> signaling_sender, mojo::PendingReceiver<mojom::SignallingReceiver> signalling_receiver, mojo::PendingRemote<mojom::SharingWebRtcConnectionDelegate> delegate, mojo::PendingReceiver<mojom::SharingWebRtcConnection> connection, @@ -132,7 +132,7 @@ const rtc::scoped_refptr<const webrtc::RTCStatsReport>& report); mojo::Receiver<mojom::SignallingReceiver> signalling_receiver_; - mojo::Remote<mojom::SignallingSender> signalling_sender_; + mojo::Remote<mojom::SignalingSender> signaling_sender_; mojo::Receiver<mojom::SharingWebRtcConnection> connection_; mojo::Remote<mojom::SharingWebRtcConnectionDelegate> delegate_; mojo::Remote<network::mojom::P2PSocketManager> p2p_socket_manager_;
diff --git a/chrome/services/sharing/webrtc/sharing_webrtc_connection_integration_test.cc b/chrome/services/sharing/webrtc/sharing_webrtc_connection_integration_test.cc index 11a82d2..6c39111f 100644 --- a/chrome/services/sharing/webrtc/sharing_webrtc_connection_integration_test.cc +++ b/chrome/services/sharing/webrtc/sharing_webrtc_connection_integration_test.cc
@@ -35,7 +35,7 @@ base::OnceCallback<void(sharing::SharingWebRtcConnection*)> on_disconnect) : connection_(pc_factory, /*ice_servers=*/{}, - host_.signalling_sender.BindNewPipeAndPassRemote(), + host_.signaling_sender.BindNewPipeAndPassRemote(), host_.signalling_receiver.BindNewPipeAndPassReceiver(), host_.delegate.BindNewPipeAndPassRemote(), host_.connection.BindNewPipeAndPassReceiver(),
diff --git a/chrome/services/sharing/webrtc/sharing_webrtc_connection_unittest.cc b/chrome/services/sharing/webrtc/sharing_webrtc_connection_unittest.cc index 77eb358..6ad65ef 100644 --- a/chrome/services/sharing/webrtc/sharing_webrtc_connection_unittest.cc +++ b/chrome/services/sharing/webrtc/sharing_webrtc_connection_unittest.cc
@@ -173,7 +173,7 @@ connection_ = std::make_unique<SharingWebRtcConnection>( mock_webrtc_pc_factory_.get(), std::vector<mojom::IceServerPtr>(), - connection_host_.signalling_sender.BindNewPipeAndPassRemote(), + connection_host_.signaling_sender.BindNewPipeAndPassRemote(), connection_host_.signalling_receiver.BindNewPipeAndPassReceiver(), connection_host_.delegate.BindNewPipeAndPassRemote(), connection_host_.connection.BindNewPipeAndPassReceiver(),
diff --git a/chrome/services/sharing/webrtc/test/mock_sharing_connection_host.h b/chrome/services/sharing/webrtc/test/mock_sharing_connection_host.h index 8bda29d..e2c5fa0 100644 --- a/chrome/services/sharing/webrtc/test/mock_sharing_connection_host.h +++ b/chrome/services/sharing/webrtc/test/mock_sharing_connection_host.h
@@ -14,7 +14,7 @@ namespace sharing { -class MockSharingConnectionHost : public mojom::SignallingSender, +class MockSharingConnectionHost : public mojom::SignalingSender, public mojom::SharingWebRtcConnectionDelegate, public network::mojom::P2PSocketManager, public network::mojom::MdnsResponder { @@ -25,7 +25,7 @@ delete; ~MockSharingConnectionHost() override; - // mojom::SignallingSender: + // mojom::SignalingSender: MOCK_METHOD2(SendOffer, void(const std::string&, SendOfferCallback)); MOCK_METHOD1(SendIceCandidates, void(std::vector<mojom::IceCandidatePtr>)); @@ -52,7 +52,7 @@ MOCK_METHOD2(RemoveNameForAddress, void(const ::net::IPAddress&, RemoveNameForAddressCallback)); - mojo::Receiver<mojom::SignallingSender> signalling_sender{this}; + mojo::Receiver<mojom::SignalingSender> signaling_sender{this}; mojo::Remote<mojom::SignallingReceiver> signalling_receiver; mojo::Receiver<mojom::SharingWebRtcConnectionDelegate> delegate{this}; mojo::Remote<mojom::SharingWebRtcConnection> connection;
diff --git a/chrome/test/data/extensions/api_test/bindings/uncaught_exception_logging/test.js b/chrome/test/data/extensions/api_test/bindings/uncaught_exception_logging/test.js index 58acb63..bfcdafd2 100644 --- a/chrome/test/data/extensions/api_test/bindings/uncaught_exception_logging/test.js +++ b/chrome/test/data/extensions/api_test/bindings/uncaught_exception_logging/test.js
@@ -2,49 +2,49 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -function createTestFunction(expected_message) { - return function(tab) { - function onDebuggerEvent(debuggee, method, params) { - if (debuggee.tabId == tab.id && method == 'Runtime.exceptionThrown') { - var exception = params.exceptionDetails.exception; - if (exception.value.indexOf(expected_message) > -1) { - chrome.debugger.onEvent.removeListener(onDebuggerEvent); - chrome.test.succeed(); - } +function verifyException(expectedMessage, tabId) { + function onDebuggerEvent(debuggee, method, params) { + if (debuggee.tabId == tabId && method == 'Runtime.exceptionThrown') { + var exception = params.exceptionDetails.exception; + if (exception.value.indexOf(expectedMessage) > -1) { + chrome.debugger.onEvent.removeListener(onDebuggerEvent); + chrome.test.succeed(); } - }; - chrome.debugger.onEvent.addListener(onDebuggerEvent); - chrome.debugger.attach({ tabId: tab.id }, "1.1", function() { - // Enabling console provides both stored and new messages via the - // Console.messageAdded event. - chrome.debugger.sendCommand({ tabId: tab.id }, "Runtime.enable"); - }); - } + } + }; + chrome.debugger.onEvent.addListener(onDebuggerEvent); + chrome.debugger.attach({ tabId: tabId }, "1.1", function() { + // Enabling console provides both stored and new messages via the + // Console.messageAdded event. + chrome.debugger.sendCommand({ tabId: tabId }, "Runtime.enable"); + }); } +let openTab; + chrome.test.runTests([ - function testExceptionInExtensionPage() { - chrome.tabs.create( - {url: chrome.runtime.getURL('extension_page.html')}, - createTestFunction('Exception thrown in extension page.')); + async function testExceptionInExtensionPage() { + ({openTab} = await import('/_test_resources/test_util/tabs_util.js')); + const tab = await openTab(chrome.runtime.getURL('extension_page.html')); + verifyException('Exception thrown in extension page.', tab.id); }, - function testExceptionInInjectedScript() { + async function testExceptionInInjectedScript() { function injectScriptAndSendMessage(tab) { chrome.tabs.executeScript( tab.id, { file: 'content_script.js' }, function() { - createTestFunction('Exception thrown in injected script.')(tab); + verifyException('Exception thrown in injected script.', tab.id); }); } - chrome.test.getConfig(function(config) { - var test_url = - 'http://localhost:PORT/extensions/test_file.html' - .replace(/PORT/, config.testServer.port); - - chrome.tabs.create({ url: test_url }, injectScriptAndSendMessage); + chrome.test.getConfig(async function(config) { + const testUrl = + `http://localhost:${config.testServer.port}/` + + 'extensions/test_file.html'; + const tab = await openTab(testUrl); + injectScriptAndSendMessage(tab); }); } ]);
diff --git a/chrome/test/data/extensions/api_test/debugger/background.js b/chrome/test/data/extensions/api_test/debugger/background.js index 49ec84f..7675e92b 100644 --- a/chrome/test/data/extensions/api_test/debugger/background.js +++ b/chrome/test/data/extensions/api_test/debugger/background.js
@@ -16,6 +16,8 @@ "'silent-debugger-extension-api' flag is enabled."; var DETACHED_WHILE_HANDLING = "Detached while handling command."; +let openTab; + chrome.test.getConfig(config => chrome.test.runTests([ function attachMalformedVersion() { @@ -92,86 +94,83 @@ fail("Debugger is not attached to the tab with id: " + tabId + ".")); }, - function closeTab() { - chrome.tabs.create({url:"inspected.html"}, function(tab) { - function onDetach(debuggee, reason) { - chrome.test.assertEq(tab.id, debuggee.tabId); - chrome.test.assertEq("target_closed", reason); - chrome.debugger.onDetach.removeListener(onDetach); - chrome.test.succeed(); - } + async function closeTab() { + ({openTab} = await import('/_test_resources/test_util/tabs_util.js')); + const tab = await openTab(chrome.runtime.getURL('inspected.html')); + function onDetach(debuggee, reason) { + chrome.test.assertEq(tab.id, debuggee.tabId); + chrome.test.assertEq("target_closed", reason); + chrome.debugger.onDetach.removeListener(onDetach); + chrome.test.succeed(); + } - var debuggee2 = {tabId: tab.id}; - chrome.debugger.attach(debuggee2, protocolVersion, function() { - chrome.debugger.onDetach.addListener(onDetach); - chrome.tabs.remove(tab.id); - }); - }); - }, - - function attachToWebUI() { - chrome.tabs.create({url:"chrome://version"}, function(tab) { - var debuggee = {tabId: tab.id}; - chrome.debugger.attach(debuggee, protocolVersion, - fail("Cannot access a chrome:// URL")); + const debuggee2 = {tabId: tab.id}; + chrome.debugger.attach(debuggee2, protocolVersion, function() { + chrome.debugger.onDetach.addListener(onDetach); chrome.tabs.remove(tab.id); }); }, - function navigateToWebUI() { - chrome.tabs.create({url:"inspected.html"}, function(tab) { - var debuggee = {tabId: tab.id}; - chrome.debugger.attach(debuggee, protocolVersion, function() { - var responded = false; + async function attachToWebUI() { + const tab = await openTab('chrome://version'); + const debuggee = {tabId: tab.id}; + chrome.debugger.attach(debuggee, protocolVersion, + fail("Cannot access a chrome:// URL")); + chrome.tabs.remove(tab.id); + }, - function onResponse() { - chrome.test.assertLastError(DETACHED_WHILE_HANDLING); - responded = true; - } + async function navigateToWebUI() { + const tab = await openTab(chrome.runtime.getURL('inspected.html')); + const debuggee = {tabId: tab.id}; + chrome.debugger.attach(debuggee, protocolVersion, function() { + var responded = false; - function onDetach(from, reason) { - chrome.debugger.onDetach.removeListener(onDetach); - chrome.test.assertTrue(responded); - chrome.test.assertEq(debuggee.tabId, from.tabId); - chrome.test.assertEq("target_closed", reason); - chrome.tabs.remove(tab.id, function() { - chrome.test.assertNoLastError(); - chrome.test.succeed(); - }); - } + function onResponse() { + chrome.test.assertLastError(DETACHED_WHILE_HANDLING); + responded = true; + } - chrome.test.assertNoLastError(); - chrome.debugger.onDetach.addListener(onDetach); - chrome.debugger.sendCommand( - debuggee, "Page.navigate", {url: "chrome://version"}, onResponse); - }); + function onDetach(from, reason) { + chrome.debugger.onDetach.removeListener(onDetach); + chrome.test.assertTrue(responded); + chrome.test.assertEq(debuggee.tabId, from.tabId); + chrome.test.assertEq("target_closed", reason); + chrome.tabs.remove(tab.id, function() { + chrome.test.assertNoLastError(); + chrome.test.succeed(); + }); + } + + chrome.test.assertNoLastError(); + chrome.debugger.onDetach.addListener(onDetach); + chrome.debugger.sendCommand( + debuggee, "Page.navigate", {url: "chrome://version"}, onResponse); }); }, - function detachDuringCommand() { - chrome.tabs.create({url:"inspected.html"}, function(tab) { - var debuggee = {tabId: tab.id}; - chrome.debugger.attach(debuggee, protocolVersion, function() { - var responded = false; + async function detachDuringCommand() { + const tab = await openTab(chrome.runtime.getURL('inspected.html')); + const debuggee = {tabId: tab.id}; + chrome.debugger.attach(debuggee, protocolVersion, function() { + var responded = false; - function onResponse() { - chrome.test.assertLastError(DETACHED_WHILE_HANDLING); - responded = true; - } + function onResponse() { + chrome.test.assertLastError(DETACHED_WHILE_HANDLING); + responded = true; + } - function onDetach() { - chrome.debugger.onDetach.removeListener(onDetach); - chrome.test.assertTrue(responded); - chrome.tabs.remove(tab.id, function() { - chrome.test.assertNoLastError(); - chrome.test.succeed(); - }); - } + function onDetach() { + chrome.debugger.onDetach.removeListener(onDetach); + chrome.test.assertTrue(responded); + chrome.tabs.remove(tab.id, function() { + chrome.test.assertNoLastError(); + chrome.test.succeed(); + }); + } - chrome.test.assertNoLastError(); - chrome.debugger.sendCommand(debuggee, "command", null, onResponse); - chrome.debugger.detach(debuggee, onDetach); - }); + chrome.test.assertNoLastError(); + chrome.debugger.sendCommand(debuggee, "command", null, onResponse); + chrome.debugger.detach(debuggee, onDetach); }); }, @@ -205,28 +204,22 @@ chrome.debugger.detach(debuggee, pass()); }, - function createAndDiscoverTab() { - function onUpdated(tabId, changeInfo) { - if (changeInfo.status == 'loading') - return; - chrome.tabs.onUpdated.removeListener(onUpdated); - chrome.debugger.getTargets(function(targets) { - var page = targets.filter( - function(t) { - return t.type == 'page' && - t.tabId == tabId && - t.title == 'Test page'; - })[0]; - if (page) { - chrome.debugger.attach( - {targetId: page.id}, protocolVersion, pass()); - } else { - chrome.test.fail("Cannot discover a newly created tab"); - } - }); - } - chrome.tabs.onUpdated.addListener(onUpdated); - chrome.tabs.create({url: "inspected.html"}); + async function createAndDiscoverTab() { + const tab = await openTab(chrome.runtime.getURL('inspected.html')); + chrome.debugger.getTargets(function(targets) { + var page = targets.filter( + function(t) { + return t.type == 'page' && + t.tabId == tab.id && + t.title == 'Test page'; + })[0]; + if (page) { + chrome.debugger.attach( + {targetId: page.id}, protocolVersion, pass()); + } else { + chrome.test.fail("Cannot discover a newly created tab"); + } + }); }, function discoverWorker() { @@ -250,56 +243,54 @@ chrome.debugger.detach(debuggee, pass()); }, - function sendCommandDuringNavigation() { - chrome.tabs.create({url:"inspected.html"}, function(tab) { - var debuggee = {tabId: tab.id}; + async function sendCommandDuringNavigation() { + const tab = await openTab(chrome.runtime.getURL('inspected.html')); + const debuggee = {tabId: tab.id}; - function checkError() { - if (chrome.runtime.lastError) { - chrome.test.fail(chrome.runtime.lastError.message); - } else { - chrome.tabs.remove(tab.id); - chrome.test.succeed(); - } + function checkError() { + if (chrome.runtime.lastError) { + chrome.test.fail(chrome.runtime.lastError.message); + } else { + chrome.tabs.remove(tab.id); + chrome.test.succeed(); } + } - function onNavigateDone() { - chrome.debugger.sendCommand(debuggee, "Page.disable", null, checkError); - } + function onNavigateDone() { + chrome.debugger.sendCommand(debuggee, "Page.disable", null, checkError); + } - function onAttach() { - chrome.debugger.sendCommand(debuggee, "Page.enable"); - chrome.debugger.sendCommand( - debuggee, "Page.navigate", {url:"about:blank"}, onNavigateDone); - } + function onAttach() { + chrome.debugger.sendCommand(debuggee, "Page.enable"); + chrome.debugger.sendCommand( + debuggee, "Page.navigate", {url:"about:blank"}, onNavigateDone); + } - chrome.debugger.attach(debuggee, protocolVersion, onAttach); - }); + chrome.debugger.attach(debuggee, protocolVersion, onAttach); }, - function sendCommandToDataUri() { - chrome.tabs.create({url:"data:text/html,<h1>hi</h1>"}, function(tab) { - var debuggee = {tabId: tab.id}; + async function sendCommandToDataUri() { + const tab = await openTab('data:text/html,<h1>hi</h1>'); + const debuggee = {tabId: tab.id}; - function checkError() { - if (chrome.runtime.lastError) { - chrome.test.fail(chrome.runtime.lastError.message); - } else { - chrome.tabs.remove(tab.id); - chrome.test.succeed(); - } + function checkError() { + if (chrome.runtime.lastError) { + chrome.test.fail(chrome.runtime.lastError.message); + } else { + chrome.tabs.remove(tab.id); + chrome.test.succeed(); } + } - function onAttach() { - chrome.debugger.sendCommand(debuggee, "Page.enable", null, checkError); - } + function onAttach() { + chrome.debugger.sendCommand(debuggee, "Page.enable", null, checkError); + } - chrome.debugger.attach(debuggee, protocolVersion, onAttach); - }); + chrome.debugger.attach(debuggee, protocolVersion, onAttach); }, // http://crbug.com/824174 - function getResponseBodyInvalidChar() { + async function getResponseBodyInvalidChar() { let requestId; function onEvent(debuggeeId, message, params) { @@ -319,105 +310,103 @@ } chrome.debugger.onEvent.addListener(onEvent); - chrome.tabs.create({url: 'inspected.html'}, function(tab) { - const debuggee = {tabId: tab.id}; - chrome.debugger.attach(debuggee, protocolVersion, function() { - chrome.debugger.sendCommand( - debuggee, 'Network.enable', null, function() { - chrome.debugger.sendCommand( - debuggee, 'Page.enable', null, function() { - // Navigate to a new page after attaching so we don't miss - // any protocol events that we might have missed while - // attaching to the first page. - chrome.debugger.sendCommand( - debuggee, 'Page.navigate', - {url: window.location.origin + '/fetch.html'}); - }); - }); - }); + const tab = await openTab(chrome.runtime.getURL('inspected.html')); + const debuggee = {tabId: tab.id}; + chrome.debugger.attach(debuggee, protocolVersion, function() { + chrome.debugger.sendCommand( + debuggee, 'Network.enable', null, function() { + chrome.debugger.sendCommand( + debuggee, 'Page.enable', null, function() { + // Navigate to a new page after attaching so we don't miss + // any protocol events that we might have missed while + // attaching to the first page. + chrome.debugger.sendCommand( + debuggee, 'Page.navigate', + {url: window.location.origin + '/fetch.html'}); + }); + }); }); }, - function offlineErrorPage() { + async function offlineErrorPage() { const url = 'http://127.0.0.1//extensions/api_test/debugger/inspected.html'; - chrome.tabs.create({url: url}, function(tab) { - var debuggee = {tabId: tab.id}; - var finished = false; - var failure = ''; - var expectingFrameNavigated = false; + const tab = await openTab(url); + const debuggee = {tabId: tab.id}; + var finished = false; + var failure = ''; + var expectingFrameNavigated = false; - function finishIfError() { - if (chrome.runtime.lastError) { - failure = chrome.runtime.lastError.message; - finish(true); - return true; - } - return false; - } - - function onAttach() { - chrome.debugger.sendCommand(debuggee, 'Network.enable', null, - finishIfError); - chrome.debugger.sendCommand(debuggee, 'Page.enable', null, - finishIfError); - var offlineParams = { offline: true, latency: 0, - downloadThroughput: 0, uploadThroughput: 0 }; - chrome.debugger.sendCommand(debuggee, - 'Network.emulateNetworkConditions', - offlineParams, onOffline); - } - - function onOffline() { - if (finishIfError()) - return; - expectingFrameNavigated = true; - chrome.debugger.sendCommand(debuggee, 'Page.reload', null, - finishIfError); - } - - function finish(detach) { - if (finished) - return; - finished = true; - chrome.debugger.onDetach.removeListener(onDetach); - chrome.debugger.onEvent.removeListener(onEvent); - if (detach) - chrome.debugger.detach(debuggee); - chrome.tabs.remove(tab.id, () => { - if (failure) - chrome.test.fail(failure); - else - chrome.test.succeed(); - }); - } - - function onDetach() { - failure = 'Detached before navigated to error page'; - finish(false); - } - - function onEvent(_, method, params) { - if (!expectingFrameNavigated || method !== 'Page.frameNavigated') - return; - - if (finishIfError()) - return; - - expectingFrameNavigated = false; - chrome.debugger.sendCommand( - debuggee, 'Page.navigate', {url: 'about:blank'}, onNavigateDone); - } - - function onNavigateDone() { - if (finishIfError()) - return; + function finishIfError() { + if (chrome.runtime.lastError) { + failure = chrome.runtime.lastError.message; finish(true); + return true; } + return false; + } - chrome.debugger.onDetach.addListener(onDetach); - chrome.debugger.onEvent.addListener(onEvent); - chrome.debugger.attach(debuggee, protocolVersion, onAttach); - }); + function onAttach() { + chrome.debugger.sendCommand(debuggee, 'Network.enable', null, + finishIfError); + chrome.debugger.sendCommand(debuggee, 'Page.enable', null, + finishIfError); + var offlineParams = { offline: true, latency: 0, + downloadThroughput: 0, uploadThroughput: 0 }; + chrome.debugger.sendCommand(debuggee, + 'Network.emulateNetworkConditions', + offlineParams, onOffline); + } + + function onOffline() { + if (finishIfError()) + return; + expectingFrameNavigated = true; + chrome.debugger.sendCommand(debuggee, 'Page.reload', null, + finishIfError); + } + + function finish(detach) { + if (finished) + return; + finished = true; + chrome.debugger.onDetach.removeListener(onDetach); + chrome.debugger.onEvent.removeListener(onEvent); + if (detach) + chrome.debugger.detach(debuggee); + chrome.tabs.remove(tab.id, () => { + if (failure) + chrome.test.fail(failure); + else + chrome.test.succeed(); + }); + } + + function onDetach() { + failure = 'Detached before navigated to error page'; + finish(false); + } + + function onEvent(_, method, params) { + if (!expectingFrameNavigated || method !== 'Page.frameNavigated') + return; + + if (finishIfError()) + return; + + expectingFrameNavigated = false; + chrome.debugger.sendCommand( + debuggee, 'Page.navigate', {url: 'about:blank'}, onNavigateDone); + } + + function onNavigateDone() { + if (finishIfError()) + return; + finish(true); + } + + chrome.debugger.onDetach.addListener(onDetach); + chrome.debugger.onEvent.addListener(onEvent); + chrome.debugger.attach(debuggee, protocolVersion, onAttach); }, function autoAttachToOOPIF() {
diff --git a/chrome/test/data/extensions/api_test/debugger_extension/background.js b/chrome/test/data/extensions/api_test/debugger_extension/background.js index 318d6dae..6f9bb7dc 100644 --- a/chrome/test/data/extensions/api_test/debugger_extension/background.js +++ b/chrome/test/data/extensions/api_test/debugger_extension/background.js
@@ -10,13 +10,13 @@ chrome.test.runTests([ - function attachToWebUI() { - chrome.tabs.create({url:"chrome://version"}, function(tab) { - var debuggee = {tabId: tab.id}; - chrome.debugger.attach(debuggee, protocolVersion, - fail("Cannot attach to this target.")); - chrome.tabs.remove(tab.id); - }); + async function attachToWebUI() { + const {openTab} = await import('/_test_resources/test_util/tabs_util.js'); + const tab = await openTab('chrome://version'); + const debuggee = {tabId: tab.id}; + chrome.debugger.attach(debuggee, protocolVersion, + fail("Cannot attach to this target.")); + chrome.tabs.remove(tab.id); }, function attach() {
diff --git a/chrome/test/data/extensions/api_test/debugger_file_access/background.js b/chrome/test/data/extensions/api_test/debugger_file_access/background.js index ddf0eee..bebf2eb 100644 --- a/chrome/test/data/extensions/api_test/debugger_file_access/background.js +++ b/chrome/test/data/extensions/api_test/debugger_file_access/background.js
@@ -10,29 +10,30 @@ new URL(actual).href); } -function runNotAllowedTest(method, params, expectAllowed) { - const NOT_ALLOWED = "Not allowed"; - chrome.tabs.create({url: 'dummy.html'}, function(tab) { - var debuggee = {tabId: tab.id}; - chrome.debugger.attach(debuggee, '1.2', function() { - chrome.test.assertNoLastError(); - chrome.debugger.sendCommand(debuggee, method, params, onResponse); +let openTab; - function onResponse() { - var message; - try { - message = JSON.parse(chrome.runtime.lastError.message).message; - } catch (e) { - } - chrome.debugger.detach(debuggee, () => { - const allowed = message !== NOT_ALLOWED; - if (allowed === expectAllowed) - chrome.test.succeed(); - else - chrome.test.fail('' + message); - }); +async function runNotAllowedTest(method, params, expectAllowed) { + const NOT_ALLOWED = "Not allowed"; + const tab = await openTab(chrome.runtime.getURL('dummy.html')); + const debuggee = {tabId: tab.id}; + chrome.debugger.attach(debuggee, '1.2', function() { + chrome.test.assertNoLastError(); + chrome.debugger.sendCommand(debuggee, method, params, onResponse); + + function onResponse() { + var message; + try { + message = JSON.parse(chrome.runtime.lastError.message).message; + } catch (e) { } - }); + chrome.debugger.detach(debuggee, () => { + const allowed = message !== NOT_ALLOWED; + if (allowed === expectAllowed) + chrome.test.succeed(); + else + chrome.test.fail('' + message); + }); + } }); } @@ -42,7 +43,8 @@ }); const fileUrl = config.testDataDirectory + '/../body1.html'; const expectFileAccess = !!config.customArg; - const { openTab } = await import('/_test_resources/test_util/tabs_util.js'); + + ({ openTab } = await import('/_test_resources/test_util/tabs_util.js')); console.log(fileUrl);
diff --git a/chrome/test/data/extensions/api_test/debugger_inspect_worker/background.js b/chrome/test/data/extensions/api_test/debugger_inspect_worker/background.js index c104c77..6fc91d0 100644 --- a/chrome/test/data/extensions/api_test/debugger_inspect_worker/background.js +++ b/chrome/test/data/extensions/api_test/debugger_inspect_worker/background.js
@@ -6,8 +6,8 @@ chrome.test.getConfig(config => chrome.test.runTests([ async function testInspectWorkerForbidden() { - const tab = await new Promise(resolve => - chrome.tabs.create({url: config.customArg}, resolve)); + const {openTab} = await import('/_test_resources/test_util/tabs_util.js'); + const tab = await openTab(config.customArg); const debuggee = {tabId: tab.id}; await new Promise(resolve => chrome.debugger.attach(debuggee, protocolVersion, resolve));
diff --git a/chrome/test/data/extensions/api_test/tabs/basics/remove-multiple.html b/chrome/test/data/extensions/api_test/tabs/basics/remove-multiple.html new file mode 100644 index 0000000..c112ae4 --- /dev/null +++ b/chrome/test/data/extensions/api_test/tabs/basics/remove-multiple.html
@@ -0,0 +1,8 @@ +<!-- + * Copyright 2020 The Chromium Authors. All rights reserved. Use of this + * source code is governed by a BSD-style license that can be found in the + * LICENSE file. +--> + +<script src="tabs_util.js"></script> +<script src="remove-multiple.js"></script>
diff --git a/chrome/test/data/extensions/api_test/tabs/basics/remove-multiple.js b/chrome/test/data/extensions/api_test/tabs/basics/remove-multiple.js new file mode 100644 index 0000000..08786ae --- /dev/null +++ b/chrome/test/data/extensions/api_test/tabs/basics/remove-multiple.js
@@ -0,0 +1,74 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +var firstTabId; +var secondTabId; +var thirdTabId; +var fourthTabId; + +function createTab(createParams) { + return new Promise((resolve) => { + chrome.tabs.create(createParams, (tab) => { + var createdTabId = tab.id; + chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { + // Wait for the tab to finish loading. + if (tabId == createdTabId && changeInfo.status == 'complete') { + resolve(tab); + } + }); + }); + }); +} + +chrome.test.runTests([ + function getFirstTabId() { + chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT }, + (tabs) => { + // Make sure we start the test with one tab. + assertEq(1, tabs.length); + firstTabId = tabs[0].id; + chrome.test.succeed(); + }) + }, + function createTabs() { + // Create a second tab that has an unload handler. + createTab({index: 1, active: false, url: 'unload-storage-1.html'}) + .then((tab) => { + secondTabId = tab.id; + assertFalse(tab.active); + assertEq(1, tab.index); + // Create and switch to a third tab that has an unload handler. + return createTab( + {index: 2, active: true, url: 'unload-storage-2.html'}); + }).then((tab) => { + thirdTabId = tab.id; + assertTrue(tab.active); + assertEq(2, tab.index); + // Create a fourth tab that does not have an unload handler (it will + // open the default New Tab Page). + return createTab({index: 3, active: false }); + }).then((tab) => { + fourthTabId = tab.id; + assertFalse(tab.active); + assertEq(3, tab.index); + chrome.test.succeed(); + }); + }, + function removeCreatedTabs() { + chrome.tabs.remove([secondTabId, thirdTabId, fourthTabId], () => { + // The tabs should've set the 'did_run_unload_1' and + // 'did_run_unload_2' values to 'yes' from their unload handler, + // which are accessible from the first tab. + assertEq('yes', localStorage.getItem('did_run_unload_1')); + assertEq('yes', localStorage.getItem('did_run_unload_2')); + chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT }, + (tabs) => { + // Make sure we only have one tab left (the first tab) in the window. + assertEq(1, tabs.length); + assertEq(firstTabId, tabs[0].id); + chrome.test.succeed(); + }); + }); + } +]);
diff --git a/chrome/test/data/extensions/api_test/tabs/basics/remove.html b/chrome/test/data/extensions/api_test/tabs/basics/remove.html new file mode 100644 index 0000000..d4daf0d0 --- /dev/null +++ b/chrome/test/data/extensions/api_test/tabs/basics/remove.html
@@ -0,0 +1,8 @@ +<!-- + * Copyright 2020 The Chromium Authors. All rights reserved. Use of this + * source code is governed by a BSD-style license that can be found in the + * LICENSE file. +--> + +<script src="tabs_util.js"></script> +<script src="remove.js"></script>
diff --git a/chrome/test/data/extensions/api_test/tabs/basics/remove.js b/chrome/test/data/extensions/api_test/tabs/basics/remove.js new file mode 100644 index 0000000..ed1486f8 --- /dev/null +++ b/chrome/test/data/extensions/api_test/tabs/basics/remove.js
@@ -0,0 +1,30 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +var secondTabId; +chrome.test.runTests([ + function createSecondTab() { + // Create and switch to a second tab that has an unload handler. + chrome.tabs.create({index: 1, active: true, url: 'unload-storage-1.html'}, + (tab) => { + secondTabId = tab.id; + assertTrue(tab.active); + assertEq(1, tab.index); + chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { + // Wait for the second tab to finish loading before moving on. + if (tabId == secondTabId && changeInfo.status == 'complete') { + chrome.test.succeed(); + } + }); + }); + }, + function removeSecondTab() { + chrome.tabs.remove(secondTabId, () => { + // The second tab should've set the 'did_run_unload_1' value from + // its unload handler, which is accessible from the first tab too. + assertEq('yes', localStorage.getItem('did_run_unload_1')); + chrome.test.succeed(); + }); + } +]);
diff --git a/chrome/test/data/extensions/api_test/tabs/basics/unload-storage-1.html b/chrome/test/data/extensions/api_test/tabs/basics/unload-storage-1.html new file mode 100644 index 0000000..369137a4 --- /dev/null +++ b/chrome/test/data/extensions/api_test/tabs/basics/unload-storage-1.html
@@ -0,0 +1,10 @@ +<!-- + * Copyright 2020 The Chromium Authors. All rights reserved. Use of this + * source code is governed by a BSD-style license that can be found in the + * LICENSE file. +--> + +<script src="unload-storage-1.js"></script> +<body> + Page with unload handler that stores {"did_run_unload_1": "yes"}. +</body>
diff --git a/chrome/test/data/extensions/api_test/tabs/basics/unload-storage-1.js b/chrome/test/data/extensions/api_test/tabs/basics/unload-storage-1.js new file mode 100644 index 0000000..5e6c804e --- /dev/null +++ b/chrome/test/data/extensions/api_test/tabs/basics/unload-storage-1.js
@@ -0,0 +1,7 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +window.addEventListener('unload', (e) => { + localStorage.setItem('did_run_unload_1', 'yes'); +});
diff --git a/chrome/test/data/extensions/api_test/tabs/basics/unload-storage-2.html b/chrome/test/data/extensions/api_test/tabs/basics/unload-storage-2.html new file mode 100644 index 0000000..d20fd9f --- /dev/null +++ b/chrome/test/data/extensions/api_test/tabs/basics/unload-storage-2.html
@@ -0,0 +1,10 @@ +<!-- + * Copyright 2020 The Chromium Authors. All rights reserved. Use of this + * source code is governed by a BSD-style license that can be found in the + * LICENSE file. +--> + +<script src="unload-storage-2.js"></script> +<body> + Page with unload handler that stores {"did_run_unload_2": "yes"}. +</body>
diff --git a/chrome/test/data/extensions/api_test/tabs/basics/unload-storage-2.js b/chrome/test/data/extensions/api_test/tabs/basics/unload-storage-2.js new file mode 100644 index 0000000..e918ced --- /dev/null +++ b/chrome/test/data/extensions/api_test/tabs/basics/unload-storage-2.js
@@ -0,0 +1,7 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +window.addEventListener('unload', (e) => { + localStorage.setItem('did_run_unload_2', 'yes'); +});
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn index 447aa94..42f4524a 100644 --- a/chrome/test/data/webui/BUILD.gn +++ b/chrome/test/data/webui/BUILD.gn
@@ -262,6 +262,7 @@ "$root_gen_dir/chrome/test/data/webui/settings/chromeos/test_multidevice_browser_proxy.m.js", "$root_gen_dir/chrome/test/data/webui/settings/chromeos/personalization_page_test.m.js", "$root_gen_dir/chrome/test/data/webui/settings/chromeos/test_wallpaper_browser_proxy.m.js", + "$root_gen_dir/chrome/test/data/webui/settings/chromeos/timezone_selector_test.m.js", ] } defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
diff --git a/chrome/test/data/webui/new_tab_page/app_test.js b/chrome/test/data/webui/new_tab_page/app_test.js index 2019358..66a81d0 100644 --- a/chrome/test/data/webui/new_tab_page/app_test.js +++ b/chrome/test/data/webui/new_tab_page/app_test.js
@@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {$$, BackgroundManager, BackgroundSelectionType, BrowserProxy} from 'chrome://new-tab-page/new_tab_page.js'; +import {$$, BackgroundManager, BackgroundSelectionType, BrowserProxy, PromoBrowserCommandProxy} from 'chrome://new-tab-page/new_tab_page.js'; import {isMac} from 'chrome://resources/js/cr.m.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; import {assertNotStyle, assertStyle, createTestProxy, createTheme} from 'chrome://test/new_tab_page/test_support.js'; import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js'; -import {flushTasks} from 'chrome://test/test_util.m.js'; +import {eventToPromise, flushTasks} from 'chrome://test/test_util.m.js'; suite('NewTabPageAppTest', () => { /** @type {!AppElement} */ @@ -389,4 +389,38 @@ await testProxy.callbackRouterRemote.$.flushForTesting(); assertTrue(app.$.mostVisited.hasAttribute('use-title-pill')); }); + + test('executes promo browser command', async () => { + const testProxy = PromoBrowserCommandProxy.getInstance(); + testProxy.handler = TestBrowserProxy.fromClass( + promoBrowserCommand.mojom.CommandHandlerRemote); + testProxy.handler.setResultFor( + 'executeCommand', Promise.resolve({commandExecuted: true})); + + const commandId = 123; // Unsupported command. + const clickInfo = {middleButton: true}; + window.dispatchEvent(new MessageEvent('message', { + data: { + frameType: 'one-google-bar', + messageType: 'execute-browser-command', + commandId, + clickInfo, + }, + source: window, + origin: window.origin, + })); + + // Make sure the command and click information are sent to the browser. + const [expectedCommandId, expectedClickInfo] = + await testProxy.handler.whenCalled('executeCommand'); + // Unsupported commands get resolved to the default command before being + // sent to the browser. + assertEquals( + promoBrowserCommand.mojom.Command.kUnknownCommand, expectedCommandId); + assertEquals(clickInfo, expectedClickInfo); + + // Make sure the promo frame gets notified whether the command was executed. + const {data: commandExecuted} = await eventToPromise('message', window); + assertTrue(commandExecuted); + }); });
diff --git a/chrome/test/data/webui/settings/chromeos/BUILD.gn b/chrome/test/data/webui/settings/chromeos/BUILD.gn index b74589e92..418e979 100644 --- a/chrome/test/data/webui/settings/chromeos/BUILD.gn +++ b/chrome/test/data/webui/settings/chromeos/BUILD.gn
@@ -58,6 +58,7 @@ "test_multidevice_browser_proxy.js", "test_wallpaper_browser_proxy.js", "test_os_sync_browser_proxy.js", + "timezone_selector_test.js", ] namespace_rewrites = os_settings_namespace_rewrites + os_test_namespace_rewrites
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js index 723f5fb..e202f1c8 100644 --- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js +++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -1531,4 +1531,23 @@ mocha.run(); }); +// Tests for the Date Time timezone selector +// eslint-disable-next-line no-var +var OSSettingsTimezoneSelectorTest = class extends OSSettingsBrowserTest { + /** @override */ + get browsePreload() { + return super.browsePreload + + 'chromeos/date_time_page/timezone_selector.html'; + } + + /** @override */ + get extraLibraries() { + return super.extraLibraries.concat(['timezone_selector_test.js']); + } +}; + +TEST_F('OSSettingsTimezoneSelectorTest', 'AllJsTests', () => { + mocha.run(); +}); + GEN('#endif // defined(NDEBUG)');
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js index f3f8f01..b510a5f 100644 --- a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js +++ b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
@@ -54,6 +54,7 @@ ['PeoplePageChangePicture', 'people_page_change_picture_test.m.js'], ['PeoplePageKerberosAccounts', 'people_page_kerberos_accounts_test.m.js'], ['PrivacyPage', 'os_privacy_page_test.m.js'], + ['TimezoneSelector', 'timezone_selector_test.m.js'], ].forEach(test => registerTest(...test)); function registerTest(testName, module, caseName) {
diff --git a/chrome/test/data/webui/settings/chromeos/timezone_selector_test.js b/chrome/test/data/webui/settings/chromeos/timezone_selector_test.js new file mode 100644 index 0000000..45bb1cf --- /dev/null +++ b/chrome/test/data/webui/settings/chromeos/timezone_selector_test.js
@@ -0,0 +1,64 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// clang-format off +// #import 'chrome://os-settings/chromeos/os_settings.js'; + +// #import {flush} from'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +// #import {assertEquals} from '../../chai_assert.js'; +// #import {assert} from 'chrome://resources/js/assert.m.js'; +// clang-format on + +suite('TimezoneSelectorTests', function() { + /** @type {TimezoneSelector} */ + let timezoneSelector = null; + + setup(function() { + PolymerTest.clearBody(); + }); + + teardown(function() { + timezoneSelector.remove(); + }); + + test('Per-user timezone disabled', async () => { + timezoneSelector = document.createElement('timezone-selector'); + timezoneSelector.prefs = { + 'cros': { + 'flags': { + 'per_user_timezone_enabled': { + value: false, + } + } + } + }; + document.body.appendChild(timezoneSelector); + + Polymer.dom.flush(); + + assertEquals(null, timezoneSelector.$$('#userTimeZoneSelector')); + assertEquals(null, timezoneSelector.$$('#systemTimezoneSelector')); + }); + + test('Per-user timezone enabled', async () => { + timezoneSelector = document.createElement('timezone-selector'); + timezoneSelector.prefs = { + 'cros': { + 'flags': { + 'per_user_timezone_enabled': { + value: true, + } + } + } + }; + document.body.appendChild(timezoneSelector); + + Polymer.dom.flush(); + + const userTimezoneSelector = + assert(timezoneSelector.$$('#userTimeZoneSelector')); + const systemTimezoneSelector = + assert(timezoneSelector.$$('#systemTimezoneSelector')); + }); +});
diff --git a/chrome/test/media_router/media_router_integration_browsertest.cc b/chrome/test/media_router/media_router_integration_browsertest.cc index 36282fc..3483370e 100644 --- a/chrome/test/media_router/media_router_integration_browsertest.cc +++ b/chrome/test/media_router/media_router_integration_browsertest.cc
@@ -500,6 +500,14 @@ web_contents->GetDelegate()->IsFullscreenForTabOrPending(web_contents)); } +// Flaky on MSan bots: http://crbug.com/879885 +#if defined(MEMORY_SANITIZER) +#define MAYBE_OpenLocalMediaFileCastFailNoFullscreen \ + DISABLED_OpenLocalMediaFileCastFailNoFullscreen +#else +#define MAYBE_OpenLocalMediaFileCastFailNoFullscreen \ + OpenLocalMediaFileCastFailNoFullscreen +#endif // Tests that failed route creation of local file does not enter fullscreen. IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest, OpenLocalMediaFileCastFailNoFullscreen) {
diff --git a/chrome/updater/win/setup/setup.cc b/chrome/updater/win/setup/setup.cc index bd4cc8db..81bc5592 100644 --- a/chrome/updater/win/setup/setup.cc +++ b/chrome/updater/win/setup/setup.cc
@@ -93,7 +93,7 @@ list->AddWorkItem(new installer::InstallServiceWorkItem( kWindowsServiceName, kWindowsServiceName, base::CommandLine(com_service_path), base::ASCIIToUTF16(UPDATER_KEY), - CLSID_UpdaterServiceClass, GUID_NULL)); + {CLSID_UpdaterServiceClass}, {})); } // Adds work items to register the COM Interfaces with Windows.
diff --git a/chrome/updater/win/setup/uninstall.cc b/chrome/updater/win/setup/uninstall.cc index fe98068..e3b54d5 100644 --- a/chrome/updater/win/setup/uninstall.cc +++ b/chrome/updater/win/setup/uninstall.cc
@@ -50,7 +50,7 @@ WorkItem::kWow64Default); if (!installer::InstallServiceWorkItem::DeleteService( kWindowsServiceName, base::ASCIIToUTF16(UPDATER_KEY), - CLSID_UpdaterServiceClass, GUID_NULL)) + {CLSID_UpdaterServiceClass}, {})) LOG(WARNING) << "DeleteService failed."; }
diff --git a/chromecast/media/audio/BUILD.gn b/chromecast/media/audio/BUILD.gn index be15aa3..c871565 100644 --- a/chromecast/media/audio/BUILD.gn +++ b/chromecast/media/audio/BUILD.gn
@@ -13,6 +13,15 @@ default_output_buffer_size_in_frames = 2048 } +cast_source_set("audio_log") { + sources = [ + "audio_log.cc", + "audio_log.h", + ] + + deps = [ "//base" ] +} + cast_source_set("audio_io_thread") { sources = [ "audio_io_thread.cc",
diff --git a/chromecast/media/audio/audio_log.cc b/chromecast/media/audio/audio_log.cc new file mode 100644 index 0000000..bd6c64f --- /dev/null +++ b/chromecast/media/audio/audio_log.cc
@@ -0,0 +1,175 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/media/audio/audio_log.h" + +#include <algorithm> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/location.h" +#include "base/memory/scoped_refptr.h" +#include "base/no_destructor.h" +#include "base/sequenced_task_runner.h" +#include "base/synchronization/lock.h" +#include "base/thread_annotations.h" +#include "base/threading/sequenced_task_runner_handle.h" + +namespace logging { + +namespace { +constexpr int kBufferSize = 256; +constexpr int kMaxBuffers = 16; +} // namespace + +class AudioLogMessage::StreamBuf : public std::streambuf { + public: + StreamBuf() = default; + + StreamBuf(const StreamBuf&) = delete; + StreamBuf& operator=(const StreamBuf&) = delete; + + void Initialize(const char* file, int line, LogSeverity severity) { + file_ = file; + line_ = line; + severity_ = severity; + setp(buffer_, buffer_ + kBufferSize); + } + + void Log() { + ::logging::LogMessage message(file_, line_, severity_); + int size = pptr() - pbase(); + message.stream().write(buffer_, size); + } + + private: + const char* file_ = nullptr; + int line_; + LogSeverity severity_; + char buffer_[kBufferSize]; +}; + +namespace { + +class BufferManager { + public: + static BufferManager* Get(); + + void Setup() { + base::AutoLock lock(lock_); + if (ready_) { + return; + } + + task_runner_ = base::SequencedTaskRunnerHandle::Get(); + dispose_callback_ = base::BindRepeating( + &BufferManager::HandleDisposedBuffers, base::Unretained(this)); + + for (int i = 0; i < kMaxBuffers; ++i) { + free_buffers_[i] = &buffers_[i]; + } + num_free_buffers_ = kMaxBuffers; + ready_ = true; + } + + AudioLogMessage::StreamBuf* GetBuffer(const char* file, + int line, + LogSeverity severity) { + AudioLogMessage::StreamBuf* buffer; + { + base::AutoLock lock(lock_); + if (num_free_buffers_ == 0) { + ++num_missing_buffers_; + return nullptr; + } + + --num_free_buffers_; + buffer = free_buffers_[num_free_buffers_]; + } + buffer->Initialize(file, line, severity); + return buffer; + } + + void Dispose(AudioLogMessage::StreamBuf* buffer) { + if (!buffer) { + return; + } + + { + base::AutoLock lock(lock_); + DCHECK_LT(num_disposed_buffers_, kMaxBuffers); + disposed_buffers_[num_disposed_buffers_] = buffer; + ++num_disposed_buffers_; + } + DCHECK(task_runner_); + task_runner_->PostTask(FROM_HERE, dispose_callback_); + } + + private: + void HandleDisposedBuffers() { + AudioLogMessage::StreamBuf* buffers[kMaxBuffers]; + int num_buffers; + int num_missing; + { + base::AutoLock lock(lock_); + std::copy_n(disposed_buffers_, num_disposed_buffers_, buffers); + num_buffers = num_disposed_buffers_; + num_disposed_buffers_ = 0; + + num_missing = num_missing_buffers_; + num_missing_buffers_ = 0; + } + + for (int i = 0; i < num_buffers; ++i) { + buffers[i]->Log(); + { + base::AutoLock lock(lock_); + free_buffers_[num_free_buffers_] = buffers[i]; + ++num_free_buffers_; + } + } + + LOG_IF(ERROR, num_missing > 0) + << num_missing << " log messages lost due to lack of buffers"; + } + + AudioLogMessage::StreamBuf buffers_[kMaxBuffers]; + + base::Lock lock_; + bool ready_ GUARDED_BY(lock_) = false; + AudioLogMessage::StreamBuf* free_buffers_[kMaxBuffers] GUARDED_BY(lock_); + int num_free_buffers_ GUARDED_BY(lock_) = 0; + + AudioLogMessage::StreamBuf* disposed_buffers_[kMaxBuffers] GUARDED_BY(lock_); + int num_disposed_buffers_ GUARDED_BY(lock_) = 0; + + int num_missing_buffers_ GUARDED_BY(lock_) = 0; + + scoped_refptr<base::SequencedTaskRunner> task_runner_; + base::RepeatingClosure dispose_callback_; +}; + +// static +BufferManager* BufferManager::Get() { + static base::NoDestructor<BufferManager> g_buffer_manager; + return g_buffer_manager.get(); +} + +} // namespace + +AudioLogMessage::AudioLogMessage(const char* file, + int line, + LogSeverity severity) + : buffer_(BufferManager::Get()->GetBuffer(file, line, severity)), + stream_(buffer_) {} + +AudioLogMessage::~AudioLogMessage() { + BufferManager::Get()->Dispose(buffer_); +} + +void InitializeAudioLog() { + BufferManager::Get()->Setup(); +} + +} // namespace logging
diff --git a/chromecast/media/audio/audio_log.h b/chromecast/media/audio/audio_log.h new file mode 100644 index 0000000..3968336b --- /dev/null +++ b/chromecast/media/audio/audio_log.h
@@ -0,0 +1,47 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_MEDIA_AUDIO_AUDIO_LOG_H_ +#define CHROMECAST_MEDIA_AUDIO_AUDIO_LOG_H_ + +#include <ostream> + +#include "base/logging.h" + +namespace logging { + +#define AUDIO_LOG_STREAM(severity) \ + COMPACT_GOOGLE_LOG_EX_##severity(AudioLogMessage).stream() + +#define AUDIO_LOG(severity) \ + LAZY_STREAM(AUDIO_LOG_STREAM(severity), LOG_IS_ON(severity)) + +#define AUDIO_LOG_IF(severity, condition) \ + LAZY_STREAM(AUDIO_LOG_STREAM(severity), LOG_IS_ON(severity) && (condition)) + +class AudioLogMessage { + public: + class StreamBuf; + + AudioLogMessage(const char* file, int line, LogSeverity severity); + ~AudioLogMessage(); + + AudioLogMessage(const AudioLogMessage&) = delete; + AudioLogMessage& operator=(const AudioLogMessage&) = delete; + + std::ostream& stream() { return stream_; } + + private: + StreamBuf* buffer_; + std::ostream stream_; +}; + +// Should be called on a lower-priority thread. Actual output of log messages +// will be done on this thread. Note that any use of AudioLogMessage prior to +// InitializeAudioLog() will not produce any output. +void InitializeAudioLog(); + +} // namespace logging + +#endif // CHROMECAST_MEDIA_AUDIO_AUDIO_LOG_H_
diff --git a/chromecast/media/audio/cast_audio_manager.cc b/chromecast/media/audio/cast_audio_manager.cc index 5bf2f2f..c2d7bf6 100644 --- a/chromecast/media/audio/cast_audio_manager.cc +++ b/chromecast/media/audio/cast_audio_manager.cc
@@ -21,10 +21,6 @@ #include "chromecast/public/media/media_pipeline_backend.h" #include "media/audio/audio_device_description.h" -#if defined(OS_ANDROID) -#include "media/audio/android/audio_track_output_stream.h" -#endif // defined(OS_ANDROID) - namespace { // TODO(alokp): Query the preferred value from media backend. const int kDefaultSampleRate = 48000; @@ -197,20 +193,6 @@ } } -::media::AudioOutputStream* CastAudioManager::MakeBitstreamOutputStream( - const ::media::AudioParameters& params, - const std::string& device_id, - const ::media::AudioManager::LogCallback& log_callback) { -#if defined(OS_ANDROID) - DCHECK(params.IsBitstreamFormat()); - return new ::media::AudioTrackOutputStream(this, params); -#else - NOTREACHED() << " Not implemented on non-android platform."; - return ::media::AudioManagerBase::MakeBitstreamOutputStream(params, device_id, - log_callback); -#endif // defined(OS_ANDROID) -} - ::media::AudioInputStream* CastAudioManager::MakeLinearInputStream( const ::media::AudioParameters& params, const std::string& device_id, @@ -281,16 +263,5 @@ return !use_cma_backend; } -#if defined(OS_ANDROID) -::media::AudioOutputStream* CastAudioManager::MakeAudioOutputStreamProxy( - const ::media::AudioParameters& params, - const std::string& device_id) { - // Override to use MakeAudioOutputStream to prevent the audio output stream - // from closing during pause/stop. - return MakeAudioOutputStream(params, device_id, - /*log_callback, not used*/ base::DoNothing()); -} -#endif // defined(OS_ANDROID) - } // namespace media } // namespace chromecast
diff --git a/chromecast/media/audio/cast_audio_manager.h b/chromecast/media/audio/cast_audio_manager.h index 0998c62..60f1351 100644 --- a/chromecast/media/audio/cast_audio_manager.h +++ b/chromecast/media/audio/cast_audio_manager.h
@@ -88,10 +88,6 @@ const ::media::AudioParameters& params, const std::string& device_id_or_group_id, const ::media::AudioManager::LogCallback& log_callback) override; - ::media::AudioOutputStream* MakeBitstreamOutputStream( - const ::media::AudioParameters& params, - const std::string& device_id, - const ::media::AudioManager::LogCallback& log_callback) override; ::media::AudioInputStream* MakeLinearInputStream( const ::media::AudioParameters& params, const std::string& device_id, @@ -108,12 +104,6 @@ virtual ::media::AudioOutputStream* MakeMixerOutputStream( const ::media::AudioParameters& params); -#if defined(OS_ANDROID) - ::media::AudioOutputStream* MakeAudioOutputStreamProxy( - const ::media::AudioParameters& params, - const std::string& device_id) override; -#endif - private: FRIEND_TEST_ALL_PREFIXES(CastAudioManagerTest, CanMakeStreamProxy); friend class CastAudioMixer;
diff --git a/chromecast/media/audio/cast_audio_manager_android.cc b/chromecast/media/audio/cast_audio_manager_android.cc index e882618c..0638fee 100644 --- a/chromecast/media/audio/cast_audio_manager_android.cc +++ b/chromecast/media/audio/cast_audio_manager_android.cc
@@ -9,6 +9,7 @@ #include "base/logging.h" #include "chromecast/media/audio/audio_buildflags.h" #include "chromecast/media/audio/cast_audio_input_stream.h" +#include "media/audio/android/audio_track_output_stream.h" #include "media/audio/audio_device_name.h" #include "media/base/audio_parameters.h" #include "media/base/channel_layout.h" @@ -106,5 +107,22 @@ return nullptr; } +::media::AudioOutputStream* CastAudioManagerAndroid::MakeBitstreamOutputStream( + const ::media::AudioParameters& params, + const std::string& device_id, + const ::media::AudioManager::LogCallback& log_callback) { + DCHECK(params.IsBitstreamFormat()); + return new ::media::AudioTrackOutputStream(this, params); +} + +::media::AudioOutputStream* CastAudioManagerAndroid::MakeAudioOutputStreamProxy( + const ::media::AudioParameters& params, + const std::string& device_id) { + // Override to use MakeAudioOutputStream to prevent the audio output stream + // from closing during pause/stop. + return MakeAudioOutputStream(params, device_id, + /*log_callback, not used*/ base::DoNothing()); +} + } // namespace media } // namespace chromecast
diff --git a/chromecast/media/audio/cast_audio_manager_android.h b/chromecast/media/audio/cast_audio_manager_android.h index a2a81ce..7afe47d 100644 --- a/chromecast/media/audio/cast_audio_manager_android.h +++ b/chromecast/media/audio/cast_audio_manager_android.h
@@ -28,6 +28,15 @@ bool use_mixer); ~CastAudioManagerAndroid() override; + // AudioManager implementation. + ::media::AudioOutputStream* MakeAudioOutputStreamProxy( + const ::media::AudioParameters& params, + const std::string& device_id) override; + ::media::AudioOutputStream* MakeBitstreamOutputStream( + const ::media::AudioParameters& params, + const std::string& device_id, + const ::media::AudioManager::LogCallback& log_callback) override; + // CastAudioManager implementation. bool HasAudioInputDevices() override; void GetAudioInputDeviceNames(
diff --git a/chromecast/media/cma/backend/mixer/BUILD.gn b/chromecast/media/cma/backend/mixer/BUILD.gn index c3870f8..cd11be8 100644 --- a/chromecast/media/cma/backend/mixer/BUILD.gn +++ b/chromecast/media/cma/backend/mixer/BUILD.gn
@@ -72,6 +72,7 @@ "//chromecast/base:chromecast_switches", "//chromecast/base:thread_health_checker", "//chromecast/media/audio:audio_io_thread", + "//chromecast/media/audio:audio_log", "//chromecast/media/audio:interleaved_channel_mixer", "//chromecast/media/audio:libcast_external_audio_pipeline_1.0", "//chromecast/media/audio:processing",
diff --git a/chromecast/media/cma/backend/mixer/audio_output_redirector.cc b/chromecast/media/cma/backend/mixer/audio_output_redirector.cc index 684258bc..5c42e678 100644 --- a/chromecast/media/cma/backend/mixer/audio_output_redirector.cc +++ b/chromecast/media/cma/backend/mixer/audio_output_redirector.cc
@@ -16,6 +16,7 @@ #include "base/strings/pattern.h" #include "base/threading/sequenced_task_runner_handle.h" #include "chromecast/media/audio/audio_fader.h" +#include "chromecast/media/audio/audio_log.h" #include "chromecast/media/audio/mixer_service/conversions.h" #include "chromecast/media/audio/mixer_service/mixer_service.pb.h" #include "chromecast/media/audio/mixer_service/mixer_socket.h" @@ -183,8 +184,9 @@ DCHECK(mixer_input_); if (mixer_input_->num_channels() != num_output_channels_) { - LOG(INFO) << "Remixing channels for " << mixer_input_->source() << " from " - << mixer_input_->num_channels() << " to " << num_output_channels_; + AUDIO_LOG(INFO) << "Remixing channels for " << mixer_input_->source() + << " from " << mixer_input_->num_channels() << " to " + << num_output_channels_; channel_mixer_ = std::make_unique<::media::ChannelMixer>( mixer::CreateAudioParametersForChannelMixer( mixer_input_->channel_layout(), mixer_input_->num_channels()),
diff --git a/chromecast/media/cma/backend/mixer/filter_group.cc b/chromecast/media/cma/backend/mixer/filter_group.cc index 1534499..114b64e 100644 --- a/chromecast/media/cma/backend/mixer/filter_group.cc +++ b/chromecast/media/cma/backend/mixer/filter_group.cc
@@ -11,6 +11,7 @@ #include "base/numerics/ranges.h" #include "base/time/time.h" #include "base/values.h" +#include "chromecast/media/audio/audio_log.h" #include "chromecast/media/audio/interleaved_channel_mixer.h" #include "chromecast/media/cma/backend/mixer/channel_layout.h" #include "chromecast/media/cma/backend/mixer/mixer_input.h" @@ -124,15 +125,16 @@ // Get default limits. if (ParseVolumeLimit(volume_limits, &default_volume_min_, &default_volume_max_)) { - LOG(INFO) << "Default volume limits for '" << name_ << "' group: [" - << default_volume_min_ << ", " << default_volume_max_ << "]"; + AUDIO_LOG(INFO) << "Default volume limits for '" << name_ << "' group: [" + << default_volume_min_ << ", " << default_volume_max_ + << "]"; } float min, max; for (const auto& item : volume_limits->DictItems()) { if (ParseVolumeLimit(&item.second, &min, &max)) { - LOG(INFO) << "Volume limits for device ID '" << item.first << "' = [" - << min << ", " << max << "]"; + AUDIO_LOG(INFO) << "Volume limits for device ID '" << item.first + << "' = [" << min << ", " << max << "]"; volume_limits_.insert({item.first, {min, max}}); } } @@ -290,8 +292,8 @@ void FilterGroup::UpdatePlayoutChannel(int playout_channel) { if (playout_channel >= num_channels_) { - LOG(ERROR) << "only " << num_channels_ << " present, wanted channel #" - << playout_channel; + AUDIO_LOG(ERROR) << "only " << num_channels_ << " present, wanted channel #" + << playout_channel; return; } post_processing_pipeline_->UpdatePlayoutChannel(playout_channel);
diff --git a/chromecast/media/cma/backend/mixer/mixer_input.cc b/chromecast/media/cma/backend/mixer/mixer_input.cc index b897058..c9befac 100644 --- a/chromecast/media/cma/backend/mixer/mixer_input.cc +++ b/chromecast/media/cma/backend/mixer/mixer_input.cc
@@ -15,6 +15,7 @@ #include "base/logging.h" #include "base/numerics/ranges.h" #include "chromecast/media/audio/audio_fader.h" +#include "chromecast/media/audio/audio_log.h" #include "chromecast/media/cma/backend/mixer/audio_output_redirector_input.h" #include "chromecast/media/cma/backend/mixer/channel_layout.h" #include "chromecast/media/cma/backend/mixer/filter_group.h" @@ -117,8 +118,9 @@ if (filter_group->num_channels() == num_channels_) { channel_mixer_.reset(); } else { - LOG(INFO) << "Remixing channels for " << source_ << " from " - << num_channels_ << " to " << filter_group->num_channels(); + AUDIO_LOG(INFO) << "Remixing channels for " << source_ << " from " + << num_channels_ << " to " + << filter_group->num_channels(); channel_mixer_ = std::make_unique<::media::ChannelMixer>( mixer::CreateAudioParametersForChannelMixer(channel_layout_, num_channels_), @@ -131,7 +133,8 @@ void MixerInput::AddAudioOutputRedirector( AudioOutputRedirectorInput* redirector) { - LOG(INFO) << "Add redirector to " << device_id_ << "(" << source_ << ")"; + AUDIO_LOG(INFO) << "Add redirector to " << device_id_ << "(" << source_ + << ")"; DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(redirector); audio_output_redirectors_.insert( @@ -146,7 +149,8 @@ void MixerInput::RemoveAudioOutputRedirector( AudioOutputRedirectorInput* redirector) { - LOG(INFO) << "Remove redirector from " << device_id_ << "(" << source_ << ")"; + AUDIO_LOG(INFO) << "Remove redirector from " << device_id_ << "(" << source_ + << ")"; DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(redirector); audio_output_redirectors_.erase( @@ -323,9 +327,9 @@ float old_target_volume = TargetVolume(); stream_volume_multiplier_ = std::max(0.0f, multiplier); float target_volume = TargetVolume(); - LOG(INFO) << device_id_ << "(" << source_ - << "): stream volume = " << stream_volume_multiplier_ - << ", effective multiplier = " << target_volume; + AUDIO_LOG(INFO) << device_id_ << "(" << source_ + << "): stream volume = " << stream_volume_multiplier_ + << ", effective multiplier = " << target_volume; if (target_volume != old_target_volume) { slew_volume_.SetMaxSlewTimeMs(kDefaultSlewTimeMs); slew_volume_.SetVolume(target_volume); @@ -339,9 +343,9 @@ float old_target_volume = TargetVolume(); type_volume_multiplier_ = volume; float target_volume = TargetVolume(); - LOG(INFO) << device_id_ << "(" << source_ - << "): type volume = " << type_volume_multiplier_ - << ", effective multiplier = " << target_volume; + AUDIO_LOG(INFO) << device_id_ << "(" << source_ + << "): type volume = " << type_volume_multiplier_ + << ", effective multiplier = " << target_volume; if (target_volume != old_target_volume) { slew_volume_.SetMaxSlewTimeMs(kDefaultSlewTimeMs); slew_volume_.SetVolume(target_volume); @@ -354,8 +358,8 @@ volume_min_ = volume_min; volume_max_ = volume_max; float target_volume = TargetVolume(); - LOG(INFO) << device_id_ << "(" << source_ << "): set volume limits to [" - << volume_min_ << ", " << volume_max_ << "]"; + AUDIO_LOG(INFO) << device_id_ << "(" << source_ << "): set volume limits to [" + << volume_min_ << ", " << volume_max_ << "]"; if (target_volume != old_target_volume) { slew_volume_.SetMaxSlewTimeMs(kDefaultSlewTimeMs); slew_volume_.SetVolume(target_volume); @@ -367,13 +371,13 @@ float old_target_volume = TargetVolume(); output_volume_limit_ = limit; float target_volume = TargetVolume(); - LOG(INFO) << device_id_ << "(" << source_ - << "): output limit = " << output_volume_limit_ - << ", effective multiplier = " << target_volume; + AUDIO_LOG(INFO) << device_id_ << "(" << source_ + << "): output limit = " << output_volume_limit_ + << ", effective multiplier = " << target_volume; if (fade_ms < 0) { fade_ms = kDefaultSlewTimeMs; } else { - LOG(INFO) << "Fade over " << fade_ms << " ms"; + AUDIO_LOG(INFO) << "Fade over " << fade_ms << " ms"; } if (target_volume != old_target_volume) { slew_volume_.SetMaxSlewTimeMs(fade_ms); @@ -388,9 +392,9 @@ float old_target_volume = TargetVolume(); mute_volume_multiplier_ = muted ? 0.0f : 1.0f; float target_volume = TargetVolume(); - LOG(INFO) << device_id_ << "(" << source_ - << "): mute volume = " << mute_volume_multiplier_ - << ", effective multiplier = " << target_volume; + AUDIO_LOG(INFO) << device_id_ << "(" << source_ + << "): mute volume = " << mute_volume_multiplier_ + << ", effective multiplier = " << target_volume; if (target_volume != old_target_volume) { slew_volume_.SetMaxSlewTimeMs(kDefaultSlewTimeMs); slew_volume_.SetVolume(target_volume);
diff --git a/chromecast/media/cma/backend/mixer/mixer_input_connection.cc b/chromecast/media/cma/backend/mixer/mixer_input_connection.cc index 414d720..04dc882 100644 --- a/chromecast/media/cma/backend/mixer/mixer_input_connection.cc +++ b/chromecast/media/cma/backend/mixer/mixer_input_connection.cc
@@ -19,6 +19,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "chromecast/base/chromecast_switches.h" +#include "chromecast/media/audio/audio_log.h" #include "chromecast/media/audio/mixer_service/conversions.h" #include "chromecast/media/audio/mixer_service/mixer_service.pb.h" #include "chromecast/media/cma/backend/mixer/channel_layout.h" @@ -583,7 +584,7 @@ int64_t MixerInputConnection::QueueData(scoped_refptr<net::IOBuffer> data) { int frames = GetFrameCount(data.get()); if (frames == 0) { - LOG(INFO) << "End of stream for " << this; + AUDIO_LOG(INFO) << "End of stream for " << this; state_ = State::kGotEos; if (!started_) { io_task_runner_->PostTask(FROM_HERE, ready_for_playback_task_); @@ -656,8 +657,8 @@ mixer_read_size_ = read_size; if (start_threshold_frames_ == 0) { start_threshold_frames_ = read_size + fill_size_; - LOG(INFO) << this - << " Updated start threshold: " << start_threshold_frames_; + AUDIO_LOG(INFO) << this << " Updated start threshold: " + << start_threshold_frames_; } mixer_rendering_delay_ = initial_rendering_delay; if (state_ == State::kUninitialized) { @@ -687,8 +688,8 @@ const int frames_needed_to_start = std::max( start_threshold_frames_, fader_.FramesNeededFromSource(num_frames)); if (max_queued_frames_ < frames_needed_to_start) { - LOG(INFO) << "Boost queue size to " << frames_needed_to_start - << " to allow stream to start"; + AUDIO_LOG(INFO) << "Boost queue size to " << frames_needed_to_start + << " to allow stream to start"; max_queued_frames_ = frames_needed_to_start; } const bool have_enough_queued_frames = @@ -700,7 +701,7 @@ remaining_silence_frames_ = 0; if (!use_start_timestamp_ || (queue_.empty() && state_ == State::kGotEos)) { // No start timestamp, so start as soon as there are enough queued frames. - LOG(INFO) << "Start " << this; + AUDIO_LOG(INFO) << "Start " << this; started_ = true; return; } @@ -726,7 +727,7 @@ int64_t drop_us = (desired_pts_now - actual_pts_now) / playback_rate_; if (drop_us >= 0) { - LOG(INFO) << this << " Dropping audio, duration = " << drop_us; + AUDIO_LOG(INFO) << this << " Dropping audio, duration = " << drop_us; DropAudio(::media::AudioTimestampHelper::TimeToFrames( base::TimeDelta::FromMicroseconds(drop_us), input_samples_per_second_)); // Only start if we still have enough data to do so. @@ -738,20 +739,22 @@ SamplesToMicroseconds(current_buffer_offset_, input_samples_per_second_) * playback_rate_; - LOG(INFO) << this << " Start playback of PTS " << start_pts << " at " - << playback_absolute_timestamp; + AUDIO_LOG(INFO) << this << " Start playback of PTS " << start_pts + << " at " << playback_absolute_timestamp; } } else { int64_t silence_duration = -drop_us; - LOG(INFO) << this << " Adding silence. Duration = " << silence_duration; + AUDIO_LOG(INFO) << this + << " Adding silence. Duration = " << silence_duration; remaining_silence_frames_ = ::media::AudioTimestampHelper::TimeToFrames( base::TimeDelta::FromMicroseconds(silence_duration), input_samples_per_second_); // Round to nearest multiple of 4 to preserve buffer alignment. remaining_silence_frames_ = ((remaining_silence_frames_ + 2) / 4) * 4; started_ = true; - LOG(INFO) << this << " Should start playback of PTS " << actual_pts_now - << " at " << (playback_absolute_timestamp + silence_duration); + AUDIO_LOG(INFO) << this << " Should start playback of PTS " + << actual_pts_now << " at " + << (playback_absolute_timestamp + silence_duration); } } @@ -774,7 +777,8 @@ } if (frames_to_drop > 0) { - LOG(INFO) << this << " Still need to drop " << frames_to_drop << " frames"; + AUDIO_LOG(INFO) << this << " Still need to drop " << frames_to_drop + << " frames"; } } @@ -814,11 +818,11 @@ // full request. This will allow us to buffer up more data so we can fully // fade in. if (state_ == State::kNormalPlayback && !can_complete_fill) { - LOG_IF(INFO, !zero_fader_frames_) << "Stream underrun for " << this; + AUDIO_LOG_IF(INFO, !zero_fader_frames_) << "Stream underrun for " << this; zero_fader_frames_ = true; underrun = true; } else { - LOG_IF(INFO, started_ && zero_fader_frames_) + AUDIO_LOG_IF(INFO, started_ && zero_fader_frames_) << "Stream underrun recovered for " << this; zero_fader_frames_ = false; if (!skip_next_fill_for_rate_change_) { @@ -932,8 +936,9 @@ } if (rate_shifter_output_->frames() < needed_frames) { - LOG(WARNING) << "Rate shifter output is too small; " - << rate_shifter_output_->frames() << " < " << needed_frames; + AUDIO_LOG(WARNING) << "Rate shifter output is too small; " + << rate_shifter_output_->frames() << " < " + << needed_frames; auto output = ::media::AudioBus::Create(num_channels_, needed_frames); rate_shifter_output_->CopyPartialFramesTo(0, rate_shifted_offset_, 0, output.get()); @@ -1044,7 +1049,7 @@ if (audio_ready_for_playback_fired_) { return; } - LOG(INFO) << this << " ready for playback"; + AUDIO_LOG(INFO) << this << " ready for playback"; mixer_service::Generic message; auto* ready_for_playback = message.mutable_ready_for_playback(); @@ -1075,8 +1080,8 @@ void MixerInputConnection::OnAudioPlaybackError(MixerError error) { if (error == MixerError::kInputIgnored) { - LOG(INFO) << "Mixer input " << this - << " now being ignored due to output sample rate change"; + AUDIO_LOG(INFO) << "Mixer input " << this + << " now being ignored due to output sample rate change"; } io_task_runner_->PostTask(
diff --git a/chromecast/media/cma/backend/mixer/stream_mixer.cc b/chromecast/media/cma/backend/mixer/stream_mixer.cc index deac9fc..60f6e3bb 100644 --- a/chromecast/media/cma/backend/mixer/stream_mixer.cc +++ b/chromecast/media/cma/backend/mixer/stream_mixer.cc
@@ -25,6 +25,7 @@ #include "chromecast/base/serializers.h" #include "chromecast/base/thread_health_checker.h" #include "chromecast/media/audio/audio_io_thread.h" +#include "chromecast/media/audio/audio_log.h" #include "chromecast/media/audio/interleaved_channel_mixer.h" #include "chromecast/media/audio/mixer_service/loopback_interrupt_reason.h" #include "chromecast/media/base/audio_device_ids.h" @@ -220,6 +221,9 @@ io_task_runner_ = mixer_task_runner_; } + io_task_runner_->PostTask(FROM_HERE, + base::BindOnce(&logging::InitializeAudioLog)); + if (fixed_output_sample_rate_ != MixerOutputStream::kInvalidSampleRate) { LOG(INFO) << "Setting fixed sample rate to " << fixed_output_sample_rate_; } @@ -307,8 +311,8 @@ // Attempt to fall back to built-in cast_audio.json, unless we were reset with // an override config. if (!mixer_pipeline_ && override_config.empty()) { - LOG(WARNING) << "Invalid cast_audio.json config loaded. Retrying with " - "read-only config"; + AUDIO_LOG(WARNING) << "Invalid cast_audio.json config loaded. Retrying with" + " read-only config"; callback(false, "Unable to build pipeline."); // TODO(bshaya): Send more specific // error message. @@ -325,10 +329,11 @@ fixed_num_output_channels_ != mixer_pipeline_->GetOutputChannelCount()) { // Just log a warning, but this is still fine because we will remap the // channels prior to output. - LOG(WARNING) << "PostProcessor configuration output channel count does not " - << "match command line flag: " - << mixer_pipeline_->GetOutputChannelCount() << " vs " - << fixed_num_output_channels_ << ". Channels will be remapped"; + AUDIO_LOG(WARNING) << "PostProcessor configuration output channel count" + << " does not match command line flag: " + << mixer_pipeline_->GetOutputChannelCount() << " vs " + << fixed_num_output_channels_ + << ". Channels will be remapped"; } if (state_ == kStateRunning) { @@ -397,7 +402,7 @@ } void StreamMixer::SetNumOutputChannelsOnThread(int num_channels) { - LOG(INFO) << "Set the number of output channels to " << num_channels; + AUDIO_LOG(INFO) << "Set the number of output channels to " << num_channels; enable_dynamic_channel_count_ = true; fixed_num_output_channels_ = num_channels; @@ -408,7 +413,7 @@ } void StreamMixer::Start() { - LOG(INFO) << __func__ << " with " << inputs_.size() << " active inputs"; + AUDIO_LOG(INFO) << __func__ << " with " << inputs_.size() << " active inputs"; DCHECK(mixer_task_runner_->BelongsToCurrentThread()); DCHECK(state_ == kStateStopped); @@ -458,9 +463,9 @@ num_output_channels_ = output_->GetNumChannels(); output_samples_per_second_ = output_->GetSampleRate(); - LOG(INFO) << "Output " << num_output_channels_ << " " - << ChannelString(num_output_channels_) << " at " - << output_samples_per_second_ << " samples per second"; + AUDIO_LOG(INFO) << "Output " << num_output_channels_ << " " + << ChannelString(num_output_channels_) << " at " + << output_samples_per_second_ << " samples per second"; // Make sure the number of frames meets the filter alignment requirements. frames_per_write_ = output_->OptimalWriteFramesCount() & ~(filter_frame_alignment_ - 1); @@ -476,8 +481,8 @@ if (!enable_dynamic_channel_count_ && num_output_channels_ == 1) { num_loopback_channels = 1; } - LOG(INFO) << "Using " << num_loopback_channels << " loopback " - << ChannelString(num_loopback_channels); + AUDIO_LOG(INFO) << "Using " << num_loopback_channels << " loopback " + << ChannelString(num_loopback_channels); loopback_channel_mixer_ = std::make_unique<InterleavedChannelMixer>( mixer::GuessChannelLayout(mixer_pipeline_->GetLoopbackChannelCount()), mixer_pipeline_->GetLoopbackChannelCount(), @@ -533,7 +538,7 @@ } void StreamMixer::Stop(LoopbackInterruptReason reason) { - LOG(INFO) << __func__; + AUDIO_LOG(INFO) << __func__; DCHECK(mixer_task_runner_->BelongsToCurrentThread()); weak_factory_.InvalidateWeakPtrs(); @@ -596,9 +601,10 @@ } int StreamMixer::GetEffectiveChannelCount(MixerInput::Source* input_source) { - LOG(INFO) << "Input source channel count = " << input_source->num_channels(); + AUDIO_LOG(INFO) << "Input source channel count = " + << input_source->num_channels(); if (!enable_dynamic_channel_count_) { - LOG(INFO) << "Dynamic channel count not enabled; using stereo"; + AUDIO_LOG(INFO) << "Dynamic channel count not enabled; using stereo"; return kDefaultInputChannels; } @@ -631,10 +637,11 @@ DCHECK(input_group) << "Could not find a processor for " << input_source->device_id(); - LOG(INFO) << "Add input " << input_source << " to " << input_group->name() - << " @ " << input_group->GetInputSampleRate() - << " samples per second. Is primary source? = " - << input_source->primary(); + AUDIO_LOG(INFO) << "Add input " << input_source << " to " + << input_group->name() << " @ " + << input_group->GetInputSampleRate() + << " samples per second. Is primary source? = " + << input_source->primary(); auto input = std::make_unique<MixerInput>(input_source, input_group); if (state_ != kStateRunning) { @@ -673,7 +680,7 @@ DCHECK(mixer_task_runner_->BelongsToCurrentThread()); DCHECK(input_source); - LOG(INFO) << "Remove input " << input_source; + AUDIO_LOG(INFO) << "Remove input " << input_source; auto it = inputs_.find(input_source); if (it != inputs_.end()) { @@ -719,7 +726,7 @@ } DCHECK(playout_channel == kChannelAll || playout_channel >= 0); - LOG(INFO) << "Update playout channel: " << playout_channel; + AUDIO_LOG(INFO) << "Update playout channel: " << playout_channel; playout_channel_ = playout_channel; mixer_pipeline_->SetPlayoutChannel(playout_channel_); } @@ -764,7 +771,7 @@ DCHECK(mixer_task_runner_->BelongsToCurrentThread()); if (inputs_.empty() && base::TimeTicks::Now() >= close_timestamp_ && !mixer_pipeline_->IsRinging()) { - LOG(INFO) << "Close timeout"; + AUDIO_LOG(INFO) << "Close timeout"; Stop(LoopbackInterruptReason::kOutputStopped); return; } @@ -843,7 +850,7 @@ void StreamMixer::AddAudioOutputRedirector( std::unique_ptr<AudioOutputRedirector> redirector) { MAKE_SURE_MIXER_THREAD(AddAudioOutputRedirector, std::move(redirector)); - LOG(INFO) << __func__; + AUDIO_LOG(INFO) << __func__; DCHECK(redirector); AudioOutputRedirector* key = redirector.get(); @@ -867,7 +874,7 @@ void StreamMixer::RemoveAudioOutputRedirectorOnThread( AudioOutputRedirector* redirector) { DCHECK(mixer_task_runner_->BelongsToCurrentThread()); - LOG(INFO) << __func__; + AUDIO_LOG(INFO) << __func__; audio_output_redirectors_.erase(redirector); } @@ -908,7 +915,7 @@ MAKE_SURE_MIXER_THREAD(SetOutputLimit, type, limit); DCHECK(type != AudioContentType::kOther); - LOG(INFO) << "Set volume limit for " << type << " to " << limit; + AUDIO_LOG(INFO) << "Set volume limit for " << type << " to " << limit; volume_info_[type].limit = limit; int fade_ms = kUseDefaultFade; if (type == AudioContentType::kMedia) {
diff --git a/chromeos/components/media_app_ui/resources/js/launch.js b/chromeos/components/media_app_ui/resources/js/launch.js index 707c4ca..115add9 100644 --- a/chromeos/components/media_app_ui/resources/js/launch.js +++ b/chromeos/components/media_app_ui/resources/js/launch.js
@@ -195,6 +195,55 @@ await saveBlobToFile(handle, blob); }); +guestMessagePipe.registerHandler(Message.SAVE_AS, async (message) => { + const {blob, oldFileToken, pickedFileToken} = + /** @type {!SaveAsMessage} */ (message); + const oldFileDescriptor = currentFiles.find(fd => fd.token === oldFileToken); + /** @type {!FileDescriptor} */ + const pickedFileDescriptor = { + // We silently take over the old file's file descriptor by taking its token, + // note we can be passed an undefined token if the file we are saving was + // dragged into the media app. + token: oldFileToken || tokenGenerator.next().value, + file: null, + handle: tokenMap.get(pickedFileToken) + }; + const oldFileIndex = currentFiles.findIndex(fd => fd.token === oldFileToken); + tokenMap.set(pickedFileDescriptor.token, pickedFileDescriptor.handle); + // Give the old file a new token, if we couldn't find the old file we assume + // its been deleted (or pasted/dragged into the media app) and skip this + // step. + if (oldFileDescriptor) { + oldFileDescriptor.token = generateToken(oldFileDescriptor.handle); + } + try { + // Note `pickedFileHandle` could be the same as a `FileSystemFileHandle` + // that exists in `tokenMap`. Possibly even the `File` currently open. But + // that's OK. E.g. the next overwrite-file request will just invoke + // `saveBlobToFile` in the same way. Note there may be no currently writable + // file (e.g. save from clipboard). + await saveBlobToFile(pickedFileDescriptor.handle, blob); + } catch (/** @type {!DOMException} */ e) { + // If something went wrong revert the token back to its original + // owner so future file actions function correctly. + if (oldFileDescriptor && oldFileToken) { + oldFileDescriptor.token = oldFileToken; + tokenMap.set(oldFileToken, oldFileDescriptor.handle); + } + throw e; + } + + // Note: oldFileIndex may be `-1` here which causes the new file to be added + // to the start of the array, this is WAI. + currentFiles.splice(oldFileIndex + 1, 0, pickedFileDescriptor); + // Silently update entry index without triggering a reload of the media app. + entryIndex = oldFileIndex + 1; + + /** @type {!SaveAsResponse} */ + const response = {newFilename: pickedFileDescriptor.handle.name}; + return response; +}); + /** * Shows a file picker to get a writable file. * @param {string} suggestedName @@ -231,7 +280,8 @@ assertCast(crypto).getRandomValues(randomBuffer); for (let i = 0; i < randomBuffer.length; ++i) { const token = randomBuffer[i]; - if (!tokenMap.has(token)) { + // Disallow "0" as a token. + if (token && !tokenMap.has(token)) { yield Number(token); } }
diff --git a/chromeos/components/media_app_ui/resources/js/media_app.externs.js b/chromeos/components/media_app_ui/resources/js/media_app.externs.js index 1bdb833..42762dfd 100644 --- a/chromeos/components/media_app_ui/resources/js/media_app.externs.js +++ b/chromeos/components/media_app_ui/resources/js/media_app.externs.js
@@ -76,6 +76,14 @@ * @type {function(string): !Promise<number>|undefined} */ mediaApp.AbstractFile.prototype.renameOriginalFile; +/** + * A function that will save the provided blob in the file pointed to by + * pickedFileToken. Once saved the new file takes over this.token and becomes + * currently writable. The original file is given a new token + * and pushed forward in the navigation order. + * @type {function(!Blob, number): !Promise<undefined>|undefined} + */ +mediaApp.AbstractFile.prototype.saveAs; /** * Wraps an HTML FileList object.
diff --git a/chromeos/components/media_app_ui/resources/js/message_types.js b/chromeos/components/media_app_ui/resources/js/message_types.js index 7b5b2b42..ae5fe48 100644 --- a/chromeos/components/media_app_ui/resources/js/message_types.js +++ b/chromeos/components/media_app_ui/resources/js/message_types.js
@@ -21,6 +21,7 @@ OVERWRITE_FILE: 'overwrite-file', RENAME_FILE: 'rename-file', REQUEST_SAVE_FILE: 'request-save-file', + SAVE_AS: 'save-as', SAVE_COPY: 'save-copy' }; @@ -138,3 +139,22 @@ * @typedef {{blob: !Blob, token: number}} */ let SaveCopyMessage; + +/** + * Message sent by the unprivileged context to the privileged context requesting + * for the provided blob to be saved in the location specified by + * `pickedFileToken`. Once saved the new file takes over oldFileToken if it is + * provided, else it gives itself a fresh token, then it becomes currently + * writable. The file specified by oldFileToken is given a new token and pushed + * forward in the navigation order. This method can be called with any file, not + * just the currently writable file. + * @typedef {{blob: !Blob, oldFileToken: ?number, pickedFileToken: number}} + */ +let SaveAsMessage; + +/** + * Response message sent by the privileged context with the name of the new + * current file. + * @typedef {{newFilename: string}} + */ +let SaveAsResponse;
diff --git a/chromeos/components/media_app_ui/resources/js/receiver.js b/chromeos/components/media_app_ui/resources/js/receiver.js index 6f81e02..da47c0e 100644 --- a/chromeos/components/media_app_ui/resources/js/receiver.js +++ b/chromeos/components/media_app_ui/resources/js/receiver.js
@@ -70,6 +70,23 @@ Message.RENAME_FILE, {token: this.token, newFilename: newName})); return renameResponse.renameResult; } + + /** + * @override + * @param {!Blob} blob + * @param {number} pickedFileToken + * @return {!Promise<undefined>} + */ + async saveAs(blob, pickedFileToken) { + /** @type {!SaveAsMessage} */ + const message = {blob, oldFileToken: this.token, pickedFileToken}; + const result = /** @type {!SaveAsResponse} */ ( + await parentMessagePipe.sendMessage(Message.SAVE_AS, message)); + this.name = result.newFilename; + this.blob = blob; + this.size = blob.size; + this.mimeType = blob.type; + } } /**
diff --git a/chromeos/components/media_app_ui/test/driver_api.js b/chromeos/components/media_app_ui/test/driver_api.js index ffab3aa71..f1b0ac2 100644 --- a/chromeos/components/media_app_ui/test/driver_api.js +++ b/chromeos/components/media_app_ui/test/driver_api.js
@@ -23,7 +23,7 @@ * renameLastFile: (string|undefined), * requestFullscreen: (boolean|undefined), * requestSaveFile: (boolean|undefined), - * saveCopy: (boolean|undefined), + * saveAs: (string|undefined), * testQuery: string, * }} */
diff --git a/chromeos/components/media_app_ui/test/guest_query_receiver.js b/chromeos/components/media_app_ui/test/guest_query_receiver.js index c152e35..cc46c058 100644 --- a/chromeos/components/media_app_ui/test/guest_query_receiver.js +++ b/chromeos/components/media_app_ui/test/guest_query_receiver.js
@@ -97,15 +97,23 @@ existingFile.name, existingFile.mimeType); result = token.toString(); } - } else if (data.saveCopy) { + } else if (data.saveAs) { const existingFile = assertCast(lastReceivedFileList).item(0); if (!existingFile) { - result = 'saveCopy failed, no file loaded'; + result = 'saveAs failed, no file loaded'; } else { - const token = await DELEGATE.requestSaveFile( - existingFile.name, existingFile.mimeType); - await DELEGATE.saveCopy(existingFile, token); - result = 'file successfully saved'; + const file = firstReceivedItem(); + try { + const token = await DELEGATE.requestSaveFile( + existingFile.name, existingFile.mimeType); + const testBlob = new Blob([data.saveAs]); + await assertCast(file.saveAs).call(file, testBlob, token); + result = file.name; + extraResultData = {blobText: await file.blob.text()}; + } catch (/** @type{!Error} */ error) { + result = `saveAs failed Error: ${error}`; + extraResultData = {filename: file.name}; + } } } else if (data.getFileErrors) { result =
diff --git a/chromeos/components/media_app_ui/test/media_app_ui_browsertest.js b/chromeos/components/media_app_ui/test/media_app_ui_browsertest.js index 51ad118..0c006a5 100644 --- a/chromeos/components/media_app_ui/test/media_app_ui_browsertest.js +++ b/chromeos/components/media_app_ui/test/media_app_ui_browsertest.js
@@ -445,10 +445,10 @@ assertEquals(result, '222'); assertEquals(currentFiles.length, 3); - // The error stays on the third, now unopenable. But, since we've advanced, - // it has now rotated into the second slot. But! Also we don't validate it - // until it rotates into the first slot, so the error won't be present yet. - // If we implement pre-loading, this expectation can change to + // The error stays on the third, now unopenable. But, since we've advanced, it + // has now rotated into the second slot. But! Also we don't validate it until + // it rotates into the first slot, so the error won't be present yet. If we + // implement pre-loading, this expectation can change to // ',NotAllowedError,'. assertEquals(await getFileErrors(), ',,'); @@ -841,20 +841,68 @@ testDone(); }); -// Tests the IPC behind the saveCopy delegate function. -TEST_F('MediaAppUIBrowserTest', 'SaveCopyIPC', async () => { +// Tests the IPC behind the saveAs function on received files. +TEST_F('MediaAppUIBrowserTest', 'SaveAsIPC', async () => { // Mock out choose file system entries since it can only be interacted with // via trusted user gestures. - const newFileHandle = new FakeFileSystemFileHandle(); + const newFileHandle = new FakeFileSystemFileHandle('new_file.jpg'); window.showSaveFilePicker = () => Promise.resolve(newFileHandle); const testImage = await createTestImageFile(10, 10); - await loadFile(testImage, new FakeFileSystemFileHandle()); + const testHandle = new FakeFileSystemFileHandle('original_file.jpg'); + await loadFile(testImage, testHandle); + const originalFileToken = currentFiles[0].token; + assertEquals(entryIndex, 0); - const result = await sendTestMessage({saveCopy: true}); - assertEquals(result.testQueryResult, 'file successfully saved'); + const result = await sendTestMessage({saveAs: 'foo'}); + // Make sure the receivedFile object has the correct state. + assertEquals(result.testQueryResult, 'new_file.jpg'); + assertEquals(await result.testQueryResultData['blobText'], 'foo'); + // Confirm the right string was written to the new file. const writeResult = await newFileHandle.lastWritable.closePromise; - assertEquals(await writeResult.text(), await testImage.text()); + assertEquals(await writeResult.text(), 'foo'); + // Make sure we have created a new file descriptor, and that + // the original file is still available. + assertEquals(entryIndex, 1); + assertEquals(currentFiles[0].handle, testHandle); + assertEquals(currentFiles[0].handle.name, 'original_file.jpg'); + assertNotEquals(currentFiles[0].token, originalFileToken); + assertEquals(currentFiles[1].handle, newFileHandle); + assertEquals(currentFiles[1].handle.name, 'new_file.jpg'); + assertEquals(currentFiles[1].token, originalFileToken); + assertEquals(tokenMap.get(currentFiles[0].token), currentFiles[0].handle); + assertEquals(tokenMap.get(currentFiles[1].token), currentFiles[1].handle); + testDone(); +}); + +// Tests the error handling behind the saveAs function on received files. +TEST_F('MediaAppUIBrowserTest', 'SaveAsErrorHandling', async () => { + // Prevent the trusted context from throwing errors which cause the test to + // fail. + guestMessagePipe.logClientError = error => console.log(JSON.stringify(error)); + guestMessagePipe.rethrowErrors = false; + const newFileHandle = new FakeFileSystemFileHandle('new_file.jpg'); + newFileHandle.nextCreateWritableError = + new DOMException('Fake exception', 'FakeError'); + window.showSaveFilePicker = () => Promise.resolve(newFileHandle); + const testImage = await createTestImageFile(10, 10); + const testHandle = new FakeFileSystemFileHandle('original_file.jpg'); + await loadFile(testImage, testHandle); + const originalFileToken = currentFiles[0].token; + + const result = await sendTestMessage({saveAs: 'foo'}); + + // Make sure we revert back to our original state. + assertEquals( + result.testQueryResult, + 'saveAs failed Error: FakeError: save-as: Fake exception'); + assertEquals(result.testQueryResultData['filename'], 'original_file.jpg'); + assertEquals(entryIndex, 0); + assertEquals(currentFiles.length, 1); + assertEquals(currentFiles[0].handle, testHandle); + assertEquals(currentFiles[0].handle.name, 'original_file.jpg'); + assertEquals(currentFiles[0].token, originalFileToken); + assertEquals(tokenMap.get(currentFiles[0].token), currentFiles[0].handle); testDone(); });
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc index ad8922a8..2cf5a888 100644 --- a/chromeos/constants/chromeos_features.cc +++ b/chromeos/constants/chromeos_features.cc
@@ -362,6 +362,10 @@ const base::Feature kPrintJobManagementApp{"PrintJobManagementApp", base::FEATURE_ENABLED_BY_DEFAULT}; +// Changes Print Preview Save to Drive to use local Drive. +const base::Feature kPrintSaveToDrive{"PrintSaveToDrive", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Controls whether to enable quick answers. const base::Feature kQuickAnswers{"QuickAnswers", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h index 411dcd7..37b4855 100644 --- a/chromeos/constants/chromeos_features.h +++ b/chromeos/constants/chromeos_features.h
@@ -157,6 +157,8 @@ COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kPrintJobManagementApp; COMPONENT_EXPORT(CHROMEOS_CONSTANTS) +extern const base::Feature kPrintSaveToDrive; +COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kPrinterStatus; COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kQuickAnswers; COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
diff --git a/components/arc/mojom/audio.mojom b/components/arc/mojom/audio.mojom index 6d90a02..eb9a344c7 100644 --- a/components/arc/mojom/audio.mojom +++ b/components/arc/mojom/audio.mojom
@@ -19,7 +19,7 @@ ShowVolumeControls@0(); // Request that the volume be changed to |volume|. - // This is a privileged API and should only be used on whitelisted cases. + // This is a privileged API and should only be used on allowlisted cases. // |percent| is of the range [0, 100]. [MinVersion=3] OnSystemVolumeUpdateRequest@1(int32 percent); };
diff --git a/components/arc/mojom/auth.mojom b/components/arc/mojom/auth.mojom index 8045fa16..d51576f 100644 --- a/components/arc/mojom/auth.mojom +++ b/components/arc/mojom/auth.mojom
@@ -132,8 +132,8 @@ ERROR_ACCOUNT_NOT_READY = 14, // Checkin failed. ERROR_CHECKIN_FAILED = 15, - // Issues with whitelisting work account. - ERROR_ACCOUNT_NOT_WHITELISTED = 16, + // Issues with allowlisting work account. + ERROR_ACCOUNT_NOT_ALLOWLISTED = 16, // Error parsing JSON, most likely policy JSON ERROR_JSON = 17, // ManagedProvisioning failed.
diff --git a/components/arc/mojom/cert_store.mojom b/components/arc/mojom/cert_store.mojom index 24ec3fb3..984ffdd 100644 --- a/components/arc/mojom/cert_store.mojom +++ b/components/arc/mojom/cert_store.mojom
@@ -75,7 +75,7 @@ interface CertStoreHost { // The helper method, which does not correspond to keymaster interface. // It returns a list of Chrome OS corporate usage client certificates if - // any Android app is whitelisted to use them, otherwise returns an + // any Android app is allowlisted to use them, otherwise returns an // empty list. ListCertificates@0() => (array<Certificate> certs);
diff --git a/components/arc/mojom/intent_helper.mojom b/components/arc/mojom/intent_helper.mojom index 697d6c8..989195a 100644 --- a/components/arc/mojom/intent_helper.mojom +++ b/components/arc/mojom/intent_helper.mojom
@@ -392,8 +392,8 @@ // specified. Data can be sent as extras by including a JSON map string which // will be automatically converted to a bundle accessible by the receiver. // - // Note: Broadcasts can only be sent to whitelisted packages. Packages can be - // added to the whitelist in ArcBridgeService.java in the Android source. + // Note: Broadcasts can only be sent to allowlisted packages. Packages can be + // added to the allowlist in ArcBridgeService.java in the Android source. [MinVersion=1] SendBroadcast@1(string action, string package_name, string cls,
diff --git a/components/arc/session/arc_property_util.cc b/components/arc/session/arc_property_util.cc index ccdca60f..d1ee992 100644 --- a/components/arc/session/arc_property_util.cc +++ b/components/arc/session/arc_property_util.cc
@@ -15,6 +15,7 @@ #include "base/process/launch.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "chromeos/constants/chromeos_switches.h" namespace brillo { @@ -40,11 +41,12 @@ constexpr char kPAIRegionsPropertyName[] = "pai-regions"; // Properties related to dynamically adding native bridge 64 bit support. -constexpr char kAbilistPropertyPrefix[] = "ro.product.cpu.abilist="; +constexpr char kAbilistPropertyPrefixTemplate[] = "ro.%sproduct.cpu.abilist="; constexpr char kAbilistPropertyExpected[] = "x86_64,x86,armeabi-v7a,armeabi"; constexpr char kAbilistPropertyReplacement[] = "x86_64,x86,arm64-v8a,armeabi-v7a,armeabi"; -constexpr char kAbilist64PropertyPrefix[] = "ro.product.cpu.abilist64="; +constexpr char kAbilist64PropertyPrefixTemplate[] = + "ro.%sproduct.cpu.abilist64="; constexpr char kAbilist64PropertyExpected[] = "x86_64"; constexpr char kAbilist64PropertyReplacement[] = "x86_64,arm64-v8a"; constexpr char kDalvikVmIsaArm64[] = "ro.dalvik.vm.isa.arm64=x86_64"; @@ -158,7 +160,9 @@ bool ExpandPropertyContents(const std::string& content, brillo::CrosConfigInterface* config, std::string* expanded_content, - bool add_native_bridge_64bit_support) { + bool add_native_bridge_64bit_support, + bool append_dalvik_isa, + const std::string& partition_name) { const std::vector<std::string> lines = base::SplitString( content, "\n", base::WhitespaceHandling::KEEP_WHITESPACE, base::SplitResult::SPLIT_WANT_ALL); @@ -204,27 +208,30 @@ expanded += line.substr(prev_match); line = expanded; } while (inserted); + if (add_native_bridge_64bit_support) { - // Special-case ro.product.cpu.abilist / ro.product.cpu.abilist64 to add - // ARM64. + // Special-case ro.<partition>.product.cpu.abilist and + // ro.<partition>.product.cpu.abilist64 to add ARM64. + std::string prefix = base::StringPrintf(kAbilistPropertyPrefixTemplate, + partition_name.c_str()); std::string value; - if (FindProperty(kAbilistPropertyPrefix, &value, line)) { + if (FindProperty(prefix, &value, line)) { if (value == kAbilistPropertyExpected) { - line = std::string(kAbilistPropertyPrefix) + - std::string(kAbilistPropertyReplacement); + line = prefix + std::string(kAbilistPropertyReplacement); } else { - LOG(ERROR) << "Found unexpected value for " << kAbilistPropertyPrefix - << ", value " << value; + LOG(ERROR) << "Found unexpected value for " << prefix << ", value " + << value; return false; } } - if (FindProperty(kAbilist64PropertyPrefix, &value, line)) { + prefix = base::StringPrintf(kAbilist64PropertyPrefixTemplate, + partition_name.c_str()); + if (FindProperty(prefix, &value, line)) { if (value == kAbilist64PropertyExpected) { - line = std::string(kAbilist64PropertyPrefix) + - std::string(kAbilist64PropertyReplacement); + line = prefix + std::string(kAbilist64PropertyReplacement); } else { - LOG(ERROR) << "Found unexpected value for " - << kAbilist64PropertyPrefix << ", value " << value; + LOG(ERROR) << "Found unexpected value for " << prefix << ", value " + << value; return false; } } @@ -248,7 +255,7 @@ } } - if (add_native_bridge_64bit_support) { + if (append_dalvik_isa) { // Special-case to add ro.dalvik.vm.isa.arm64. new_properties += std::string(kDalvikVmIsaArm64) + "\n"; } @@ -261,7 +268,9 @@ const base::FilePath& output, CrosConfig* config, bool append, - bool add_native_bridge_64bit_support) { + bool add_native_bridge_64bit_support, + bool append_dalvik_isa, + const std::string& partition_name) { std::string content; std::string expanded; if (!base::ReadFileToString(input, &content)) { @@ -269,7 +278,8 @@ return false; } if (!ExpandPropertyContents(content, config, &expanded, - add_native_bridge_64bit_support)) + add_native_bridge_64bit_support, + append_dalvik_isa, partition_name)) return false; if (append && base::PathExists(output)) { if (!base::AppendToFile(output, expanded.data(), expanded.size())) { @@ -330,7 +340,8 @@ brillo::CrosConfigInterface* config, std::string* expanded_content) { return ExpandPropertyContents(content, config, expanded_content, - /*add_native_bridge_64bit_support=*/false); + /*add_native_bridge_64bit_support=*/false, + false, ""); } bool TruncateAndroidPropertyForTesting(const std::string& line, @@ -342,7 +353,8 @@ const base::FilePath& output, CrosConfig* config) { return ExpandPropertyFile(input, output, config, /*append=*/false, - /*add_native_bridge_64bit_support=*/false); + /*add_native_bridge_64bit_support=*/false, false, + ""); } bool ExpandPropertyFiles(const base::FilePath& source_path, @@ -355,13 +367,24 @@ // default.prop may not exist. Silently skip it if not found. for (const auto& tuple : - {std::tuple<const char*, bool, bool>{"default.prop", true, false}, - {"build.prop", false, true}, - {"vendor_build.prop", false, false}}) { + // The order has to match the one in PropertyLoadBootDefaults() in + // system/core/init/property_service.cpp. + // Note: Our vendor image doesn't have /vendor/default.prop although + // PropertyLoadBootDefaults() tries to open it. + {std::tuple<const char*, bool, bool, const char*>{"default.prop", true, + false, ""}, + {"build.prop", false, true, ""}, + {"system_ext_build.prop", true, false, "system_ext."}, + {"vendor_build.prop", false, false, "vendor."}, + {"odm_build.prop", true, false, "odm."}, + {"product_build.prop", true, false, "product."}}) { const char* file = std::get<0>(tuple); const bool is_optional = std::get<1>(tuple); - const bool add_native_bridge_properties = + // When true, unconditionally add |kDalvikVmIsaArm64| property. + const bool append_dalvik_isa = std::get<2>(tuple) && add_native_bridge_64bit_support; + // Search for ro.<partition_name>product.cpu.abilist* properties. + const char* partition_name = std::get<3>(tuple); if (is_optional && !base::PathExists(source_path.Append(file))) continue; @@ -369,7 +392,8 @@ if (!ExpandPropertyFile( source_path.Append(file), single_file ? dest_path : dest_path.Append(file), &config, - /*append=*/single_file, add_native_bridge_properties)) { + /*append=*/single_file, add_native_bridge_64bit_support, + append_dalvik_isa, partition_name)) { LOG(ERROR) << "Failed to expand " << source_path.Append(file); return false; }
diff --git a/components/arc/session/arc_property_util_unittest.cc b/components/arc/session/arc_property_util_unittest.cc index 55126dd..fec2e8ee 100644 --- a/components/arc/session/arc_property_util_unittest.cc +++ b/components/arc/session/arc_property_util_unittest.cc
@@ -379,54 +379,88 @@ EXPECT_FALSE(ExpandPropertyFiles(source_dir, dest_prop_file, true, false)); // Add default.prop to the source, but not build.prop. - base::FilePath default_prop = source_dir.Append("default.prop"); + const base::FilePath default_prop = source_dir.Append("default.prop"); constexpr const char kDefaultProp[] = "ro.foo=bar\n"; base::WriteFile(default_prop, kDefaultProp, strlen(kDefaultProp)); EXPECT_FALSE(ExpandPropertyFiles(source_dir, dest_prop_file, true, false)); // Add build.prop too. The call should not succeed still. - base::FilePath build_prop = source_dir.Append("build.prop"); + const base::FilePath build_prop = source_dir.Append("build.prop"); constexpr const char kBuildProp[] = "ro.baz=boo\n"; base::WriteFile(build_prop, kBuildProp, strlen(kBuildProp)); EXPECT_FALSE(ExpandPropertyFiles(source_dir, dest_prop_file, true, false)); // Add vendor_build.prop too. Then the call should succeed. - base::FilePath vendor_build_prop = source_dir.Append("vendor_build.prop"); + const base::FilePath vendor_build_prop = + source_dir.Append("vendor_build.prop"); constexpr const char kVendorBuildProp[] = "ro.a=b\n"; base::WriteFile(vendor_build_prop, kVendorBuildProp, strlen(kVendorBuildProp)); EXPECT_TRUE(ExpandPropertyFiles(source_dir, dest_prop_file, true, false)); + // Add other optional files too. Then the call should succeed. + const base::FilePath system_ext_build_prop = + source_dir.Append("system_ext_build.prop"); + constexpr const char kSystemExtBuildProp[] = "ro.c=d\n"; + base::WriteFile(system_ext_build_prop, kSystemExtBuildProp, + strlen(kSystemExtBuildProp)); + EXPECT_TRUE(ExpandPropertyFiles(source_dir, dest_prop_file, true, false)); + + const base::FilePath odm_build_prop = source_dir.Append("odm_build.prop"); + constexpr const char kOdmBuildProp[] = "ro.e=f\n"; + base::WriteFile(odm_build_prop, kOdmBuildProp, strlen(kOdmBuildProp)); + EXPECT_TRUE(ExpandPropertyFiles(source_dir, dest_prop_file, true, false)); + + const base::FilePath product_build_prop = + source_dir.Append("product_build.prop"); + constexpr const char kProductBuildProp[] = "ro.g=h\n"; + base::WriteFile(product_build_prop, kProductBuildProp, + strlen(kProductBuildProp)); + EXPECT_TRUE(ExpandPropertyFiles(source_dir, dest_prop_file, true, false)); + // Verify only one dest file exists. EXPECT_FALSE( base::PathExists(dest_prop_file.DirName().Append("default.prop"))); EXPECT_FALSE(base::PathExists(dest_prop_file.DirName().Append("build.prop"))); EXPECT_FALSE( base::PathExists(dest_prop_file.DirName().Append("vendor_build.prop"))); + EXPECT_FALSE(base::PathExists( + dest_prop_file.DirName().Append("system_ext_build.prop"))); + EXPECT_FALSE( + base::PathExists(dest_prop_file.DirName().Append("odm_build.prop"))); + EXPECT_FALSE( + base::PathExists(dest_prop_file.DirName().Append("product_build.prop"))); EXPECT_TRUE(base::PathExists(dest_prop_file)); // Verify the content. // Note: ExpandPropertyFileForTesting() adds a trailing LF. std::string content; EXPECT_TRUE(base::ReadFileToString(dest_prop_file, &content)); - EXPECT_EQ(base::StringPrintf("%s\n%s\n%s\n", kDefaultProp, kBuildProp, - kVendorBuildProp), - content); + EXPECT_EQ( + base::StringPrintf("%s\n%s\n%s\n%s\n%s\n%s\n", kDefaultProp, kBuildProp, + kSystemExtBuildProp, kVendorBuildProp, kOdmBuildProp, + kProductBuildProp), + content); // Expand it again, verify the previous result is cleared. EXPECT_TRUE(ExpandPropertyFiles(source_dir, dest_prop_file, true, false)); EXPECT_TRUE(base::ReadFileToString(dest_prop_file, &content)); - EXPECT_EQ(base::StringPrintf("%s\n%s\n%s\n", kDefaultProp, kBuildProp, - kVendorBuildProp), - content); + EXPECT_EQ( + base::StringPrintf("%s\n%s\n%s\n%s\n%s\n%s\n", kDefaultProp, kBuildProp, + kSystemExtBuildProp, kVendorBuildProp, kOdmBuildProp, + kProductBuildProp), + content); - // If default.prop does not exist in the source path, it should still process - // the other files. + // If optional ones e.g. default.prop does not exist in the source path, it + // should still process the other files. base::DeleteFile(source_dir.Append("default.prop")); + base::DeleteFile(source_dir.Append("odm_build.prop")); EXPECT_TRUE(ExpandPropertyFiles(source_dir, dest_prop_file, true, false)); EXPECT_TRUE(base::ReadFileToString(dest_prop_file, &content)); - EXPECT_EQ(base::StringPrintf("%s\n%s\n", kBuildProp, kVendorBuildProp), - content); + EXPECT_EQ( + base::StringPrintf("%s\n%s\n%s\n%s\n", kBuildProp, kSystemExtBuildProp, + kVendorBuildProp, kProductBuildProp), + content); // Finally, test the case where source is valid but the dest is not. EXPECT_FALSE(ExpandPropertyFiles(source_dir, base::FilePath("/nonexistent"), @@ -454,7 +488,10 @@ base::WriteFile(build_prop, kBuildProp, strlen(kBuildProp)); base::FilePath vendor_build_prop = source_dir.Append("vendor_build.prop"); - constexpr const char kVendorBuildProp[] = "ro.a=b\n"; + constexpr const char kVendorBuildProp[] = + "ro.a=b\n" + "ro.vendor.product.cpu.abilist=x86_64,x86,armeabi-v7a,armeabi\n" + "ro.vendor.product.cpu.abilist64=x86_64\n"; base::WriteFile(vendor_build_prop, kVendorBuildProp, strlen(kVendorBuildProp)); @@ -485,7 +522,11 @@ EXPECT_EQ(std::string(kBuildPropModified) + "\n", content); EXPECT_TRUE( base::ReadFileToString(dest_dir.Append("vendor_build.prop"), &content)); - EXPECT_EQ(std::string(kVendorBuildProp) + "\n", content); + constexpr const char kVendorBuildPropModified[] = + "ro.a=b\n" + "ro.vendor.product.cpu.abilist=x86_64,x86,arm64-v8a,armeabi-v7a,armeabi\n" + "ro.vendor.product.cpu.abilist64=x86_64,arm64-v8a\n"; + EXPECT_EQ(std::string(kVendorBuildPropModified) + "\n", content); // Expand to a single file with experiment on, verify properties are added / // modified as expected. @@ -498,7 +539,7 @@ // Verify the contents. EXPECT_TRUE(base::ReadFileToString(dest_prop_file, &content)); EXPECT_EQ(base::StringPrintf("%s\n%s\n%s\n", kDefaultProp, kBuildPropModified, - kVendorBuildProp), + kVendorBuildPropModified), content); // Verify that unexpected property values generate an error.
diff --git a/components/autofill/core/browser/ui/popup_types.h b/components/autofill/core/browser/ui/popup_types.h index 1656116..21918b9 100644 --- a/components/autofill/core/browser/ui/popup_types.h +++ b/components/autofill/core/browser/ui/popup_types.h
@@ -34,6 +34,9 @@ kUserAborted, // The user explicitly dismissed the popup (e.g. ESC key). kViewDestroyed, // The popup view (or its controller) goes out of scope. kWidgetChanged, // The platform-native UI changed (e.g. window resize). + kInsufficientSpace, // Not enough space in content area to display an display + // at least one row of the popup within the bounds of the + // content area. }; } // namespace autofill
diff --git a/components/feed/core/v2/enums.cc b/components/feed/core/v2/enums.cc index a675de4..3dd3af87 100644 --- a/components/feed/core/v2/enums.cc +++ b/components/feed/core/v2/enums.cc
@@ -33,8 +33,10 @@ return out << "kDataInStoreIsStale"; case LoadStreamStatus::kDataInStoreIsStaleTimestampInFuture: return out << "kDataInStoreIsStaleTimestampInFuture"; - case LoadStreamStatus::kCannotLoadFromNetworkSupressedForHistoryDelete: - return out << "kCannotLoadFromNetworkSupressedForHistoryDelete"; + case LoadStreamStatus:: + kCannotLoadFromNetworkSupressedForHistoryDelete_DEPRECATED: + return out + << "kCannotLoadFromNetworkSupressedForHistoryDelete_DEPRECATED"; case LoadStreamStatus::kCannotLoadFromNetworkOffline: return out << "kCannotLoadFromNetworkOffline"; case LoadStreamStatus::kCannotLoadFromNetworkThrottled:
diff --git a/components/feed/core/v2/enums.h b/components/feed/core/v2/enums.h index ae0c95c..dfb9544 100644 --- a/components/feed/core/v2/enums.h +++ b/components/feed/core/v2/enums.h
@@ -31,7 +31,7 @@ // The timestamp for stored data is in the future, so we're treating stored // data as it it is stale. kDataInStoreIsStaleTimestampInFuture = 9, - kCannotLoadFromNetworkSupressedForHistoryDelete = 10, + kCannotLoadFromNetworkSupressedForHistoryDelete_DEPRECATED = 10, kCannotLoadFromNetworkOffline = 11, kCannotLoadFromNetworkThrottled = 12, kLoadNotAllowedEulaNotAccepted = 13,
diff --git a/components/feed/core/v2/feed_network.h b/components/feed/core/v2/feed_network.h index 9e585ec..40d93da 100644 --- a/components/feed/core/v2/feed_network.h +++ b/components/feed/core/v2/feed_network.h
@@ -49,6 +49,7 @@ // |CancelRequests()|. virtual void SendQueryRequest( const feedwire::Request& request, + bool force_signed_out_request, base::OnceCallback<void(QueryRequestResult)> callback) = 0; // Send a feedwire::FeedActionRequest, and receive the response in |callback|.
diff --git a/components/feed/core/v2/feed_network_impl.cc b/components/feed/core/v2/feed_network_impl.cc index 433500e..9ed2346a 100644 --- a/components/feed/core/v2/feed_network_impl.cc +++ b/components/feed/core/v2/feed_network_impl.cc
@@ -146,6 +146,7 @@ NetworkFetch(const GURL& url, const std::string& request_type, std::string request_body, + bool force_signed_out_request, signin::IdentityManager* identity_manager, network::SharedURLLoaderFactory* loader_factory, const std::string& api_key, @@ -154,6 +155,7 @@ : url_(url), request_type_(request_type), request_body_(std::move(request_body)), + force_signed_out_request_(force_signed_out_request), identity_manager_(identity_manager), loader_factory_(loader_factory), api_key_(api_key), @@ -182,7 +184,7 @@ void Start(base::OnceCallback<void(RawResponse)> done_callback) { done_callback_ = std::move(done_callback); - if (!identity_manager_->HasPrimaryAccount()) { + if (force_signed_out_request_ || !identity_manager_->HasPrimaryAccount()) { StartLoader(); return; } @@ -381,6 +383,7 @@ const std::string request_type_; std::string access_token_; const std::string request_body_; + bool force_signed_out_request_; signin::IdentityManager* const identity_manager_; std::unique_ptr<signin::PrimaryAccountAccessTokenFetcher> token_fetcher_; std::unique_ptr<network::SimpleURLLoader> simple_loader_; @@ -417,6 +420,7 @@ void FeedNetworkImpl::SendQueryRequest( const feedwire::Request& request, + bool force_signed_out_request, base::OnceCallback<void(QueryRequestResult)> callback) { std::string binary_proto; request.SerializeToString(&binary_proto); @@ -432,7 +436,7 @@ AddMothershipPayloadQueryParams(base64proto, delegate_->GetLanguageTag(), url); - Send(url, "GET", /*request_body=*/{}, + Send(url, "GET", /*request_body=*/{}, force_signed_out_request, base::BindOnce(&ParseAndForwardResponse<QueryRequestResult, NetworkRequestType::kFeedQuery>, std::move(callback))); @@ -445,6 +449,7 @@ request.SerializeToString(&binary_proto); Send(GURL(kUploadActionUrl), "POST", std::move(binary_proto), + /*force_signed_out_request=*/false, base::BindOnce( &ParseAndForwardResponse<ActionRequestResult, NetworkRequestType::kUploadActions>, @@ -458,10 +463,12 @@ void FeedNetworkImpl::Send(const GURL& url, const std::string& request_type, std::string request_body, + bool force_signed_out_request, base::OnceCallback<void(RawResponse)> callback) { auto fetch = std::make_unique<NetworkFetch>( - url, request_type, std::move(request_body), identity_manager_, - loader_factory_.get(), api_key_, tick_clock_, pref_service_); + url, request_type, std::move(request_body), force_signed_out_request, + identity_manager_, loader_factory_.get(), api_key_, tick_clock_, + pref_service_); NetworkFetch* fetch_unowned = fetch.get(); pending_requests_.emplace(std::move(fetch));
diff --git a/components/feed/core/v2/feed_network_impl.h b/components/feed/core/v2/feed_network_impl.h index 660780e..c8d487d 100644 --- a/components/feed/core/v2/feed_network_impl.h +++ b/components/feed/core/v2/feed_network_impl.h
@@ -53,6 +53,7 @@ void SendQueryRequest( const feedwire::Request& request, + bool force_signed_out_request, base::OnceCallback<void(QueryRequestResult)> callback) override; void SendActionRequest( @@ -71,6 +72,7 @@ void Send(const GURL& url, const std::string& request_type, std::string request_body, + bool force_signed_out_request, base::OnceCallback<void(FeedNetworkImpl::RawResponse)> callback); void SendComplete(NetworkFetch* fetch,
diff --git a/components/feed/core/v2/feed_network_impl_unittest.cc b/components/feed/core/v2/feed_network_impl_unittest.cc index c86564d..69d4cce 100644 --- a/components/feed/core/v2/feed_network_impl_unittest.cc +++ b/components/feed/core/v2/feed_network_impl_unittest.cc
@@ -189,7 +189,7 @@ TEST_F(FeedNetworkTest, SendQueryRequestEmpty) { CallbackReceiver<QueryRequestResult> receiver; - feed_network()->SendQueryRequest(feedwire::Request(), receiver.Bind()); + feed_network()->SendQueryRequest(feedwire::Request(), false, receiver.Bind()); ASSERT_TRUE(receiver.GetResult()); const QueryRequestResult& result = *receiver.GetResult(); @@ -199,7 +199,8 @@ TEST_F(FeedNetworkTest, SendQueryRequestSendsValidRequest) { CallbackReceiver<QueryRequestResult> receiver; - feed_network()->SendQueryRequest(GetTestFeedRequest(), receiver.Bind()); + feed_network()->SendQueryRequest(GetTestFeedRequest(), false, + receiver.Bind()); network::ResourceRequest resource_request = RespondToQueryRequest("", net::HTTP_OK); @@ -217,9 +218,24 @@ "ContentSuggestions.Feed.Network.ResponseStatus.FeedQuery", 200, 1); } +TEST_F(FeedNetworkTest, SendQueryRequestForceSignedOut) { + CallbackReceiver<QueryRequestResult> receiver; + feed_network()->SendQueryRequest( + GetTestFeedRequest(), /*force_signed_out_request=*/true, receiver.Bind()); + network::ResourceRequest resource_request = + RespondToQueryRequest("", net::HTTP_OK); + + EXPECT_EQ( + "https://www.google.com/httpservice/retry/TrellisClankService/" + "FeedQuery?reqpld=CAHCPgQSAggB&fmt=bin&hl=en&key=dummy_api_key", + resource_request.url); + EXPECT_FALSE(resource_request.headers.HasHeader("Authorization")); +} + TEST_F(FeedNetworkTest, SendQueryRequestInvalidResponse) { CallbackReceiver<QueryRequestResult> receiver; - feed_network()->SendQueryRequest(GetTestFeedRequest(), receiver.Bind()); + feed_network()->SendQueryRequest(GetTestFeedRequest(), false, + receiver.Bind()); RespondToQueryRequest("invalid", net::HTTP_OK); ASSERT_TRUE(receiver.GetResult()); @@ -230,7 +246,8 @@ TEST_F(FeedNetworkTest, SendQueryRequestReceivesResponse) { CallbackReceiver<QueryRequestResult> receiver; - feed_network()->SendQueryRequest(GetTestFeedRequest(), receiver.Bind()); + feed_network()->SendQueryRequest(GetTestFeedRequest(), false, + receiver.Bind()); RespondToQueryRequest(GetTestFeedResponse(), net::HTTP_OK); ASSERT_TRUE(receiver.GetResult()); @@ -246,7 +263,8 @@ TEST_F(FeedNetworkTest, SendQueryRequestIgnoresBodyForNon200Response) { CallbackReceiver<QueryRequestResult> receiver; - feed_network()->SendQueryRequest(GetTestFeedRequest(), receiver.Bind()); + feed_network()->SendQueryRequest(GetTestFeedRequest(), false, + receiver.Bind()); RespondToQueryRequest(GetTestFeedResponse(), net::HTTP_FORBIDDEN); ASSERT_TRUE(receiver.GetResult()); @@ -260,7 +278,8 @@ TEST_F(FeedNetworkTest, CancelRequest) { CallbackReceiver<QueryRequestResult> receiver; - feed_network()->SendQueryRequest(GetTestFeedRequest(), receiver.Bind()); + feed_network()->SendQueryRequest(GetTestFeedRequest(), false, + receiver.Bind()); feed_network()->CancelRequests(); task_environment_.FastForwardUntilNoTasksRemain(); @@ -270,7 +289,8 @@ TEST_F(FeedNetworkTest, RequestTimeout) { base::HistogramTester histogram_tester; CallbackReceiver<QueryRequestResult> receiver; - feed_network()->SendQueryRequest(GetTestFeedRequest(), receiver.Bind()); + feed_network()->SendQueryRequest(GetTestFeedRequest(), false, + receiver.Bind()); task_environment_.FastForwardBy(TimeDelta::FromSeconds(30)); ASSERT_TRUE(receiver.GetResult()); @@ -283,11 +303,12 @@ TEST_F(FeedNetworkTest, ParallelRequests) { CallbackReceiver<QueryRequestResult> receiver1, receiver2; - feed_network()->SendQueryRequest(GetTestFeedRequest(), receiver1.Bind()); + feed_network()->SendQueryRequest(GetTestFeedRequest(), false, + receiver1.Bind()); // Make another request with a different URL so Respond() won't affect both // requests. feed_network()->SendQueryRequest( - GetTestFeedRequest(feedwire::FeedQuery::NEXT_PAGE_SCROLL), + GetTestFeedRequest(feedwire::FeedQuery::NEXT_PAGE_SCROLL), false, receiver2.Bind()); // Respond to both requests, avoiding FastForwardUntilNoTasksRemain until @@ -310,7 +331,8 @@ TEST_F(FeedNetworkTest, ShouldReportResponseStatusCode) { CallbackReceiver<QueryRequestResult> receiver; base::HistogramTester histogram_tester; - feed_network()->SendQueryRequest(GetTestFeedRequest(), receiver.Bind()); + feed_network()->SendQueryRequest(GetTestFeedRequest(), false, + receiver.Bind()); RespondToQueryRequest(GetTestFeedResponse(), net::HTTP_FORBIDDEN); EXPECT_THAT( @@ -324,7 +346,8 @@ CallbackReceiver<QueryRequestResult> receiver; base::HistogramTester histogram_tester; - feed_network()->SendQueryRequest(GetTestFeedRequest(), receiver.Bind()); + feed_network()->SendQueryRequest(GetTestFeedRequest(), false, + receiver.Bind()); identity_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithError( GoogleServiceAuthError( GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS)); @@ -349,7 +372,8 @@ TEST_F(FeedNetworkTest, ShouldIncludeAPIKeyForNoSignedInUser) { identity_env()->ClearPrimaryAccount(); CallbackReceiver<QueryRequestResult> receiver; - feed_network()->SendQueryRequest(GetTestFeedRequest(), receiver.Bind()); + feed_network()->SendQueryRequest(GetTestFeedRequest(), false, + receiver.Bind()); network::ResourceRequest resource_request = RespondToQueryRequest(GetTestFeedResponse(), net::HTTP_OK); @@ -364,7 +388,8 @@ CallbackReceiver<QueryRequestResult> receiver; const TimeDelta kDuration = TimeDelta::FromMilliseconds(12345); - feed_network()->SendQueryRequest(GetTestFeedRequest(), receiver.Bind()); + feed_network()->SendQueryRequest(GetTestFeedRequest(), false, + receiver.Bind()); task_environment_.FastForwardBy(kDuration); RespondToQueryRequest(GetTestFeedResponse(), net::HTTP_OK); @@ -379,7 +404,8 @@ CallbackReceiver<QueryRequestResult> receiver; profile_prefs().SetString(feed::prefs::kHostOverrideHost, "http://www.newhost.com/"); - feed_network()->SendQueryRequest(GetTestFeedRequest(), receiver.Bind()); + feed_network()->SendQueryRequest(GetTestFeedRequest(), false, + receiver.Bind()); response_headers_ = base::MakeRefCounted<net::HttpResponseHeaders>( net::HttpUtil::AssembleRawHeaders(
diff --git a/components/feed/core/v2/feed_stream.cc b/components/feed/core/v2/feed_stream.cc index 6b5a4aa..68e604b 100644 --- a/components/feed/core/v2/feed_stream.cc +++ b/components/feed/core/v2/feed_stream.cc
@@ -467,16 +467,6 @@ } } - // TODO(harringtond): |suppress_refreshes_until_| was historically used - // for privacy purposes after clearing data to make sure sync data made it - // to the server. I'm not sure we need this now. But also, it was - // documented as not affecting manually triggered refreshes, but coded in - // a way that it does. I've tried to keep the same functionality as the - // old feed code, but we should revisit this. - if (tick_clock_->NowTicks() < suppress_refreshes_until_) { - return LoadStreamStatus::kCannotLoadFromNetworkSupressedForHistoryDelete; - } - if (delegate_->IsOffline()) { return LoadStreamStatus::kCannotLoadFromNetworkOffline; } @@ -489,6 +479,10 @@ return LoadStreamStatus::kNoStatus; } +bool FeedStream::ShouldForceSignedOutFeedQueryRequest() const { + return base::TimeTicks::Now() < signed_out_refreshes_until_; +} + RequestMetadata FeedStream::GetRequestMetadata() { RequestMetadata result; result.chrome_info = chrome_info_; @@ -503,11 +497,10 @@ TriggerStreamLoad(); } -void FeedStream::OnHistoryDeleted() { - // Due to privacy, we should not fetch for a while (unless the user - // explicitly asks for new suggestions) to give sync the time to propagate - // the changes in history to the server. - suppress_refreshes_until_ = +void FeedStream::OnAllHistoryDeleted() { + // Give sync the time to propagate the changes in history to the server. + // In the interim, only send signed-out FeedQuery requests. + signed_out_refreshes_until_ = tick_clock_->NowTicks() + kSuppressRefreshDuration; ClearAll(); }
diff --git a/components/feed/core/v2/feed_stream.h b/components/feed/core/v2/feed_stream.h index 8b4ae07..19c8f3b 100644 --- a/components/feed/core/v2/feed_stream.h +++ b/components/feed/core/v2/feed_stream.h
@@ -175,8 +175,8 @@ void OnSignedIn(); // The user signed out of Chrome. void OnSignedOut(); - // The user has deleted their Chrome history. - void OnHistoryDeleted(); + // The user has deleted all browsing history. + void OnAllHistoryDeleted(); // Chrome's cached data was cleared. void OnCacheDataCleared(); @@ -216,6 +216,10 @@ LoadStreamStatus ShouldMakeFeedQueryRequest(bool is_load_more = false, bool consume_quota = true); + // Returns true if a FeedQuery request made right now should be made without + // user credentials. + bool ShouldForceSignedOutFeedQueryRequest() const; + // Unloads the model. Surfaces are not updated, and will remain frozen until a // model load is requested. void UnloadModel(); @@ -302,7 +306,7 @@ // Mutable state. RequestThrottler request_throttler_; - base::TimeTicks suppress_refreshes_until_; + base::TimeTicks signed_out_refreshes_until_; std::vector<base::OnceCallback<void(bool)>> load_more_complete_callbacks_; Metadata metadata_; int unload_on_detach_sequence_number_ = 0;
diff --git a/components/feed/core/v2/feed_stream_unittest.cc b/components/feed/core/v2/feed_stream_unittest.cc index 12946ed1..e6402844 100644 --- a/components/feed/core/v2/feed_stream_unittest.cc +++ b/components/feed/core/v2/feed_stream_unittest.cc
@@ -259,7 +259,9 @@ // FeedNetwork implementation. void SendQueryRequest( const feedwire::Request& request, + bool force_signed_out_request, base::OnceCallback<void(QueryRequestResult)> callback) override { + forced_signed_out_request = force_signed_out_request; ++send_query_call_count; // Emulate a successful response. // The response body is currently an empty message, because most of the @@ -330,6 +332,7 @@ base::Optional<feedwire::FeedActionRequest> action_request_sent; int action_request_call_count = 0; std::string consistency_token; + bool forced_signed_out_request = false; private: base::Optional<feedwire::Response> injected_response_; @@ -1010,26 +1013,28 @@ EXPECT_EQ("loading -> 2 slices", surface.DescribeUpdates()); } -TEST_F(FeedStreamTest, DoNotLoadFromNetworkAfterHistoryIsDeleted) { - stream_->OnHistoryDeleted(); +TEST_F(FeedStreamTest, ForceSignedOutRequestAfterHistoryIsDeleted) { + stream_->OnAllHistoryDeleted(); task_environment_.FastForwardBy(kSuppressRefreshDuration - base::TimeDelta::FromSeconds(1)); response_translator_.InjectResponse(MakeTypicalInitialModelState()); TestSurface surface(stream_.get()); WaitForIdleTaskQueue(); - EXPECT_EQ("loading -> no-cards", surface.DescribeUpdates()); + EXPECT_EQ("loading -> 2 slices", surface.DescribeUpdates()); + EXPECT_TRUE(network_.forced_signed_out_request); +} - EXPECT_EQ(LoadStreamStatus::kCannotLoadFromNetworkSupressedForHistoryDelete, - metrics_reporter_->load_stream_status); - - surface.Detach(); - task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(2)); - surface.Clear(); - surface.Attach(stream_.get()); +TEST_F(FeedStreamTest, AllowSignedInRequestAfterHistoryIsDeletedAfterDelay) { + stream_->OnAllHistoryDeleted(); + task_environment_.FastForwardBy(kSuppressRefreshDuration + + base::TimeDelta::FromSeconds(1)); + response_translator_.InjectResponse(MakeTypicalInitialModelState()); + TestSurface surface(stream_.get()); WaitForIdleTaskQueue(); EXPECT_EQ("loading -> 2 slices", surface.DescribeUpdates()); + EXPECT_FALSE(network_.forced_signed_out_request); } TEST_F(FeedStreamTest, ShouldMakeFeedQueryRequestConsumesQuota) {
diff --git a/components/feed/core/v2/public/feed_service.cc b/components/feed/core/v2/public/feed_service.cc index 04795aa..5912974 100644 --- a/components/feed/core/v2/public/feed_service.cc +++ b/components/feed/core/v2/public/feed_service.cc
@@ -43,14 +43,8 @@ namespace internal { bool ShouldClearFeed(const history::DeletionInfo& deletion_info) { - // We ignore expirations since they're not user-initiated. - if (deletion_info.is_from_expiration()) - return false; - - // If a user deletes a single URL, we don't consider this a clear user - // intent to clear our data. - return deletion_info.IsAllHistory() || - deletion_info.deleted_rows().size() > 1; + // Only clear the feed if all history is deleted. + return deletion_info.IsAllHistory(); } } // namespace internal @@ -71,7 +65,7 @@ void OnURLsDeleted(history::HistoryService* history_service, const history::DeletionInfo& deletion_info) override { if (internal::ShouldClearFeed(deletion_info)) - feed_stream_->OnHistoryDeleted(); + feed_stream_->OnAllHistoryDeleted(); } private:
diff --git a/components/feed/core/v2/public/feed_service_unittest.cc b/components/feed/core/v2/public/feed_service_unittest.cc index 50fa7e27..8a0bd9ed 100644 --- a/components/feed/core/v2/public/feed_service_unittest.cc +++ b/components/feed/core/v2/public/feed_service_unittest.cc
@@ -14,16 +14,10 @@ TEST(ShouldClearFeed, ShouldClearFeed) { EXPECT_TRUE(ShouldClearFeed(DeletionInfo::ForAllHistory())); - EXPECT_TRUE(ShouldClearFeed(DeletionInfo::ForUrls( - { - history::URLRow(GURL("http://url1")), - history::URLRow(GURL("http://url2")), - }, - /*favicon_urls=*/{}))); - EXPECT_FALSE(ShouldClearFeed(DeletionInfo::ForUrls( { history::URLRow(GURL("http://url1")), + history::URLRow(GURL("http://url2")), }, /*favicon_urls=*/{}))); }
diff --git a/components/feed/core/v2/surface_updater.cc b/components/feed/core/v2/surface_updater.cc index fb35e9c7..4854079b 100644 --- a/components/feed/core/v2/surface_updater.cc +++ b/components/feed/core/v2/surface_updater.cc
@@ -146,7 +146,8 @@ case LoadStreamStatus::kModelAlreadyLoaded: case LoadStreamStatus::kDataInStoreIsStale: case LoadStreamStatus::kDataInStoreIsStaleTimestampInFuture: - case LoadStreamStatus::kCannotLoadFromNetworkSupressedForHistoryDelete: + case LoadStreamStatus:: + kCannotLoadFromNetworkSupressedForHistoryDelete_DEPRECATED: case LoadStreamStatus::kLoadNotAllowedEulaNotAccepted: case LoadStreamStatus::kLoadNotAllowedArticlesListHidden: case LoadStreamStatus::kCannotParseNetworkResponseBody:
diff --git a/components/feed/core/v2/tasks/load_more_task.cc b/components/feed/core/v2/tasks/load_more_task.cc index ad6aac4..dd5c513 100644 --- a/components/feed/core/v2/tasks/load_more_task.cc +++ b/components/feed/core/v2/tasks/load_more_task.cc
@@ -55,6 +55,7 @@ stream_->GetRequestMetadata(), stream_->GetMetadata()->GetConsistencyToken(), stream_->GetModel()->GetNextPageToken()), + stream_->ShouldForceSignedOutFeedQueryRequest(), base::BindOnce(&LoadMoreTask::QueryRequestComplete, GetWeakPtr())); }
diff --git a/components/feed/core/v2/tasks/load_stream_task.cc b/components/feed/core/v2/tasks/load_stream_task.cc index fdf0e66..df119df 100644 --- a/components/feed/core/v2/tasks/load_stream_task.cc +++ b/components/feed/core/v2/tasks/load_stream_task.cc
@@ -120,6 +120,7 @@ CreateFeedQueryRefreshRequest( GetRequestReason(load_type_), stream_->GetRequestMetadata(), stream_->GetMetadata()->GetConsistencyToken()), + stream_->ShouldForceSignedOutFeedQueryRequest(), base::BindOnce(&LoadStreamTask::QueryRequestComplete, GetWeakPtr())); }
diff --git a/components/ntp_tiles/most_visited_sites_unittest.cc b/components/ntp_tiles/most_visited_sites_unittest.cc index 7efb2e5..56b566f 100644 --- a/components/ntp_tiles/most_visited_sites_unittest.cc +++ b/components/ntp_tiles/most_visited_sites_unittest.cc
@@ -21,6 +21,7 @@ #include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" #include "base/task/cancelable_task_tracker.h" +#include "base/test/gmock_callback_support.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "base/threading/thread_task_runner_handle.h" @@ -58,9 +59,10 @@ using suggestions::ChromeSuggestion; using suggestions::SuggestionsProfile; using suggestions::SuggestionsService; -using testing::AtLeast; +using testing::_; using testing::AllOf; using testing::AnyNumber; +using testing::AtLeast; using testing::ByMove; using testing::Contains; using testing::ElementsAre; @@ -78,7 +80,6 @@ using testing::SaveArg; using testing::SizeIs; using testing::StrictMock; -using testing::_; const char kHomepageUrl[] = "http://homepa.ge/"; const char kHomepageTitle[] = "Homepage"; @@ -134,15 +135,6 @@ tiles[0].url == GURL(url) && tiles[0].source == source; } -// testing::InvokeArgument<N> does not work with base::Callback, fortunately -// gmock makes it simple to create action templates that do for the various -// possible numbers of arguments. -ACTION_TEMPLATE(InvokeCallbackArgument, - HAS_1_TEMPLATE_PARAMS(int, k), - AND_1_VALUE_PARAMS(p0)) { - std::move(std::get<k>(args)).Run(p0); -} - NTPTile MakeTile(const std::string& title, const std::string& url, TileSource source) { @@ -181,10 +173,7 @@ class MockTopSites : public TopSites { public: MOCK_METHOD0(ShutdownOnUIThread, void()); - void GetMostVisitedURLs(GetMostVisitedURLsCallback callback) override { - GetMostVisitedURLs_(callback); - } - MOCK_METHOD1(GetMostVisitedURLs_, void(GetMostVisitedURLsCallback& callback)); + MOCK_METHOD1(GetMostVisitedURLs, void(GetMostVisitedURLsCallback callback)); MOCK_METHOD0(SyncWithHistory, void()); MOCK_CONST_METHOD0(HasBlockedUrls, bool()); MOCK_METHOD1(AddBlockedUrl, void(const GURL& url)); @@ -408,7 +397,7 @@ // implementation in TopSites (which doesn't use base::CallbackList). class TopSitesCallbackList { public: - void Add(TopSites::GetMostVisitedURLsCallback& callback) { + void Add(TopSites::GetMostVisitedURLsCallback callback) { callbacks_.push_back(std::move(callback)); } @@ -593,8 +582,8 @@ FakeHomepageClient* homepage_client = RegisterNewHomepageClient(); homepage_client->SetHomepageTileEnabled(true); DisableRemoteSuggestions(); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{})); + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{})); EXPECT_CALL(*mock_top_sites_, SyncWithHistory()); EXPECT_CALL(*mock_top_sites_, IsBlocked(Eq(GURL(kHomepageUrl)))) .Times(AnyNumber()) @@ -608,8 +597,8 @@ TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomepageWithoutClient) { DisableRemoteSuggestions(); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{})); + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{})); EXPECT_CALL(*mock_top_sites_, SyncWithHistory()); EXPECT_CALL(mock_observer_, OnURLsAvailable(Contains( @@ -629,8 +618,8 @@ homepage_client->SetHomepageTileEnabled(true); homepage_client->SetHomepageTitle(base::UTF8ToUTF16(kHomepageTitle)); DisableRemoteSuggestions(); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{})); + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{})); EXPECT_CALL(*mock_top_sites_, SyncWithHistory()); EXPECT_CALL(*mock_top_sites_, IsBlocked(Eq(GURL(kHomepageUrl)))) .Times(AnyNumber()) @@ -659,8 +648,8 @@ DisableRemoteSuggestions(); // Ensure that home tile is available as usual. - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{})); + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{})); EXPECT_CALL(*mock_top_sites_, SyncWithHistory()); EXPECT_CALL(*mock_top_sites_, IsBlocked(Eq(GURL(kHomepageUrl)))) .Times(AnyNumber()) @@ -675,8 +664,8 @@ // Disable home page and rebuild _without_ Resync. The tile should be gone. homepage_client->SetHomepageTileEnabled(false); DisableRemoteSuggestions(); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{})); + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{})); EXPECT_CALL(*mock_top_sites_, SyncWithHistory()).Times(0); EXPECT_CALL(mock_observer_, OnURLsAvailable(Not(FirstPersonalizedTileIs( "", kHomepageUrl, TileSource::HOMEPAGE)))); @@ -688,8 +677,8 @@ FakeHomepageClient* homepage_client = RegisterNewHomepageClient(); homepage_client->SetHomepageTileEnabled(true); DisableRemoteSuggestions(); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{})); + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{})); EXPECT_CALL(*mock_top_sites_, SyncWithHistory()); EXPECT_CALL(*mock_top_sites_, IsBlocked(Eq(GURL(kHomepageUrl)))) .Times(AnyNumber()) @@ -706,8 +695,8 @@ FakeHomepageClient* homepage_client = RegisterNewHomepageClient(); homepage_client->SetHomepageTileEnabled(true); DisableRemoteSuggestions(); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>( + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>( (MostVisitedURLList{MakeMostVisitedURL("Site 1", "http://site1/")}))); EXPECT_CALL(*mock_top_sites_, SyncWithHistory()); EXPECT_CALL(*mock_top_sites_, IsBlocked(Eq(GURL(kHomepageUrl)))) @@ -727,8 +716,8 @@ FakeHomepageClient* homepage_client = RegisterNewHomepageClient(); homepage_client->SetHomepageTileEnabled(true); DisableRemoteSuggestions(); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>((MostVisitedURLList{ + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>((MostVisitedURLList{ MakeMostVisitedURL("Site 1", "http://site1/"), MakeMostVisitedURL("Site 2", "http://site2/"), MakeMostVisitedURL("Site 3", "http://site3/"), @@ -756,8 +745,8 @@ FakeHomepageClient* homepage_client = RegisterNewHomepageClient(); homepage_client->SetHomepageTileEnabled(true); DisableRemoteSuggestions(); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>((MostVisitedURLList{ + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>((MostVisitedURLList{ MakeMostVisitedURL("Site 1", "http://site1/"), MakeMostVisitedURL("Site 2", "http://site2/"), MakeMostVisitedURL("Site 3", "http://site3/"), @@ -785,8 +774,8 @@ FakeHomepageClient* homepage_client = RegisterNewHomepageClient(); homepage_client->SetHomepageTileEnabled(true); DisableRemoteSuggestions(); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>( + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>( (MostVisitedURLList{MakeMostVisitedURL("Site 1", "http://site1/"), MakeMostVisitedURL("", kHomepageUrl)}))); EXPECT_CALL(*mock_top_sites_, SyncWithHistory()); @@ -809,8 +798,8 @@ FakeHomepageClient* homepage_client = RegisterNewHomepageClient(); homepage_client->SetHomepageTileEnabled(false); DisableRemoteSuggestions(); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{})); + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{})); EXPECT_CALL(*mock_top_sites_, SyncWithHistory()); EXPECT_CALL(*mock_top_sites_, IsBlocked(Eq(GURL(kHomepageUrl)))) .Times(AnyNumber()) @@ -831,8 +820,8 @@ homepage_client->SetHomepageTileEnabled(true); homepage_client->SetHomepageUrl(GURL(kEmptyHomepageUrl)); DisableRemoteSuggestions(); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{})); + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{})); EXPECT_CALL(*mock_top_sites_, SyncWithHistory()); EXPECT_CALL(*mock_top_sites_, IsBlocked(Eq(kEmptyHomepageUrl))) .Times(AnyNumber()) @@ -849,8 +838,8 @@ FakeHomepageClient* homepage_client = RegisterNewHomepageClient(); homepage_client->SetHomepageTileEnabled(true); DisableRemoteSuggestions(); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>( + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>( (MostVisitedURLList{MakeMostVisitedURL("", kHomepageUrl)}))); EXPECT_CALL(*mock_top_sites_, SyncWithHistory()); EXPECT_CALL(*mock_top_sites_, IsBlocked(Eq(GURL(kHomepageUrl)))) @@ -876,8 +865,8 @@ homepage_client->SetHomepageTileEnabled(true); DisableRemoteSuggestions(); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillOnce(InvokeCallbackArgument<0>( + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillOnce(base::test::RunOnceCallback<0>( (MostVisitedURLList{MakeMostVisitedURL("", kHomepageUrl)}))); EXPECT_CALL(*mock_top_sites_, SyncWithHistory()); EXPECT_CALL(*mock_top_sites_, IsBlocked(Eq(GURL(kHomepageUrl)))) @@ -895,8 +884,8 @@ VerifyAndClearExpectations(); DisableRemoteSuggestions(); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillOnce(InvokeCallbackArgument<0>(MostVisitedURLList{})); + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillOnce(base::test::RunOnceCallback<0>(MostVisitedURLList{})); EXPECT_CALL(*mock_top_sites_, IsBlocked(Eq(GURL(kHomepageUrl)))) .Times(AtLeast(1)) .WillRepeatedly(Return(false)); @@ -914,8 +903,8 @@ // Does not register an explore sites client. DisableRemoteSuggestions(); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{ + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{ MakeMostVisitedURL("ESPN", "http://espn.com/"), MakeMostVisitedURL("Mobile", "http://m.mobile.de/"), MakeMostVisitedURL("Google", "http://www.google.com/")})); @@ -936,8 +925,8 @@ TEST_P(MostVisitedSitesTest, ShouldIncludeTileForExploreSites) { RegisterNewExploreSitesClient(); DisableRemoteSuggestions(); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{ + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{ MakeMostVisitedURL("ESPN", "http://espn.com/"), MakeMostVisitedURL("Mobile", "http://m.mobile.de/"), MakeMostVisitedURL("Google", "http://www.google.com/")})); @@ -955,8 +944,8 @@ TEST_P(MostVisitedSitesTest, RemovesPersonalSiteIfExploreSitesTilePresent) { RegisterNewExploreSitesClient(); DisableRemoteSuggestions(); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{ + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{ MakeMostVisitedURL("ESPN", "http://espn.com/"), MakeMostVisitedURL("Mobile", "http://m.mobile.de/"), MakeMostVisitedURL("Google", "http://www.google.com/")})); @@ -995,8 +984,8 @@ pref_service_.SetString(prefs::kPopularSitesOverrideCountry, "US"); RecreateMostVisitedSites(); // Refills cache with ESPN and Google News. DisableRemoteSuggestions(); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{ + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>(MostVisitedURLList{ MakeMostVisitedURL("ESPN", "http://espn.com/"), MakeMostVisitedURL("Mobile", "http://m.mobile.de/"), MakeMostVisitedURL("Google", "http://www.google.com/")})); @@ -1031,8 +1020,8 @@ TEST_P(MostVisitedSitesTest, ShouldHandleTopSitesCacheHit) { // If cached, TopSites returns the tiles synchronously, running the callback // even before the function returns. - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>( + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>( MostVisitedURLList{MakeMostVisitedURL("Site 1", "http://site1/")})); InSequence seq; @@ -1070,8 +1059,8 @@ CHECK(top_sites_callbacks_.empty()); // Update by TopSites is propagated. - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillOnce(InvokeCallbackArgument<0>( + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillOnce(base::test::RunOnceCallback<0>( MostVisitedURLList{MakeMostVisitedURL("Site 2", "http://site2/")})); if (IsPopularSitesFeatureEnabled()) { EXPECT_CALL(*mock_top_sites_, IsBlocked(_)).WillRepeatedly(Return(false)); @@ -1133,8 +1122,8 @@ void ExpectBuildWithTopSites( const MostVisitedURLList& expected_list, std::map<SectionType, NTPTilesVector>* sections) { - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>(expected_list)); + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>(expected_list)); EXPECT_CALL(*mock_top_sites_, SyncWithHistory()); EXPECT_CALL(*mock_custom_links_, IsInitialized()) .WillRepeatedly(Return(false)); @@ -1205,8 +1194,8 @@ // Uninitialize custom links and rebuild tiles. Tiles should be Top Sites. EXPECT_CALL(*mock_custom_links_, Uninitialize()); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>( + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>( MostVisitedURLList{MakeMostVisitedURL(kTestTitle, kTestUrl)})); EXPECT_CALL(*mock_custom_links_, IsInitialized()) .WillRepeatedly(Return(false)); @@ -1523,8 +1512,8 @@ // Undo the action. This should uninitialize custom links. EXPECT_CALL(*mock_custom_links_, UndoAction()).Times(0); EXPECT_CALL(*mock_custom_links_, Uninitialize()); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>( + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>( MostVisitedURLList{MakeMostVisitedURL(kTestTitle, kTestUrl)})); EXPECT_CALL(*mock_custom_links_, IsInitialized()) .WillRepeatedly(Return(false)); @@ -1705,8 +1694,8 @@ // tiles with Top Sites. EXPECT_CALL(*mock_custom_links_, IsInitialized()) .WillRepeatedly(Return(false)); - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillRepeatedly(InvokeCallbackArgument<0>( + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillRepeatedly(base::test::RunOnceCallback<0>( MostVisitedURLList{MakeMostVisitedURL(kTestTitle1, kTestUrl1)})); EXPECT_CALL(*mock_custom_links_, IsInitialized()) .WillRepeatedly(Return(false)); @@ -1847,7 +1836,7 @@ TEST_P(MostVisitedSitesWithCacheHitTest, ShouldSwitchToTopSitesIfEmptyUpdateBySuggestionsService) { - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) .WillOnce(Invoke(&top_sites_callbacks_, &TopSitesCallbackList::Add)); suggestions_service_callbacks_.Notify(SuggestionsProfile()); VerifyAndClearExpectations(); @@ -1899,7 +1888,7 @@ &SuggestionsService::ResponseCallbackList::Add)); EXPECT_CALL(mock_suggestions_service_, GetSuggestionsDataFromCache()) .WillOnce(Return(SuggestionsProfile())); // Empty cache. - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) .WillOnce(Invoke(&top_sites_callbacks_, &TopSitesCallbackList::Add)); EXPECT_CALL(*mock_top_sites_, SyncWithHistory()); EXPECT_CALL(mock_suggestions_service_, FetchSuggestionsData()) @@ -2079,8 +2068,8 @@ EXPECT_TRUE(top_sites_callbacks_.empty()); // Update from top sites is propagated to observer. - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) - .WillOnce(InvokeCallbackArgument<0>( + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) + .WillOnce(base::test::RunOnceCallback<0>( MostVisitedURLList{MakeMostVisitedURL("Site 4", "http://site4/"), MakeMostVisitedURL("Site 5", "http://site5/"), MakeMostVisitedURL("Site 6", "http://site6/")})); @@ -2142,7 +2131,7 @@ base::RunLoop().RunUntilIdle(); for (int i = 0; i < 4; ++i) { - EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs_(_)) + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_)) .WillOnce(Invoke(&top_sites_callbacks_, &TopSitesCallbackList::Add)); mock_top_sites_->NotifyTopSitesChanged( history::TopSitesObserver::ChangeReason::MOST_VISITED);
diff --git a/components/omnibox/browser/autocomplete_match.cc b/components/omnibox/browser/autocomplete_match.cc index a90880d..4915ef3e 100644 --- a/components/omnibox/browser/autocomplete_match.cc +++ b/components/omnibox/browser/autocomplete_match.cc
@@ -1103,14 +1103,6 @@ return res; } -bool AutocompleteMatch::ShouldShowTabMatchButtonInlineInResultView() const { - // TODO(pkasting): This kind of presentational logic does not belong on - // AutocompleteMatch and should be e.g. a static method in - // OmniboxMatchCellView that takes an AutocompleteMatch. - return has_tab_match && !associated_keyword && - !OmniboxFieldTrial::IsSuggestionButtonRowEnabled(); -} - void AutocompleteMatch::UpgradeMatchWithPropertiesFrom( const AutocompleteMatch& duplicate_match) { // For Entity Matches, absorb the duplicate match's |allowed_to_be_default|
diff --git a/components/omnibox/browser/autocomplete_match.h b/components/omnibox/browser/autocomplete_match.h index 72e3d69..78c398f 100644 --- a/components/omnibox/browser/autocomplete_match.h +++ b/components/omnibox/browser/autocomplete_match.h
@@ -420,14 +420,6 @@ // See base/trace_event/memory_usage_estimator.h for more info. size_t EstimateMemoryUsage() const; - // Not to be confused with |has_tab_match|, this returns true if the match - // has a matching tab and will use a switch-to-tab button inline in Result - // View. It returns false, for example, when the switch button is not shown - // because a keyword match is taking precedence. It also returns false when - // Suggestion Button Row is enabled, as the Switch-to-tab button will appear - // in the button row. - bool ShouldShowTabMatchButtonInlineInResultView() const; - // Upgrades this match by absorbing the best properties from // |duplicate_match|. For instance: if |duplicate_match| has a higher // relevance score, this match's own relevance score will be upgraded.
diff --git a/components/omnibox/browser/omnibox_controller.cc b/components/omnibox/browser/omnibox_controller.cc index d824f5f..b76109b 100644 --- a/components/omnibox/browser/omnibox_controller.cc +++ b/components/omnibox/browser/omnibox_controller.cc
@@ -88,7 +88,7 @@ void OmniboxController::ClearPopupKeywordMode() const { // |popup_| can be nullptr in tests. if (popup_ && popup_->IsOpen() && - popup_->selected_line_state() == OmniboxPopupModel::KEYWORD) { + popup_->selected_line_state() == OmniboxPopupModel::KEYWORD_MODE) { popup_->SetSelectedLineState(OmniboxPopupModel::NORMAL); } }
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc index 61d593e..a90e884 100644 --- a/components/omnibox/browser/omnibox_edit_model.cc +++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -914,7 +914,7 @@ user_text_ = MaybeStripKeyword(user_text_); if (PopupIsOpen()) - popup_model()->SetSelectedLineState(OmniboxPopupModel::KEYWORD); + popup_model()->SetSelectedLineState(OmniboxPopupModel::KEYWORD_MODE); else StartAutocomplete(false, true); @@ -973,7 +973,7 @@ // difference, as we'll need it below. popup_model() may be nullptr in tests. bool was_toggled_into_keyword_mode = popup_model() && - popup_model()->selected_line_state() == OmniboxPopupModel::KEYWORD; + popup_model()->selected_line_state() == OmniboxPopupModel::KEYWORD_MODE; bool entry_by_tab = keyword_mode_entry_method_ == OmniboxEventProto::TAB; @@ -1552,9 +1552,10 @@ } else if (popup_model()->selected_line() != OmniboxPopupModel::kNoMatch) { const AutocompleteMatch& selected_match = result().match_at(popup_model()->selected_line()); - *match = - (popup_model()->selected_line_state() == OmniboxPopupModel::KEYWORD) ? - *selected_match.associated_keyword : selected_match; + *match = (popup_model()->selected_line_state() == + OmniboxPopupModel::KEYWORD_MODE) + ? *selected_match.associated_keyword + : selected_match; found_match_for_text = true; } if (found_match_for_text && alternate_nav_url &&
diff --git a/components/omnibox/browser/omnibox_popup_model.cc b/components/omnibox/browser/omnibox_popup_model.cc index 0cca6c1..6efe67e 100644 --- a/components/omnibox/browser/omnibox_popup_model.cc +++ b/components/omnibox/browser/omnibox_popup_model.cc
@@ -47,11 +47,11 @@ } bool OmniboxPopupModel::Selection::IsChangeToKeyword(Selection from) const { - return state == KEYWORD && from.state != KEYWORD; + return state == KEYWORD_MODE && from.state != KEYWORD_MODE; } bool OmniboxPopupModel::Selection::IsButtonFocused() const { - return state != NORMAL && state != KEYWORD; + return state != NORMAL && state != KEYWORD_MODE; } /////////////////////////////////////////////////////////////////////////////// @@ -157,7 +157,7 @@ return; const AutocompleteMatch& match = result().match_at(selection_.line); - DCHECK((selection_.state != KEYWORD) || match.associated_keyword.get()); + DCHECK((selection_.state != KEYWORD_MODE) || match.associated_keyword.get()); if (selection_.IsButtonFocused()) { old_focused_url_ = match.destination_url; edit_model_->SetAccessibilityLabel(match); @@ -178,10 +178,11 @@ base::string16(), keyword, is_keyword_hint, base::string16()); } else if (old_selection.line != selection_.line || - old_selection.IsButtonFocused()) { + (old_selection.IsButtonFocused() && + new_selection.state != KEYWORD_MODE)) { // Otherwise, only update the edit model for line number changes, or - // when the old selection was a button. Updating the edit model for every - // state change breaks keyword mode. + // when the old selection was a button and we're not entering keyword mode. + // Updating the edit model for every state change breaks keyword mode. if (reset_to_default) { edit_model_->OnPopupDataChanged( match.inline_autocompletion, @@ -357,7 +358,7 @@ // Keyword mode is only accessible by Tabbing forward. if (direction == kForward) { if (step == kStateOrLine) { - all_states.push_back(KEYWORD); + all_states.push_back(KEYWORD_MODE); } } all_states.push_back(FOCUSED_BUTTON_TAB_SWITCH); @@ -496,7 +497,7 @@ } case NORMAL: return true; - case KEYWORD: + case KEYWORD_MODE: return match.associated_keyword != nullptr; case FOCUSED_BUTTON_KEYWORD: return match.associated_keyword != nullptr; @@ -506,7 +507,7 @@ if (OmniboxFieldTrial::IsSuggestionButtonRowEnabled()) return match.has_tab_match; else - return match.ShouldShowTabMatchButtonInlineInResultView(); + return match.has_tab_match && !match.associated_keyword; case FOCUSED_BUTTON_PEDAL: return match.pedal != nullptr; case FOCUSED_BUTTON_REMOVE_SUGGESTION: @@ -515,8 +516,7 @@ if (OmniboxFieldTrial::IsSuggestionButtonRowEnabled()) return match.SupportsDeletion(); else - return !match.associated_keyword && - !match.ShouldShowTabMatchButtonInlineInResultView() && + return !match.associated_keyword && !match.has_tab_match && match.SupportsDeletion(); default: break; @@ -614,7 +614,7 @@ // Don't add an additional message for removable suggestions without // button focus, since they are relatively common. break; - case KEYWORD: + case KEYWORD_MODE: // TODO(tommycli): Investigate whether the accessibility messaging for // Keyword mode belongs here. break;
diff --git a/components/omnibox/browser/omnibox_popup_model.h b/components/omnibox/browser/omnibox_popup_model.h index 4de17a8..01eb9629 100644 --- a/components/omnibox/browser/omnibox_popup_model.h +++ b/components/omnibox/browser/omnibox_popup_model.h
@@ -61,14 +61,14 @@ // NORMAL means the row is focused, and Enter key navigates to the match. NORMAL = 1, - // KEYWORD state means actually in keyword mode, as distinct from the - // FOCUSED_BUTTON_KEYWORD state, which is only for button focus. - KEYWORD = 2, - // FOCUSED_BUTTON_KEYWORD is used when the keyword button is in focus, not // actually in Keyword Mode. This is currently only used if deciated button // row is enabled - FOCUSED_BUTTON_KEYWORD = 3, + FOCUSED_BUTTON_KEYWORD = 2, + + // KEYWORD_MODE state means actually in keyword mode, as distinct from the + // FOCUSED_BUTTON_KEYWORD state, which is only for button focus. + KEYWORD_MODE = 3, // FOCUSED_BUTTON_TAB_SWITCH state means the Switch Tab button is focused. // Pressing enter will switch to the tab match.
diff --git a/components/omnibox/browser/omnibox_popup_model_unittest.cc b/components/omnibox/browser/omnibox_popup_model_unittest.cc index 9a12709..421e36a 100644 --- a/components/omnibox/browser/omnibox_popup_model_unittest.cc +++ b/components/omnibox/browser/omnibox_popup_model_unittest.cc
@@ -233,7 +233,7 @@ Selection(1, OmniboxPopupModel::NORMAL), Selection(1, OmniboxPopupModel::FOCUSED_BUTTON_REMOVE_SUGGESTION), Selection(2, OmniboxPopupModel::NORMAL), - Selection(2, OmniboxPopupModel::KEYWORD), + Selection(2, OmniboxPopupModel::KEYWORD_MODE), Selection(3, OmniboxPopupModel::FOCUSED_BUTTON_HEADER), Selection(3, OmniboxPopupModel::NORMAL), Selection(0, OmniboxPopupModel::NORMAL),
diff --git a/components/omnibox/browser/url_index_private_data.cc b/components/omnibox/browser/url_index_private_data.cc index 04288e1..f685ca4 100644 --- a/components/omnibox/browser/url_index_private_data.cc +++ b/components/omnibox/browser/url_index_private_data.cc
@@ -523,8 +523,6 @@ HistoryIDVector URLIndexPrivateData::HistoryIDsFromWords( const String16Vector& unsorted_words) { - // This histogram name reflects the historic name of this function. - SCOPED_UMA_HISTOGRAM_TIMER("Omnibox.HistoryQuickHistoryIDSetFromWords"); // Break the terms down into individual terms (words), get the candidate // set for each term, and intersect each to get a final candidate list. // Note that a single 'term' from the user's perspective might be
diff --git a/components/optimization_guide/hint_cache.cc b/components/optimization_guide/hint_cache.cc index c7b883a..b3027be 100644 --- a/components/optimization_guide/hint_cache.cc +++ b/components/optimization_guide/hint_cache.cc
@@ -15,66 +15,63 @@ namespace optimization_guide { -namespace { - -// The default number of host-keyed hints retained within the host keyed cache. -// When the limit is exceeded, the least recently used hint is purged from -// |host_keyed_cache_|. -const size_t kDefaultMaxMemoryCacheHostKeyedHints = 20; - -} // namespace - HintCache::HintCache( std::unique_ptr<OptimizationGuideStore> optimization_guide_store, - base::Optional<int> - max_memory_cache_host_keyed_hints /*= base::Optional<int>()*/) + int max_memory_cache_host_keyed_hints) : optimization_guide_store_(std::move(optimization_guide_store)), - host_keyed_cache_(std::max(max_memory_cache_host_keyed_hints.value_or( - kDefaultMaxMemoryCacheHostKeyedHints), - 1)), + host_keyed_cache_(max_memory_cache_host_keyed_hints), url_keyed_hint_cache_(features::MaxURLKeyedHintCacheSize()), - clock_(base::DefaultClock::GetInstance()) { - DCHECK(optimization_guide_store_); -} + clock_(base::DefaultClock::GetInstance()) {} HintCache::~HintCache() = default; void HintCache::Initialize(bool purge_existing_data, base::OnceClosure callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - optimization_guide_store_->Initialize( - purge_existing_data, - base::BindOnce(&HintCache::OnStoreInitialized, base::Unretained(this), - std::move(callback))); + + if (optimization_guide_store_) { + optimization_guide_store_->Initialize( + purge_existing_data, + base::BindOnce(&HintCache::OnStoreInitialized, base::Unretained(this), + std::move(callback))); + return; + } + std::move(callback).Run(); } std::unique_ptr<StoreUpdateData> HintCache::MaybeCreateUpdateDataForComponentHints( const base::Version& version) const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return optimization_guide_store_->MaybeCreateUpdateDataForComponentHints( - version); + if (optimization_guide_store_) { + return optimization_guide_store_->MaybeCreateUpdateDataForComponentHints( + version); + } + return nullptr; } std::unique_ptr<StoreUpdateData> HintCache::CreateUpdateDataForFetchedHints( base::Time update_time) const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return optimization_guide_store_->CreateUpdateDataForFetchedHints( - update_time); + if (optimization_guide_store_) { + return optimization_guide_store_->CreateUpdateDataForFetchedHints( + update_time); + } + return nullptr; } void HintCache::UpdateComponentHints( std::unique_ptr<StoreUpdateData> component_data, base::OnceClosure callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(component_data); - // Clear the host-keyed cache prior to updating the store with the new - // component data. - host_keyed_cache_.Clear(); - - optimization_guide_store_->UpdateComponentHints(std::move(component_data), - std::move(callback)); + if (optimization_guide_store_) { + DCHECK(component_data); + optimization_guide_store_->UpdateComponentHints(std::move(component_data), + std::move(callback)); + return; + } + std::move(callback).Run(); } void HintCache::UpdateFetchedHints( @@ -93,29 +90,34 @@ ProcessAndCacheHints(get_hints_response.get()->mutable_hints(), fetched_hints_update_data.get()); - optimization_guide_store_->UpdateFetchedHints( - std::move(fetched_hints_update_data), std::move(callback)); + + if (optimization_guide_store_) { + optimization_guide_store_->UpdateFetchedHints( + std::move(fetched_hints_update_data), std::move(callback)); + } else { + std::move(callback).Run(); + } } void HintCache::PurgeExpiredFetchedHints() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(optimization_guide_store_); - optimization_guide_store_->PurgeExpiredFetchedHints(); + if (optimization_guide_store_) + optimization_guide_store_->PurgeExpiredFetchedHints(); } void HintCache::ClearFetchedHints() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(optimization_guide_store_); url_keyed_hint_cache_.Clear(); ClearHostKeyedHints(); } void HintCache::ClearHostKeyedHints() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(optimization_guide_store_); host_keyed_cache_.Clear(); - optimization_guide_store_->ClearFetchedHintsFromDatabase(); + + if (optimization_guide_store_) + optimization_guide_store_->ClearFetchedHintsFromDatabase(); } bool HintCache::HasHint(const std::string& host) { @@ -123,9 +125,12 @@ auto hint_it = host_keyed_cache_.Get(host); if (hint_it == host_keyed_cache_.end()) { - // If not in-memory, check database. - OptimizationGuideStore::EntryKey hint_entry_key; - return optimization_guide_store_->FindHintEntryKey(host, &hint_entry_key); + if (optimization_guide_store_) { + // If not in-memory, check database. + OptimizationGuideStore::EntryKey hint_entry_key; + return optimization_guide_store_->FindHintEntryKey(host, &hint_entry_key); + } + return false; } // The hint for |host| was requested but no hint was returned. @@ -148,6 +153,11 @@ // there, then asynchronously load it from the store and return. auto hint_it = host_keyed_cache_.Get(host); if (hint_it == host_keyed_cache_.end()) { + if (!optimization_guide_store_) { + std::move(callback).Run(nullptr); + return; + } + OptimizationGuideStore::EntryKey hint_entry_key; if (!optimization_guide_store_->FindHintEntryKey(host, &hint_entry_key)) { std::move(callback).Run(nullptr); @@ -242,14 +252,14 @@ } base::Time HintCache::GetFetchedHintsUpdateTime() const { - if (!optimization_guide_store_) { - return base::Time(); - } - return optimization_guide_store_->GetFetchedHintsUpdateTime(); + if (optimization_guide_store_) + return optimization_guide_store_->GetFetchedHintsUpdateTime(); + return base::Time(); } void HintCache::OnStoreInitialized(base::OnceClosure callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(optimization_guide_store_); std::move(callback).Run(); } @@ -281,9 +291,11 @@ bool HintCache::ProcessAndCacheHints( google::protobuf::RepeatedPtrField<proto::Hint>* hints, optimization_guide::StoreUpdateData* update_data) { - // If there's no update data, then there's nothing to do. - if (!update_data) + if (optimization_guide_store_ && !update_data) { + // If there's no update data, then there's nothing to do. return false; + } + bool processed_hints_to_store = false; // Process each hint in the the hint configuration. The hints are mutable // because once processing is completed on each individual hint, it is moved @@ -313,7 +325,8 @@ std::make_unique<MemoryHint>( expiry_time, std::make_unique<optimization_guide::proto::Hint>(hint))); - update_data->MoveHintIntoUpdateData(std::move(hint)); + if (update_data) + update_data->MoveHintIntoUpdateData(std::move(hint)); processed_hints_to_store = true; break; case proto::FULL_URL: @@ -347,8 +360,11 @@ bool HintCache::IsHintStoreAvailable() const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(optimization_guide_store_); - return optimization_guide_store_->IsAvailable(); + + if (optimization_guide_store_) + return optimization_guide_store_->IsAvailable(); + + return false; } } // namespace optimization_guide
diff --git a/components/optimization_guide/hint_cache.h b/components/optimization_guide/hint_cache.h index 3cbd625..a003d394 100644 --- a/components/optimization_guide/hint_cache.h +++ b/components/optimization_guide/hint_cache.h
@@ -33,18 +33,17 @@ // synchronously retrieve recently loaded hints keyed by URL or host. class HintCache { public: - // Construct the HintCache with a backing store and an optional max host-keyed - // cache size. While |optimization_guide_store| is required, - // |max_memory_cache_hints| is optional and the default max size will be used - // if it is not provided. + // Construct the HintCache with an optional backing store and max host-keyed + // cache size. If a backing store is not provided, all hints will only be + // stored in-memory. explicit HintCache( std::unique_ptr<OptimizationGuideStore> optimization_guide_store, - base::Optional<int> max_memory_cache_hints = base::Optional<int>()); + int max_host_keyed_memory_cache_size); ~HintCache(); - // Initializes the backing store contained within the hint cache and - // asynchronously runs the callback after initialization is complete. - // If |purge_existing_data| is set to true, then the cache will purge any + // Initializes the backing store contained within the hint cache, if provided, + // and asynchronously runs the callback after initialization is complete. If + // |purge_existing_data| is set to true, then the cache will purge any // pre-existing data and begin in a clean state. void Initialize(bool purge_existing_data, base::OnceClosure callback);
diff --git a/components/optimization_guide/hint_cache_unittest.cc b/components/optimization_guide/hint_cache_unittest.cc index 51a0d394..725f9d0 100644 --- a/components/optimization_guide/hint_cache_unittest.cc +++ b/components/optimization_guide/hint_cache_unittest.cc
@@ -31,7 +31,8 @@ return "host.domain" + base::NumberToString(index) + ".org"; } -class HintCacheTest : public ProtoDatabaseProviderTestBase { +class HintCacheTest : public ProtoDatabaseProviderTestBase, + public testing::WithParamInterface<bool> { public: HintCacheTest() : loaded_hint_(nullptr) {} @@ -52,8 +53,10 @@ auto database_path = temp_dir_.GetPath(); auto database_task_runner = task_environment_.GetMainThreadTaskRunner(); hint_cache_ = std::make_unique<HintCache>( - std::make_unique<OptimizationGuideStore>( - db_provider_.get(), database_path, database_task_runner), + IsBackedByPersistentStore() + ? std::make_unique<OptimizationGuideStore>( + db_provider_.get(), database_path, database_task_runner) + : nullptr, memory_cache_size); is_store_initialized_ = false; hint_cache_->Initialize(purge_existing_data, @@ -149,6 +152,8 @@ base::RunLoop().RunUntilIdle(); } + bool IsBackedByPersistentStore() const { return GetParam(); } + private: void OnStoreInitialized() { is_store_initialized_ = true; } void OnUpdateComponentHints() { are_component_hints_updated_ = true; } @@ -171,7 +176,14 @@ DISALLOW_COPY_AND_ASSIGN(HintCacheTest); }; -TEST_F(HintCacheTest, ComponentUpdate) { +INSTANTIATE_TEST_SUITE_P(WithPersistentStore, + HintCacheTest, + testing::Values(true, false)); + +TEST_P(HintCacheTest, ComponentUpdate) { + if (!IsBackedByPersistentStore()) + return; + const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); @@ -206,7 +218,10 @@ EXPECT_TRUE(hint_cache()->HasHint("subdomain.domain.org")); } -TEST_F(HintCacheTest, ComponentUpdateWithSameVersionIgnored) { +TEST_P(HintCacheTest, ComponentUpdateWithSameVersionIgnored) { + if (!IsBackedByPersistentStore()) + return; + const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); @@ -220,7 +235,10 @@ EXPECT_FALSE(hint_cache()->MaybeCreateUpdateDataForComponentHints(version)); } -TEST_F(HintCacheTest, ComponentUpdateWithEarlierVersionIgnored) { +TEST_P(HintCacheTest, ComponentUpdateWithEarlierVersionIgnored) { + if (!IsBackedByPersistentStore()) + return; + const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); @@ -236,7 +254,10 @@ EXPECT_FALSE(hint_cache()->MaybeCreateUpdateDataForComponentHints(version_1)); } -TEST_F(HintCacheTest, ComponentUpdateWithLaterVersionProcessed) { +TEST_P(HintCacheTest, ComponentUpdateWithLaterVersionProcessed) { + if (!IsBackedByPersistentStore()) + return; + const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); @@ -305,7 +326,10 @@ EXPECT_TRUE(hint_cache()->HasHint("host.domain2.org")); } -TEST_F(HintCacheTest, ComponentHintsAvailableAfterRestart) { +TEST_P(HintCacheTest, ComponentHintsAvailableAfterRestart) { + if (!IsBackedByPersistentStore()) + return; + for (int i = 0; i < 2; ++i) { const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize, @@ -350,7 +374,10 @@ } } -TEST_F(HintCacheTest, ComponentHintsUpdatableAfterRestartWithPurge) { +TEST_P(HintCacheTest, ComponentHintsUpdatableAfterRestartWithPurge) { + if (!IsBackedByPersistentStore()) + return; + for (int i = 0; i < 2; ++i) { const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize, @@ -391,7 +418,10 @@ } } -TEST_F(HintCacheTest, ComponentHintsNotRetainedAfterRestartWithPurge) { +TEST_P(HintCacheTest, ComponentHintsNotRetainedAfterRestartWithPurge) { + if (!IsBackedByPersistentStore()) + return; + for (int i = 0; i < 2; ++i) { const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize, @@ -438,7 +468,10 @@ } } -TEST_F(HintCacheTest, TestMemoryCacheLeastRecentlyUsedPurge) { +TEST_P(HintCacheTest, TestMemoryCacheLeastRecentlyUsedPurge) { + if (!IsBackedByPersistentStore()) + return; + const int kTestHintCount = 10; const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); @@ -478,7 +511,9 @@ } } -TEST_F(HintCacheTest, TestHostNotInCache) { +TEST_P(HintCacheTest, TestHostNotInCache) { + if (!IsBackedByPersistentStore()) + return; const int kTestHintCount = 10; const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); @@ -500,7 +535,10 @@ EXPECT_FALSE(hint_cache()->HasHint(GetHostDomainOrg(kTestHintCount))); } -TEST_F(HintCacheTest, TestMemoryCacheLoadCallback) { +TEST_P(HintCacheTest, TestMemoryCacheLoadCallback) { + if (!IsBackedByPersistentStore()) + return; + const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); @@ -525,7 +563,13 @@ EXPECT_EQ(hint_key, GetLoadedHint()->key()); } -TEST_F(HintCacheTest, StoreValidFetchedHints) { +TEST_P(HintCacheTest, StoreValidFetchedHints) { + if (!IsBackedByPersistentStore()) { + // Checking the fetched hints update time is not relevant when we don't have + // a backing store. + return; + } + const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); @@ -550,7 +594,7 @@ EXPECT_EQ(hint_cache()->GetFetchedHintsUpdateTime(), stored_time); } -TEST_F(HintCacheTest, ParseEmptyFetchedHints) { +TEST_P(HintCacheTest, ParseEmptyFetchedHints) { const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); @@ -559,12 +603,18 @@ std::make_unique<proto::GetHintsResponse>(); UpdateFetchedHintsAndWait(std::move(get_hints_response), stored_time, {}, {}); - // Empty Fetched Hints causes the metadata entry to be updated. + // Empty Fetched Hints causes the metadata entry to be updated if store is + // available. EXPECT_TRUE(are_fetched_hints_updated()); - EXPECT_EQ(hint_cache()->GetFetchedHintsUpdateTime(), stored_time); + + if (IsBackedByPersistentStore()) { + EXPECT_EQ(hint_cache()->GetFetchedHintsUpdateTime(), stored_time); + } else { + EXPECT_EQ(hint_cache()->GetFetchedHintsUpdateTime(), base::Time()); + } } -TEST_F(HintCacheTest, StoreValidFetchedHintsWithServerProvidedExpiryTime) { +TEST_P(HintCacheTest, StoreValidFetchedHintsWithServerProvidedExpiryTime) { const int kMemoryCacheSize = 5; const int kFetchedHintExpirationSecs = 60; CreateAndInitializeHintCache(kMemoryCacheSize); @@ -589,8 +639,12 @@ {"host.domain.org"}, {navigation_url}); EXPECT_TRUE(are_fetched_hints_updated()); - // Next update time for hints should be updated. - EXPECT_EQ(hint_cache()->GetFetchedHintsUpdateTime(), stored_time); + if (IsBackedByPersistentStore()) { + // Next update time for hints should be updated. + EXPECT_EQ(hint_cache()->GetFetchedHintsUpdateTime(), stored_time); + } else { + EXPECT_EQ(hint_cache()->GetFetchedHintsUpdateTime(), base::Time()); + } // Should be loaded right when response is received. EXPECT_TRUE(hint_cache()->GetHostKeyedHintIfLoaded("host.domain.org")); @@ -601,7 +655,7 @@ EXPECT_FALSE(hint_cache()->GetHostKeyedHintIfLoaded("host.domain.org")); } -TEST_F(HintCacheTest, StoreValidFetchedHintsWithDefaultExpiryTime) { +TEST_P(HintCacheTest, StoreValidFetchedHintsWithDefaultExpiryTime) { const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); @@ -622,8 +676,12 @@ {"host.domain.org"}, {}); EXPECT_TRUE(are_fetched_hints_updated()); - // Next update time for hints should be updated. - EXPECT_EQ(hint_cache()->GetFetchedHintsUpdateTime(), stored_time); + if (IsBackedByPersistentStore()) { + // Next update time for hints should be updated. + EXPECT_EQ(hint_cache()->GetFetchedHintsUpdateTime(), stored_time); + } else { + EXPECT_EQ(hint_cache()->GetFetchedHintsUpdateTime(), base::Time()); + } // Should be loaded right when response is received. EXPECT_TRUE(hint_cache()->GetHostKeyedHintIfLoaded("host.domain.org")); @@ -635,13 +693,13 @@ EXPECT_FALSE(hint_cache()->GetHostKeyedHintIfLoaded("host.domain.org")); } -TEST_F(HintCacheTest, CacheValidURLKeyedHint) { +TEST_P(HintCacheTest, CacheValidURLKeyedHint) { const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); std::unique_ptr<StoreUpdateData> update_data = hint_cache()->CreateUpdateDataForFetchedHints(base::Time()); - ASSERT_TRUE(update_data); + ASSERT_EQ(update_data != nullptr, IsBackedByPersistentStore()); GURL url("https://whatever.com/r/werd"); @@ -650,18 +708,19 @@ // Only URL-keyed hint included so there are no hints to store within the // update data. - EXPECT_FALSE(hint_cache()->ProcessAndCacheHints(&hints, update_data.get())); + EXPECT_FALSE(hint_cache()->ProcessAndCacheHints( + &hints, IsBackedByPersistentStore() ? update_data.get() : nullptr)); EXPECT_TRUE(hint_cache()->GetURLKeyedHint(url)); } -TEST_F(HintCacheTest, URLKeyedHintExpired) { +TEST_P(HintCacheTest, URLKeyedHintExpired) { const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); std::unique_ptr<StoreUpdateData> update_data = hint_cache()->CreateUpdateDataForFetchedHints(base::Time()); - ASSERT_TRUE(update_data); + ASSERT_EQ(update_data != nullptr, IsBackedByPersistentStore()); GURL url("https://whatever.com/r/werd"); int cache_duration_in_secs = 60; @@ -671,7 +730,8 @@ // Only URL-keyed hint included so there are no hints to store within the // update data. - EXPECT_FALSE(hint_cache()->ProcessAndCacheHints(&hints, update_data.get())); + EXPECT_FALSE(hint_cache()->ProcessAndCacheHints( + &hints, IsBackedByPersistentStore() ? update_data.get() : nullptr)); EXPECT_TRUE(hint_cache()->GetURLKeyedHint(url)); @@ -679,7 +739,13 @@ EXPECT_FALSE(hint_cache()->GetURLKeyedHint(url)); } -TEST_F(HintCacheTest, PurgeExpiredFetchedHints) { +TEST_P(HintCacheTest, PurgeExpiredFetchedHints) { + if (!IsBackedByPersistentStore()) { + // Purging expired fetched hints is only really relevant for when we have + // a backing store. + return; + } + const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); @@ -723,13 +789,13 @@ EXPECT_TRUE(hint_cache()->HasHint("notpurged.com")); } -TEST_F(HintCacheTest, ClearFetchedHints) { +TEST_P(HintCacheTest, ClearFetchedHints) { const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); std::unique_ptr<StoreUpdateData> update_data = hint_cache()->CreateUpdateDataForFetchedHints(base::Time()); - ASSERT_TRUE(update_data); + ASSERT_EQ(update_data != nullptr, IsBackedByPersistentStore()); GURL url("https://whatever.com/r/werd"); int cache_duration_in_secs = 60; @@ -755,7 +821,8 @@ // Only URL-keyed hint included so there are no hints to store within the // update data. - EXPECT_FALSE(hint_cache()->ProcessAndCacheHints(&hints, update_data.get())); + EXPECT_FALSE(hint_cache()->ProcessAndCacheHints( + &hints, IsBackedByPersistentStore() ? update_data.get() : nullptr)); EXPECT_TRUE(hint_cache()->GetURLKeyedHint(url)); EXPECT_TRUE(hint_cache()->GetHostKeyedHintIfLoaded(host)); @@ -765,13 +832,13 @@ EXPECT_FALSE(hint_cache()->GetHostKeyedHintIfLoaded(host)); } -TEST_F(HintCacheTest, UnsupportedURLsForURLKeyedHints) { +TEST_P(HintCacheTest, UnsupportedURLsForURLKeyedHints) { const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); std::unique_ptr<StoreUpdateData> update_data = hint_cache()->CreateUpdateDataForFetchedHints(base::Time()); - ASSERT_TRUE(update_data); + ASSERT_EQ(update_data != nullptr, IsBackedByPersistentStore()); GURL https_url("https://whatever.com/r/werd"); GURL http_url("http://werd.com/werd/"); @@ -788,7 +855,8 @@ // Only URL-keyed hint included so there are no hints to store within the // update data. - EXPECT_FALSE(hint_cache()->ProcessAndCacheHints(&hints, update_data.get())); + EXPECT_FALSE(hint_cache()->ProcessAndCacheHints( + &hints, IsBackedByPersistentStore() ? update_data.get() : nullptr)); EXPECT_TRUE(hint_cache()->GetURLKeyedHint(https_url)); EXPECT_TRUE(hint_cache()->GetURLKeyedHint(http_url)); @@ -797,13 +865,13 @@ EXPECT_FALSE(hint_cache()->GetURLKeyedHint(auth_url)); } -TEST_F(HintCacheTest, URLsWithNoURLKeyedHints) { +TEST_P(HintCacheTest, URLsWithNoURLKeyedHints) { const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); std::unique_ptr<StoreUpdateData> update_data = hint_cache()->CreateUpdateDataForFetchedHints(base::Time()); - ASSERT_TRUE(update_data); + ASSERT_EQ(update_data != nullptr, IsBackedByPersistentStore()); GURL https_url_without_hint("https://whatever.com/r/nohint"); GURL https_url_with_hint("https://whatever.com/r/hint"); @@ -817,7 +885,8 @@ // Only URL-keyed hint included so there are no hints to store within the // update data. - EXPECT_FALSE(hint_cache()->ProcessAndCacheHints(&hints, update_data.get())); + EXPECT_FALSE(hint_cache()->ProcessAndCacheHints( + &hints, IsBackedByPersistentStore() ? update_data.get() : nullptr)); // Add the url without hint to the url-keyed cache via UpdateFetchedHints. std::unique_ptr<proto::GetHintsResponse> get_hints_response = @@ -842,7 +911,7 @@ EXPECT_FALSE(hint_cache()->HasURLKeyedEntryForURL(https_url_unseen)); } -TEST_F(HintCacheTest, ProcessHintsNoUpdateData) { +TEST_P(HintCacheTest, ProcessHintsNoUpdateData) { const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); @@ -855,10 +924,11 @@ google::protobuf::RepeatedPtrField<proto::Hint> hints; *(hints.Add()) = hint; - EXPECT_FALSE(hint_cache()->ProcessAndCacheHints(&hints, nullptr)); + EXPECT_EQ(hint_cache()->ProcessAndCacheHints(&hints, nullptr), + !IsBackedByPersistentStore()); } -TEST_F(HintCacheTest, +TEST_P(HintCacheTest, ProcessHintsWithNoPageHintsOrWhitelistedOptimizationsAndUpdateData) { const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); @@ -872,12 +942,15 @@ std::unique_ptr<StoreUpdateData> update_data = StoreUpdateData::CreateComponentStoreUpdateData(base::Version("1.0.0")); - EXPECT_FALSE(hint_cache()->ProcessAndCacheHints(&hints, update_data.get())); - // Verify there is 1 store entries: 1 for the metadata entry. - EXPECT_EQ(1ul, update_data->TakeUpdateEntries()->size()); + EXPECT_FALSE(hint_cache()->ProcessAndCacheHints( + &hints, IsBackedByPersistentStore() ? update_data.get() : nullptr)); + if (IsBackedByPersistentStore()) { + // Verify there is 1 store entries: 1 for the metadata entry. + EXPECT_EQ(1ul, update_data->TakeUpdateEntries()->size()); + } } -TEST_F(HintCacheTest, +TEST_P(HintCacheTest, ProcessHintsWithNoPageHintsButHasWhitelistedOptimizationsAndUpdateData) { const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); @@ -893,13 +966,16 @@ std::unique_ptr<StoreUpdateData> update_data = StoreUpdateData::CreateComponentStoreUpdateData(base::Version("1.0.0")); - EXPECT_TRUE(hint_cache()->ProcessAndCacheHints(&hints, update_data.get())); - // Verify there is 1 store entries: 1 for the metadata entry plus the 1 - // added hint entry. - EXPECT_EQ(2ul, update_data->TakeUpdateEntries()->size()); + EXPECT_TRUE(hint_cache()->ProcessAndCacheHints( + &hints, IsBackedByPersistentStore() ? update_data.get() : nullptr)); + if (IsBackedByPersistentStore()) { + // Verify there is 1 store entries: 1 for the metadata entry plus the 1 + // added hint entry. + EXPECT_EQ(2ul, update_data->TakeUpdateEntries()->size()); + } } -TEST_F(HintCacheTest, ProcessHintsWithPageHintsAndUpdateData) { +TEST_P(HintCacheTest, ProcessHintsWithPageHintsAndUpdateData) { const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); @@ -919,10 +995,13 @@ std::unique_ptr<StoreUpdateData> update_data = StoreUpdateData::CreateComponentStoreUpdateData(base::Version("1.0.0")); - EXPECT_TRUE(hint_cache()->ProcessAndCacheHints(&hints, update_data.get())); - // Verify there are 2 store entries: 1 for the metadata entry plus - // the 1 added hint entry. - EXPECT_EQ(2ul, update_data->TakeUpdateEntries()->size()); + EXPECT_TRUE(hint_cache()->ProcessAndCacheHints( + &hints, IsBackedByPersistentStore() ? update_data.get() : nullptr)); + if (IsBackedByPersistentStore()) { + // Verify there are 2 store entries: 1 for the metadata entry plus + // the 1 added hint entry. + EXPECT_EQ(2ul, update_data->TakeUpdateEntries()->size()); + } } } // namespace
diff --git a/components/optimization_guide/hints_fetcher.cc b/components/optimization_guide/hints_fetcher.cc index ed199b2a..949baf5 100644 --- a/components/optimization_guide/hints_fetcher.cc +++ b/components/optimization_guide/hints_fetcher.cc
@@ -126,6 +126,11 @@ bool HintsFetcher::WasHostCoveredByFetch(PrefService* pref_service, const std::string& host, const base::Clock* time_clock) { + if (!optimization_guide::features::ShouldPersistHintsToDisk()) { + // Don't consult the pref if we aren't even persisting hints to disk. + return false; + } + DictionaryPrefUpdate hosts_fetched( pref_service, prefs::kHintsFetcherHostsSuccessfullyFetched); base::Optional<double> value = @@ -325,6 +330,11 @@ void HintsFetcher::UpdateHostsSuccessfullyFetched( base::TimeDelta valid_duration) { + if (!optimization_guide::features::ShouldPersistHintsToDisk()) { + // Do not persist any state if we aren't persisting hints to disk. + return; + } + DictionaryPrefUpdate hosts_fetched_list( pref_service_, prefs::kHintsFetcherHostsSuccessfullyFetched); @@ -417,7 +427,7 @@ base::Optional<double> value = hosts_fetched->FindDoubleKey(HashHostForDictionary(host)); - if (value) { + if (value && optimization_guide::features::ShouldPersistHintsToDisk()) { base::Time host_valid_time = base::Time::FromDeltaSinceWindowsEpoch( base::TimeDelta::FromSecondsD(*value)); host_hints_due_for_refresh =
diff --git a/components/optimization_guide/hints_fetcher_unittest.cc b/components/optimization_guide/hints_fetcher_unittest.cc index 8e7469bb..397c2dd 100644 --- a/components/optimization_guide/hints_fetcher_unittest.cc +++ b/components/optimization_guide/hints_fetcher_unittest.cc
@@ -34,7 +34,8 @@ constexpr char optimization_guide_service_url[] = "https://hintsserver.com/"; -class HintsFetcherTest : public testing::Test { +class HintsFetcherTest : public testing::Test, + public testing::WithParamInterface<bool> { public: HintsFetcherTest() : task_environment_(base::test::TaskEnvironment::MainThreadType::UI, @@ -43,8 +44,12 @@ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( &test_url_loader_factory_)) { base::test::ScopedFeatureList scoped_list; - scoped_list.InitAndEnableFeatureWithParameters( - features::kRemoteOptimizationGuideFetching, {}); + scoped_list.InitWithFeaturesAndParameters( + {{features::kRemoteOptimizationGuideFetching, {}}, + {features::kOptimizationHints, + {{"persist_hints_to_disk", + ShouldPersistHintsToDisk() ? "true" : "false"}}}}, + {}); pref_service_ = std::make_unique<TestingPrefServiceSimple>(); prefs::RegisterProfilePrefs(pref_service_->registry()); @@ -101,6 +106,8 @@ hints_fetcher_->SetTimeClockForTesting(clock); } + bool ShouldPersistHintsToDisk() const { return GetParam(); } + protected: bool FetchHints(const std::vector<std::string>& hosts, const std::vector<GURL>& urls) { @@ -166,7 +173,11 @@ DISALLOW_COPY_AND_ASSIGN(HintsFetcherTest); }; -TEST_F(HintsFetcherTest, +INSTANTIATE_TEST_SUITE_P(WithPersistentStore, + HintsFetcherTest, + testing::Values(true, false)); + +TEST_P(HintsFetcherTest, FetchOptimizationGuideServiceHintsLogsHistogramUponExiting) { base::HistogramTester histogram_tester; @@ -180,7 +191,7 @@ 1, 1); } -TEST_F(HintsFetcherTest, FetchOptimizationGuideServiceHints) { +TEST_P(HintsFetcherTest, FetchOptimizationGuideServiceHints) { base::HistogramTester histogram_tester; std::string response_content; @@ -205,7 +216,7 @@ // Tests to ensure that multiple hint fetches by the same object cannot be in // progress simultaneously. -TEST_F(HintsFetcherTest, FetchInProgress) { +TEST_P(HintsFetcherTest, FetchInProgress) { base::SimpleTestClock test_clock; SetTimeClockForTesting(&test_clock); @@ -234,7 +245,7 @@ // Tests that the hints are refreshed again for hosts for whom hints were // fetched recently. -TEST_F(HintsFetcherTest, FetchInProgress_HostsHintsRefreshed) { +TEST_P(HintsFetcherTest, FetchInProgress_HostsHintsRefreshed) { base::SimpleTestClock test_clock; SetTimeClockForTesting(&test_clock); @@ -298,7 +309,7 @@ } // Tests 404 response from request. -TEST_F(HintsFetcherTest, FetchReturned404) { +TEST_P(HintsFetcherTest, FetchReturned404) { base::HistogramTester histogram_tester; std::string response_content; @@ -317,7 +328,7 @@ HintsFetcherRequestStatus::kResponseError, 1); } -TEST_F(HintsFetcherTest, FetchReturnBadResponse) { +TEST_P(HintsFetcherTest, FetchReturnBadResponse) { base::HistogramTester histogram_tester; std::string response_content = "not proto"; @@ -334,7 +345,7 @@ HintsFetcherRequestStatus::kResponseError, 1); } -TEST_F(HintsFetcherTest, FetchAttemptWhenNetworkOffline) { +TEST_P(HintsFetcherTest, FetchAttemptWhenNetworkOffline) { base::HistogramTester histogram_tester; SetConnectionOffline(); @@ -362,7 +373,7 @@ 1); } -TEST_F(HintsFetcherTest, HintsFetchSuccessfulHostsRecorded) { +TEST_P(HintsFetcherTest, HintsFetchSuccessfulHostsRecorded) { std::vector<std::string> hosts{"host1.com", "host2.com"}; std::string response_content; @@ -371,6 +382,9 @@ EXPECT_TRUE(SimulateResponse(response_content, net::HTTP_OK)); EXPECT_TRUE(hints_fetched()); + if (!ShouldPersistHintsToDisk()) + return; + const base::DictionaryValue* hosts_fetched = pref_service()->GetDictionary( prefs::kHintsFetcherHostsSuccessfullyFetched); base::Optional<double> value; @@ -387,7 +401,7 @@ } } -TEST_F(HintsFetcherTest, HintsFetchFailsHostNotRecorded) { +TEST_P(HintsFetcherTest, HintsFetchFailsHostNotRecorded) { std::vector<std::string> hosts{"host1.com", "host2.com"}; std::string response_content; @@ -396,6 +410,9 @@ EXPECT_TRUE(SimulateResponse(response_content, net::HTTP_NOT_FOUND)); EXPECT_FALSE(hints_fetched()); + if (!ShouldPersistHintsToDisk()) + return; + const base::DictionaryValue* hosts_fetched = pref_service()->GetDictionary( prefs::kHintsFetcherHostsSuccessfullyFetched); for (const std::string& host : hosts) { @@ -403,7 +420,7 @@ } } -TEST_F(HintsFetcherTest, HintsFetchClearHostsSuccessfullyFetched) { +TEST_P(HintsFetcherTest, HintsFetchClearHostsSuccessfullyFetched) { std::vector<std::string> hosts{"host1.com", "host2.com"}; std::string response_content; @@ -412,6 +429,9 @@ EXPECT_TRUE(SimulateResponse(response_content, net::HTTP_OK)); EXPECT_TRUE(hints_fetched()); + if (!ShouldPersistHintsToDisk()) + return; + const base::DictionaryValue* hosts_fetched = pref_service()->GetDictionary( prefs::kHintsFetcherHostsSuccessfullyFetched); for (const std::string& host : hosts) { @@ -426,7 +446,10 @@ } } -TEST_F(HintsFetcherTest, HintsFetcherHostsCovered) { +TEST_P(HintsFetcherTest, HintsFetcherHostsCovered) { + if (!ShouldPersistHintsToDisk()) + return; + std::vector<std::string> hosts{"host1.com", "host2.com"}; base::Time host_invalid_time = base::Time::Now() + base::TimeDelta().FromHours(1); @@ -437,7 +460,7 @@ EXPECT_TRUE(WasHostCoveredByFetch(hosts[1])); } -TEST_F(HintsFetcherTest, HintsFetcherCoveredHostExpired) { +TEST_P(HintsFetcherTest, HintsFetcherCoveredHostExpired) { std::string response_content; std::vector<std::string> hosts{"host1.com", "host2.com"}; base::Time host_invalid_time = @@ -452,6 +475,9 @@ EXPECT_TRUE(SimulateResponse(response_content, net::HTTP_OK)); EXPECT_TRUE(hints_fetched()); + if (!ShouldPersistHintsToDisk()) + return; + // The first pair of hosts should be recorded as failed to be // covered by a recent hints fetcher as they have expired. EXPECT_FALSE(WasHostCoveredByFetch(hosts[0])); @@ -469,7 +495,7 @@ EXPECT_TRUE(WasHostCoveredByFetch(hosts_valid[1])); } -TEST_F(HintsFetcherTest, HintsFetcherHostNotCovered) { +TEST_P(HintsFetcherTest, HintsFetcherHostNotCovered) { std::vector<std::string> hosts{"host1.com", "host2.com"}; base::Time host_invalid_time = base::Time::Now() + base::TimeDelta().FromHours(1); @@ -484,7 +510,10 @@ EXPECT_FALSE(WasHostCoveredByFetch("newhost.com")); } -TEST_F(HintsFetcherTest, HintsFetcherRemoveExpiredOnSuccessfullyFetched) { +TEST_P(HintsFetcherTest, HintsFetcherRemoveExpiredOnSuccessfullyFetched) { + if (!ShouldPersistHintsToDisk()) + return; + std::string response_content; std::vector<std::string> hosts_expired{"host1.com", "host2.com"}; base::Time host_invalid_time = @@ -512,7 +541,10 @@ EXPECT_TRUE(WasHostCoveredByFetch(hosts_valid[1])); } -TEST_F(HintsFetcherTest, HintsFetcherSuccessfullyFetchedHostsFull) { +TEST_P(HintsFetcherTest, HintsFetcherSuccessfullyFetchedHostsFull) { + if (!ShouldPersistHintsToDisk()) + return; + std::string response_content; std::vector<std::string> hosts; size_t max_hosts = @@ -541,7 +573,7 @@ EXPECT_TRUE(WasHostCoveredByFetch(extra_hosts[1])); } -TEST_F(HintsFetcherTest, MaxHostsForOptimizationGuideServiceHintsFetch) { +TEST_P(HintsFetcherTest, MaxHostsForOptimizationGuideServiceHintsFetch) { std::string response_content; std::vector<std::string> all_hosts; @@ -564,6 +596,9 @@ EXPECT_TRUE(SimulateResponse(response_content, net::HTTP_OK)); EXPECT_TRUE(hints_fetched()); + if (!ShouldPersistHintsToDisk()) + return; + DictionaryPrefUpdate hosts_fetched( pref_service(), prefs::kHintsFetcherHostsSuccessfullyFetched); EXPECT_EQ(max_hosts_in_fetch_request, hosts_fetched->size()); @@ -575,7 +610,7 @@ } } -TEST_F(HintsFetcherTest, MaxUrlsForOptimizationGuideServiceHintsFetch) { +TEST_P(HintsFetcherTest, MaxUrlsForOptimizationGuideServiceHintsFetch) { base::HistogramTester histogram_tester; std::string response_content; std::vector<GURL> all_urls; @@ -612,7 +647,7 @@ } } -TEST_F(HintsFetcherTest, OnlyURLsToFetch) { +TEST_P(HintsFetcherTest, OnlyURLsToFetch) { base::HistogramTester histogram_tester; std::string response_content; @@ -631,7 +666,7 @@ static_cast<int>(HintsFetcherRequestStatus::kSuccess), 1); } -TEST_F(HintsFetcherTest, NoHostsOrURLsToFetch) { +TEST_P(HintsFetcherTest, NoHostsOrURLsToFetch) { base::HistogramTester histogram_tester; std::string response_content;
diff --git a/components/optimization_guide/optimization_guide_features.cc b/components/optimization_guide/optimization_guide_features.cc index 1595d86b..45567c1 100644 --- a/components/optimization_guide/optimization_guide_features.cc +++ b/components/optimization_guide/optimization_guide_features.cc
@@ -242,6 +242,12 @@ kOptimizationTargetPrediction, "max_host_model_features_cache_size", 100); } +size_t MaxHostKeyedHintCacheSize() { + size_t max_host_keyed_hint_cache_size = GetFieldTrialParamByFeatureAsInt( + kOptimizationHints, "max_host_keyed_hint_cache_size", 30); + return max_host_keyed_hint_cache_size; +} + size_t MaxURLKeyedHintCacheSize() { size_t max_url_keyed_hint_cache_size = GetFieldTrialParamByFeatureAsInt( kOptimizationHints, "max_url_keyed_hint_cache_size", 30); @@ -250,6 +256,11 @@ return max_url_keyed_hint_cache_size; } +bool ShouldPersistHintsToDisk() { + return GetFieldTrialParamByFeatureAsBool(kOptimizationHints, + "persist_hints_to_disk", true); +} + bool ShouldOverrideOptimizationTargetDecisionForMetricsPurposes( proto::OptimizationTarget optimization_target) { if (optimization_target != proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)
diff --git a/components/optimization_guide/optimization_guide_features.h b/components/optimization_guide/optimization_guide_features.h index a569117..f93fd6e 100644 --- a/components/optimization_guide/optimization_guide_features.h +++ b/components/optimization_guide/optimization_guide_features.h
@@ -122,9 +122,17 @@ size_t MaxHostModelFeaturesCacheSize(); // The maximum number of hints allowed to be maintained in a least-recently-used -// cache. +// cache for hosts. +size_t MaxHostKeyedHintCacheSize(); + +// The maximum number of hints allowed to be maintained in a least-recently-used +// cache for URLs. size_t MaxURLKeyedHintCacheSize(); +// Returns true if hints should be persisted to disk. If this is false, hints +// will just be stored in-memory and evicted if not recently used. +bool ShouldPersistHintsToDisk(); + // Returns true if the optimization target decision for |optimization_target| // should not be propagated to the caller in an effort to fully understand the // statistics for the served model and not taint the resulting data.
diff --git a/components/safe_browsing/content/web_ui/safe_browsing_ui.cc b/components/safe_browsing/content/web_ui/safe_browsing_ui.cc index e49b045..9f570c2 100644 --- a/components/safe_browsing/content/web_ui/safe_browsing_ui.cc +++ b/components/safe_browsing/content/web_ui/safe_browsing_ui.cc
@@ -278,6 +278,7 @@ } void WebUIInfoSingleton::AddToDeepScanRequests( + const GURL& tab_url, const enterprise_connectors::ContentAnalysisRequest& request) { if (!HasListener()) return; @@ -289,6 +290,7 @@ base::Time::Now(); } + deep_scan_requests_[request.request_token()].tab_url = tab_url; deep_scan_requests_[request.request_token()].content_analysis_request = request; @@ -1346,6 +1348,7 @@ #if BUILDFLAG(FULL_SAFE_BROWSING) std::string SerializeContentAnalysisRequest( + const GURL& tab_url, const enterprise_connectors::ContentAnalysisRequest& request) { base::DictionaryValue request_dict; @@ -1378,6 +1381,9 @@ } request_dict.SetKey("request_data", std::move(request_data)); } + if (tab_url.is_valid()) { + request_dict.SetStringKey("tab_url", tab_url.spec()); + } base::ListValue tags; for (const std::string& tag : request.tags()) @@ -1618,8 +1624,9 @@ value.SetStringKey("request", SerializeDeepScanningRequest(data.request.value())); } else if (data.content_analysis_request.has_value()) { - value.SetStringKey("request", SerializeContentAnalysisRequest( - data.content_analysis_request.value())); + value.SetStringKey( + "request", SerializeContentAnalysisRequest( + data.tab_url, data.content_analysis_request.value())); } if (!data.response_time.is_null()) {
diff --git a/components/safe_browsing/content/web_ui/safe_browsing_ui.h b/components/safe_browsing/content/web_ui/safe_browsing_ui.h index 8e10f470..9f742be6 100644 --- a/components/safe_browsing/content/web_ui/safe_browsing_ui.h +++ b/components/safe_browsing/content/web_ui/safe_browsing_ui.h
@@ -45,6 +45,7 @@ base::Optional<DeepScanningClientRequest> request; base::Optional<enterprise_connectors::ContentAnalysisRequest> content_analysis_request; + GURL tab_url; base::Time response_time; std::string response_status; @@ -333,6 +334,7 @@ // and response. void AddToDeepScanRequests(const DeepScanningClientRequest& request); void AddToDeepScanRequests( + const GURL& tab_url, const enterprise_connectors::ContentAnalysisRequest& request); // Add the new response to |deep_scan_requests_| and send it to all the open
diff --git a/components/security_interstitials/core/common_string_util.cc b/components/security_interstitials/core/common_string_util.cc index 13a9d34..4ea21ca 100644 --- a/components/security_interstitials/core/common_string_util.cc +++ b/components/security_interstitials/core/common_string_util.cc
@@ -67,6 +67,26 @@ load_time_data->SetString("pem", base::StrCat(encoded_chain)); } +void PopulateLegacyTLSStrings(base::DictionaryValue* load_time_data, + const base::string16& hostname) { + load_time_data->SetString("tabTitle", + l10n_util::GetStringUTF16(IDS_SSL_V2_TITLE)); + load_time_data->SetString("heading", + l10n_util::GetStringUTF16(IDS_LEGACY_TLS_HEADING)); + load_time_data->SetString( + "primaryButtonText", + l10n_util::GetStringUTF16(IDS_SSL_OVERRIDABLE_SAFETY_BUTTON)); + load_time_data->SetString( + "primaryParagraph", + l10n_util::GetStringUTF16(IDS_LEGACY_TLS_PRIMARY_PARAGRAPH)); + load_time_data->SetString( + "explanationParagraph", + l10n_util::GetStringUTF16(IDS_LEGACY_TLS_EXPLANATION)); + load_time_data->SetString( + "finalParagraph", l10n_util::GetStringFUTF16( + IDS_SSL_OVERRIDABLE_PROCEED_PARAGRAPH, hostname)); +} + } // namespace common_string_util } // namespace security_interstitials
diff --git a/components/security_interstitials/core/common_string_util.h b/components/security_interstitials/core/common_string_util.h index da10ee7..832a9b7e 100644 --- a/components/security_interstitials/core/common_string_util.h +++ b/components/security_interstitials/core/common_string_util.h
@@ -32,6 +32,11 @@ // For determining whether to use the old or new icon sets. void PopulateNewIconStrings(base::DictionaryValue* load_time_data); +// Fills in the details for a legacy TLS error. Abstracts the strings for +// access from ios/. +void PopulateLegacyTLSStrings(base::DictionaryValue* load_time_data, + const base::string16& hostname); + } // common_string_util } // namespace security_interstitials
diff --git a/components/vector_icons/BUILD.gn b/components/vector_icons/BUILD.gn index f90cc6a..966bb53 100644 --- a/components/vector_icons/BUILD.gn +++ b/components/vector_icons/BUILD.gn
@@ -36,6 +36,7 @@ "folder_managed_touch.icon", "folder_open.icon", "folder_touch.icon", + "font_access.icon", "forward_arrow.icon", "headset.icon", "help.icon",
diff --git a/components/vector_icons/font_access.icon b/components/vector_icons/font_access.icon new file mode 100644 index 0000000..9de44ba0 --- /dev/null +++ b/components/vector_icons/font_access.icon
@@ -0,0 +1,39 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +CANVAS_DIMENSIONS, 24, +MOVE_TO, 20, 2, +H_LINE_TO, 4, +R_CUBIC_TO, -1.1f, 0, -2, 0.9f, -2, 2, +R_V_LINE_TO, 16, +R_CUBIC_TO, 0, 1.1f, 0.9f, 2, 2, 2, +R_H_LINE_TO, 16, +R_CUBIC_TO, 1.1f, 0, 2, -0.9f, 2, -2, +V_LINE_TO, 4, +R_CUBIC_TO, 0, -1.1f, -0.9f, -2, -2, -2, +CLOSE, +R_MOVE_TO, 0, 18, +H_LINE_TO, 4, +V_LINE_TO, 4, +R_H_LINE_TO, 16, +R_V_LINE_TO, 16, +CLOSE, +MOVE_TO, 10.69f, 6, +R_H_LINE_TO, 2.6f, +R_LINE_TO, 4.51f, 12, +R_H_LINE_TO, -2.5f, +R_LINE_TO, -1.01f, -2.87f, +H_LINE_TO, 9.7f, +LINE_TO, 8.7f, 18, +H_LINE_TO, 6.2f, +R_LINE_TO, 4.49f, -12, +CLOSE, +R_MOVE_TO, 2.87f, 7.06f, +R_LINE_TO, -1.06f, -3.02f, +R_LINE_TO, -0.43f, -1.44f, +R_H_LINE_TO, -0.13f, +R_LINE_TO, -0.44f, 1.44f, +R_LINE_TO, -1.07f, 3.02f, +R_H_LINE_TO, 3.13f, +CLOSE
diff --git a/components/viz/service/display_embedder/skia_output_device_gl.cc b/components/viz/service/display_embedder/skia_output_device_gl.cc index 8985ec9..2aa2a73 100644 --- a/components/viz/service/display_embedder/skia_output_device_gl.cc +++ b/components/viz/service/display_embedder/skia_output_device_gl.cc
@@ -15,6 +15,7 @@ #include "gpu/command_buffer/service/gl_utils.h" #include "gpu/command_buffer/service/mailbox_manager.h" #include "gpu/command_buffer/service/shared_context_state.h" +#include "gpu/command_buffer/service/shared_image_factory.h" #include "gpu/command_buffer/service/texture_base.h" #include "gpu/command_buffer/service/texture_manager.h" #include "third_party/skia/include/core/SkSurface.h" @@ -34,6 +35,7 @@ SkiaOutputDeviceGL::SkiaOutputDeviceGL( gpu::MailboxManager* mailbox_manager, + gpu::SharedImageRepresentationFactory* representation_factory, gpu::SharedContextState* context_state, scoped_refptr<gl::GLSurface> gl_surface, scoped_refptr<gpu::gles2::FeatureInfo> feature_info, @@ -42,6 +44,7 @@ : SkiaOutputDevice(memory_tracker, std::move(did_swap_buffer_complete_callback)), mailbox_manager_(mailbox_manager), + representation_factory_(representation_factory), context_state_(context_state), gl_surface_(std::move(gl_surface)), supports_async_swap_(gl_surface_->SupportsAsyncSwap()) { @@ -328,11 +331,26 @@ scoped_refptr<gl::GLImage> SkiaOutputDeviceGL::GetGLImageForMailbox( const gpu::Mailbox& mailbox) { - // TODO(crbug.com/1005306): Use SharedImageManager to get textures here once + // TODO(crbug.com/1005306): Remove ConsumeTexture here once // all clients are using SharedImageInterface to create textures. + // For example, the legacy mailbox still uses GL textures (no overlay) + // and is still used. auto* texture_base = mailbox_manager_->ConsumeTexture(mailbox); - if (!texture_base) - return nullptr; + if (!texture_base) { + auto overlay = representation_factory_->ProduceOverlay(mailbox); + if (!overlay) + return nullptr; + + // Return GLImage since the ScopedReadAccess isn't being held by anyone. + // TODO(crbug.com/1011555): Have SkiaOutputSurfaceImplOnGpu hold on to the + // ScopedReadAccess for overlays like it does for PromiseImage based + // resources. + std::unique_ptr<gpu::SharedImageRepresentationOverlay::ScopedReadAccess> + scoped_overlay_read_access = + overlay->BeginScopedReadAccess(/*need_gl_image=*/true); + DCHECK(scoped_overlay_read_access); + return scoped_overlay_read_access->gl_image(); + } if (texture_base->GetType() == gpu::TextureBase::Type::kPassthrough) { gpu::gles2::TexturePassthrough* texture =
diff --git a/components/viz/service/display_embedder/skia_output_device_gl.h b/components/viz/service/display_embedder/skia_output_device_gl.h index c237b00..50a659f7 100644 --- a/components/viz/service/display_embedder/skia_output_device_gl.h +++ b/components/viz/service/display_embedder/skia_output_device_gl.h
@@ -22,6 +22,7 @@ namespace gpu { class MailboxManager; class SharedContextState; +class SharedImageRepresentationFactory; namespace gles2 { class FeatureInfo; @@ -34,6 +35,7 @@ public: SkiaOutputDeviceGL( gpu::MailboxManager* mailbox_manager, + gpu::SharedImageRepresentationFactory* representation_factory, gpu::SharedContextState* context_state, scoped_refptr<gl::GLSurface> gl_surface, scoped_refptr<gpu::gles2::FeatureInfo> feature_info, @@ -74,6 +76,7 @@ scoped_refptr<gl::GLImage> GetGLImageForMailbox(const gpu::Mailbox& mailbox); gpu::MailboxManager* const mailbox_manager_; + gpu::SharedImageRepresentationFactory* const representation_factory_; gpu::SharedContextState* const context_state_; scoped_refptr<gl::GLSurface> gl_surface_;
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc index fb553c6..49e8487 100644 --- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc +++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -1015,7 +1015,8 @@ GetDidSwapBuffersCompleteCallback()); } else { output_device_ = std::make_unique<SkiaOutputDeviceGL>( - dependency_->GetMailboxManager(), context_state_.get(), + dependency_->GetMailboxManager(), + shared_image_representation_factory_.get(), context_state_.get(), gl_surface_, feature_info_, memory_tracker_.get(), GetDidSwapBuffersCompleteCallback()); }
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index 2864ee1..f04858c 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -1879,6 +1879,8 @@ "utility_process_host.cc", "utility_process_host.h", "utility_process_host_receiver_bindings.cc", + "utility_sandbox_delegate.cc", + "utility_sandbox_delegate.h", "v8_snapshot_files.cc", "v8_snapshot_files.h", "video_capture_service.cc", @@ -2212,6 +2214,7 @@ "installedapp/installed_app_provider_impl_win.h", "renderer_host/virtual_keyboard_controller_win.cc", "renderer_host/virtual_keyboard_controller_win.h", + "utility_sandbox_delegate_win.cc", ] defines += [ # This prevents the inclusion of atlhost.h which paired @@ -2506,12 +2509,6 @@ "web_contents/web_contents_view_android.h", ] - jumbo_excluded_sources = [ - # Files with kJavaLangClass and similar constants: - # Bug https://crbug.com/787557. - "android/java/java_method.cc", # and in gin_java_bound_object.cc. - ] - set_sources_assignment_filter([]) sources += [ "memory/swap_metrics_driver_impl_linux.cc",
diff --git a/content/browser/DEPS b/content/browser/DEPS index 3b03c6f1..ba84fad 100644 --- a/content/browser/DEPS +++ b/content/browser/DEPS
@@ -168,7 +168,7 @@ "+services/network/cookie_manager.h", "+third_party/leveldatabase", ], - "utility_process_host\.cc": [ + "utility_sandbox_delegate_win\.cc": [ # TODO(crbug.com/1049894): Remove. "+services/network/network_sandbox_win.h", ],
diff --git a/content/browser/OWNERS b/content/browser/OWNERS index 87183d7..e2241f21 100644 --- a/content/browser/OWNERS +++ b/content/browser/OWNERS
@@ -35,6 +35,12 @@ # Service sandbox mappings require security review per-file service_sandbox_type.h=file://ipc/SECURITY_OWNERS +# Utility sandbox delegate requires security review. +per-file utility_sandbox_delegate.*=set noparent +per-file utility_sandbox_delegate.*=file://ipc/SECURITY_OWNERS +per-file utility_sandbox_delegate_win.*=set noparent +per-file utility_sandbox_delegate_win.*=file://sandbox/win/OWNERS + # BackForwardCache per-file back_forward_cache_browsertest.cc=arthursonzogni@chromium.org per-file back_forward_cache_browsertest.cc=altimin@chromium.org
diff --git a/content/browser/accessibility/accessibility_tree_formatter_mac_browsertest.mm b/content/browser/accessibility/accessibility_tree_formatter_mac_browsertest.mm index ea118c9..2dfbb58 100644 --- a/content/browser/accessibility/accessibility_tree_formatter_mac_browsertest.mm +++ b/content/browser/accessibility/accessibility_tree_formatter_mac_browsertest.mm
@@ -165,7 +165,7 @@ </script>)~~", {":3;AXSelectedTextMarkerRange=*"}, R"~~(AXWebArea ++AXGroup -++++AXStaticText AXSelectedTextMarkerRange={anchor: {:3, 0, down}, focus: {:2, -1, down}} AXValue='Paragraph' +++++AXStaticText AXSelectedTextMarkerRange={anchor: {:2, -1, down}, focus: {:3, 0, down}} AXValue='Paragraph' )~~"); }
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.h b/content/browser/accessibility/browser_accessibility_cocoa.h index d1d406e..cb25390 100644 --- a/content/browser/accessibility/browser_accessibility_cocoa.h +++ b/content/browser/accessibility/browser_accessibility_cocoa.h
@@ -12,20 +12,25 @@ #include "base/strings/string16.h" #include "content/browser/accessibility/browser_accessibility.h" #include "content/browser/accessibility/browser_accessibility_manager.h" +#include "content/common/content_export.h" namespace content { // Used to store changes in edit fields, required by VoiceOver in order to // support character echo and other announcements during editing. -struct AXTextEdit { - AXTextEdit() = default; - AXTextEdit(base::string16 inserted_text, base::string16 deleted_text) - : inserted_text(inserted_text), deleted_text(deleted_text) {} +struct CONTENT_EXPORT AXTextEdit { + AXTextEdit(); + AXTextEdit(base::string16 inserted_text, + base::string16 deleted_text, + id edit_text_marker); + AXTextEdit(const AXTextEdit& other); + ~AXTextEdit(); bool IsEmpty() const { return inserted_text.empty() && deleted_text.empty(); } base::string16 inserted_text; base::string16 deleted_text; + base::scoped_nsprotocol<id> edit_text_marker; }; // Returns true if the given object is AXTextMarker object. @@ -35,7 +40,7 @@ bool IsAXTextMarkerRange(id); // Returns browser accessibility position for the given AXTextMarker. -BrowserAccessibilityPosition::AXPositionInstance AXTextMarkerToPosition(id); +CONTENT_EXPORT BrowserAccessibilityPosition::AXPositionInstance AXTextMarkerToPosition(id); // Returns browser accessibility range for the given AXTextMarkerRange. BrowserAccessibilityPosition::AXRangeType AXTextMarkerRangeToRange(id);
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm index 0104027..cf856f5 100644 --- a/content/browser/accessibility/browser_accessibility_cocoa.mm +++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -707,6 +707,20 @@ } // namespace +namespace content { + +AXTextEdit::AXTextEdit() = default; +AXTextEdit::AXTextEdit(base::string16 inserted_text, + base::string16 deleted_text, + id edit_text_marker) + : inserted_text(inserted_text), + deleted_text(deleted_text), + edit_text_marker(edit_text_marker, base::scoped_policy::RETAIN) {} +AXTextEdit::AXTextEdit(const AXTextEdit& other) = default; +AXTextEdit::~AXTextEdit() = default; + +} // namespace content + #if defined(MAC_OS_X_VERSION_10_12) && \ (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) #warning NSAccessibilityRequiredAttributeChrome \ @@ -1864,10 +1878,11 @@ if (size_t{sel_start} == newValue.length() && size_t{sel_end} == newValue.length()) { // Don't include oldValue as it would be announced -- very confusing. - return content::AXTextEdit(newValue, base::string16()); + return content::AXTextEdit(newValue, base::string16(), nil); } } - return content::AXTextEdit(insertedText, deletedText); + return content::AXTextEdit(insertedText, deletedText, + CreateTextMarker(_owner->CreatePositionAt(i))); } - (BOOL)instanceActive { @@ -2237,7 +2252,9 @@ - (id)selectedTextMarkerRange { if (![self instanceActive]) return nil; - return CreateTextMarkerRange(GetSelectedRange(*_owner)); + // Voiceover expects this range to be backwards in order to read the selected + // words correctly. + return CreateTextMarkerRange(GetSelectedRange(*_owner).AsBackwardRange()); } - (NSValue*)size {
diff --git a/content/browser/accessibility/browser_accessibility_cocoa_browsertest.mm b/content/browser/accessibility/browser_accessibility_cocoa_browsertest.mm index 4148e32..48f8d9d 100644 --- a/content/browser/accessibility/browser_accessibility_cocoa_browsertest.mm +++ b/content/browser/accessibility/browser_accessibility_cocoa_browsertest.mm
@@ -63,6 +63,43 @@ } // namespace IN_PROC_BROWSER_TEST_F(BrowserAccessibilityCocoaBrowserTest, + AXTextMarkerForTextEdit) { + EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL))); + + AccessibilityNotificationWaiter waiter(shell()->web_contents(), + ui::kAXModeComplete, + ax::mojom::Event::kLoadComplete); + GURL url(R"HTML(data:text/html, + <input />)HTML"); + + EXPECT_TRUE(NavigateToURL(shell(), url)); + waiter.WaitForNotification(); + + BrowserAccessibility* text_field = FindNode(ax::mojom::Role::kTextField); + ASSERT_NE(nullptr, text_field); + EXPECT_TRUE(content::ExecuteScript( + shell()->web_contents(), "document.querySelector('input').focus()")); + + content::SimulateKeyPress(shell()->web_contents(), + ui::DomKey::FromCharacter('B'), ui::DomCode::US_B, + ui::VKEY_B, false, false, false, false); + + base::scoped_nsobject<BrowserAccessibilityCocoa> cocoa_text_field( + [ToBrowserAccessibilityCocoa(text_field) retain]); + AccessibilityNotificationWaiter value_waiter(shell()->web_contents(), + ui::kAXModeComplete, + ax::mojom::Event::kValueChanged); + value_waiter.WaitForNotification(); + AXTextEdit text_edit = [cocoa_text_field computeTextEdit]; + EXPECT_NE(text_edit.edit_text_marker, nil); + + EXPECT_EQ( + content::AXTextMarkerToPosition(text_edit.edit_text_marker)->ToString(), + "TextPosition anchor_id=5 text_offset=1 affinity=downstream " + "annotated_text=B<>"); +} + +IN_PROC_BROWSER_TEST_F(BrowserAccessibilityCocoaBrowserTest, AXCellForColumnAndRow) { EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.h b/content/browser/accessibility/browser_accessibility_manager_mac.h index 8fb447a7..b930963 100644 --- a/content/browser/accessibility/browser_accessibility_manager_mac.h +++ b/content/browser/accessibility/browser_accessibility_manager_mac.h
@@ -60,7 +60,8 @@ NSDictionary* GetUserInfoForValueChangedNotification( const BrowserAccessibilityCocoa* native_node, const base::string16& deleted_text, - const base::string16& inserted_text) const; + const base::string16& inserted_text, + id edit_text_marker) const; void AnnounceActiveDescendant(BrowserAccessibility* node) const;
diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.mm b/content/browser/accessibility/browser_accessibility_manager_mac.mm index 0f7768b2..bcacfd0 100644 --- a/content/browser/accessibility/browser_accessibility_manager_mac.mm +++ b/content/browser/accessibility/browser_accessibility_manager_mac.mm
@@ -94,6 +94,8 @@ NSString* const NSAccessibilityTextChangeElement = @"AXTextChangeElement"; NSString* const NSAccessibilityTextEditType = @"AXTextEditType"; NSString* const NSAccessibilityTextChangeValue = @"AXTextChangeValue"; +NSString* const NSAccessibilityChangeValueStartMarker = + @"AXTextChangeValueStartMarker"; NSString* const NSAccessibilityTextChangeValueLength = @"AXTextChangeValueLength"; NSString* const NSAccessibilityTextChangeValues = @"AXTextChangeValues"; @@ -308,16 +310,18 @@ if (base::mac::IsAtLeastOS10_11() && !text_edits_.empty()) { base::string16 deleted_text; base::string16 inserted_text; - int32_t id = node->GetId(); - const auto iterator = text_edits_.find(id); + int32_t node_id = node->GetId(); + const auto iterator = text_edits_.find(node_id); + id edit_text_marker = nil; if (iterator != text_edits_.end()) { AXTextEdit text_edit = iterator->second; deleted_text = text_edit.deleted_text; inserted_text = text_edit.inserted_text; + edit_text_marker = text_edit.edit_text_marker; } NSDictionary* user_info = GetUserInfoForValueChangedNotification( - native_node, deleted_text, inserted_text); + native_node, deleted_text, inserted_text, edit_text_marker); BrowserAccessibility* root = GetRoot(); if (!root) @@ -540,29 +544,42 @@ BrowserAccessibilityManagerMac::GetUserInfoForValueChangedNotification( const BrowserAccessibilityCocoa* native_node, const base::string16& deleted_text, - const base::string16& inserted_text) const { + const base::string16& inserted_text, + id edit_text_marker) const { DCHECK(native_node); if (deleted_text.empty() && inserted_text.empty()) return nil; NSMutableArray* changes = [[[NSMutableArray alloc] init] autorelease]; if (!deleted_text.empty()) { - [changes addObject:@{ - NSAccessibilityTextEditType : @(AXTextEditTypeDelete), - NSAccessibilityTextChangeValueLength : @(deleted_text.length()), - NSAccessibilityTextChangeValue : base::SysUTF16ToNSString(deleted_text) - }]; + NSMutableDictionary* change = + [NSMutableDictionary dictionaryWithDictionary:@{ + NSAccessibilityTextEditType : @(AXTextEditTypeDelete), + NSAccessibilityTextChangeValueLength : @(deleted_text.length()), + NSAccessibilityTextChangeValue : + base::SysUTF16ToNSString(deleted_text) + }]; + if (edit_text_marker) { + change[NSAccessibilityChangeValueStartMarker] = edit_text_marker; + } + [changes addObject:change]; } if (!inserted_text.empty()) { // TODO(nektar): Figure out if this is a paste, insertion or typing. // Changes to Blink would be required. A heuristic is currently used. auto edit_type = inserted_text.length() > 1 ? @(AXTextEditTypeInsert) : @(AXTextEditTypeTyping); - [changes addObject:@{ - NSAccessibilityTextEditType : edit_type, - NSAccessibilityTextChangeValueLength : @(inserted_text.length()), - NSAccessibilityTextChangeValue : base::SysUTF16ToNSString(inserted_text) - }]; + NSMutableDictionary* change = + [NSMutableDictionary dictionaryWithDictionary:@{ + NSAccessibilityTextEditType : edit_type, + NSAccessibilityTextChangeValueLength : @(inserted_text.length()), + NSAccessibilityTextChangeValue : + base::SysUTF16ToNSString(inserted_text) + }]; + if (edit_text_marker) { + change[NSAccessibilityChangeValueStartMarker] = edit_text_marker; + } + [changes addObject:change]; } return @{
diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc index 7d995dfef..2aa9ab5 100644 --- a/content/browser/frame_host/navigation_controller_impl_unittest.cc +++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -31,6 +31,7 @@ #include "content/browser/web_contents/web_contents_impl.h" #include "content/browser/webui/content_web_ui_controller_factory.h" #include "content/browser/webui/web_ui_controller_factory_registry.h" +#include "content/common/content_navigation_policy.h" #include "content/common/frame_messages.h" #include "content/common/view_messages.h" #include "content/public/browser/render_view_host.h" @@ -3271,10 +3272,17 @@ NavigateAndCommit(url1); NavigateAndCommit(url2); - // First two entries should have the same SiteInstance. SiteInstance* instance1 = controller.GetEntryAtIndex(0)->site_instance(); SiteInstance* instance2 = controller.GetEntryAtIndex(1)->site_instance(); - EXPECT_EQ(instance1, instance2); + if (CanSameSiteMainFrameNavigationsChangeSiteInstances()) { + // If ProactivelySwapBrowsingInstance is enabled for same-site navigations, + // the same-site navigation from |url1| to |url2| should use different + // SiteInstances. + EXPECT_NE(instance1, instance2); + } else { + // Otherwise, the first two entries should have the same SiteInstance. + EXPECT_EQ(instance1, instance2); + } std::unique_ptr<TestWebContents> other_contents( static_cast<TestWebContents*>(CreateTestWebContents().release())); @@ -3603,10 +3611,17 @@ NavigateAndCommit(url1); NavigateAndCommit(url2); - // First two entries should have the same SiteInstance. SiteInstance* instance1 = controller.GetEntryAtIndex(0)->site_instance(); SiteInstance* instance2 = controller.GetEntryAtIndex(1)->site_instance(); - EXPECT_EQ(instance1, instance2); + if (CanSameSiteMainFrameNavigationsChangeSiteInstances()) { + // If ProactivelySwapBrowsingInstance is enabled for same-site navigations, + // the same-site navigation from |url1| to |url2| should use different + // SiteInstances. + EXPECT_NE(instance1, instance2); + } else { + // Otherwise, the first two entries should have the same SiteInstance. + EXPECT_EQ(instance1, instance2); + } std::unique_ptr<TestWebContents> other_contents( static_cast<TestWebContents*>(CreateTestWebContents().release()));
diff --git a/content/browser/frame_host/navigator_unittest.cc b/content/browser/frame_host/navigator_unittest.cc index f21aa07..8410fd1 100644 --- a/content/browser/frame_host/navigator_unittest.cc +++ b/content/browser/frame_host/navigator_unittest.cc
@@ -147,12 +147,24 @@ EXPECT_FALSE(request->common_params().has_user_gesture); EXPECT_EQ(kUrl2, request->common_params().url); EXPECT_FALSE(request->browser_initiated()); - EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); + + if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) { + // If same-site ProactivelySwapBrowsingInstance or main-frame RenderDocument + // is enabled, the RFH should change so we should have a speculative RFH. + EXPECT_TRUE(GetSpeculativeRenderFrameHost(node)); + EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)->is_loading()); + } else { + EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); + } EXPECT_FALSE(main_test_rfh()->is_loading()); // Have the current RenderFrameHost commit the navigation navigation->ReadyToCommit(); - EXPECT_TRUE(main_test_rfh()->is_loading()); + if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) { + EXPECT_TRUE(GetSpeculativeRenderFrameHost(node)->is_loading()); + } else { + EXPECT_TRUE(main_test_rfh()->is_loading()); + } EXPECT_FALSE(node->navigation_request()); // Commit the navigation. @@ -1190,6 +1202,14 @@ contents()->NavigateAndCommit(kUrl1); FrameTreeNode* node = main_test_rfh()->frame_tree_node(); + // The test below only makes sense if the same-site navigation below will not + // create a speculative RFH, so we need to ensure that we won't trigger a + // same-site cross-RFH navigation. + // Note: this will not disable RenderDocument. + // TODO(crbug.com/936696): Skip this test when main-frame RenderDocument is + // enabled. + DisableProactiveBrowsingInstanceSwapFor(main_test_rfh()); + // Navigate same-site. auto navigation = NavigationSimulator::CreateBrowserInitiated(kUrl2, contents());
diff --git a/content/browser/frame_host/render_frame_host_impl_unittest.cc b/content/browser/frame_host/render_frame_host_impl_unittest.cc index 1740444..45aa0d70 100644 --- a/content/browser/frame_host/render_frame_host_impl_unittest.cc +++ b/content/browser/frame_host/render_frame_host_impl_unittest.cc
@@ -5,6 +5,7 @@ #include <memory> #include "content/browser/frame_host/render_frame_host_impl.h" +#include "content/public/test/test_utils.h" #include "content/test/navigation_simulator_impl.h" #include "content/test/test_render_view_host.h" #include "content/test/test_web_contents.h" @@ -35,7 +36,13 @@ simulator->Commit(); } RenderFrameHost* initial_rfh = main_rfh(); - + // This test is for a bug that only happens when there is no RFH swap on + // same-site navigations, so we should disable same-site proactive + // BrowsingInstance for |initial_rfh| before continiung. + // Note: this will not disable RenderDocument. + // TODO(crbug.com/936696): Skip this test when main-frame RenderDocument is + // enabled. + DisableProactiveBrowsingInstanceSwapFor(initial_rfh); // Verify expected main world origin in a steady state - after a commit it // should be the same as the last committed origin. EXPECT_EQ(url::Origin::Create(initial_url),
diff --git a/content/browser/frame_host/render_frame_host_manager_browsertest.cc b/content/browser/frame_host/render_frame_host_manager_browsertest.cc index badd38f..57964d8 100644 --- a/content/browser/frame_host/render_frame_host_manager_browsertest.cc +++ b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
@@ -6045,6 +6045,78 @@ site_instance_1->GetProcess()); } +// Tests that navigations that started but haven't committed yet will be +// overridden by navigations started later if both navigations created +// speculative RFHs. +IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesSameSiteTest, + MultipleNavigationsStarted) { + ASSERT_TRUE(embedded_test_server()->Start()); + GURL a1_url(embedded_test_server()->GetURL("a.com", "/title1.html")); + GURL a2_url(embedded_test_server()->GetURL("a.com", "/title2.html")); + GURL b1_url(embedded_test_server()->GetURL("b.com", "/title1.html")); + GURL b2_url(embedded_test_server()->GetURL("b.com", "/title2.html")); + WebContentsImpl* web_contents = + static_cast<WebContentsImpl*>(shell()->web_contents()); + + // 1) Navigate to A1. + EXPECT_TRUE(NavigateToURL(shell(), a1_url)); + auto* a1_rfh = web_contents->GetMainFrame(); + FrameTreeNode* node = a1_rfh->frame_tree_node(); + scoped_refptr<SiteInstanceImpl> a1_site_instance = + static_cast<SiteInstanceImpl*>(a1_rfh->GetSiteInstance()); + + // 2) Start same-site navigation to A2 without committing. + TestNavigationManager navigation_a2(shell()->web_contents(), a2_url); + shell()->LoadURL(a2_url); + EXPECT_TRUE(navigation_a2.WaitForRequestStart()); + // Verify that we're now navigating to |a2_url|. + EXPECT_EQ(node->navigation_request()->GetURL(), a2_url); + // We should have a speculative RFH for this navigation. + RenderFrameHostImpl* a2_speculative_rfh = + node->render_manager()->speculative_frame_host(); + EXPECT_TRUE(a2_speculative_rfh); + EXPECT_NE(a1_rfh, a2_speculative_rfh); + // The speculative RFH should use a different BrowsingInstance than the + // current RFH. + scoped_refptr<SiteInstanceImpl> a2_site_instance = + static_cast<SiteInstanceImpl*>(a2_speculative_rfh->GetSiteInstance()); + EXPECT_FALSE(a1_site_instance->IsRelatedSiteInstance(a2_site_instance.get())); + + // 3) Start cross-site navigation to B1 without committing. + TestNavigationManager navigation_b1(shell()->web_contents(), b1_url); + shell()->LoadURL(b1_url); + EXPECT_TRUE(navigation_b1.WaitForRequestStart()); + // Verify that we're now navigating to |b1_url|. + EXPECT_EQ(node->navigation_request()->GetURL(), b1_url); + // We should have a speculative RFH for this navigation. + RenderFrameHostImpl* b1_speculative_rfh = + node->render_manager()->speculative_frame_host(); + EXPECT_TRUE(b1_speculative_rfh); + EXPECT_NE(a1_rfh, b1_speculative_rfh); + // The speculative RFH should use a different BrowsingInstance than the + // current RFH. + scoped_refptr<SiteInstanceImpl> b1_site_instance = + static_cast<SiteInstanceImpl*>(b1_speculative_rfh->GetSiteInstance()); + EXPECT_FALSE(a1_site_instance->IsRelatedSiteInstance(b1_site_instance.get())); + + // 4) Start same-site navigation to B2 without committing. + TestNavigationManager navigation_b2(shell()->web_contents(), b2_url); + shell()->LoadURL(b2_url); + EXPECT_TRUE(navigation_b2.WaitForRequestStart()); + // Verify that we're now navigating to |b2_url|. + EXPECT_EQ(node->navigation_request()->GetURL(), b2_url); + // We should have a speculative RFH for this navigation. + RenderFrameHostImpl* b2_speculative_rfh = + node->render_manager()->speculative_frame_host(); + EXPECT_TRUE(b2_speculative_rfh); + EXPECT_NE(a1_rfh, b2_speculative_rfh); + // The speculative RFH should use a different BrowsingInstance than the + // current RFH. + scoped_refptr<SiteInstanceImpl> b2_site_instance = + static_cast<SiteInstanceImpl*>(b2_speculative_rfh->GetSiteInstance()); + EXPECT_FALSE(a1_site_instance->IsRelatedSiteInstance(b2_site_instance.get())); +} + // Tests history same-site process reuse: // 1. Visit A1, A2, B. // 2. Go back to A2 (should use new process).
diff --git a/content/browser/media/media_web_contents_observer.cc b/content/browser/media/media_web_contents_observer.cc index 40ca34d..7522ac159 100644 --- a/content/browser/media/media_web_contents_observer.cc +++ b/content/browser/media/media_web_contents_observer.cc
@@ -220,6 +220,8 @@ IPC_MESSAGE_HANDLER( MediaPlayerDelegateHostMsg_OnPictureInPictureAvailabilityChanged, OnPictureInPictureAvailabilityChanged) + IPC_MESSAGE_HANDLER(MediaPlayerDelegateHostMsg_OnAudioOutputSinkChanged, + OnAudioOutputSinkChanged); IPC_MESSAGE_HANDLER(MediaPlayerDelegateHostMsg_OnBufferUnderflow, OnBufferUnderflow) IPC_MESSAGE_UNHANDLED(handled = false) @@ -375,6 +377,11 @@ MediaPlayerId(render_frame_host, delegate_id), available); } +void MediaWebContentsObserver::OnAudioOutputSinkChanged( + RenderFrameHost* render_frame_host, + int delegate_id, + std::string hashed_device_id) {} + void MediaWebContentsObserver::OnBufferUnderflow( RenderFrameHost* render_frame_host, int delegate_id) {
diff --git a/content/browser/media/media_web_contents_observer.h b/content/browser/media/media_web_contents_observer.h index 39cef801..6c03674c 100644 --- a/content/browser/media/media_web_contents_observer.h +++ b/content/browser/media/media_web_contents_observer.h
@@ -40,7 +40,7 @@ namespace gfx { class Size; -} // namespace size +} // namespace gfx namespace content { @@ -148,6 +148,9 @@ void OnPictureInPictureAvailabilityChanged(RenderFrameHost* render_frame_host, int delegate_id, bool available); + void OnAudioOutputSinkChanged(RenderFrameHost* render_frame_host, + int delegate_id, + std::string hashed_device_id); void OnBufferUnderflow(RenderFrameHost* render_frame_host, int delegate_id); device::mojom::WakeLock* GetAudioWakeLock();
diff --git a/content/browser/media/session/media_session_controller.cc b/content/browser/media/session/media_session_controller.cc index e59f84b..042b18fd 100644 --- a/content/browser/media/session/media_session_controller.cc +++ b/content/browser/media/session/media_session_controller.cc
@@ -4,10 +4,15 @@ #include "content/browser/media/session/media_session_controller.h" +#include "content/browser/frame_host/render_frame_host_impl.h" +#include "content/browser/media/media_devices_util.h" +#include "content/browser/media/media_web_contents_observer.h" #include "content/browser/media/session/media_session_impl.h" #include "content/common/media/media_player_delegate_messages.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/media_device_id.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" #include "media/base/media_content_type.h" @@ -87,6 +92,26 @@ id_.render_frame_host->GetRoutingID(), id_.delegate_id)); } +void MediaSessionController::OnSetAudioSinkId( + int player_id, + const std::string& raw_device_id) { + // The sink id needs to be hashed before it is suitable for use in the + // renderer process. + auto salt_and_origin = content::GetMediaDeviceSaltAndOrigin( + id_.render_frame_host->GetProcess()->GetID(), + id_.render_frame_host->GetRoutingID()); + + std::string hashed_sink_id = GetHMACForMediaDeviceID( + salt_and_origin.device_id_salt, salt_and_origin.origin, raw_device_id); + + // Grant the renderer the permission to use this audio output device. + static_cast<RenderFrameHostImpl*>(id_.render_frame_host) + ->SetAudioOutputDeviceIdForGlobalMediaControls(hashed_sink_id); + + id_.render_frame_host->Send(new MediaPlayerDelegateMsg_SetAudioSinkId( + id_.render_frame_host->GetRoutingID(), id_.delegate_id, hashed_sink_id)); +} + RenderFrameHost* MediaSessionController::render_frame_host() const { return id_.render_frame_host; }
diff --git a/content/browser/media/session/media_session_controller.h b/content/browser/media/session/media_session_controller.h index 3be6a819..424633b 100644 --- a/content/browser/media/session/media_session_controller.h +++ b/content/browser/media/session/media_session_controller.h
@@ -6,6 +6,7 @@ #define CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_CONTROLLER_H_ #include "base/compiler_specific.h" +#include "base/memory/weak_ptr.h" #include "base/optional.h" #include "base/time/time.h" #include "content/browser/media/session/media_session_player_observer.h" @@ -50,6 +51,8 @@ void OnSetVolumeMultiplier(int player_id, double volume_multiplier) override; void OnEnterPictureInPicture(int player_id) override; void OnExitPictureInPicture(int player_id) override; + void OnSetAudioSinkId(int player_id, + const std::string& raw_device_id) override; RenderFrameHost* render_frame_host() const override; base::Optional<media_session::MediaPosition> GetPosition( int player_id) const override;
diff --git a/content/browser/media/session/media_session_impl.cc b/content/browser/media/session/media_session_impl.cc index ac1f222..493f719 100644 --- a/content/browser/media/session/media_session_impl.cc +++ b/content/browser/media/session/media_session_impl.cc
@@ -26,6 +26,7 @@ #include "content/public/browser/navigation_handle.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_client.h" +#include "media/audio/audio_device_description.h" #include "media/base/media_content_type.h" #include "media/base/media_switches.h" #include "mojo/public/cpp/bindings/callback_helpers.h" @@ -1071,7 +1072,13 @@ normal_players_.begin()->first.player_id); } -void MediaSessionImpl::SetAudioSinkId(const base::Optional<std::string>& id) {} +void MediaSessionImpl::SetAudioSinkId(const base::Optional<std::string>& id) { + for (const auto& it : normal_players_) { + it.first.observer->OnSetAudioSinkId( + it.first.player_id, + id.value_or(media::AudioDeviceDescription::kDefaultDeviceId)); + } +} void MediaSessionImpl::GetMediaImageBitmap( const media_session::MediaImage& image,
diff --git a/content/browser/media/session/media_session_impl_browsertest.cc b/content/browser/media/session/media_session_impl_browsertest.cc index 255ea2f..d8bb5076 100644 --- a/content/browser/media/session/media_session_impl_browsertest.cc +++ b/content/browser/media/session/media_session_impl_browsertest.cc
@@ -40,10 +40,10 @@ using media_session::mojom::MediaPlaybackState; using media_session::mojom::MediaSessionInfo; +using ::testing::_; using ::testing::Eq; using ::testing::Expectation; using ::testing::NiceMock; -using ::testing::_; namespace { @@ -56,6 +56,8 @@ constexpr gfx::Size kDefaultFaviconSize = gfx::Size(16, 16); +const std::string kExampleSinkId = "example_device_id"; + class MockAudioFocusDelegate : public content::AudioFocusDelegate { public: MockAudioFocusDelegate(content::MediaSessionImpl* media_session, @@ -213,6 +215,10 @@ media_session_->Seek(base::TimeDelta::FromSeconds(-1)); } + void UISetAudioSink(const std::string& sink_id) { + media_session_->SetAudioSinkId(sink_id); + } + void SystemStartDucking() { media_session_->StartDucking(); } void SystemStopDucking() { media_session_->StopDucking(); } @@ -2592,6 +2598,17 @@ } } +IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, + SinkIdChangeNotifiesObservers) { + auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); + + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + + UISetAudioSink(kExampleSinkId); + EXPECT_EQ(player_observer->received_set_audio_sink_id_calls(), 1); + EXPECT_EQ(player_observer->GetAudioSinkId(0), kExampleSinkId); +} + class MediaSessionFaviconBrowserTest : public ContentBrowserTest { protected: MediaSessionFaviconBrowserTest() = default;
diff --git a/content/browser/media/session/media_session_impl_service_routing_unittest.cc b/content/browser/media/session/media_session_impl_service_routing_unittest.cc index 9e398cab..5ab5476 100644 --- a/content/browser/media/session/media_session_impl_service_routing_unittest.cc +++ b/content/browser/media/session/media_session_impl_service_routing_unittest.cc
@@ -53,6 +53,8 @@ void(int player_id, double volume_multiplier)); MOCK_METHOD1(OnEnterPictureInPicture, void(int player_id)); MOCK_METHOD1(OnExitPictureInPicture, void(int player_id)); + MOCK_METHOD2(OnSetAudioSinkId, + void(int player_id, const std::string& raw_device_id)); base::Optional<media_session::MediaPosition> GetPosition( int player_id) const override {
diff --git a/content/browser/media/session/media_session_impl_uma_unittest.cc b/content/browser/media/session/media_session_impl_uma_unittest.cc index a46977e..099c5889 100644 --- a/content/browser/media/session/media_session_impl_uma_unittest.cc +++ b/content/browser/media/session/media_session_impl_uma_unittest.cc
@@ -41,6 +41,8 @@ } void OnEnterPictureInPicture(int player_id) override {} void OnExitPictureInPicture(int player_id) override {} + void OnSetAudioSinkId(int player_id, + const std::string& raw_device_id) override {} base::Optional<media_session::MediaPosition> GetPosition( int player_id) const override {
diff --git a/content/browser/media/session/media_session_player_observer.h b/content/browser/media/session/media_session_player_observer.h index 2c65cda..dbd1e5fb 100644 --- a/content/browser/media/session/media_session_player_observer.h +++ b/content/browser/media/session/media_session_player_observer.h
@@ -44,6 +44,11 @@ // The given |player_id| has been requested to exit picture-in-picture. virtual void OnExitPictureInPicture(int player_id) = 0; + // The given |player_id| has been requested to route audio output to the + // specified audio device. + virtual void OnSetAudioSinkId(int player_id, + const std::string& raw_device_id) = 0; + // Returns the position for |player_id|. virtual base::Optional<media_session::MediaPosition> GetPosition( int player_id) const = 0;
diff --git a/content/browser/media/session/media_session_service_impl_browsertest.cc b/content/browser/media/session/media_session_service_impl_browsertest.cc index 380ea98..2b2a30b5 100644 --- a/content/browser/media/session/media_session_service_impl_browsertest.cc +++ b/content/browser/media/session/media_session_service_impl_browsertest.cc
@@ -54,6 +54,8 @@ } void OnEnterPictureInPicture(int player_id) override {} void OnExitPictureInPicture(int player_id) override {} + void OnSetAudioSinkId(int player_id, + const std::string& raw_device_id) override {} base::Optional<media_session::MediaPosition> GetPosition( int player_id) const override {
diff --git a/content/browser/media/session/mock_media_session_player_observer.cc b/content/browser/media/session/mock_media_session_player_observer.cc index 48b10d0..1ad3845d 100644 --- a/content/browser/media/session/mock_media_session_player_observer.cc +++ b/content/browser/media/session/mock_media_session_player_observer.cc
@@ -72,6 +72,16 @@ players_[player_id].is_in_picture_in_picture_ = false; } +void MockMediaSessionPlayerObserver::OnSetAudioSinkId( + int player_id, + const std::string& raw_device_id) { + EXPECT_GE(player_id, 0); + EXPECT_EQ(players_.size(), 1u); + + ++received_set_audio_sink_id_calls_; + players_[player_id].audio_sink_id_ = raw_device_id; +} + base::Optional<media_session::MediaPosition> MockMediaSessionPlayerObserver::GetPosition(int player_id) const { EXPECT_GE(player_id, 0); @@ -105,6 +115,12 @@ return players_[player_id].volume_multiplier_; } +const std::string& MockMediaSessionPlayerObserver::GetAudioSinkId( + size_t player_id) { + EXPECT_GT(players_.size(), player_id); + return players_[player_id].audio_sink_id_; +} + void MockMediaSessionPlayerObserver::SetPlaying(size_t player_id, bool playing) { EXPECT_GT(players_.size(), player_id); @@ -144,15 +160,23 @@ return received_exit_picture_in_picture_calls_; } +int MockMediaSessionPlayerObserver::received_set_audio_sink_id_calls() const { + return received_set_audio_sink_id_calls_; +} + bool MockMediaSessionPlayerObserver::HasVideo(int player_id) const { EXPECT_GE(player_id, 0); EXPECT_GT(players_.size(), static_cast<size_t>(player_id)); return false; } -MockMediaSessionPlayerObserver::MockPlayer::MockPlayer(bool is_playing, - double volume_multiplier) - : is_playing_(is_playing), volume_multiplier_(volume_multiplier) {} +MockMediaSessionPlayerObserver::MockPlayer::MockPlayer( + bool is_playing, + double volume_multiplier, + const std::string& audio_sink_id) + : is_playing_(is_playing), + volume_multiplier_(volume_multiplier), + audio_sink_id_(audio_sink_id) {} MockMediaSessionPlayerObserver::MockPlayer::~MockPlayer() = default;
diff --git a/content/browser/media/session/mock_media_session_player_observer.h b/content/browser/media/session/mock_media_session_player_observer.h index 28e9723..e50db8b4 100644 --- a/content/browser/media/session/mock_media_session_player_observer.h +++ b/content/browser/media/session/mock_media_session_player_observer.h
@@ -30,6 +30,8 @@ void OnSetVolumeMultiplier(int player_id, double volume_multiplier) override; void OnEnterPictureInPicture(int player_id) override; void OnExitPictureInPicture(int player_id) override; + void OnSetAudioSinkId(int player_id, + const std::string& raw_device_id) override; base::Optional<media_session::MediaPosition> GetPosition( int player_id) const override; bool IsPictureInPictureAvailable(int player_id) const override; @@ -46,6 +48,9 @@ // Returns the volume multiplier of |player_id|. double GetVolumeMultiplier(size_t player_id); + // Returns the sink id being used for the audio output of |player_id| + const std::string& GetAudioSinkId(size_t player_id); + // Simulate a play state change for |player_id|. void SetPlaying(size_t player_id, bool playing); @@ -58,12 +63,15 @@ int received_seek_backward_calls() const; int received_enter_picture_in_picture_calls() const; int received_exit_picture_in_picture_calls() const; + int received_set_audio_sink_id_calls() const; private: // Internal representation of the players to keep track of their statuses. struct MockPlayer { public: - MockPlayer(bool is_playing = true, double volume_multiplier = 1.0f); + explicit MockPlayer(bool is_playing = true, + double volume_multiplier = 1.0f, + const std::string& audio_sink_id = ""); ~MockPlayer(); MockPlayer(const MockPlayer&); @@ -71,6 +79,7 @@ double volume_multiplier_; base::Optional<media_session::MediaPosition> position_; bool is_in_picture_in_picture_; + std::string audio_sink_id_; }; // Basic representation of the players. The position in the vector is the @@ -85,6 +94,7 @@ int received_seek_backward_calls_ = 0; int received_enter_picture_in_picture_calls_ = 0; int received_exit_picture_in_picture_calls_ = 0; + int received_set_audio_sink_id_calls_ = 0; }; } // namespace content
diff --git a/content/browser/media/session/pepper_player_delegate.cc b/content/browser/media/session/pepper_player_delegate.cc index 6512ecbc..2e5648c8 100644 --- a/content/browser/media/session/pepper_player_delegate.cc +++ b/content/browser/media/session/pepper_player_delegate.cc
@@ -72,6 +72,12 @@ // Pepper player cannot exit picture-in-picture. Do nothing. } +void PepperPlayerDelegate::OnSetAudioSinkId(int player_id, + const std::string& raw_device_id) { + // Pepper player cannot change audio sinks. Do nothing. + NOTREACHED(); +} + base::Optional<media_session::MediaPosition> PepperPlayerDelegate::GetPosition( int player_id) const { // Pepper does not support position data.
diff --git a/content/browser/media/session/pepper_player_delegate.h b/content/browser/media/session/pepper_player_delegate.h index 33a9b2b..9b24467d 100644 --- a/content/browser/media/session/pepper_player_delegate.h +++ b/content/browser/media/session/pepper_player_delegate.h
@@ -32,6 +32,8 @@ double volume_multiplier) override; void OnEnterPictureInPicture(int player_id) override; void OnExitPictureInPicture(int player_id) override; + void OnSetAudioSinkId(int player_id, + const std::string& raw_device_id) override; base::Optional<media_session::MediaPosition> GetPosition( int player_id) const override; bool IsPictureInPictureAvailable(int player_id) const override;
diff --git a/content/browser/utility_process_host.cc b/content/browser/utility_process_host.cc index 22d001d..088622a 100644 --- a/content/browser/utility_process_host.cc +++ b/content/browser/utility_process_host.cc
@@ -18,6 +18,7 @@ #include "components/network_session_configurator/common/network_switches.h" #include "content/browser/browser_child_process_host_impl.h" #include "content/browser/renderer_host/render_process_host_impl.h" +#include "content/browser/utility_sandbox_delegate.h" #include "content/browser/v8_snapshot_files.h" #include "content/common/child_process_host_impl.h" #include "content/common/in_process_child_thread_params.h" @@ -29,10 +30,8 @@ #include "content/public/common/content_switches.h" #include "content/public/common/process_type.h" #include "content/public/common/sandboxed_process_launcher_delegate.h" -#include "content/public/common/zygote/zygote_buildflags.h" #include "media/base/media_switches.h" #include "media/webrtc/webrtc_switches.h" -#include "sandbox/policy/features.h" #include "sandbox/policy/sandbox_type.h" #include "sandbox/policy/switches.h" #include "services/network/public/cpp/network_switches.h" @@ -45,229 +44,8 @@ #include "components/os_crypt/os_crypt_switches.h" #endif -#if defined(OS_WIN) -#include "sandbox/win/src/sandbox_policy.h" -#include "sandbox/win/src/sandbox_types.h" -#include "services/audio/audio_sandbox_win.h" -#include "services/network/network_sandbox_win.h" -#endif - -#if BUILDFLAG(USE_ZYGOTE_HANDLE) -#include "content/common/zygote/zygote_handle_impl_linux.h" -#endif - namespace content { -// NOTE: changes to this class need to be reviewed by the security team. -class UtilitySandboxedProcessLauncherDelegate - : public SandboxedProcessLauncherDelegate { - public: - UtilitySandboxedProcessLauncherDelegate( - sandbox::policy::SandboxType sandbox_type, - const base::EnvironmentMap& env, - const base::CommandLine& cmd_line) - : -#if defined(OS_POSIX) - env_(env), -#endif - sandbox_type_(sandbox_type), - cmd_line_(cmd_line) { -#if DCHECK_IS_ON() - bool supported_sandbox_type = - sandbox_type_ == sandbox::policy::SandboxType::kNoSandbox || -#if defined(OS_WIN) - sandbox_type_ == - sandbox::policy::SandboxType::kNoSandboxAndElevatedPrivileges || - sandbox_type_ == sandbox::policy::SandboxType::kXrCompositing || - sandbox_type_ == sandbox::policy::SandboxType::kProxyResolver || - sandbox_type_ == sandbox::policy::SandboxType::kPdfConversion || - sandbox_type_ == sandbox::policy::SandboxType::kIconReader || -#endif - sandbox_type_ == sandbox::policy::SandboxType::kUtility || - sandbox_type_ == sandbox::policy::SandboxType::kNetwork || - sandbox_type_ == sandbox::policy::SandboxType::kCdm || - sandbox_type_ == sandbox::policy::SandboxType::kPrintCompositor || - sandbox_type_ == sandbox::policy::SandboxType::kPpapi || - sandbox_type_ == sandbox::policy::SandboxType::kVideoCapture || -#if defined(OS_CHROMEOS) - sandbox_type_ == sandbox::policy::SandboxType::kIme || - sandbox_type_ == sandbox::policy::SandboxType::kTts || -#endif // OS_CHROMEOS - sandbox_type_ == sandbox::policy::SandboxType::kAudio || -#if !defined(OS_MAC) - sandbox_type_ == sandbox::policy::SandboxType::kSharingService || -#endif - sandbox_type_ == sandbox::policy::SandboxType::kSpeechRecognition; - DCHECK(supported_sandbox_type); -#endif // DCHECK_IS_ON() - } - - ~UtilitySandboxedProcessLauncherDelegate() override = default; - -#if defined(OS_WIN) - bool GetAppContainerId(std::string* appcontainer_id) override { - if (sandbox_type_ == sandbox::policy::SandboxType::kXrCompositing && - base::FeatureList::IsEnabled(sandbox::policy::features::kXRSandbox)) { - *appcontainer_id = base::WideToUTF8(cmd_line_.GetProgram().value()); - return true; - } - return false; - } - - bool DisableDefaultPolicy() override { - switch (sandbox_type_) { - case sandbox::policy::SandboxType::kAudio: - // Default policy is disabled for audio process to allow audio drivers - // to read device properties (https://crbug.com/883326). - return true; - case sandbox::policy::SandboxType::kNetwork: - // Default policy is disabled for network process to allow incremental - // sandbox mitigations to be applied via experiments. - return true; - case sandbox::policy::SandboxType::kXrCompositing: - return base::FeatureList::IsEnabled( - sandbox::policy::features::kXRSandbox); - default: - return false; - } - } - - bool ShouldLaunchElevated() override { - return sandbox_type_ == - sandbox::policy::SandboxType::kNoSandboxAndElevatedPrivileges; - } - - bool PreSpawnTarget(sandbox::TargetPolicy* policy) override { - if (sandbox_type_ == sandbox::policy::SandboxType::kNetwork) - return network::NetworkPreSpawnTarget(policy, cmd_line_); - - if (sandbox_type_ == sandbox::policy::SandboxType::kAudio) - return audio::AudioPreSpawnTarget(policy); - - if (sandbox_type_ == sandbox::policy::SandboxType::kProxyResolver) { - sandbox::MitigationFlags flags = policy->GetDelayedProcessMitigations(); - flags |= sandbox::MITIGATION_DYNAMIC_CODE_DISABLE; - if (sandbox::SBOX_ALL_OK != policy->SetDelayedProcessMitigations(flags)) - return false; - return true; - } - - if (sandbox_type_ == sandbox::policy::SandboxType::kSpeechRecognition) { - policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW); - policy->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW); - policy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS, - sandbox::USER_LIMITED); - } - - if (sandbox_type_ == sandbox::policy::SandboxType::kIconReader) { - policy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS, - sandbox::USER_LOCKDOWN); - policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_UNTRUSTED); - policy->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW); - policy->SetLockdownDefaultDacl(); - policy->SetAlternateDesktop(true); - - sandbox::MitigationFlags flags = policy->GetDelayedProcessMitigations(); - flags |= sandbox::MITIGATION_DYNAMIC_CODE_DISABLE; - if (sandbox::SBOX_ALL_OK != policy->SetDelayedProcessMitigations(flags)) - return false; - - // Allow file read. These should match IconLoader::GroupForFilepath(). - policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, - sandbox::TargetPolicy::FILES_ALLOW_READONLY, - L"\\??\\*.exe"); - policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, - sandbox::TargetPolicy::FILES_ALLOW_READONLY, - L"\\??\\*.dll"); - policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, - sandbox::TargetPolicy::FILES_ALLOW_READONLY, - L"\\??\\*.ico"); - } - - if (sandbox_type_ == sandbox::policy::SandboxType::kXrCompositing && - base::FeatureList::IsEnabled(sandbox::policy::features::kXRSandbox)) { - // There were issues with some mitigations, causing an inability - // to load OpenVR and Oculus APIs. - // TODO(https://crbug.com/881919): Try to harden the XR Compositor - // sandbox to use mitigations and restrict the token. - policy->SetProcessMitigations(0); - policy->SetDelayedProcessMitigations(0); - - std::string appcontainer_id; - if (!GetAppContainerId(&appcontainer_id)) { - return false; - } - sandbox::ResultCode result = - sandbox::policy::SandboxWin::AddAppContainerProfileToPolicy( - cmd_line_, sandbox_type_, appcontainer_id, policy); - if (result != sandbox::SBOX_ALL_OK) { - return false; - } - - // Unprotected token/job. - policy->SetTokenLevel(sandbox::USER_UNPROTECTED, - sandbox::USER_UNPROTECTED); - sandbox::policy::SandboxWin::SetJobLevel( - cmd_line_, sandbox::JOB_UNPROTECTED, 0, policy); - } - - if (sandbox_type_ == sandbox::policy::SandboxType::kSharingService) { - auto result = - sandbox::policy::SandboxWin::AddWin32kLockdownPolicy(policy, false); - if (result != sandbox::SBOX_ALL_OK) - return false; - - auto delayed_flags = policy->GetDelayedProcessMitigations(); - delayed_flags |= sandbox::MITIGATION_DYNAMIC_CODE_DISABLE; - result = policy->SetDelayedProcessMitigations(delayed_flags); - if (result != sandbox::SBOX_ALL_OK) - return false; - } - - return true; - } -#endif // OS_WIN - -#if BUILDFLAG(USE_ZYGOTE_HANDLE) - ZygoteHandle GetZygote() override { - // If the sandbox has been disabled for a given type, don't use a zygote. - if (sandbox::policy::IsUnsandboxedSandboxType(sandbox_type_)) - return nullptr; - - // Utility processes which need specialized sandboxes fork from the - // unsandboxed zygote and then apply their actual sandboxes in the forked - // process upon startup. - if (sandbox_type_ == sandbox::policy::SandboxType::kNetwork || -#if defined(OS_CHROMEOS) - sandbox_type_ == sandbox::policy::SandboxType::kIme || - sandbox_type_ == sandbox::policy::SandboxType::kTts || -#endif // OS_CHROMEOS - sandbox_type_ == sandbox::policy::SandboxType::kAudio || - sandbox_type_ == sandbox::policy::SandboxType::kSpeechRecognition) { - return GetUnsandboxedZygote(); - } - - // All other types use the pre-sandboxed zygote. - return GetGenericZygote(); - } -#endif // BUILDFLAG(USE_ZYGOTE_HANDLE) - -#if defined(OS_POSIX) - base::EnvironmentMap GetEnvironment() override { return env_; } -#endif // OS_POSIX - - sandbox::policy::SandboxType GetSandboxType() override { - return sandbox_type_; - } - - private: -#if defined(OS_POSIX) - base::EnvironmentMap env_; -#endif // OS_POSIX - sandbox::policy::SandboxType sandbox_type_; - base::CommandLine cmd_line_; -}; - UtilityMainThreadFactoryFunction g_utility_main_thread_factory = nullptr; void UtilityProcessHost::RegisterUtilityMainThreadFactory(
diff --git a/content/browser/utility_sandbox_delegate.cc b/content/browser/utility_sandbox_delegate.cc new file mode 100644 index 0000000..0d00694 --- /dev/null +++ b/content/browser/utility_sandbox_delegate.cc
@@ -0,0 +1,98 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/utility_sandbox_delegate.h" + +#include "base/check.h" +#include "build/build_config.h" +#include "content/public/common/sandboxed_process_launcher_delegate.h" +#include "content/public/common/zygote/zygote_buildflags.h" +#include "sandbox/policy/sandbox_type.h" + +#if BUILDFLAG(USE_ZYGOTE_HANDLE) +#include "content/common/zygote/zygote_handle_impl_linux.h" +#endif + +namespace content { + +UtilitySandboxedProcessLauncherDelegate:: + UtilitySandboxedProcessLauncherDelegate( + sandbox::policy::SandboxType sandbox_type, + const base::EnvironmentMap& env, + const base::CommandLine& cmd_line) + : +#if defined(OS_POSIX) + env_(env), +#endif + sandbox_type_(sandbox_type), + cmd_line_(cmd_line) { +#if DCHECK_IS_ON() + bool supported_sandbox_type = + sandbox_type_ == sandbox::policy::SandboxType::kNoSandbox || +#if defined(OS_WIN) + sandbox_type_ == + sandbox::policy::SandboxType::kNoSandboxAndElevatedPrivileges || + sandbox_type_ == sandbox::policy::SandboxType::kXrCompositing || + sandbox_type_ == sandbox::policy::SandboxType::kProxyResolver || + sandbox_type_ == sandbox::policy::SandboxType::kPdfConversion || + sandbox_type_ == sandbox::policy::SandboxType::kIconReader || +#endif + sandbox_type_ == sandbox::policy::SandboxType::kUtility || + sandbox_type_ == sandbox::policy::SandboxType::kNetwork || + sandbox_type_ == sandbox::policy::SandboxType::kCdm || + sandbox_type_ == sandbox::policy::SandboxType::kPrintCompositor || + sandbox_type_ == sandbox::policy::SandboxType::kPpapi || + sandbox_type_ == sandbox::policy::SandboxType::kVideoCapture || +#if defined(OS_CHROMEOS) + sandbox_type_ == sandbox::policy::SandboxType::kIme || + sandbox_type_ == sandbox::policy::SandboxType::kTts || +#endif // OS_CHROMEOS + sandbox_type_ == sandbox::policy::SandboxType::kAudio || +#if !defined(OS_MAC) + sandbox_type_ == sandbox::policy::SandboxType::kSharingService || +#endif + sandbox_type_ == sandbox::policy::SandboxType::kSpeechRecognition; + DCHECK(supported_sandbox_type); +#endif // DCHECK_IS_ON() +} + +UtilitySandboxedProcessLauncherDelegate:: + ~UtilitySandboxedProcessLauncherDelegate() {} + +sandbox::policy::SandboxType +UtilitySandboxedProcessLauncherDelegate::GetSandboxType() { + return sandbox_type_; +} + +#if defined(OS_POSIX) +base::EnvironmentMap UtilitySandboxedProcessLauncherDelegate::GetEnvironment() { + return env_; +} +#endif // OS_POSIX + +#if BUILDFLAG(USE_ZYGOTE_HANDLE) +ZygoteHandle UtilitySandboxedProcessLauncherDelegate::GetZygote() { + // If the sandbox has been disabled for a given type, don't use a zygote. + if (sandbox::policy::IsUnsandboxedSandboxType(sandbox_type_)) + return nullptr; + + // Utility processes which need specialized sandboxes fork from the + // unsandboxed zygote and then apply their actual sandboxes in the forked + // process upon startup. + if (sandbox_type_ == sandbox::policy::SandboxType::kNetwork || +#if defined(OS_CHROMEOS) + sandbox_type_ == sandbox::policy::SandboxType::kIme || + sandbox_type_ == sandbox::policy::SandboxType::kTts || +#endif // OS_CHROMEOS + sandbox_type_ == sandbox::policy::SandboxType::kAudio || + sandbox_type_ == sandbox::policy::SandboxType::kSpeechRecognition) { + return GetUnsandboxedZygote(); + } + + // All other types use the pre-sandboxed zygote. + return GetGenericZygote(); +} +#endif // BUILDFLAG(USE_ZYGOTE_HANDLE) + +} // namespace content
diff --git a/content/browser/utility_sandbox_delegate.h b/content/browser/utility_sandbox_delegate.h new file mode 100644 index 0000000..a9d6f76 --- /dev/null +++ b/content/browser/utility_sandbox_delegate.h
@@ -0,0 +1,59 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_UTILITY_SANDBOX_DELEGATE_H_ +#define CONTENT_BROWSER_UTILITY_SANDBOX_DELEGATE_H_ + +#include "base/command_line.h" +#include "base/environment.h" +#include "build/build_config.h" +#include "content/public/common/sandboxed_process_launcher_delegate.h" +#include "content/public/common/zygote/zygote_buildflags.h" +#include "sandbox/policy/sandbox_type.h" + +#if BUILDFLAG(USE_ZYGOTE_HANDLE) +#include "content/common/zygote/zygote_handle_impl_linux.h" +#endif // BUILDFLAG(USE_ZYGOTE_HANDLE) + +#if defined(OS_WIN) +#include "sandbox/win/src/sandbox_policy.h" +#endif // OS_WIN + +namespace content { +class UtilitySandboxedProcessLauncherDelegate + : public SandboxedProcessLauncherDelegate { + public: + UtilitySandboxedProcessLauncherDelegate( + sandbox::policy::SandboxType sandbox_type, + const base::EnvironmentMap& env, + const base::CommandLine& cmd_line); + ~UtilitySandboxedProcessLauncherDelegate() override; + + sandbox::policy::SandboxType GetSandboxType() override; + +#if defined(OS_WIN) + bool GetAppContainerId(std::string* appcontainer_id) override; + bool DisableDefaultPolicy() override; + bool ShouldLaunchElevated() override; + bool PreSpawnTarget(sandbox::TargetPolicy* policy) override; +#endif // OS_WIN + +#if BUILDFLAG(USE_ZYGOTE_HANDLE) + ZygoteHandle GetZygote() override; +#endif // BUILDFLAG(USE_ZYGOTE_HANDLE) + +#if defined(OS_POSIX) + base::EnvironmentMap GetEnvironment() override; +#endif // OS_POSIX + + private: +#if defined(OS_POSIX) + base::EnvironmentMap env_; +#endif // OS_POSIX + sandbox::policy::SandboxType sandbox_type_; + base::CommandLine cmd_line_; +}; +} // namespace content + +#endif // CONTENT_BROWSER_UTILITY_SANDBOX_DELEGATE_H_
diff --git a/content/browser/utility_sandbox_delegate_win.cc b/content/browser/utility_sandbox_delegate_win.cc new file mode 100644 index 0000000..8bffc71 --- /dev/null +++ b/content/browser/utility_sandbox_delegate_win.cc
@@ -0,0 +1,142 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/utility_sandbox_delegate.h" + +#include "base/check.h" +#include "base/feature_list.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/common/sandboxed_process_launcher_delegate.h" +#include "sandbox/policy/features.h" +#include "sandbox/policy/sandbox_type.h" +#include "sandbox/win/src/sandbox_policy.h" +#include "sandbox/win/src/sandbox_types.h" +#include "services/audio/audio_sandbox_win.h" +#include "services/network/network_sandbox_win.h" + +namespace content { +bool UtilitySandboxedProcessLauncherDelegate::GetAppContainerId( + std::string* appcontainer_id) { + if (sandbox_type_ == sandbox::policy::SandboxType::kXrCompositing && + base::FeatureList::IsEnabled(sandbox::policy::features::kXRSandbox)) { + *appcontainer_id = base::WideToUTF8(cmd_line_.GetProgram().value()); + return true; + } + return false; +} + +bool UtilitySandboxedProcessLauncherDelegate::DisableDefaultPolicy() { + switch (sandbox_type_) { + case sandbox::policy::SandboxType::kAudio: + // Default policy is disabled for audio process to allow audio drivers + // to read device properties (https://crbug.com/883326). + return true; + case sandbox::policy::SandboxType::kNetwork: + // Default policy is disabled for network process to allow incremental + // sandbox mitigations to be applied via experiments. + return true; + case sandbox::policy::SandboxType::kXrCompositing: + return base::FeatureList::IsEnabled( + sandbox::policy::features::kXRSandbox); + default: + return false; + } +} + +bool UtilitySandboxedProcessLauncherDelegate::ShouldLaunchElevated() { + return sandbox_type_ == + sandbox::policy::SandboxType::kNoSandboxAndElevatedPrivileges; +} + +bool UtilitySandboxedProcessLauncherDelegate::PreSpawnTarget( + sandbox::TargetPolicy* policy) { + if (sandbox_type_ == sandbox::policy::SandboxType::kNetwork) + return network::NetworkPreSpawnTarget(policy, cmd_line_); + + if (sandbox_type_ == sandbox::policy::SandboxType::kAudio) + return audio::AudioPreSpawnTarget(policy); + + if (sandbox_type_ == sandbox::policy::SandboxType::kProxyResolver) { + sandbox::MitigationFlags flags = policy->GetDelayedProcessMitigations(); + flags |= sandbox::MITIGATION_DYNAMIC_CODE_DISABLE; + if (sandbox::SBOX_ALL_OK != policy->SetDelayedProcessMitigations(flags)) + return false; + return true; + } + + if (sandbox_type_ == sandbox::policy::SandboxType::kSpeechRecognition) { + policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW); + policy->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW); + policy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS, + sandbox::USER_LIMITED); + } + + if (sandbox_type_ == sandbox::policy::SandboxType::kIconReader) { + policy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS, + sandbox::USER_LOCKDOWN); + policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_UNTRUSTED); + policy->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW); + policy->SetLockdownDefaultDacl(); + policy->SetAlternateDesktop(true); + + sandbox::MitigationFlags flags = policy->GetDelayedProcessMitigations(); + flags |= sandbox::MITIGATION_DYNAMIC_CODE_DISABLE; + if (sandbox::SBOX_ALL_OK != policy->SetDelayedProcessMitigations(flags)) + return false; + + // Allow file read. These should match IconLoader::GroupForFilepath(). + policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, + sandbox::TargetPolicy::FILES_ALLOW_READONLY, + L"\\??\\*.exe"); + policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, + sandbox::TargetPolicy::FILES_ALLOW_READONLY, + L"\\??\\*.dll"); + policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, + sandbox::TargetPolicy::FILES_ALLOW_READONLY, + L"\\??\\*.ico"); + } + + if (sandbox_type_ == sandbox::policy::SandboxType::kXrCompositing && + base::FeatureList::IsEnabled(sandbox::policy::features::kXRSandbox)) { + // There were issues with some mitigations, causing an inability + // to load OpenVR and Oculus APIs. + // TODO(https://crbug.com/881919): Try to harden the XR Compositor + // sandbox to use mitigations and restrict the token. + policy->SetProcessMitigations(0); + policy->SetDelayedProcessMitigations(0); + + std::string appcontainer_id; + if (!GetAppContainerId(&appcontainer_id)) { + return false; + } + sandbox::ResultCode result = + sandbox::policy::SandboxWin::AddAppContainerProfileToPolicy( + cmd_line_, sandbox_type_, appcontainer_id, policy); + if (result != sandbox::SBOX_ALL_OK) { + return false; + } + + // Unprotected token/job. + policy->SetTokenLevel(sandbox::USER_UNPROTECTED, sandbox::USER_UNPROTECTED); + sandbox::policy::SandboxWin::SetJobLevel( + cmd_line_, sandbox::JOB_UNPROTECTED, 0, policy); + } + + if (sandbox_type_ == sandbox::policy::SandboxType::kSharingService) { + auto result = + sandbox::policy::SandboxWin::AddWin32kLockdownPolicy(policy, false); + if (result != sandbox::SBOX_ALL_OK) + return false; + + auto delayed_flags = policy->GetDelayedProcessMitigations(); + delayed_flags |= sandbox::MITIGATION_DYNAMIC_CODE_DISABLE; + result = policy->SetDelayedProcessMitigations(delayed_flags); + if (result != sandbox::SBOX_ALL_OK) + return false; + } + + return true; +} + +} // namespace content
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc index b8561753..cc362411 100644 --- a/content/browser/web_contents/web_contents_impl_unittest.cc +++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -979,20 +979,36 @@ EXPECT_TRUE(contents()->CrossProcessNavigationPending()); // Suppose the original renderer navigates before the new one is ready. - NavigationSimulator::NavigateAndCommitFromDocument( - GURL("http://www.google.com/foo"), orig_rfh); + const GURL url3("http://www.google.com/foo"); + NavigationSimulator::NavigateAndCommitFromDocument(url3, orig_rfh); // Verify that the pending navigation is cancelled. - EXPECT_FALSE(orig_rfh->is_waiting_for_beforeunload_completion()); SiteInstance* instance2 = contents()->GetSiteInstance(); + if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) { + // If same-site ProactivelySwapBrowsingInstance or main-frame RenderDocument + // is enabled, the RFH should change. + EXPECT_NE(orig_rfh, main_test_rfh()); + } else { + EXPECT_EQ(orig_rfh, main_test_rfh()); + } + if (CanSameSiteMainFrameNavigationsChangeSiteInstances()) { + // When ProactivelySwapBrowsingInstance is enabled on same-site navigations, + // the SiteInstance will change. + EXPECT_NE(instance1, instance2); + } else { + EXPECT_EQ(instance1, instance2); + } + EXPECT_FALSE(main_test_rfh()->is_waiting_for_beforeunload_completion()); + EXPECT_EQ(main_test_rfh()->GetLastCommittedURL(), url3); EXPECT_FALSE(contents()->CrossProcessNavigationPending()); - EXPECT_EQ(orig_rfh, main_test_rfh()); - EXPECT_EQ(instance1, instance2); EXPECT_EQ(nullptr, contents()->GetPendingMainFrame()); } // Tests that if we go back twice (same-site then cross-site), and the same-site -// RFH commits first, the cross-site RFH's navigation is canceled. +// RFH commits first, the cross-site RFH's navigation is canceled. If the +// same-site navigation is a cross-RFH navigation, however, the same-site +// navigation will get canceled instead and we are left with the newer +// cross-site navigation. // TODO(avi,creis): Consider changing this behavior to better match the user's // intent. TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) { @@ -1032,8 +1048,20 @@ SiteInstance* instance3 = contents()->GetSiteInstance(); EXPECT_FALSE(contents()->CrossProcessNavigationPending()); - EXPECT_EQ(google_rfh, main_test_rfh()); - EXPECT_EQ(instance2, instance3); + if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) { + // If same-site ProactivelySwapBrowsingInstance or main-frame RenderDocument + // is enabled, the RFH should change. + EXPECT_NE(google_rfh, main_test_rfh()); + } else { + EXPECT_EQ(google_rfh, main_test_rfh()); + } + if (CanSameSiteMainFrameNavigationsChangeSiteInstances()) { + // When ProactivelySwapBrowsingInstance is enabled on same-site navigations, + // the SiteInstance will change. + EXPECT_NE(instance2, instance3); + } else { + EXPECT_EQ(instance2, instance3); + } EXPECT_FALSE(contents()->GetPendingMainFrame()); EXPECT_EQ(url3, entry3->GetURL()); EXPECT_EQ(instance3, @@ -1041,28 +1069,60 @@ // Go back within the site. auto back_navigation1 = - NavigationSimulator::CreateHistoryNavigation(-1, contents()); + NavigationSimulatorImpl::CreateHistoryNavigation(-1, contents()); back_navigation1->Start(); - EXPECT_FALSE(contents()->CrossProcessNavigationPending()); + + auto* first_pending_rfh = contents()->GetPendingMainFrame(); + GlobalFrameRoutingId first_pending_rfh_id; + if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) { + EXPECT_TRUE(contents()->CrossProcessNavigationPending()); + EXPECT_TRUE(first_pending_rfh); + first_pending_rfh_id = first_pending_rfh->GetGlobalFrameRoutingId(); + } else { + EXPECT_FALSE(contents()->CrossProcessNavigationPending()); + EXPECT_FALSE(first_pending_rfh); + } EXPECT_EQ(entry2, controller().GetPendingEntry()); // Before that commits, go back again. back_navigation1->ReadyToCommit(); auto back_navigation2 = - NavigationSimulator::CreateHistoryNavigation(-1, contents()); + NavigationSimulatorImpl::CreateHistoryNavigation(-1, contents()); back_navigation2->Start(); EXPECT_TRUE(contents()->CrossProcessNavigationPending()); EXPECT_TRUE(contents()->GetPendingMainFrame()); EXPECT_EQ(entry1, controller().GetPendingEntry()); + if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) { + // When ProactivelySwapBrowsingInstance or RenderDocument is enabled on + // same-site main frame navigation, the first back navigation will create a + // speculative RFH even though it's a same-site navigation, and the + // speculative RFH will be overwritten by the second back-navigation that + // will also create a speculative RFH. + EXPECT_NE(first_pending_rfh_id, + contents()->GetPendingMainFrame()->GetGlobalFrameRoutingId()); + // Calling Commit() on the first back navigation below will cause a DCHECK + // failure because we've already called DidFinishNavigaition on it, so we + // will call it on the second back navigation instead. + back_navigation2->Commit(); + } else { + // DidNavigate from the first back. This aborts the second back's + // speculative RFH. + back_navigation1->Commit(); + } - // DidNavigate from the first back. This aborts the second back's pending RFH. - back_navigation1->Commit(); - - // We should commit this page and forget about the second back. + // We have committed this navigation and forgot about the second back if + // CanSameSiteMainFrameNavigationsChangeRenderFrameHosts() is false, or the + // first back if it's true. EXPECT_FALSE(contents()->CrossProcessNavigationPending()); EXPECT_FALSE(controller().GetPendingEntry()); EXPECT_EQ(google_rfh, main_test_rfh()); - EXPECT_EQ(url2, controller().GetLastCommittedEntry()->GetURL()); + if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) { + // We committed the second back navigation and landed on the first page. + EXPECT_EQ(url1, controller().GetLastCommittedEntry()->GetURL()); + } else { + // We committed the second back navigation and landed on the second page. + EXPECT_EQ(url2, controller().GetLastCommittedEntry()->GetURL()); + } // We should not have corrupted the NTP entry. EXPECT_EQ(instance3, @@ -1113,8 +1173,21 @@ SiteInstance* instance3 = contents()->GetSiteInstance(); EXPECT_FALSE(contents()->CrossProcessNavigationPending()); - EXPECT_EQ(google_rfh, main_test_rfh()); - EXPECT_EQ(instance2, instance3); + if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) { + // If same-site ProactivelySwapBrowsingInstance or main-frame RenderDocument + // is enabled, the RFH should change. + EXPECT_NE(google_rfh, main_test_rfh()); + google_rfh = main_test_rfh(); + } else { + EXPECT_EQ(google_rfh, main_test_rfh()); + } + if (CanSameSiteMainFrameNavigationsChangeSiteInstances()) { + // When ProactivelySwapBrowsingInstance is enabled on same-site navigations, + // the SiteInstance will change. + EXPECT_NE(instance2, instance3); + } else { + EXPECT_EQ(instance2, instance3); + } EXPECT_FALSE(contents()->GetPendingMainFrame()); EXPECT_EQ(url3, entry3->GetURL()); EXPECT_EQ(instance3, @@ -1124,7 +1197,11 @@ auto back_navigation1 = NavigationSimulator::CreateHistoryNavigation(-1, contents()); back_navigation1->ReadyToCommit(); - EXPECT_FALSE(contents()->CrossProcessNavigationPending()); + if (CanSameSiteMainFrameNavigationsChangeSiteInstances()) { + EXPECT_TRUE(contents()->CrossProcessNavigationPending()); + } else { + EXPECT_FALSE(contents()->CrossProcessNavigationPending()); + } EXPECT_EQ(entry2, controller().GetPendingEntry()); // Before that commits, go back again. @@ -1200,6 +1277,16 @@ // First, make a non-user initiated same-site navigation. const GURL kSameSiteUrl("http://foo/1"); TestRenderFrameHost* orig_rfh = main_test_rfh(); + // When ProactivelySwapBrowsingInstance or RenderDocument is enabled on + // same-site main frame navigations, the same-site navigation below will + // create a speculative RFH that will be overwritten when the cross-site + // navigation starts, finishing the same-site navigation, so the scenario in + // this test cannot be tested. We should disable same-site proactive + // BrowsingInstance for |orig_rfh| before continuing. + // Note: this will not disable RenderDocument. + // TODO(crbug.com/936696): Skip this test when main-frame RenderDocument is + // enabled. + DisableProactiveBrowsingInstanceSwapFor(orig_rfh); auto same_site_navigation = NavigationSimulator::CreateRendererInitiated( kSameSiteUrl, main_test_rfh()); same_site_navigation->SetHasUserGesture(false); @@ -1365,7 +1452,13 @@ // Now, navigate to another page on the same site. const GURL url2("http://www.google.com/search?q=kittens"); NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url2); - EXPECT_EQ(orig_rfh, main_test_rfh()); + if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) { + // If ProactivelySwapBrowsingInstance is enabled on same-site navigations, + // the same-site navigation above will use a new RFH. + EXPECT_NE(orig_rfh, main_test_rfh()); + } else { + EXPECT_EQ(orig_rfh, main_test_rfh()); + } // Sanity-check: Confirm we're not starting out in fullscreen mode. EXPECT_FALSE(contents()->IsFullscreen()); @@ -1376,8 +1469,8 @@ main_test_rfh()->frame_tree_node()->UpdateUserActivationState( blink::mojom::UserActivationUpdateType::kNotifyActivation, blink::mojom::UserActivationNotificationType::kTest); - orig_rfh->EnterFullscreen(blink::mojom::FullscreenOptions::New(), - base::BindOnce(&ExpectTrue)); + main_test_rfh()->EnterFullscreen(blink::mojom::FullscreenOptions::New(), + base::BindOnce(&ExpectTrue)); EXPECT_TRUE(contents()->IsFullscreen()); EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents())); @@ -1872,16 +1965,38 @@ navigation2->Start(); EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount()); navigation2->Commit(); - EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount()); + if (CanSameSiteMainFrameNavigationsChangeSiteInstances()) { + // When ProactivelySwapBrowsingInstance turned on for same-site navigations, + // the BrowsingInstance will change on same-site navigations. + EXPECT_NE(instance, contents->GetSiteInstance()); + // Check the previous instance's count. + EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount()); + // Update the current instance. + instance = contents->GetSiteInstance(); + EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount()); + } // Navigate to a URL in a different site in the same BrowsingInstance. const GURL kUrl2("http://b.com"); - auto navigation3 = - NavigationSimulator::CreateRendererInitiated(kUrl2, main_test_rfh()); + auto navigation3 = NavigationSimulator::CreateRendererInitiated( + kUrl2, contents->GetMainFrame()); navigation3->ReadyToCommit(); - EXPECT_FALSE(contents->CrossProcessNavigationPending()); EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount()); + if (AreAllSitesIsolatedForTesting() || + CanCrossSiteNavigationsProactivelySwapBrowsingInstances()) { + EXPECT_TRUE(contents->CrossProcessNavigationPending()); + } else { + EXPECT_FALSE(contents->CrossProcessNavigationPending()); + } navigation3->Commit(); + if (CanCrossSiteNavigationsProactivelySwapBrowsingInstances()) { + // When ProactivelySwapBrowsingInstance turned on, the BrowsingInstance will + // change on cross-site navigations. + EXPECT_NE(instance, contents->GetSiteInstance()); + EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount()); + // Update the current instance. + instance = contents->GetSiteInstance(); + } EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount()); // Navigate to a URL in a different site and different BrowsingInstance, by @@ -2337,7 +2452,15 @@ NavigationSimulator::CreateRendererInitiated(kUrl2, main_test_rfh()); navigation->SetHasUserGesture(false); navigation->Commit(); - EXPECT_EQ(1u, dialog_manager.reset_count()); + if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) { + // If we changed RenderFrameHost on a renderer-initiated navigation above, + // we would trigger RenderFrameHostManager::UnloadOldFrame, similar to the + // first (user/browser-initiated) navigation, which will trigger dialog + // cancellations and increment the reset_count to 2. + EXPECT_EQ(2u, dialog_manager.reset_count()); + } else { + EXPECT_EQ(1u, dialog_manager.reset_count()); + } contents()->SetJavaScriptDialogManagerForTesting(nullptr); }
diff --git a/content/common/media/media_player_delegate_messages.h b/content/common/media/media_player_delegate_messages.h index c8091a5..fec05a9 100644 --- a/content/common/media/media_player_delegate_messages.h +++ b/content/common/media/media_player_delegate_messages.h
@@ -74,6 +74,10 @@ IPC_MESSAGE_ROUTED1(MediaPlayerDelegateMsg_ExitPictureInPicture, int /* delegate_id, distinguishes instances */) +IPC_MESSAGE_ROUTED2(MediaPlayerDelegateMsg_SetAudioSinkId, + int /* delegate_id, distinguishes instances */, + std::string /* sink_id */) + IPC_MESSAGE_ROUTED2(MediaPlayerDelegateMsg_NotifyPowerExperimentState, int /* delegate_id, distinguishes instances */, bool /* is experiment starting (true) or stopping? */) @@ -120,6 +124,10 @@ int /* delegate_id, distinguishes instances */, bool /* picture-in-picture availability */) +IPC_MESSAGE_ROUTED2(MediaPlayerDelegateHostMsg_OnAudioOutputSinkChanged, + int /* delegate_id, distinguishes instances */, + std::string /* hashed_device_id */) + IPC_MESSAGE_ROUTED1(MediaPlayerDelegateHostMsg_OnBufferUnderflow, int /* delegate_id, distinguishes instances */)
diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc index e1d57b2..cd0c0bd 100644 --- a/content/renderer/accessibility/render_accessibility_impl.cc +++ b/content/renderer/accessibility/render_accessibility_impl.cc
@@ -222,6 +222,11 @@ settings->SetAriaModalPrunesAXTree(true); #endif + if (render_frame_->IsMainFrame()) + event_schedule_mode_ = EventScheduleMode::kDeferEvents; + else + event_schedule_mode_ = EventScheduleMode::kProcessEventsImmediately; + const WebDocument& document = GetMainDocument(); if (!document.IsNull()) { ax_context_ = std::make_unique<WebAXContext>(document);
diff --git a/content/renderer/accessibility/render_accessibility_impl.h b/content/renderer/accessibility/render_accessibility_impl.h index c7d3c66..1105b591 100644 --- a/content/renderer/accessibility/render_accessibility_impl.h +++ b/content/renderer/accessibility/render_accessibility_impl.h
@@ -297,8 +297,8 @@ // (only when debugging flags are enabled, never under normal circumstances). bool has_injected_stylesheet_ = false; - // We defer events to improve performance durung the initial page load. - EventScheduleMode event_schedule_mode_ = EventScheduleMode::kDeferEvents; + // We defer events to improve performance during the initial page load. + EventScheduleMode event_schedule_mode_; // Whether we should highlight annotation results visually on the page // for debugging.
diff --git a/content/renderer/media/renderer_webmediaplayer_delegate.cc b/content/renderer/media/renderer_webmediaplayer_delegate.cc index dac5291..e4a67d4 100644 --- a/content/renderer/media/renderer_webmediaplayer_delegate.cc +++ b/content/renderer/media/renderer_webmediaplayer_delegate.cc
@@ -222,11 +222,17 @@ routing_id(), delegate_id, available)); } +void RendererWebMediaPlayerDelegate::DidAudioOutputSinkChange( + int delegate_id, + const std::string& hashed_device_id) { + Send(new MediaPlayerDelegateHostMsg_OnAudioOutputSinkChanged( + routing_id(), delegate_id, hashed_device_id)); +} + void RendererWebMediaPlayerDelegate::DidBufferUnderflow(int player_id) { Send(new MediaPlayerDelegateHostMsg_OnBufferUnderflow(routing_id(), player_id)); } - void RendererWebMediaPlayerDelegate::WasHidden() { RecordAction(base::UserMetricsAction("Media.Hidden")); @@ -268,6 +274,8 @@ OnMediaDelegateEnterPictureInPicture) IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_ExitPictureInPicture, OnMediaDelegateExitPictureInPicture) + IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_SetAudioSinkId, + OnMediaDelegateSetAudioSink) IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_NotifyPowerExperimentState, OnMediaDelegatePowerExperimentState) IPC_MESSAGE_UNHANDLED(return false) @@ -397,6 +405,14 @@ observer->OnExitPictureInPicture(); } +void RendererWebMediaPlayerDelegate::OnMediaDelegateSetAudioSink( + int player_id, + std::string sink_id) { + Observer* observer = id_map_.Lookup(player_id); + if (observer) + observer->OnSetAudioSink(sink_id); +} + void RendererWebMediaPlayerDelegate::OnMediaDelegatePowerExperimentState( int player_id, bool state) {
diff --git a/content/renderer/media/renderer_webmediaplayer_delegate.h b/content/renderer/media/renderer_webmediaplayer_delegate.h index 251e7f2..7f4b6b71 100644 --- a/content/renderer/media/renderer_webmediaplayer_delegate.h +++ b/content/renderer/media/renderer_webmediaplayer_delegate.h
@@ -72,6 +72,8 @@ const media_session::MediaPosition& position) override; void DidPictureInPictureAvailabilityChange(int delegate_id, bool available) override; + void DidAudioOutputSinkChange(int delegate_id, + const std::string& hashed_device_id) override; void DidBufferUnderflow(int player_id) override; // content::RenderFrameObserver overrides. @@ -105,6 +107,7 @@ void OnMediaDelegateBecamePersistentVideo(int player_id, bool value); void OnMediaDelegateEnterPictureInPicture(int player_id); void OnMediaDelegateExitPictureInPicture(int player_id); + void OnMediaDelegateSetAudioSink(int player_id, std::string sink_id); void OnMediaDelegatePowerExperimentState(int player_id, bool state); // Schedules UpdateTask() to run soon.
diff --git a/content/renderer/media/renderer_webmediaplayer_delegate_browsertest.cc b/content/renderer/media/renderer_webmediaplayer_delegate_browsertest.cc index 81488cfa..74268bc 100644 --- a/content/renderer/media/renderer_webmediaplayer_delegate_browsertest.cc +++ b/content/renderer/media/renderer_webmediaplayer_delegate_browsertest.cc
@@ -52,6 +52,7 @@ MOCK_METHOD1(OnVolumeMultiplierUpdate, void(double)); MOCK_METHOD1(OnBecamePersistentVideo, void(bool)); MOCK_METHOD0(OnPictureInPictureModeEnded, void()); + MOCK_METHOD1(OnSetAudioSink, void(const std::string&)); }; class RendererWebMediaPlayerDelegateTest : public content::RenderViewTest {
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index c0200aa..5530052 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc
@@ -531,13 +531,14 @@ const InProcessChildThreadParams& params, int32_t client_id, std::unique_ptr<blink::scheduler::WebThreadScheduler> scheduler) - : ChildThreadImpl(base::DoNothing(), - Options::Builder() - .InBrowserProcess(params) - .ConnectToBrowser(true) - .IPCTaskRunner(scheduler->IPCTaskRunner()) - .ExposesInterfacesToBrowser() - .Build()), + : ChildThreadImpl( + base::DoNothing(), + Options::Builder() + .InBrowserProcess(params) + .ConnectToBrowser(true) + .IPCTaskRunner(scheduler->DeprecatedDefaultTaskRunner()) + .ExposesInterfacesToBrowser() + .Build()), main_thread_scheduler_(std::move(scheduler)), categorized_worker_pool_(new CategorizedWorkerPool()), client_id_(client_id) { @@ -561,12 +562,13 @@ RenderThreadImpl::RenderThreadImpl( base::RepeatingClosure quit_closure, std::unique_ptr<blink::scheduler::WebThreadScheduler> scheduler) - : ChildThreadImpl(std::move(quit_closure), - Options::Builder() - .ConnectToBrowser(true) - .IPCTaskRunner(scheduler->IPCTaskRunner()) - .ExposesInterfacesToBrowser() - .Build()), + : ChildThreadImpl( + std::move(quit_closure), + Options::Builder() + .ConnectToBrowser(true) + .IPCTaskRunner(scheduler->DeprecatedDefaultTaskRunner()) + .ExposesInterfacesToBrowser() + .Build()), main_thread_scheduler_(std::move(scheduler)), categorized_worker_pool_(new CategorizedWorkerPool()), client_id_(GetClientIdFromCommandLine()) { @@ -2181,8 +2183,9 @@ void RenderThreadImpl::OnRendererInterfaceReceiver( mojo::PendingAssociatedReceiver<mojom::Renderer> receiver) { DCHECK(!renderer_receiver_.is_bound()); - renderer_receiver_.Bind(std::move(receiver), - GetWebMainThreadScheduler()->IPCTaskRunner()); + renderer_receiver_.Bind( + std::move(receiver), + GetWebMainThreadScheduler()->DeprecatedDefaultTaskRunner()); } bool RenderThreadImpl::NeedsToRecordFirstActivePaint(
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc index e1c6a8b6..8505c8c 100644 --- a/content/renderer/render_view_browsertest.cc +++ b/content/renderer/render_view_browsertest.cc
@@ -215,7 +215,6 @@ // public blink API... result.name = frame->AssignedName().Utf8(); result.unique_name = test_render_frame->unique_name(); - result.frame_policy.sandbox_flags = frame->EffectiveSandboxFlagsForTesting(); // result.should_enforce_strict_mixed_content_checking is calculated in the // browser... result.origin = frame->GetSecurityOrigin();
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt index 7cc5531..e598907 100644 --- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt +++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -151,7 +151,7 @@ # Vulkan / Passthrough command decoder crbug.com/angleproject/3930 [ vulkan passthrough ] conformance/textures/misc/copy-tex-image-and-sub-image-2d.html [ Failure ] crbug.com/angleproject/2914 [ vulkan passthrough ] conformance/textures/misc/texture-copying-feedback-loops.html [ Failure ] -crbug.com/angleproject/4684 [ vulkan passthrough ] conformance/textures/misc/texture-mips.html [ Failure ] +crbug.com/angleproject/4931 [ win nvidia vulkan passthrough ] conformance/textures/misc/texture-mips.html [ Failure ] # Win / Intel / Vulkan / Passthrough command decoder crbug.com/angleproject/4922 [ win intel vulkan passthrough ] conformance/context/context-attributes-alpha-depth-stencil-antialias.html [ RetryOnFailure ] @@ -743,10 +743,6 @@ # All platforms, Vulkan backend crbug.com/1099961 [ swiftshader passthrough ] conformance/textures/misc/copy-tex-image-and-sub-image-2d.html [ Failure ] -crbug.com/1099961 [ swiftshader passthrough ] conformance/textures/misc/texture-mips.html [ Failure ] -crbug.com/1099961 [ swiftshader passthrough ] conformance/textures/misc/texture-size-cube-maps.html [ Failure ] -crbug.com/1099961 [ swiftshader passthrough ] conformance/textures/misc/texture-size.html [ Failure ] -crbug.com/1099961 [ swiftshader passthrough ] conformance/textures/misc/texture-sub-image-cube-maps.html [ Failure ] # Mac. All backends. crbug.com/1099960 [ mac swiftshader passthrough ] conformance/context/context-no-alpha-fbo-with-alpha.html [ Failure ]
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h index 82f0b9e..57778a8c 100644 --- a/extensions/browser/extension_function_histogram_value.h +++ b/extensions/browser/extension_function_histogram_value.h
@@ -1556,6 +1556,7 @@ AUTOTESTPRIVATE_STOPTHROUGHPUTTRACKERDATACOLLECTION = 1493, INPUTMETHODPRIVATE_GETAUTOCORRECTRANGE = 1494, FILEMANAGERPRIVATEINTERNAL_SHARESHEETHASTARGETS = 1495, + FILEMANAGERPRIVATEINTERNAL_INVOKESHARESHEET = 1496, // Last entry: Add new entries above, then run: // python tools/metrics/histograms/update_extension_histograms.py ENUM_BOUNDARY
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn index 415e8c2..01a6328 100644 --- a/extensions/common/BUILD.gn +++ b/extensions/common/BUILD.gn
@@ -334,6 +334,7 @@ deps = [ "//base", "//build:branding_buildflags", + "//build:lacros_buildflags", "//components/crx_file", "//components/nacl/common:buildflags", "//components/url_formatter",
diff --git a/extensions/common/api/_permission_features.json b/extensions/common/api/_permission_features.json index 83e216a2..6991d9e7 100644 --- a/extensions/common/api/_permission_features.json +++ b/extensions/common/api/_permission_features.json
@@ -498,7 +498,11 @@ "61FF4757F9420B62B19BA5C96084649339DB31F5", // http://crbug.com/731941 "6FB7E1B6C0247B687AC14772E87A117F5F5E4497", // http://crbug.com/731941 "9834387FDA1F66A1B5CA06CB442137B556F12F2A", // http://crbug.com/772346 - "A9A9FC0228ADF541F0334F22BEFB8F9C245B21D7" // http://crbug.com/839189 + "A9A9FC0228ADF541F0334F22BEFB8F9C245B21D7", // http://crbug.com/839189 + "F2BCE012B9B7E2D57902B5A4F954EB01A7E548FD", // http://crbug.com/1105137 + "D467F51D3846ED6D137F9FD403AE11CE416CD995", // http://crbug.com/1105137 + "3823525AD445E0025E449F964C20922996B0F97F", // http://crbug.com/1105137 + "827B5D482FADCE120F4694AD0FA0680E3717C6EC" // http://crbug.com/1105137 ] }], "networkingPrivate": {
diff --git a/extensions/common/features/feature.cc b/extensions/common/features/feature.cc index 36689fd..58996f9 100644 --- a/extensions/common/features/feature.cc +++ b/extensions/common/features/feature.cc
@@ -11,6 +11,7 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "build/build_config.h" +#include "build/lacros_buildflags.h" #include "extensions/common/extension.h" #include "extensions/common/manifest.h" @@ -18,7 +19,12 @@ // static Feature::Platform Feature::GetCurrentPlatform() { -#if defined(OS_CHROMEOS) +// TODO(https://crbug.com/1052397): For readability, this should become +// defined(OS_CHROMEOS) && BUILDFLAG(IS_LACROS). The second conditional should +// be defined(OS_CHROMEOS) && BUILDFLAG(IS_ASH). +#if BUILDFLAG(IS_LACROS) + return LACROS_PLATFORM; +#elif defined(OS_CHROMEOS) && !BUILDFLAG(IS_LACROS) return CHROMEOS_PLATFORM; #elif defined(OS_LINUX) return LINUX_PLATFORM;
diff --git a/extensions/common/features/feature.h b/extensions/common/features/feature.h index 2bdc3e2d..ce5d263 100644 --- a/extensions/common/features/feature.h +++ b/extensions/common/features/feature.h
@@ -46,6 +46,7 @@ enum Platform { UNSPECIFIED_PLATFORM, CHROMEOS_PLATFORM, + LACROS_PLATFORM, LINUX_PLATFORM, MACOSX_PLATFORM, WIN_PLATFORM
diff --git a/gpu/command_buffer/common/sync_token.cc b/gpu/command_buffer/common/sync_token.cc index 2ed75f2..0d6e9e6 100644 --- a/gpu/command_buffer/common/sync_token.cc +++ b/gpu/command_buffer/common/sync_token.cc
@@ -4,6 +4,8 @@ #include "gpu/command_buffer/common/sync_token.h" +#include <sstream> + namespace gpu { SyncToken::SyncToken() @@ -21,4 +23,19 @@ SyncToken::SyncToken(const SyncToken& other) = default; +std::string SyncToken::ToDebugString() const { + // At the level of the generic command buffer code, the command buffer ID is + // an arbitrary 64-bit number. For the debug output, print its upper and lower + // 32bit words separately. This ensures more readable output for IDs allocated + // by gpu/ipc code which uses these words for channel and route IDs, but it's + // still a lossless representation if the IDs don't match this convention. + uint64_t id = command_buffer_id().GetUnsafeValue(); + uint32_t channel_or_high = 0xffffffff & id; + uint32_t route_or_low = id >> 32; + std::ostringstream stream; + stream << static_cast<int>(namespace_id()) << ":" << channel_or_high << ":" + << route_or_low << ":" << release_count(); + return stream.str(); +} + } // namespace gpu
diff --git a/gpu/command_buffer/common/sync_token.h b/gpu/command_buffer/common/sync_token.h index a8aa0c6..52d696ca 100644 --- a/gpu/command_buffer/common/sync_token.h +++ b/gpu/command_buffer/common/sync_token.h
@@ -83,6 +83,8 @@ bool operator!=(const SyncToken& other) const { return !(*this == other); } + std::string ToDebugString() const; + private: bool verified_flush_; CommandBufferNamespace namespace_id_;
diff --git a/gpu/command_buffer/service/shared_image_backing_d3d.cc b/gpu/command_buffer/service/shared_image_backing_d3d.cc index 194259c..05748005 100644 --- a/gpu/command_buffer/service/shared_image_backing_d3d.cc +++ b/gpu/command_buffer/service/shared_image_backing_d3d.cc
@@ -6,6 +6,7 @@ #include "base/trace_event/memory_dump_manager.h" #include "components/viz/common/resources/resource_format_utils.h" +#include "components/viz/common/resources/resource_sizes.h" #include "gpu/command_buffer/common/shared_image_trace_utils.h" #include "gpu/command_buffer/service/shared_image_representation_d3d.h" #include "gpu/command_buffer/service/shared_image_representation_skia_gl.h" @@ -45,7 +46,7 @@ uint32_t usage, Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain, scoped_refptr<gles2::TexturePassthrough> texture, - scoped_refptr<gl::GLImageD3D> image, + scoped_refptr<gl::GLImage> image, size_t buffer_index, Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture, base::win::ScopedHandle shared_handle, @@ -66,7 +67,6 @@ d3d11_texture_(std::move(d3d11_texture)), shared_handle_(std::move(shared_handle)), dxgi_keyed_mutex_(std::move(dxgi_keyed_mutex)) { - DCHECK(d3d11_texture_); DCHECK(texture_); } @@ -171,6 +171,10 @@ return shared_handle_.Get(); } +gl::GLImage* SharedImageBackingD3D::GetGLImage() const { + return image_.get(); +} + bool SharedImageBackingD3D::PresentSwapChain() { TRACE_EVENT0("gpu", "SharedImageBackingD3D::PresentSwapChain"); if (buffer_index_ != 0) { @@ -195,7 +199,7 @@ api->glBindTextureFn(GL_TEXTURE_2D, texture_->service_id()); if (!image_->BindTexImage(GL_TEXTURE_2D)) { - DLOG(ERROR) << "GLImageD3D::BindTexImage failed"; + DLOG(ERROR) << "GLImage::BindTexImage failed"; return false; } @@ -223,4 +227,12 @@ manager, this, tracker); } +std::unique_ptr<SharedImageRepresentationOverlay> +SharedImageBackingD3D::ProduceOverlay(SharedImageManager* manager, + MemoryTypeTracker* tracker) { + TRACE_EVENT0("gpu", "SharedImageBackingD3D::ProduceOverlay"); + return std::make_unique<SharedImageRepresentationOverlayD3D>(manager, this, + tracker); +} + } // namespace gpu
diff --git a/gpu/command_buffer/service/shared_image_backing_d3d.h b/gpu/command_buffer/service/shared_image_backing_d3d.h index a808c3c..50e06454 100644 --- a/gpu/command_buffer/service/shared_image_backing_d3d.h +++ b/gpu/command_buffer/service/shared_image_backing_d3d.h
@@ -32,7 +32,8 @@ // Implementation of SharedImageBacking that holds buffer (front buffer/back // buffer of swap chain) texture (as gles2::Texture/gles2::TexturePassthrough) // and a reference to created swap chain. -class SharedImageBackingD3D : public ClearTrackingSharedImageBacking { +class GPU_GLES2_EXPORT SharedImageBackingD3D + : public ClearTrackingSharedImageBacking { public: SharedImageBackingD3D( const Mailbox& mailbox, @@ -44,7 +45,7 @@ uint32_t usage, Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain, scoped_refptr<gles2::TexturePassthrough> texture, - scoped_refptr<gl::GLImageD3D> image, + scoped_refptr<gl::GLImage> image, size_t buffer_index, Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture, base::win::ScopedHandle shared_handle, @@ -73,6 +74,7 @@ void EndAccessD3D11(); HANDLE GetSharedHandle() const; + gl::GLImage* GetGLImage() const; bool PresentSwapChain() override; @@ -81,6 +83,10 @@ ProduceGLTexturePassthrough(SharedImageManager* manager, MemoryTypeTracker* tracker) override; + std::unique_ptr<SharedImageRepresentationOverlay> ProduceOverlay( + SharedImageManager* manager, + MemoryTypeTracker* tracker) override; + std::unique_ptr<SharedImageRepresentationSkia> ProduceSkia( SharedImageManager* manager, MemoryTypeTracker* tracker, @@ -89,8 +95,10 @@ private: Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain_; scoped_refptr<gles2::TexturePassthrough> texture_; - scoped_refptr<gl::GLImageD3D> image_; + scoped_refptr<gl::GLImage> image_; const size_t buffer_index_; + + // Texture could be nullptr if an empty backing is needed for testing. Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture_; // If d3d11_texture_ has a keyed mutex, it will be stored in
diff --git a/gpu/command_buffer/service/shared_image_representation_d3d.cc b/gpu/command_buffer/service/shared_image_representation_d3d.cc index c400fff..06b3ae18 100644 --- a/gpu/command_buffer/service/shared_image_representation_d3d.cc +++ b/gpu/command_buffer/service/shared_image_representation_d3d.cc
@@ -133,4 +133,22 @@ } #endif // BUILDFLAG(USE_DAWN) +SharedImageRepresentationOverlayD3D::SharedImageRepresentationOverlayD3D( + SharedImageManager* manager, + SharedImageBacking* backing, + MemoryTypeTracker* tracker) + : SharedImageRepresentationOverlay(manager, backing, tracker) {} + +bool SharedImageRepresentationOverlayD3D::BeginReadAccess() { + // Note: only the DX11 video decoder uses this overlay and does not need to + // synchronize read access from different devices. + return true; +} + +void SharedImageRepresentationOverlayD3D::EndReadAccess() {} + +gl::GLImage* SharedImageRepresentationOverlayD3D::GetGLImage() { + return static_cast<SharedImageBackingD3D*>(backing())->GetGLImage(); +} + } // namespace gpu
diff --git a/gpu/command_buffer/service/shared_image_representation_d3d.h b/gpu/command_buffer/service/shared_image_representation_d3d.h index 72e41f1..c73d4c7f 100644 --- a/gpu/command_buffer/service/shared_image_representation_d3d.h +++ b/gpu/command_buffer/service/shared_image_representation_d3d.h
@@ -20,6 +20,8 @@ namespace gpu { +class SharedImageBackingD3D; + // Representation of a SharedImageBackingD3D as a GL TexturePassthrough. class SharedImageRepresentationGLTexturePassthroughD3D : public SharedImageRepresentationGLTexturePassthrough { @@ -65,5 +67,21 @@ }; #endif // BUILDFLAG(USE_DAWN) +// Representation of a SharedImageBackingD3D as an overlay. +class SharedImageRepresentationOverlayD3D + : public SharedImageRepresentationOverlay { + public: + SharedImageRepresentationOverlayD3D(SharedImageManager* manager, + SharedImageBacking* backing, + MemoryTypeTracker* tracker); + ~SharedImageRepresentationOverlayD3D() override = default; + + private: + bool BeginReadAccess() override; + void EndReadAccess() override; + + gl::GLImage* GetGLImage() override; +}; + } // namespace gpu #endif // GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_REPRESENTATION_D3D_H_
diff --git a/infra/config/generated/luci-notify.cfg b/infra/config/generated/luci-notify.cfg index 2042535..196a6c9 100644 --- a/infra/config/generated/luci-notify.cfg +++ b/infra/config/generated/luci-notify.cfg
@@ -102,6 +102,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -120,6 +121,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -146,6 +148,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -164,6 +167,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -208,6 +212,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -226,6 +231,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -251,6 +257,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -313,6 +320,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -338,6 +346,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -356,6 +365,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -407,6 +417,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -432,6 +443,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -463,6 +475,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -494,6 +507,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -520,6 +534,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -546,6 +561,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -741,6 +757,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -766,6 +783,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -790,6 +808,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -814,6 +833,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -839,6 +859,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -864,6 +885,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -889,6 +911,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -914,6 +937,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -939,6 +963,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -963,6 +988,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -981,6 +1007,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -1006,6 +1033,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -1030,6 +1058,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -1055,6 +1084,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -1079,6 +1109,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -1103,6 +1134,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -1127,6 +1159,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -1151,6 +1184,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -1176,6 +1210,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -1201,6 +1236,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -1225,6 +1261,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -1249,6 +1286,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -1325,6 +1363,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1343,6 +1382,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1360,6 +1400,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1378,6 +1419,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1396,6 +1438,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -1421,6 +1464,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -1446,6 +1490,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1463,6 +1508,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1480,6 +1526,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1497,6 +1544,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1514,6 +1562,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1531,6 +1580,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1548,6 +1598,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1565,6 +1616,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -1775,6 +1827,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1793,6 +1846,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1811,6 +1865,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1829,6 +1884,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1846,6 +1902,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1876,6 +1933,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1919,6 +1977,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1937,6 +1996,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1955,6 +2015,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1973,6 +2034,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -1991,6 +2053,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2008,6 +2071,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -2033,6 +2097,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2050,6 +2115,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2067,6 +2133,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2084,6 +2151,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2102,6 +2170,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2120,6 +2189,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2278,6 +2348,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2296,6 +2367,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2314,6 +2386,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2332,6 +2405,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2350,6 +2424,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2368,6 +2443,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2386,6 +2462,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2404,6 +2481,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2422,6 +2500,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2440,6 +2519,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2556,6 +2636,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -2587,6 +2668,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2605,6 +2687,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2636,6 +2719,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2654,6 +2738,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2686,6 +2771,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2704,6 +2790,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -2729,6 +2816,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -2767,6 +2855,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2785,6 +2874,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2803,6 +2893,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -2857,6 +2948,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } notifications { on_occurrence: FAILURE @@ -2882,6 +2974,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2900,6 +2993,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2918,6 +3012,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2963,6 +3058,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2981,6 +3077,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci" @@ -2999,6 +3096,7 @@ email { rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" } + template: "tree_closure_email_template" } builders { bucket: "ci"
diff --git a/infra/config/generated/luci-notify/email-templates/tree_closure_email_template.template b/infra/config/generated/luci-notify/email-templates/tree_closure_email_template.template new file mode 100644 index 0000000..b9a4941 --- /dev/null +++ b/infra/config/generated/luci-notify/email-templates/tree_closure_email_template.template
@@ -0,0 +1,35 @@ +[Chromium tree closure] Builder "{{ .Build.Builder | formatBuilderID }}" + +Builder "{{ .Build.Builder | formatBuilderID }}" failed at {{ .Build.EndTime | time }} on step(s) {{ stepNames .MatchingFailedSteps }}. + +<b>This failure is tree closing.</b> + +<table> + <tr> + <td>New status:</td> + <td><b>{{ .Build.Status }}</b></td> + </tr> + <tr> + <td>Previous status:</td> + <td>{{ .OldStatus }}</td> + </tr> + <tr> + <td>Builder:</td> + <td>{{ .Build.Builder | formatBuilderID }}</td> + </tr> + <tr> + <td>Failed steps:</td> + <td>{{ stepNames .MatchingFailedSteps }}</td> + </tr> + <tr> + <td>Created at:</td> + <td>{{ .Build.CreateTime | time }}</td> + </tr> + <tr> + <td>Finished at:</td> + <td>{{ .Build.EndTime | time }}</td> + </tr> +</table> + +Build details: {{ buildUrl . }} +<br/><br/>
diff --git a/infra/config/main.star b/infra/config/main.star index d2801f4..7788566 100755 --- a/infra/config/main.star +++ b/infra/config/main.star
@@ -29,6 +29,7 @@ 'luci-logdog.cfg', 'luci-milo.cfg', 'luci-notify.cfg', + 'luci-notify/email-templates/*.template', 'luci-scheduler.cfg', 'project.cfg', 'project.pyl',
diff --git a/infra/config/notifiers.star b/infra/config/notifiers.star index 6bd2230..44fbf7d0 100644 --- a/infra/config/notifiers.star +++ b/infra/config/notifiers.star
@@ -71,6 +71,10 @@ notify_rotation_urls = [ "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff", ], + template = luci.notifier_template( + name = 'tree_closure_email_template', + body = io.read_file('templates/tree_closure_email.template'), + ), ) tree_closure_notifier(
diff --git a/infra/config/templates/tree_closure_email.template b/infra/config/templates/tree_closure_email.template new file mode 100644 index 0000000..b9a4941 --- /dev/null +++ b/infra/config/templates/tree_closure_email.template
@@ -0,0 +1,35 @@ +[Chromium tree closure] Builder "{{ .Build.Builder | formatBuilderID }}" + +Builder "{{ .Build.Builder | formatBuilderID }}" failed at {{ .Build.EndTime | time }} on step(s) {{ stepNames .MatchingFailedSteps }}. + +<b>This failure is tree closing.</b> + +<table> + <tr> + <td>New status:</td> + <td><b>{{ .Build.Status }}</b></td> + </tr> + <tr> + <td>Previous status:</td> + <td>{{ .OldStatus }}</td> + </tr> + <tr> + <td>Builder:</td> + <td>{{ .Build.Builder | formatBuilderID }}</td> + </tr> + <tr> + <td>Failed steps:</td> + <td>{{ stepNames .MatchingFailedSteps }}</td> + </tr> + <tr> + <td>Created at:</td> + <td>{{ .Build.CreateTime | time }}</td> + </tr> + <tr> + <td>Finished at:</td> + <td>{{ .Build.EndTime | time }}</td> + </tr> +</table> + +Build details: {{ buildUrl . }} +<br/><br/>
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm index a909e3c..4f89ce7 100644 --- a/ios/chrome/browser/flags/about_flags.mm +++ b/ios/chrome/browser/flags/about_flags.mm
@@ -680,6 +680,10 @@ {"scroll-to-text-ios", flag_descriptions::kScrollToTextIOSName, flag_descriptions::kScrollToTextIOSDescription, flags_ui::kOsIos, FEATURE_VALUE_TYPE(web::features::kScrollToTextIOS)}, + {"legacy-tls-interstitial", + flag_descriptions::kIOSLegacyTLSInterstitialsName, + flag_descriptions::kIOSLegacyTLSInterstitialsDescription, flags_ui::kOsIos, + FEATURE_VALUE_TYPE(web::features::kIOSLegacyTLSInterstitial)}, }; bool SkipConditionalFeatureEntry(const flags_ui::FeatureEntry& entry) {
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc index 3b0fd2b..07af8b9 100644 --- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc +++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -312,6 +312,12 @@ "an individual promotion causes that promotion but no other promotions to " "occur."; +const char kIOSLegacyTLSInterstitialsName[] = "Show legacy TLS interstitials"; +const char kIOSLegacyTLSInterstitialsDescription[] = + "When enabled, an interstitial will be shown on main-frame navigations " + "that use legacy TLS connections, and subresources using legacy TLS " + "connections will be blocked."; + const char kIOSLookalikeUrlNavigationSuggestionsUIName[] = "Lookalike URL Navigation Suggestions UI"; const char kIOSLookalikeUrlNavigationSuggestionsUIDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h index 445fdd68..731f000 100644 --- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h +++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -268,6 +268,11 @@ extern const char kInProductHelpDemoModeName[]; extern const char kInProductHelpDemoModeDescription[]; +// Title and description for the flag to enable interstitials on legacy TLS +// connections. +extern const char kIOSLegacyTLSInterstitialsName[]; +extern const char kIOSLegacyTLSInterstitialsDescription[]; + // Title and description for the flag to enable interstitials on lookalike // URL navigations. extern const char kIOSLookalikeUrlNavigationSuggestionsUIName[];
diff --git a/ios/chrome/browser/tabs/BUILD.gn b/ios/chrome/browser/tabs/BUILD.gn index 5bfa4418..206cd8d 100644 --- a/ios/chrome/browser/tabs/BUILD.gn +++ b/ios/chrome/browser/tabs/BUILD.gn
@@ -111,6 +111,7 @@ "//ios/chrome/browser/web_state_list", "//ios/chrome/browser/web_state_list/web_usage_enabler", "//ios/components/security_interstitials", + "//ios/components/security_interstitials/legacy_tls", "//ios/components/security_interstitials/lookalikes", "//ios/public/provider/chrome/browser", "//ios/web/common:features",
diff --git a/ios/chrome/browser/tabs/tab_helper_util.mm b/ios/chrome/browser/tabs/tab_helper_util.mm index 3c2b174..0a87708 100644 --- a/ios/chrome/browser/tabs/tab_helper_util.mm +++ b/ios/chrome/browser/tabs/tab_helper_util.mm
@@ -78,6 +78,7 @@ #import "ios/chrome/browser/web/tab_id_tab_helper.h" #import "ios/chrome/browser/web/web_state_delegate_tab_helper.h" #import "ios/components/security_interstitials/ios_blocking_page_tab_helper.h" +#import "ios/components/security_interstitials/legacy_tls/legacy_tls_tab_allow_list.h" #import "ios/components/security_interstitials/lookalikes/lookalike_url_container.h" #import "ios/components/security_interstitials/lookalikes/lookalike_url_tab_allow_list.h" #import "ios/components/security_interstitials/lookalikes/lookalike_url_tab_helper.h" @@ -197,6 +198,10 @@ LookalikeUrlContainer::CreateForWebState(web_state); } + if (base::FeatureList::IsEnabled(web::features::kIOSLegacyTLSInterstitial)) { + LegacyTLSTabAllowList::CreateForWebState(web_state); + } + // TODO(crbug.com/794115): pre-rendered WebState have lots of unnecessary // tab helpers for historical reasons. For the moment, AttachTabHelpers // allows to inhibit the creation of some of them. Once PreloadController
diff --git a/ios/chrome/browser/web/BUILD.gn b/ios/chrome/browser/web/BUILD.gn index c7ca81d..c04df53 100644 --- a/ios/chrome/browser/web/BUILD.gn +++ b/ios/chrome/browser/web/BUILD.gn
@@ -262,8 +262,10 @@ "//ios/chrome/browser/ui/util", "//ios/chrome/browser/web:feature_flags", "//ios/components/security_interstitials", + "//ios/components/security_interstitials/legacy_tls", "//ios/components/security_interstitials/lookalikes", "//ios/components/webui:url_constants", + "//ios/net", "//ios/public/provider/chrome/browser", "//ios/public/provider/chrome/browser/voice", "//ios/web", @@ -335,7 +337,9 @@ "//ios/chrome/browser/web", "//ios/chrome/test/fakes", "//ios/components/security_interstitials", + "//ios/components/security_interstitials/legacy_tls", "//ios/components/security_interstitials/lookalikes", + "//ios/net", "//ios/web/common:features", "//ios/web/common:web_view_creation_util", "//ios/web/public/test", @@ -424,6 +428,7 @@ "forms_egtest.mm", "http_auth_egtest.mm", "js_print_egtest.mm", + "legacy_tls_egtest.mm", "lookalike_url_egtest.mm", "navigation_egtest.mm", "progress_indicator_egtest.mm",
diff --git a/ios/chrome/browser/web/chrome_web_client.h b/ios/chrome/browser/web/chrome_web_client.h index a716184..86e5182 100644 --- a/ios/chrome/browser/web/chrome_web_client.h +++ b/ios/chrome/browser/web/chrome_web_client.h
@@ -51,6 +51,8 @@ bool overridable, int64_t navigation_id, const base::Callback<void(bool)>& callback) override; + bool IsLegacyTLSAllowedForHost(web::WebState* web_state, + const std::string& hostname) override; void PrepareErrorPage(web::WebState* web_state, const GURL& url, NSError* error,
diff --git a/ios/chrome/browser/web/chrome_web_client.mm b/ios/chrome/browser/web/chrome_web_client.mm index 589a1fd..a45cf93 100644 --- a/ios/chrome/browser/web/chrome_web_client.mm +++ b/ios/chrome/browser/web/chrome_web_client.mm
@@ -33,11 +33,15 @@ #import "ios/chrome/browser/web/error_page_util.h" #include "ios/chrome/browser/web/features.h" #import "ios/components/security_interstitials/ios_blocking_page_tab_helper.h" +#import "ios/components/security_interstitials/legacy_tls/legacy_tls_blocking_page.h" +#import "ios/components/security_interstitials/legacy_tls/legacy_tls_controller_client.h" +#import "ios/components/security_interstitials/legacy_tls/legacy_tls_tab_allow_list.h" #import "ios/components/security_interstitials/lookalikes/lookalike_url_blocking_page.h" #import "ios/components/security_interstitials/lookalikes/lookalike_url_container.h" #import "ios/components/security_interstitials/lookalikes/lookalike_url_controller_client.h" #import "ios/components/security_interstitials/lookalikes/lookalike_url_error.h" #include "ios/components/webui/web_ui_url_constants.h" +#import "ios/net/protocol_handler_util.h" #include "ios/public/provider/chrome/browser/browser_url_rewriter_provider.h" #import "ios/public/provider/chrome/browser/chrome_browser_provider.h" #include "ios/public/provider/chrome/browser/chrome_browser_provider.h" @@ -47,6 +51,7 @@ #include "ios/web/common/user_agent.h" #include "ios/web/public/navigation/browser_url_rewriter.h" #include "ios/web/public/navigation/navigation_manager.h" +#include "net/base/net_errors.h" #include "net/http/http_util.h" #include "services/metrics/public/cpp/ukm_source_id.h" #include "ui/base/l10n/l10n_util.h" @@ -127,6 +132,23 @@ return base::SysUTF8ToNSString(error_page_content); } +// Returns the legacy TLS error page HTML. +NSString* GetLegacyTLSErrorPageHTML(web::WebState* web_state, + int64_t navigation_id) { + // Construct the blocking page and associate it with the WebState. + std::unique_ptr<security_interstitials::IOSSecurityInterstitialPage> page = + std::make_unique<LegacyTLSBlockingPage>( + web_state, web_state->GetVisibleURL() /*request_url*/, + std::make_unique<LegacyTLSControllerClient>( + web_state, web_state->GetVisibleURL(), + GetApplicationContext()->GetApplicationLocale())); + std::string error_page_content = page->GetHtmlContents(); + security_interstitials::IOSBlockingPageTabHelper::FromWebState(web_state) + ->AssociateBlockingPage(navigation_id, std::move(page)); + + return base::SysUTF8ToNSString(error_page_content); +} + // Returns a string describing the product name and version, of the // form "productname/version". Used as part of the user agent string. std::string GetMobileProduct() { @@ -293,6 +315,12 @@ std::move(null_callback)); } +bool ChromeWebClient::IsLegacyTLSAllowedForHost(web::WebState* web_state, + const std::string& hostname) { + return LegacyTLSTabAllowList::FromWebState(web_state)->IsDomainAllowed( + hostname); +} + void ChromeWebClient::PrepareErrorPage( web::WebState* web_state, const GURL& url, @@ -335,6 +363,10 @@ DCHECK_EQ(kLookalikeUrlErrorCode, final_underlying_error.code); std::move(error_html_callback) .Run(GetLookalikeUrlErrorPageHtml(web_state, navigation_id)); + } else if ([final_underlying_error.domain isEqual:net::kNSErrorDomain] && + final_underlying_error.code == net::ERR_SSL_OBSOLETE_VERSION) { + std::move(error_html_callback) + .Run(GetLegacyTLSErrorPageHTML(web_state, navigation_id)); } else if (info.has_value()) { base::OnceCallback<void(bool)> proceed_callback; base::OnceCallback<void(NSString*)> blocking_page_callback =
diff --git a/ios/chrome/browser/web/chrome_web_client_unittest.mm b/ios/chrome/browser/web/chrome_web_client_unittest.mm index b4c5ee6..e5818188 100644 --- a/ios/chrome/browser/web/chrome_web_client_unittest.mm +++ b/ios/chrome/browser/web/chrome_web_client_unittest.mm
@@ -33,6 +33,7 @@ #import "ios/components/security_interstitials/ios_blocking_page_tab_helper.h" #import "ios/components/security_interstitials/lookalikes/lookalike_url_container.h" #import "ios/components/security_interstitials/lookalikes/lookalike_url_error.h" +#import "ios/net/protocol_handler_util.h" #include "ios/web/common/features.h" #import "ios/web/common/web_view_creation_util.h" #import "ios/web/public/test/error_test_util.h" @@ -40,6 +41,7 @@ #import "ios/web/public/test/fakes/test_web_state.h" #import "ios/web/public/test/js_test_util.h" #include "ios/web/public/test/scoped_testing_web_client.h" +#include "net/base/net_errors.h" #include "net/http/http_status_code.h" #include "net/ssl/ssl_info.h" #include "net/test/cert_test_util.h" @@ -483,6 +485,41 @@ << base::SysNSStringToUTF8(page); } +// Tests PrepareErrorPage for a legacy TLS error, which results in a +// committed legacy TLS interstitial. +TEST_F(ChromeWebClientTest, PrepareErrorPageForLegacyTLSError) { + web::TestWebState web_state; + web_state.SetBrowserState(browser_state()); + security_interstitials::IOSBlockingPageTabHelper::CreateForWebState( + &web_state); + auto navigation_manager = std::make_unique<web::TestNavigationManager>(); + web_state.SetNavigationManager(std::move(navigation_manager)); + + NSError* error = [NSError errorWithDomain:net::kNSErrorDomain + code:net::ERR_SSL_OBSOLETE_VERSION + userInfo:nil]; + __block bool callback_called = false; + __block NSString* page = nil; + base::OnceCallback<void(NSString*)> callback = + base::BindOnce(^(NSString* error_html) { + callback_called = true; + page = error_html; + }); + + ChromeWebClient web_client; + web_client.PrepareErrorPage(&web_state, GURL(kTestUrl), error, + /*is_post=*/false, + /*is_off_the_record=*/false, + /*info=*/base::Optional<net::SSLInfo>(), + /*navigation_id=*/0, std::move(callback)); + + EXPECT_TRUE(callback_called); + NSString* error_string = + l10n_util::GetNSString(IDS_LEGACY_TLS_PRIMARY_PARAGRAPH); + EXPECT_TRUE([page containsString:error_string]) + << base::SysNSStringToUTF8(page); +} + // Tests the default user agent for different views. TEST_F(ChromeWebClientTest, DefaultUserAgent) { if (@available(iOS 13, *)) {
diff --git a/ios/chrome/browser/web/legacy_tls_egtest.mm b/ios/chrome/browser/web/legacy_tls_egtest.mm new file mode 100644 index 0000000..3266d0e --- /dev/null +++ b/ios/chrome/browser/web/legacy_tls_egtest.mm
@@ -0,0 +1,190 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/ios/ios_util.h" +#include "components/strings/grit/components_strings.h" +#import "ios/chrome/test/earl_grey/chrome_earl_grey.h" +#import "ios/chrome/test/earl_grey/chrome_test_case.h" +#import "ios/testing/earl_grey/earl_grey_test.h" +#include "ios/testing/embedded_test_server_handlers.h" +#include "ios/web/common/features.h" +#include "net/ssl/ssl_config.h" +#include "net/ssl/ssl_server_config.h" +#include "net/test/embedded_test_server/default_handlers.h" +#include "ui/base/l10n/l10n_util.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +@interface LegacyTLSTestCase : ChromeTestCase { + // A test server that connects via TLS 1.0. + std::unique_ptr<net::test_server::EmbeddedTestServer> _legacyTLSServer; + // A URL for the legacy TLS test server, which should trigger legacy TLS + // interstitials. + GURL _legacyTLSURL; + // A URL for the normal test server. + GURL _safeURL; + // Text that is found on the legacy TLS interstitial. + std::string _interstitialContent; + // Text that is found on |_legacyTLSURL| if the user clicks through. + std::string _unsafeContent; + // Text that is found on |_safeURL|. + std::string _safeContent; +} + +@end + +@implementation LegacyTLSTestCase + +- (AppLaunchConfiguration)appConfigurationForTestCase { + AppLaunchConfiguration config; + config.features_enabled.push_back(web::features::kSSLCommittedInterstitials); + config.features_enabled.push_back(web::features::kIOSLegacyTLSInterstitial); + config.relaunch_policy = NoForceRelaunchAndResetState; + return config; +} + +- (void)setUp { + [super setUp]; + + // Setup server that will negotiate TLS 1.0. + _legacyTLSServer = std::make_unique<net::test_server::EmbeddedTestServer>( + net::test_server::EmbeddedTestServer::TYPE_HTTPS); + net::SSLServerConfig ssl_config; + ssl_config.version_min = net::SSL_PROTOCOL_VERSION_TLS1; + ssl_config.version_max = net::SSL_PROTOCOL_VERSION_TLS1; + _legacyTLSServer->SetSSLConfig(net::EmbeddedTestServer::CERT_OK, ssl_config); + RegisterDefaultHandlers(_legacyTLSServer.get()); + GREYAssertTrue(_legacyTLSServer->Start(), + @"Legacy TLS test server failed to start."); + + _legacyTLSURL = _legacyTLSServer->GetURL("/"); + _interstitialContent = + l10n_util::GetStringUTF8(IDS_LEGACY_TLS_PRIMARY_PARAGRAPH); + // The legacy TLS server will cause self-signed cert warnings after we click + // through the legacy TLS interstitial, so pull the heading string for that + // to match on. + _unsafeContent = l10n_util::GetStringUTF8(IDS_SSL_V2_HEADING); + + RegisterDefaultHandlers(self.testServer); + GREYAssertTrue(self.testServer->Start(), @"Test server failed to start."); + _safeURL = self.testServer->GetURL("/defaultresponse"); + _safeContent = "Default response"; +} + +// On iOS < 14, the legacy TLS interstitial should not be shown. +- (void)testLegacyTLSInterstitialNotShownOnOldVersions { + if (base::ios::IsRunningOnIOS14OrLater()) { + return; + } + [ChromeEarlGrey loadURL:_legacyTLSURL]; + [ChromeEarlGrey waitForWebStateContainingText:_unsafeContent]; +} + +// The remaining tests in this file are only relevant for iOS 14 or later. + +// Test that loading a page from a server over TLS 1.0 causes the legacy TLS +// interstitial to show. +- (void)testLegacyTLSShowsInterstitial { + if (!base::ios::IsRunningOnIOS14OrLater()) { + return; + } + [ChromeEarlGrey loadURL:_legacyTLSURL]; + [ChromeEarlGrey waitForWebStateContainingText:_interstitialContent]; +} + +// Test that going back to safety returns the user to the previous page. +- (void)testLegacyTLSInterstitialBackToSafety { + if (!base::ios::IsRunningOnIOS14OrLater()) { + return; + } + // Load a benign page first. + [ChromeEarlGrey loadURL:_safeURL]; + [ChromeEarlGrey waitForWebStateContainingText:_safeContent]; + + // Trigger the legacy TLS interstitial. + [ChromeEarlGrey loadURL:_legacyTLSURL]; + [ChromeEarlGrey waitForWebStateContainingText:_interstitialContent]; + + // Tap on the "Back to safety" button and verify that we navigate back to the + // previous page. + [ChromeEarlGrey tapWebStateElementWithID:@"primary-button"]; + [ChromeEarlGrey waitForWebStateContainingText:_safeContent]; +} + +// Test that clicking through the interstitial works. +- (void)testLegacyTLSInterstitialProceed { + if (!base::ios::IsRunningOnIOS14OrLater()) { + return; + } + // Load a benign page first. + [ChromeEarlGrey loadURL:_safeURL]; + [ChromeEarlGrey waitForWebStateContainingText:_safeContent]; + + // Trigger the legacy TLS interstitial. + [ChromeEarlGrey loadURL:_legacyTLSURL]; + [ChromeEarlGrey waitForWebStateContainingText:_interstitialContent]; + + // Tap on the "Proceed" link and verify that we go to the unsafe page (in this + // case, a cert error interstitial for the test server's self-signed cert). + [ChromeEarlGrey tapWebStateElementWithID:@"details-button"]; + [ChromeEarlGrey tapWebStateElementWithID:@"proceed-link"]; + [ChromeEarlGrey waitForWebStateContainingText:_unsafeContent]; +} + +// Test that clicking through the interstitial is remembered on a reload, and +// we don't show the interstitial again. +- (void)testLegacyTLSInterstitialAllowlist { + if (!base::ios::IsRunningOnIOS14OrLater()) { + return; + } + // Trigger the legacy TLS interstitial. + [ChromeEarlGrey loadURL:_legacyTLSURL]; + [ChromeEarlGrey waitForWebStateContainingText:_interstitialContent]; + + // Tap on the "Proceed" link and verify that we go to the unsafe page. + [ChromeEarlGrey tapWebStateElementWithID:@"details-button"]; + [ChromeEarlGrey tapWebStateElementWithID:@"proceed-link"]; + [ChromeEarlGrey waitForWebStateContainingText:_unsafeContent]; + + // Navigate away to another page. + [ChromeEarlGrey loadURL:_safeURL]; + [ChromeEarlGrey waitForWebStateContainingText:_safeContent]; + + // Navigate to the legacy TLS page again. Legacy TLS interstitial should not + // be shown. + [ChromeEarlGrey loadURL:_legacyTLSURL]; + [ChromeEarlGrey waitForWebStateContainingText:_unsafeContent]; +} + +// Test that the allowlist is cleared after session restart and that we show the +// legacy TLS interstitial again. +- (void)testLegacyTLSInterstitialAllowlistClearedOnRestart { + if (!base::ios::IsRunningOnIOS14OrLater()) { + return; + } + // Trigger the legacy TLS interstitial. + [ChromeEarlGrey loadURL:_legacyTLSURL]; + [ChromeEarlGrey waitForWebStateContainingText:_interstitialContent]; + + // Tap on the "Proceed" link and verify that we go to the unsafe page. + [ChromeEarlGrey tapWebStateElementWithID:@"details-button"]; + [ChromeEarlGrey tapWebStateElementWithID:@"proceed-link"]; + [ChromeEarlGrey waitForWebStateContainingText:_unsafeContent]; + + // Navigate away to another page. + [ChromeEarlGrey loadURL:_safeURL]; + [ChromeEarlGrey waitForWebStateContainingText:_safeContent]; + + // Do a session restoration. + [ChromeEarlGrey triggerRestoreViaTabGridRemoveAllUndo]; + [ChromeEarlGrey waitForWebStateContainingText:_safeContent]; + + // Navigate to the legacy TLS page again. The interstitial should trigger. + [ChromeEarlGrey loadURL:_legacyTLSURL]; + [ChromeEarlGrey waitForWebStateContainingText:_interstitialContent]; +} + +@end
diff --git a/ios/components/security_interstitials/legacy_tls/BUILD.gn b/ios/components/security_interstitials/legacy_tls/BUILD.gn new file mode 100644 index 0000000..b6c6233 --- /dev/null +++ b/ios/components/security_interstitials/legacy_tls/BUILD.gn
@@ -0,0 +1,24 @@ +# Copyright 2020 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("legacy_tls") { + configs += [ "//build/config/compiler:enable_arc" ] + sources = [ + "legacy_tls_blocking_page.h", + "legacy_tls_blocking_page.mm", + "legacy_tls_controller_client.h", + "legacy_tls_controller_client.mm", + "legacy_tls_tab_allow_list.h", + "legacy_tls_tab_allow_list.mm", + ] + deps = [ + "//base", + "//components/security_interstitials/core", + "//components/strings:components_strings_grit", + "//ios/components/security_interstitials", + "//ios/web/public", + "//net", + "//ui/base", + ] +}
diff --git a/ios/components/security_interstitials/legacy_tls/DEPS b/ios/components/security_interstitials/legacy_tls/DEPS new file mode 100644 index 0000000..e972e39 --- /dev/null +++ b/ios/components/security_interstitials/legacy_tls/DEPS
@@ -0,0 +1,4 @@ +include_rules = [ + "+ios/net", + "+net/base", +]
diff --git a/ios/components/security_interstitials/legacy_tls/legacy_tls_blocking_page.h b/ios/components/security_interstitials/legacy_tls/legacy_tls_blocking_page.h new file mode 100644 index 0000000..acd3f5e --- /dev/null +++ b/ios/components/security_interstitials/legacy_tls/legacy_tls_blocking_page.h
@@ -0,0 +1,46 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_COMPONENTS_SECURITY_INTERSTITIALS_LEGACY_TLS_LEGACY_TLS_BLOCKING_PAGE_H_ +#define IOS_COMPONENTS_SECURITY_INTERSTITIALS_LEGACY_TLS_LEGACY_TLS_BLOCKING_PAGE_H_ + +#include "ios/components/security_interstitials/ios_security_interstitial_page.h" +#include "ios/components/security_interstitials/legacy_tls/legacy_tls_controller_client.h" + +class GURL; + +// This class is responsible for showing/hiding the interstitial page that is +// shown for legacy TLS connections. +class LegacyTLSBlockingPage + : public security_interstitials::IOSSecurityInterstitialPage { + public: + ~LegacyTLSBlockingPage() override; + + // Creates a legacy TLS blocking page. + LegacyTLSBlockingPage(web::WebState* web_state, + const GURL& request_url, + std::unique_ptr<LegacyTLSControllerClient> client); + + protected: + // SecurityInterstitialPage implementation: + bool ShouldCreateNewNavigation() const override; + void PopulateInterstitialStrings( + base::DictionaryValue* load_time_data) const override; + + private: + void HandleScriptCommand(const base::DictionaryValue& message, + const GURL& origin_url, + bool user_is_interacting, + web::WebFrame* sender_frame) override; + + void AfterShow() override; + + web::WebState* web_state_ = nullptr; + const GURL request_url_; + std::unique_ptr<LegacyTLSControllerClient> controller_; + + DISALLOW_COPY_AND_ASSIGN(LegacyTLSBlockingPage); +}; + +#endif // IOS_COMPONENTS_SECURITY_INTERSTITIALS_LEGACY_TLS_LEGACY_TLS_BLOCKING_PAGE_H_
diff --git a/ios/components/security_interstitials/legacy_tls/legacy_tls_blocking_page.mm b/ios/components/security_interstitials/legacy_tls/legacy_tls_blocking_page.mm new file mode 100644 index 0000000..421f9b4 --- /dev/null +++ b/ios/components/security_interstitials/legacy_tls/legacy_tls_blocking_page.mm
@@ -0,0 +1,100 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ios/components/security_interstitials/legacy_tls/legacy_tls_blocking_page.h" + +#include <utility> + +#include "base/strings/string_number_conversions.h" +#include "base/values.h" +#include "components/security_interstitials/core/common_string_util.h" +#include "components/security_interstitials/core/metrics_helper.h" +#include "ios/components/security_interstitials/ios_blocking_page_controller_client.h" +#include "ios/components/security_interstitials/ios_blocking_page_metrics_helper.h" +#include "net/base/net_errors.h" +#include "ui/base/l10n/l10n_util.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +LegacyTLSBlockingPage::LegacyTLSBlockingPage( + web::WebState* web_state, + const GURL& request_url, + std::unique_ptr<LegacyTLSControllerClient> client) + : security_interstitials::IOSSecurityInterstitialPage(web_state, + request_url, + client.get()), + web_state_(web_state), + request_url_(request_url), + controller_(std::move(client)) { + DCHECK(web_state_); + + // Creating an interstitial without showing it (e.g. from + // chrome://interstitials) leaks memory, so don't create it here. +} + +LegacyTLSBlockingPage::~LegacyTLSBlockingPage() = default; + +bool LegacyTLSBlockingPage::ShouldCreateNewNavigation() const { + return true; +} + +void LegacyTLSBlockingPage::PopulateInterstitialStrings( + base::DictionaryValue* load_time_data) const { + CHECK(load_time_data); + + // Shared with SSL errors. + security_interstitials::common_string_util::PopulateSSLLayoutStrings( + net::ERR_SSL_OBSOLETE_VERSION, load_time_data); + + load_time_data->SetBoolean("overridable", true); + load_time_data->SetBoolean("hide_primary_button", false); + load_time_data->SetBoolean("bad_clock", false); + load_time_data->SetString("type", "LEGACY_TLS"); + + const base::string16 hostname( + security_interstitials::common_string_util::GetFormattedHostName( + request_url_)); + security_interstitials::common_string_util::PopulateLegacyTLSStrings( + load_time_data, hostname); +} + +void LegacyTLSBlockingPage::HandleScriptCommand( + const base::DictionaryValue& message, + const GURL& origin_url, + bool user_is_interacting, + web::WebFrame* sender_frame) { + std::string command_string; + if (!message.GetString("command", &command_string)) { + LOG(ERROR) << "JS message parameter not found: command"; + return; + } + + // Remove the command prefix so that the string value can be converted to a + // SecurityInterstitialCommand enum value. + std::size_t delimiter = command_string.find("."); + if (delimiter == std::string::npos) { + return; + } + + // Parse the command int value from the text after the delimiter. + int command = 0; + if (!base::StringToInt(command_string.substr(delimiter + 1), &command)) { + NOTREACHED() << "Command cannot be parsed to an int : " << command_string; + return; + } + + if (command == security_interstitials::CMD_DONT_PROCEED) { + controller_->metrics_helper()->RecordUserDecision( + security_interstitials::MetricsHelper::DONT_PROCEED); + controller_->GoBack(); + } else if (command == security_interstitials::CMD_PROCEED) { + controller_->metrics_helper()->RecordUserDecision( + security_interstitials::MetricsHelper::PROCEED); + controller_->Proceed(); + } +} + +void LegacyTLSBlockingPage::AfterShow() {}
diff --git a/ios/components/security_interstitials/legacy_tls/legacy_tls_controller_client.h b/ios/components/security_interstitials/legacy_tls/legacy_tls_controller_client.h new file mode 100644 index 0000000..bb9955e0 --- /dev/null +++ b/ios/components/security_interstitials/legacy_tls/legacy_tls_controller_client.h
@@ -0,0 +1,34 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_COMPONENTS_SECURITY_INTERSTITIALS_LEGACY_TLS_LEGACY_TLS_CONTROLLER_CLIENT_H_ +#define IOS_COMPONENTS_SECURITY_INTERSTITIALS_LEGACY_TLS_LEGACY_TLS_CONTROLLER_CLIENT_H_ + +#include "ios/components/security_interstitials/ios_blocking_page_controller_client.h" +#include "url/gurl.h" + +class GURL; + +namespace web { +class WebState; +} // namespace web + +// Controller client used for legacy TLS blocking pages. +class LegacyTLSControllerClient + : public security_interstitials::IOSBlockingPageControllerClient { + public: + LegacyTLSControllerClient(web::WebState* web_state, + const GURL& request_url, + const std::string& app_locale); + ~LegacyTLSControllerClient() override; + + // security_interstitials::ControllerClient: + void Proceed() override; + + private: + // The URL of the page causing the insterstitial. + const GURL request_url_; +}; + +#endif // IOS_COMPONENTS_SECURITY_INTERSTITIALS_LEGACY_TLS_LEGACY_TLS_CONTROLLER_CLIENT_H_
diff --git a/ios/components/security_interstitials/legacy_tls/legacy_tls_controller_client.mm b/ios/components/security_interstitials/legacy_tls/legacy_tls_controller_client.mm new file mode 100644 index 0000000..bd69afd --- /dev/null +++ b/ios/components/security_interstitials/legacy_tls/legacy_tls_controller_client.mm
@@ -0,0 +1,43 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ios/components/security_interstitials/legacy_tls/legacy_tls_controller_client.h" + +#include "components/security_interstitials/core/metrics_helper.h" +#include "ios/components/security_interstitials/ios_blocking_page_metrics_helper.h" +#include "ios/components/security_interstitials/legacy_tls/legacy_tls_tab_allow_list.h" +#import "ios/web/public/web_state.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +namespace { +// Creates a metrics helper for |url|. +std::unique_ptr<security_interstitials::IOSBlockingPageMetricsHelper> +CreateMetricsHelper(web::WebState* web_state, const GURL& url) { + security_interstitials::MetricsHelper::ReportDetails reporting_info; + reporting_info.metric_prefix = "legacy_tls"; + return std::make_unique<security_interstitials::IOSBlockingPageMetricsHelper>( + web_state, url, reporting_info); +} +} // namespace + +LegacyTLSControllerClient::LegacyTLSControllerClient( + web::WebState* web_state, + const GURL& request_url, + const std::string& app_locale) + : IOSBlockingPageControllerClient( + web_state, + CreateMetricsHelper(web_state, request_url), + app_locale), + request_url_(request_url) {} + +LegacyTLSControllerClient::~LegacyTLSControllerClient() {} + +void LegacyTLSControllerClient::Proceed() { + LegacyTLSTabAllowList::FromWebState(web_state()) + ->AllowDomain(request_url_.host()); + Reload(); +}
diff --git a/ios/components/security_interstitials/legacy_tls/legacy_tls_tab_allow_list.h b/ios/components/security_interstitials/legacy_tls/legacy_tls_tab_allow_list.h new file mode 100644 index 0000000..88c47978 --- /dev/null +++ b/ios/components/security_interstitials/legacy_tls/legacy_tls_tab_allow_list.h
@@ -0,0 +1,38 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_COMPONENTS_SECURITY_INTERSTITIALS_LEGACY_TLS_LEGACY_TLS_TAB_ALLOW_LIST_H_ +#define IOS_COMPONENTS_SECURITY_INTERSTITIALS_LEGACY_TLS_LEGACY_TLS_TAB_ALLOW_LIST_H_ + +#include <set> +#include <string> + +#import "ios/web/public/web_state_user_data.h" + +// LegacyTLSTabAllowList tracks the allowlist decisions for legacy TLS hosts. +// Decisions are scoped to the domain. +class LegacyTLSTabAllowList + : public web::WebStateUserData<LegacyTLSTabAllowList> { + public: + // LegacyTLSTabAllowList is move-only. + LegacyTLSTabAllowList(LegacyTLSTabAllowList&& other); + LegacyTLSTabAllowList& operator=(LegacyTLSTabAllowList&& other); + ~LegacyTLSTabAllowList() override; + + // Returns whether |domain| has been allowlisted. + bool IsDomainAllowed(const std::string& domain) const; + + // Allows future navigations to |domain|. + void AllowDomain(const std::string& domain); + + private: + explicit LegacyTLSTabAllowList(web::WebState* web_state); + friend class web::WebStateUserData<LegacyTLSTabAllowList>; + WEB_STATE_USER_DATA_KEY_DECL(); + + // Set of allowlisted domains. + std::set<std::string> allowed_domains_; +}; + +#endif // IOS_COMPONENTS_SECURITY_INTERSTITIALS_LEGACY_TLS_LEGACY_TLS_TAB_ALLOW_LIST_H_
diff --git a/ios/components/security_interstitials/legacy_tls/legacy_tls_tab_allow_list.mm b/ios/components/security_interstitials/legacy_tls/legacy_tls_tab_allow_list.mm new file mode 100644 index 0000000..55b78fe8 --- /dev/null +++ b/ios/components/security_interstitials/legacy_tls/legacy_tls_tab_allow_list.mm
@@ -0,0 +1,31 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ios/components/security_interstitials/legacy_tls/legacy_tls_tab_allow_list.h" + +#import "ios/web/public/web_state.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +WEB_STATE_USER_DATA_KEY_IMPL(LegacyTLSTabAllowList) + +LegacyTLSTabAllowList::LegacyTLSTabAllowList(web::WebState* web_state) {} + +LegacyTLSTabAllowList::LegacyTLSTabAllowList(LegacyTLSTabAllowList&& other) = + default; + +LegacyTLSTabAllowList& LegacyTLSTabAllowList::operator=( + LegacyTLSTabAllowList&& other) = default; + +LegacyTLSTabAllowList::~LegacyTLSTabAllowList() = default; + +bool LegacyTLSTabAllowList::IsDomainAllowed(const std::string& domain) const { + return allowed_domains_.find(domain) != allowed_domains_.end(); +} + +void LegacyTLSTabAllowList::AllowDomain(const std::string& domain) { + allowed_domains_.insert(domain); +}
diff --git a/ios/web/common/features.h b/ios/web/common/features.h index d7e550d..f26551c 100644 --- a/ios/web/common/features.h +++ b/ios/web/common/features.h
@@ -63,6 +63,9 @@ // See also: https://wicg.github.io/scroll-to-text-fragment/ extern const base::Feature kScrollToTextIOS; +// When enabled, display an interstitial on legacy TLS connections. +extern const base::Feature kIOSLegacyTLSInterstitial; + // When true, for each navigation, the default user agent is chosen by the // WebClient GetDefaultUserAgent() method. If it is false, the mobile version // is requested by default.
diff --git a/ios/web/common/features.mm b/ios/web/common/features.mm index df6b531..9f8e055 100644 --- a/ios/web/common/features.mm +++ b/ios/web/common/features.mm
@@ -50,6 +50,8 @@ const base::Feature kScrollToTextIOS{"ScrollToTextIOS", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kIOSLegacyTLSInterstitial{ + "IOSLegacyTLSInterstitial", base::FEATURE_DISABLED_BY_DEFAULT}; bool UseWebClientDefaultUserAgent() { if (@available(iOS 13, *)) {
diff --git a/ios/web/navigation/BUILD.gn b/ios/web/navigation/BUILD.gn index 79563e6..2b6252b 100644 --- a/ios/web/navigation/BUILD.gn +++ b/ios/web/navigation/BUILD.gn
@@ -35,6 +35,7 @@ "//ios/web/web_state/ui:crw_web_view_navigation_proxy", "//ios/web/web_state/ui:web_view_handler", "//ios/web/web_view:util", + "//net", "//ui/base", "//url", ]
diff --git a/ios/web/navigation/crw_wk_navigation_handler.mm b/ios/web/navigation/crw_wk_navigation_handler.mm index a906825..6252a077 100644 --- a/ios/web/navigation/crw_wk_navigation_handler.mm +++ b/ios/web/navigation/crw_wk_navigation_handler.mm
@@ -10,6 +10,7 @@ #include "base/strings/sys_string_conversions.h" #include "base/timer/timer.h" #import "ios/net/http_response_headers_util.h" +#import "ios/net/protocol_handler_util.h" #include "ios/web/common/features.h" #import "ios/web/common/url_scheme_util.h" #import "ios/web/js_messaging/crw_js_injector.h" @@ -39,6 +40,7 @@ #import "ios/web/web_view/error_translation_util.h" #import "ios/web/web_view/wk_web_view_util.h" #import "net/base/mac/url_conversions.h" +#include "net/base/net_errors.h" #include "net/cert/x509_util_ios.h" #include "url/gurl.h" @@ -1214,6 +1216,37 @@ }]; } +- (void)webView:(WKWebView*)webView + authenticationChallenge:(NSURLAuthenticationChallenge*)challenge + shouldAllowDeprecatedTLS:(void (^)(BOOL))decisionHandler + API_AVAILABLE(ios(14)) { + [self didReceiveWKNavigationDelegateCallback]; + DCHECK(challenge); + DCHECK(decisionHandler); + + // If the legacy TLS interstitial is not enabled, don't cause errors. + if (!base::FeatureList::IsEnabled(web::features::kIOSLegacyTLSInterstitial)) { + decisionHandler(YES); + return; + } + + if (web::GetWebClient()->IsLegacyTLSAllowedForHost( + self.webStateImpl, + base::SysNSStringToUTF8(challenge.protectionSpace.host))) { + decisionHandler(YES); + return; + } + + if (self.pendingNavigationInfo) { + self.pendingNavigationInfo.cancelled = YES; + self.pendingNavigationInfo.cancellationError = + [NSError errorWithDomain:net::kNSErrorDomain + code:net::ERR_SSL_OBSOLETE_VERSION + userInfo:nil]; + } + decisionHandler(NO); +} + - (void)webViewWebContentProcessDidTerminate:(WKWebView*)webView { [self didReceiveWKNavigationDelegateCallback];
diff --git a/ios/web/navigation/navigation_item_impl.mm b/ios/web/navigation/navigation_item_impl.mm index 6c1b103..ac70208 100644 --- a/ios/web/navigation/navigation_item_impl.mm +++ b/ios/web/navigation/navigation_item_impl.mm
@@ -36,6 +36,12 @@ namespace web { +// Value 50 was picked experimentally by examining Chrome for iOS UI. Tab strip +// on 12.9" iPad Pro trucates the title to less than 50 characters (title that +// only consists of letters "i"). Tab strip has the biggest surface to fit +// title. +const size_t kMaxTitleLength = 50; + // static std::unique_ptr<NavigationItem> NavigationItem::Create() { return std::unique_ptr<NavigationItem>(new NavigationItemImpl()); @@ -126,7 +132,12 @@ void NavigationItemImpl::SetTitle(const base::string16& title) { if (title_ == title) return; - title_ = title; + + if (title.size() > kMaxTitleLength) { + title_ = gfx::TruncateString(title, kMaxTitleLength, gfx::CHARACTER_BREAK); + } else { + title_ = title; + } cached_display_title_.clear(); }
diff --git a/ios/web/navigation/navigation_item_impl_unittest.mm b/ios/web/navigation/navigation_item_impl_unittest.mm index 57f94ea..66f1a9e 100644 --- a/ios/web/navigation/navigation_item_impl_unittest.mm +++ b/ios/web/navigation/navigation_item_impl_unittest.mm
@@ -160,6 +160,12 @@ EXPECT_EQ(original_url, item_->GetURL()); } +// Tests setting title longer than kMaxTitleLength. +TEST_F(NavigationItemTest, ExtraLongTitle) { + item_->SetTitle(base::UTF8ToUTF16(std::string(kMaxTitleLength + 1, 'i'))); + EXPECT_EQ(kMaxTitleLength, item_->GetTitle().size()); +} + // Tests NavigationItemImpl::GetDisplayTitleForURL method. TEST_F(NavigationItemTest, GetDisplayTitleForURL) { base::string16 title;
diff --git a/ios/web/public/navigation/navigation_item.h b/ios/web/public/navigation/navigation_item.h index 000457d9..b9b6260 100644 --- a/ios/web/public/navigation/navigation_item.h +++ b/ios/web/public/navigation/navigation_item.h
@@ -23,6 +23,11 @@ struct Referrer; struct SSLStatus; +// User interface limits the length of the title, so placing limit does not +// have any functional side effects, and allows to use less memory for +// navigation session. +extern const size_t kMaxTitleLength; + // A NavigationItem is a data structure that captures all the information // required to recreate a browsing state. It represents one point in the // chain of navigation managed by a NavigationManager.
diff --git a/ios/web/public/web_client.h b/ios/web/public/web_client.h index 60dc2fc..6042364 100644 --- a/ios/web/public/web_client.h +++ b/ios/web/public/web_client.h
@@ -163,6 +163,12 @@ int64_t navigation_id, const base::Callback<void(bool)>& callback); + // Allows the embedder to specify legacy TLS enforcement on a per-host basis, + // for example to allow users to bypass interstitial warnings on affected + // hosts. + virtual bool IsLegacyTLSAllowedForHost(WebState* web_state, + const std::string& hostname); + // Calls the given |callback| with the contents of an error page to display // when a navigation error occurs. |error| is always a valid pointer. The // string passed to |callback| will be nil if no error page should be
diff --git a/ios/web/web_client.mm b/ios/web/web_client.mm index 812d7812b..f7b923d0 100644 --- a/ios/web/web_client.mm +++ b/ios/web/web_client.mm
@@ -96,6 +96,11 @@ callback.Run(false); } +bool WebClient::IsLegacyTLSAllowedForHost(WebState* web_state, + const std::string& hostname) { + return false; +} + void WebClient::PrepareErrorPage(WebState* web_state, const GURL& url, NSError* error,
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc index 980649a8..7c6f63e 100644 --- a/media/blink/webmediaplayer_impl.cc +++ b/media/blink/webmediaplayer_impl.cc
@@ -1038,9 +1038,11 @@ OutputDeviceStatusCB callback = ConvertToOutputDeviceStatusCB(std::move(completion_callback)); + auto sink_id_utf8 = sink_id.Utf8(); media_task_runner_->PostTask( FROM_HERE, base::BindOnce(&SetSinkIdOnMediaThread, audio_source_provider_, - sink_id.Utf8(), std::move(callback))); + sink_id_utf8, std::move(callback))); + delegate_->DidAudioOutputSinkChange(delegate_id_, sink_id_utf8); } STATIC_ASSERT_ENUM(WebMediaPlayer::kPreloadNone, MultibufferDataSource::NONE); @@ -2563,6 +2565,11 @@ client_->RequestExitPictureInPicture(); } +void WebMediaPlayerImpl::OnSetAudioSink(const std::string& sink_id) { + SetSinkId(WebString::FromASCII(sink_id), + base::DoNothing::Once<base::Optional<blink::WebSetSinkIdError>>()); +} + void WebMediaPlayerImpl::OnVolumeMultiplierUpdate(double multiplier) { volume_multiplier_ = multiplier; SetVolume(volume_);
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h index 3675d8e..1025c7a 100644 --- a/media/blink/webmediaplayer_impl.h +++ b/media/blink/webmediaplayer_impl.h
@@ -62,12 +62,12 @@ class WebLocalFrame; class WebMediaPlayerClient; class WebMediaPlayerEncryptedMediaClient; -} +} // namespace blink namespace base { class SingleThreadTaskRunner; class TaskRunner; -} +} // namespace base namespace cc { class VideoLayer; @@ -77,7 +77,7 @@ namespace gles2 { class GLES2Interface; } -} +} // namespace gpu namespace media { class CdmContextRef; @@ -252,6 +252,7 @@ void OnSeekBackward(double seconds) override; void OnEnterPictureInPicture() override; void OnExitPictureInPicture() override; + void OnSetAudioSink(const std::string& sink_id) override; void OnVolumeMultiplierUpdate(double multiplier) override; void OnBecamePersistentVideo(bool value) override; void OnPowerExperimentState(bool state) override;
diff --git a/media/blink/webmediaplayer_impl_unittest.cc b/media/blink/webmediaplayer_impl_unittest.cc index 8f1e29a..a754b25 100644 --- a/media/blink/webmediaplayer_impl_unittest.cc +++ b/media/blink/webmediaplayer_impl_unittest.cc
@@ -280,6 +280,8 @@ MOCK_METHOD2(DidPictureInPictureAvailabilityChange, void(int, bool)); + MOCK_METHOD2(DidAudioOutputSinkChange, void(int, const std::string&)); + private: Observer* observer_ = nullptr; int player_id_ = 1234;
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn index b614f2e..9ed9a35 100644 --- a/media/gpu/BUILD.gn +++ b/media/gpu/BUILD.gn
@@ -524,6 +524,10 @@ ] libs = [ "dxguid.lib" ] } + + if (is_mac) { + deps += [ "//media/gpu/mac:unit_tests" ] + } } # TODO(crbug.com/1006266): consider using |use_chromeos_video_acceleration|.
diff --git a/media/gpu/command_buffer_helper.cc b/media/gpu/command_buffer_helper.cc index 8a445e2..63e0c4d 100644 --- a/media/gpu/command_buffer_helper.cc +++ b/media/gpu/command_buffer_helper.cc
@@ -14,9 +14,12 @@ #include "gpu/command_buffer/common/scheduling_priority.h" #include "gpu/command_buffer/service/decoder_context.h" #include "gpu/command_buffer/service/scheduler.h" +#include "gpu/command_buffer/service/shared_image_backing.h" +#include "gpu/command_buffer/service/shared_image_representation.h" #include "gpu/command_buffer/service/sync_point_manager.h" #include "gpu/ipc/service/command_buffer_stub.h" #include "gpu/ipc/service/gpu_channel.h" +#include "gpu/ipc/service/gpu_channel_manager.h" #include "media/gpu/gles2_decoder_helper.h" #include "ui/gl/gl_context.h" @@ -45,6 +48,8 @@ #endif // defined(OS_MAC) ); decoder_helper_ = GLES2DecoderHelper::Create(stub_->decoder_context()); + tracker_ = + std::make_unique<gpu::MemoryTypeTracker>(stub_->GetMemoryTracker()); } gl::GLContext* GetGLContext() override { @@ -71,6 +76,24 @@ return decoder_helper_ && decoder_helper_->MakeContextCurrent(); } + std::unique_ptr<gpu::SharedImageRepresentationFactoryRef> Register( + std::unique_ptr<gpu::SharedImageBacking> backing) override { + DVLOG(2) << __func__; + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return stub_->channel() + ->gpu_channel_manager() + ->shared_image_manager() + ->Register(std::move(backing), tracker_.get()); + } + + gpu::TextureBase* GetTexture(GLuint service_id) const override { + DVLOG(2) << __func__ << "(" << service_id << ")"; + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(stub_->decoder_context()->GetGLContext()->IsCurrent(nullptr)); + DCHECK(textures_.count(service_id)); + return textures_.at(service_id)->GetTextureBase(); + } + GLuint CreateTexture(GLenum target, GLenum internal_format, GLsizei width, @@ -211,6 +234,8 @@ WillDestroyStubCB will_destroy_stub_cb_; + std::unique_ptr<gpu::MemoryTypeTracker> tracker_; + THREAD_CHECKER(thread_checker_); DISALLOW_COPY_AND_ASSIGN(CommandBufferHelperImpl); };
diff --git a/media/gpu/command_buffer_helper.h b/media/gpu/command_buffer_helper.h index 0b028ce7..ba882cf7 100644 --- a/media/gpu/command_buffer_helper.h +++ b/media/gpu/command_buffer_helper.h
@@ -18,6 +18,9 @@ namespace gpu { class CommandBufferStub; +class SharedImageBacking; +class SharedImageRepresentationFactoryRef; +class TextureBase; } // namespace gpu namespace gl { @@ -55,6 +58,12 @@ // Makes the GL context current. virtual bool MakeContextCurrent() = 0; + // Register a shared image backing + virtual std::unique_ptr<gpu::SharedImageRepresentationFactoryRef> Register( + std::unique_ptr<gpu::SharedImageBacking> backing) = 0; + + virtual gpu::TextureBase* GetTexture(GLuint service_id) const = 0; + // Creates a texture and returns its |service_id|. // // See glTexImage2D() for argument definitions.
diff --git a/media/gpu/mac/BUILD.gn b/media/gpu/mac/BUILD.gn index cc6b29f4..b0f318f 100644 --- a/media/gpu/mac/BUILD.gn +++ b/media/gpu/mac/BUILD.gn
@@ -18,6 +18,8 @@ visibility = [ "//media/gpu" ] sources = [ + "vt_config_util.h", + "vt_config_util.mm", "vt_video_decode_accelerator_mac.cc", "vt_video_decode_accelerator_mac.h", "vt_video_encode_accelerator_mac.cc", @@ -42,3 +44,16 @@ "//ui/gl", ] } + +source_set("unit_tests") { + testonly = true + frameworks = [ + "CoreFoundation.framework", + "CoreMedia.framework", + ] + deps = [ + "//media/gpu:test_support", + "//testing/gtest", + ] + sources = [ "vt_config_util_unittest.cc" ] +}
diff --git a/media/gpu/mac/vt_config_util.h b/media/gpu/mac/vt_config_util.h new file mode 100644 index 0000000..e640f42 --- /dev/null +++ b/media/gpu/mac/vt_config_util.h
@@ -0,0 +1,31 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_GPU_MAC_VT_CONFIG_UTIL_H_ +#define MEDIA_GPU_MAC_VT_CONFIG_UTIL_H_ + +#include <CoreFoundation/CoreFoundation.h> +#include <CoreMedia/CoreMedia.h> + +#include "base/optional.h" +#include "media/base/hdr_metadata.h" +#include "media/base/video_codecs.h" +#include "media/base/video_color_space.h" +#include "media/gpu/media_gpu_export.h" +#include "media/video/video_decode_accelerator.h" + +namespace media { + +MEDIA_GPU_EXPORT CFMutableDictionaryRef +CreateFormatExtensions(CMVideoCodecType codec_type, + VideoCodecProfile profile, + const VideoColorSpace& color_space, + base::Optional<HDRMetadata> hdr_metadata); + +MEDIA_GPU_EXPORT gfx::ColorSpace GetImageBufferColorSpace( + CVImageBufferRef image_buffer); + +} // namespace media + +#endif // MEDIA_GPU_MAC_VT_CONFIG_UTIL_H_
diff --git a/media/gpu/mac/vt_config_util.mm b/media/gpu/mac/vt_config_util.mm new file mode 100644 index 0000000..20b9344 --- /dev/null +++ b/media/gpu/mac/vt_config_util.mm
@@ -0,0 +1,502 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/gpu/mac/vt_config_util.h" + +#import <Foundation/Foundation.h> + +#include <simd/simd.h> + +#include "base/mac/foundation_util.h" +#include "base/no_destructor.h" + +namespace { + +// https://developer.apple.com/documentation/avfoundation/avassettrack/1386694-formatdescriptions?language=objc +NSString* CMVideoCodecTypeToString(CMVideoCodecType code) { + NSString* result = [NSString + stringWithFormat:@"%c%c%c%c", (code >> 24) & 0xff, (code >> 16) & 0xff, + (code >> 8) & 0xff, code & 0xff]; + NSCharacterSet* characterSet = [NSCharacterSet whitespaceCharacterSet]; + return [result stringByTrimmingCharactersInSet:characterSet]; +} + +// Helper functions to convert from CFStringRef kCM* keys to NSString. +void SetDictionaryValue(NSMutableDictionary<NSString*, id>* dictionary, + CFStringRef key, + id value) { + if (value) + dictionary[base::mac::CFToNSCast(key)] = value; +} + +void SetDictionaryValue(NSMutableDictionary<NSString*, id>* dictionary, + CFStringRef key, + CFStringRef value) { + SetDictionaryValue(dictionary, key, base::mac::CFToNSCast(value)); +} + +CFStringRef GetPrimaries(media::VideoColorSpace::PrimaryID primary_id) { + switch (primary_id) { + case media::VideoColorSpace::PrimaryID::BT709: + case media::VideoColorSpace::PrimaryID::UNSPECIFIED: // Assume BT.709. + return kCMFormatDescriptionColorPrimaries_ITU_R_709_2; + + case media::VideoColorSpace::PrimaryID::BT2020: + if (@available(macos 10.11, *)) + return kCMFormatDescriptionColorPrimaries_ITU_R_2020; + DLOG(WARNING) << "kCMFormatDescriptionColorPrimaries_ITU_R_2020 " + "unsupported prior to 10.11"; + return nil; + + case media::VideoColorSpace::PrimaryID::SMPTE170M: + case media::VideoColorSpace::PrimaryID::SMPTE240M: + return kCMFormatDescriptionColorPrimaries_SMPTE_C; + + case media::VideoColorSpace::PrimaryID::BT470BG: + return kCMFormatDescriptionColorPrimaries_EBU_3213; + + case media::VideoColorSpace::PrimaryID::SMPTEST431_2: + if (@available(macos 10.11, *)) + return kCMFormatDescriptionColorPrimaries_DCI_P3; + DLOG(WARNING) << "kCMFormatDescriptionColorPrimaries_DCI_P3 unsupported " + "prior to 10.11"; + return nil; + + case media::VideoColorSpace::PrimaryID::SMPTEST432_1: + if (@available(macos 10.11, *)) + return kCMFormatDescriptionColorPrimaries_P3_D65; + DLOG(WARNING) << "kCMFormatDescriptionColorPrimaries_P3_D65 unsupported " + "prior to 10.11"; + return nil; + + default: + DLOG(ERROR) << "Unsupported primary id: " << static_cast<int>(primary_id); + return nil; + } +} + +CFStringRef GetTransferFunction( + media::VideoColorSpace::TransferID transfer_id) { + switch (transfer_id) { + case media::VideoColorSpace::TransferID::LINEAR: + if (@available(macos 10.14, *)) + return kCMFormatDescriptionTransferFunction_Linear; + DLOG(WARNING) << "kCMFormatDescriptionTransferFunction_Linear " + "unsupported prior to 10.14"; + return nil; + + case media::VideoColorSpace::TransferID::GAMMA22: + case media::VideoColorSpace::TransferID::GAMMA28: + return kCMFormatDescriptionTransferFunction_UseGamma; + + case media::VideoColorSpace::TransferID::IEC61966_2_1: + if (@available(macos 10.13, *)) + return kCVImageBufferTransferFunction_sRGB; + DLOG(WARNING) + << "kCVImageBufferTransferFunction_sRGB unsupported prior to 10.13"; + return nil; + + case media::VideoColorSpace::TransferID::SMPTE170M: + case media::VideoColorSpace::TransferID::BT709: + case media::VideoColorSpace::TransferID::UNSPECIFIED: // Assume BT.709. + return kCMFormatDescriptionTransferFunction_ITU_R_709_2; + + case media::VideoColorSpace::TransferID::BT2020_10: + case media::VideoColorSpace::TransferID::BT2020_12: + if (@available(macos 10.11, *)) + return kCMFormatDescriptionTransferFunction_ITU_R_2020; + DLOG(WARNING) << "kCMFormatDescriptionTransferFunction_ITU_R_2020 " + "unsupported prior to 10.11"; + return nil; + + case media::VideoColorSpace::TransferID::SMPTEST2084: + if (@available(macos 10.13, *)) + return kCMFormatDescriptionTransferFunction_SMPTE_ST_2084_PQ; + DLOG(WARNING) << "kCMFormatDescriptionTransferFunction_SMPTE_ST_2084_PQ " + "unsupported prior to 10.13"; + return nil; + + case media::VideoColorSpace::TransferID::ARIB_STD_B67: + if (@available(macos 10.13, *)) + return kCMFormatDescriptionTransferFunction_ITU_R_2100_HLG; + DLOG(WARNING) << "kCMFormatDescriptionTransferFunction_ITU_R_2100_HLG " + "unsupported prior to 10.13"; + return nil; + + case media::VideoColorSpace::TransferID::SMPTE240M: + return kCMFormatDescriptionTransferFunction_SMPTE_240M_1995; + + case media::VideoColorSpace::TransferID::SMPTEST428_1: + if (@available(macos 10.12, *)) + return kCMFormatDescriptionTransferFunction_SMPTE_ST_428_1; + DLOG(WARNING) << "kCMFormatDescriptionTransferFunction_SMPTE_ST_428_1 " + "unsupported prior to 10.12"; + return nil; + + default: + DLOG(ERROR) << "Unsupported transfer function: " + << static_cast<int>(transfer_id); + return nil; + } +} + +CFStringRef GetMatrix(media::VideoColorSpace::MatrixID matrix_id) { + switch (matrix_id) { + case media::VideoColorSpace::MatrixID::BT709: + case media::VideoColorSpace::MatrixID::UNSPECIFIED: // Assume BT.709. + return kCMFormatDescriptionYCbCrMatrix_ITU_R_709_2; + + case media::VideoColorSpace::MatrixID::BT2020_NCL: + if (@available(macos 10.11, *)) + return kCMFormatDescriptionYCbCrMatrix_ITU_R_2020; + DLOG(WARNING) << "kCVImageBufferYCbCrMatrix_ITU_R_2020 " + "unsupported prior to 10.11"; + return nil; + + case media::VideoColorSpace::MatrixID::FCC: + case media::VideoColorSpace::MatrixID::SMPTE170M: + case media::VideoColorSpace::MatrixID::BT470BG: + // The FCC-based coefficients don't exactly match BT.601, but they're + // close enough. + return kCMFormatDescriptionYCbCrMatrix_ITU_R_601_4; + + case media::VideoColorSpace::MatrixID::SMPTE240M: + return kCMFormatDescriptionYCbCrMatrix_SMPTE_240M_1995; + + default: + DLOG(ERROR) << "Unsupported matrix id: " << static_cast<int>(matrix_id); + return nil; + } +} + +void SetContentLightLevelInfo(const media::HDRMetadata& hdr_metadata, + NSMutableDictionary<NSString*, id>* extensions) { + if (@available(macos 10.13, *)) { + // This is a SMPTEST2086 Content Light Level Information box. + struct ContentLightLevelInfoSEI { + uint16_t max_content_light_level; + uint16_t max_frame_average_light_level; + } __attribute__((packed, aligned(2))); + static_assert(sizeof(ContentLightLevelInfoSEI) == 4, "Must be 4 bytes"); + + // Values are stored in big-endian... + ContentLightLevelInfoSEI sei; + sei.max_content_light_level = + __builtin_bswap16(hdr_metadata.max_content_light_level); + sei.max_frame_average_light_level = + __builtin_bswap16(hdr_metadata.max_frame_average_light_level); + + NSData* nsdata_sei = [NSData dataWithBytes:&sei length:4]; + SetDictionaryValue(extensions, + kCMFormatDescriptionExtension_ContentLightLevelInfo, + nsdata_sei); + } else { + DLOG(WARNING) << "kCMFormatDescriptionExtension_ContentLightLevelInfo " + "unsupported prior to 10.13"; + } +} + +void SetMasteringMetadata(const media::HDRMetadata& hdr_metadata, + NSMutableDictionary<NSString*, id>* extensions) { + if (@available(macos 10.13, *)) { + // This is a SMPTEST2086 Mastering Display Color Volume box. + struct MasteringDisplayColorVolumeSEI { + vector_ushort2 primaries[3]; // GBR + vector_ushort2 white_point; + uint32_t luminance_max; + uint32_t luminance_min; + } __attribute__((packed, aligned(4))); + static_assert(sizeof(MasteringDisplayColorVolumeSEI) == 24, + "Must be 24 bytes"); + + // Make a copy which we can manipulate. + auto md = hdr_metadata.mastering_metadata; + + constexpr float kColorCoordinateUpperBound = 50000.0f; + md.primary_r.Scale(kColorCoordinateUpperBound); + md.primary_g.Scale(kColorCoordinateUpperBound); + md.primary_b.Scale(kColorCoordinateUpperBound); + md.white_point.Scale(kColorCoordinateUpperBound); + + constexpr float kUnitOfMasteringLuminance = 10000.0f; + md.luminance_max *= kUnitOfMasteringLuminance; + md.luminance_min *= kUnitOfMasteringLuminance; + + // Values are stored in big-endian... + MasteringDisplayColorVolumeSEI sei; + sei.primaries[0].x = __builtin_bswap16(md.primary_g.x() + 0.5f); + sei.primaries[0].y = __builtin_bswap16(md.primary_g.y() + 0.5f); + sei.primaries[1].x = __builtin_bswap16(md.primary_b.x() + 0.5f); + sei.primaries[1].y = __builtin_bswap16(md.primary_b.y() + 0.5f); + sei.primaries[2].x = __builtin_bswap16(md.primary_r.x() + 0.5f); + sei.primaries[2].y = __builtin_bswap16(md.primary_r.y() + 0.5f); + sei.white_point.x = __builtin_bswap16(md.white_point.x() + 0.5f); + sei.white_point.y = __builtin_bswap16(md.white_point.y() + 0.5f); + sei.luminance_max = __builtin_bswap32(md.luminance_max + 0.5f); + sei.luminance_min = __builtin_bswap32(md.luminance_min + 0.5f); + + NSData* nsdata_sei = [NSData dataWithBytes:&sei length:24]; + SetDictionaryValue( + extensions, kCMFormatDescriptionExtension_MasteringDisplayColorVolume, + nsdata_sei); + } else { + DLOG(WARNING) << "kCMFormatDescriptionExtension_" + "MasteringDisplayColorVolume unsupported prior to 10.13"; + } +} + +// Read the value for the key in |key| to CFString and convert it to IdType. +// Use the list of pairs in |cfstr_id_pairs| to do the conversion (by doing a +// linear lookup). +template <typename IdType, typename StringIdPair> +bool GetImageBufferProperty(CVImageBufferRef image_buffer, + CFStringRef key, + const std::vector<StringIdPair>& cfstr_id_pairs, + IdType* value_as_id) { + CFStringRef value_as_string = reinterpret_cast<CFStringRef>( + CVBufferGetAttachment(image_buffer, key, nullptr)); + if (!value_as_string) + return false; + + for (const auto& p : cfstr_id_pairs) { + if (!CFStringCompare(value_as_string, p.cfstr, 0)) { + *value_as_id = p.id; + return true; + } + } + + return false; +} + +gfx::ColorSpace::PrimaryID GetImageBufferPrimary( + CVImageBufferRef image_buffer) { + struct CVImagePrimary { + const CFStringRef cfstr; + const gfx::ColorSpace::PrimaryID id; + }; + static const base::NoDestructor<std::vector<CVImagePrimary>> + kSupportedPrimaries([] { + std::vector<CVImagePrimary> supported_primaries; + supported_primaries.push_back({kCVImageBufferColorPrimaries_ITU_R_709_2, + gfx::ColorSpace::PrimaryID::BT709}); + supported_primaries.push_back({kCVImageBufferColorPrimaries_EBU_3213, + gfx::ColorSpace::PrimaryID::BT470BG}); + supported_primaries.push_back({kCVImageBufferColorPrimaries_SMPTE_C, + gfx::ColorSpace::PrimaryID::SMPTE240M}); + if (@available(macos 10.11, *)) { + supported_primaries.push_back( + {kCVImageBufferColorPrimaries_ITU_R_2020, + gfx::ColorSpace::PrimaryID::BT2020}); + } + return supported_primaries; + }()); + + // The named primaries. Default to BT709. + auto primary_id = gfx::ColorSpace::PrimaryID::BT709; + if (!GetImageBufferProperty(image_buffer, kCVImageBufferColorPrimariesKey, + *kSupportedPrimaries, &primary_id)) { + DLOG(ERROR) << "Failed to find CVImageBufferRef primaries."; + } + return primary_id; +} + +gfx::ColorSpace::TransferID GetImageBufferTransferFn( + CVImageBufferRef image_buffer, + double* gamma) { + struct CVImageTransferFn { + const CFStringRef cfstr; + const gfx::ColorSpace::TransferID id; + }; + static const base::NoDestructor<std::vector<CVImageTransferFn>> + kSupportedTransferFuncs([] { + std::vector<CVImageTransferFn> supported_transfer_funcs; + supported_transfer_funcs.push_back( + {kCVImageBufferTransferFunction_ITU_R_709_2, + gfx::ColorSpace::TransferID::BT709_APPLE}); + supported_transfer_funcs.push_back( + {kCVImageBufferTransferFunction_SMPTE_240M_1995, + gfx::ColorSpace::TransferID::SMPTE240M}); + supported_transfer_funcs.push_back( + {kCVImageBufferTransferFunction_UseGamma, + gfx::ColorSpace::TransferID::CUSTOM}); + if (@available(macos 10.11, *)) { + supported_transfer_funcs.push_back( + {kCVImageBufferTransferFunction_ITU_R_2020, + gfx::ColorSpace::TransferID::BT2020_10}); + } + if (@available(macos 10.12, *)) { + supported_transfer_funcs.push_back( + {kCVImageBufferTransferFunction_SMPTE_ST_428_1, + gfx::ColorSpace::TransferID::SMPTEST428_1}); + } + if (@available(macos 10.13, *)) { + supported_transfer_funcs.push_back( + {kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ, + gfx::ColorSpace::TransferID::SMPTEST2084}); + supported_transfer_funcs.push_back( + {kCVImageBufferTransferFunction_ITU_R_2100_HLG, + gfx::ColorSpace::TransferID::ARIB_STD_B67}); + supported_transfer_funcs.push_back( + {kCVImageBufferTransferFunction_sRGB, + gfx::ColorSpace::TransferID::IEC61966_2_1}); + } + if (@available(macos 10.14, *)) { + supported_transfer_funcs.push_back( + {kCVImageBufferTransferFunction_Linear, + gfx::ColorSpace::TransferID::LINEAR}); + } + + return supported_transfer_funcs; + }()); + + // The named transfer function. + auto transfer_id = gfx::ColorSpace::TransferID::BT709; + if (!GetImageBufferProperty(image_buffer, kCVImageBufferTransferFunctionKey, + *kSupportedTransferFuncs, &transfer_id)) { + DLOG(ERROR) << "Failed to find CVImageBufferRef transfer."; + } + + if (transfer_id != gfx::ColorSpace::TransferID::CUSTOM) + return transfer_id; + + // If we fail to retrieve the gamma parameter, fall back to BT709. + constexpr auto kDefaultTransferFn = gfx::ColorSpace::TransferID::BT709; + CFNumberRef gamma_number = + reinterpret_cast<CFNumberRef>(CVBufferGetAttachment( + image_buffer, kCVImageBufferGammaLevelKey, nullptr)); + if (!gamma_number) { + DLOG(ERROR) << "Failed to get CVImageBufferRef gamma level."; + return kDefaultTransferFn; + } + + // CGFloat is a double on 64-bit systems. + CGFloat gamma_double = 0; + if (!CFNumberGetValue(gamma_number, kCFNumberCGFloatType, &gamma_double)) { + DLOG(ERROR) << "Failed to get CVImageBufferRef gamma level as float."; + return kDefaultTransferFn; + } + + if (gamma_double == 2.2) + return gfx::ColorSpace::TransferID::GAMMA22; + if (gamma_double == 2.8) + return gfx::ColorSpace::TransferID::GAMMA28; + + *gamma = gamma_double; + return transfer_id; +} + +gfx::ColorSpace::MatrixID GetImageBufferMatrix(CVImageBufferRef image_buffer) { + struct CVImageMatrix { + const CFStringRef cfstr; + gfx::ColorSpace::MatrixID id; + }; + static const base::NoDestructor<std::vector<CVImageMatrix>> + kSupportedMatrices([] { + std::vector<CVImageMatrix> supported_matrices; + supported_matrices.push_back({kCVImageBufferYCbCrMatrix_ITU_R_709_2, + gfx::ColorSpace::MatrixID::BT709}); + supported_matrices.push_back({kCVImageBufferYCbCrMatrix_ITU_R_601_4, + gfx::ColorSpace::MatrixID::SMPTE170M}); + supported_matrices.push_back({kCVImageBufferYCbCrMatrix_SMPTE_240M_1995, + gfx::ColorSpace::MatrixID::SMPTE240M}); + if (@available(macos 10.11, *)) { + supported_matrices.push_back({kCVImageBufferYCbCrMatrix_ITU_R_2020, + gfx::ColorSpace::MatrixID::BT2020_NCL}); + } + return supported_matrices; + }()); + + auto matrix_id = gfx::ColorSpace::MatrixID::INVALID; + if (!GetImageBufferProperty(image_buffer, kCVImageBufferYCbCrMatrixKey, + *kSupportedMatrices, &matrix_id)) { + DLOG(ERROR) << "Failed to find CVImageBufferRef YUV matrix."; + } + return matrix_id; +} + +} // namespace + +namespace media { + +CFMutableDictionaryRef CreateFormatExtensions( + CMVideoCodecType codec_type, + VideoCodecProfile profile, + const VideoColorSpace& color_space, + base::Optional<HDRMetadata> hdr_metadata) { + auto* extensions = [[NSMutableDictionary alloc] init]; + SetDictionaryValue(extensions, kCMFormatDescriptionExtension_FormatName, + CMVideoCodecTypeToString(codec_type)); + + // YCbCr without alpha uses 24. See + // http://developer.apple.com/qa/qa2001/qa1183.html + SetDictionaryValue(extensions, kCMFormatDescriptionExtension_Depth, @24); + + // Set primaries. + SetDictionaryValue(extensions, kCMFormatDescriptionExtension_ColorPrimaries, + GetPrimaries(color_space.primaries)); + + // Set transfer function. + SetDictionaryValue(extensions, kCMFormatDescriptionExtension_TransferFunction, + GetTransferFunction(color_space.transfer)); + if (color_space.transfer == VideoColorSpace::TransferID::GAMMA22) { + SetDictionaryValue(extensions, kCMFormatDescriptionExtension_GammaLevel, + @2.2); + } else if (color_space.transfer == VideoColorSpace::TransferID::GAMMA28) { + SetDictionaryValue(extensions, kCMFormatDescriptionExtension_GammaLevel, + @2.8); + } + + // Set matrix. + SetDictionaryValue(extensions, kCMFormatDescriptionExtension_YCbCrMatrix, + GetMatrix(color_space.matrix)); + + // Set full range flag. + SetDictionaryValue(extensions, kCMFormatDescriptionExtension_FullRangeVideo, + @(color_space.range == gfx::ColorSpace::RangeID::FULL)); + + if (hdr_metadata) { + SetContentLightLevelInfo(*hdr_metadata, extensions); + SetMasteringMetadata(*hdr_metadata, extensions); + } + + return base::mac::NSToCFCast(extensions); +} + +gfx::ColorSpace GetImageBufferColorSpace(CVImageBufferRef image_buffer) { + double gamma; + auto primary_id = GetImageBufferPrimary(image_buffer); + auto matrix_id = GetImageBufferMatrix(image_buffer); + auto transfer_id = GetImageBufferTransferFn(image_buffer, &gamma); + + // Use a matrix id that is coherent with a primary id. Useful when we fail to + // parse the matrix. Previously it was always defaulting to MatrixID::BT709 + // See http://crbug.com/788236. + if (matrix_id == gfx::ColorSpace::MatrixID::INVALID) { + if (primary_id == gfx::ColorSpace::PrimaryID::BT470BG) + matrix_id = gfx::ColorSpace::MatrixID::BT470BG; + else + matrix_id = gfx::ColorSpace::MatrixID::BT709; + } + + // It is specified to the decoder to use luma=[16,235] chroma=[16,240] via + // the kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange. + // + // TODO(crbug.com/1103432): We'll probably need support for more than limited + // range content if we want this to be used for more than video sites. + auto range_id = gfx::ColorSpace::RangeID::LIMITED; + + if (transfer_id == gfx::ColorSpace::TransferID::CUSTOM) { + // Transfer functions can also be specified as a gamma value. + skcms_TransferFunction custom_tr_fn = {2.2f, 1, 0, 1, 0, 0, 0}; + if (transfer_id == gfx::ColorSpace::TransferID::CUSTOM) + custom_tr_fn.g = gamma; + + return gfx::ColorSpace(primary_id, gfx::ColorSpace::TransferID::CUSTOM, + matrix_id, range_id, nullptr, &custom_tr_fn); + } + + return gfx::ColorSpace(primary_id, transfer_id, matrix_id, range_id); +} + +} // namespace media
diff --git a/media/gpu/mac/vt_config_util_unittest.cc b/media/gpu/mac/vt_config_util_unittest.cc new file mode 100644 index 0000000..9f728315 --- /dev/null +++ b/media/gpu/mac/vt_config_util_unittest.cc
@@ -0,0 +1,291 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/gpu/mac/vt_config_util.h" + +#include "base/containers/span.h" +#include "base/mac/foundation_util.h" +#include "base/strings/sys_string_conversions.h" +#include "media/formats/mp4/box_definitions.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +std::string GetStrValue(CFMutableDictionaryRef dict, CFStringRef key) { + return base::SysCFStringRefToUTF8( + base::mac::CFCastStrict<CFStringRef>(CFDictionaryGetValue(dict, key))); +} + +CFStringRef GetCFStrValue(CFMutableDictionaryRef dict, CFStringRef key) { + return base::mac::CFCastStrict<CFStringRef>(CFDictionaryGetValue(dict, key)); +} + +int GetIntValue(CFMutableDictionaryRef dict, CFStringRef key) { + CFNumberRef value = + base::mac::CFCastStrict<CFNumberRef>(CFDictionaryGetValue(dict, key)); + int result; + return CFNumberGetValue(value, kCFNumberIntType, &result) ? result : -1; +} + +bool GetBoolValue(CFMutableDictionaryRef dict, CFStringRef key) { + return CFBooleanGetValue( + base::mac::CFCastStrict<CFBooleanRef>(CFDictionaryGetValue(dict, key))); +} + +base::span<const uint8_t> GetDataValue(CFMutableDictionaryRef dict, + CFStringRef key) { + CFDataRef data = + base::mac::CFCastStrict<CFDataRef>(CFDictionaryGetValue(dict, key)); + return data ? base::span<const uint8_t>( + reinterpret_cast<const uint8_t*>(CFDataGetBytePtr(data)), + CFDataGetLength(data)) + : base::span<const uint8_t>(); +} + +base::ScopedCFTypeRef<CVImageBufferRef> CreateCVImageBuffer( + media::VideoColorSpace cs) { + base::ScopedCFTypeRef<CFMutableDictionaryRef> fmt( + CreateFormatExtensions(kCMVideoCodecType_H264, media::H264PROFILE_MAIN, + cs, media::HDRMetadata())); + + base::ScopedCFTypeRef<CVImageBufferRef> image_buffer; + OSStatus err = + CVPixelBufferCreate(kCFAllocatorDefault, 16, 16, + kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, + nullptr, image_buffer.InitializeInto()); + if (err != noErr) { + EXPECT_EQ(err, noErr); + return base::ScopedCFTypeRef<CVImageBufferRef>(); + } + + CVBufferSetAttachments(image_buffer.get(), fmt, + kCVAttachmentMode_ShouldNotPropagate); + return image_buffer; +} + +gfx::ColorSpace ToBT709_APPLE(gfx::ColorSpace cs) { + return gfx::ColorSpace(cs.GetPrimaryID(), + gfx::ColorSpace::TransferID::BT709_APPLE, + cs.GetMatrixID(), cs.GetRangeID()); +} + +void AssertHasEmptyHDRMetadata(CFMutableDictionaryRef fmt) { + if (__builtin_available(macos 10.13, *)) { + // We constructed with an empty HDRMetadata, so all values should be zero. + auto mdcv = GetDataValue( + fmt, kCMFormatDescriptionExtension_MasteringDisplayColorVolume); + ASSERT_EQ(24u, mdcv.size()); + for (size_t i = 0; i < mdcv.size(); ++i) + EXPECT_EQ(0u, mdcv[i]); + + auto clli = + GetDataValue(fmt, kCMFormatDescriptionExtension_ContentLightLevelInfo); + ASSERT_EQ(4u, clli.size()); + for (size_t i = 0; i < clli.size(); ++i) + EXPECT_EQ(0u, clli[i]); + } +} + +} // namespace + +namespace media { + +TEST(VTConfigUtil, CreateFormatExtensions_H264_BT709) { + base::ScopedCFTypeRef<CFMutableDictionaryRef> fmt( + CreateFormatExtensions(kCMVideoCodecType_H264, H264PROFILE_MAIN, + VideoColorSpace::REC709(), base::nullopt)); + EXPECT_EQ("avc1", GetStrValue(fmt, kCMFormatDescriptionExtension_FormatName)); + EXPECT_EQ(24, GetIntValue(fmt, kCMFormatDescriptionExtension_Depth)); + EXPECT_EQ(kCMFormatDescriptionColorPrimaries_ITU_R_709_2, + GetCFStrValue(fmt, kCMFormatDescriptionExtension_ColorPrimaries)); + EXPECT_EQ(kCMFormatDescriptionTransferFunction_ITU_R_709_2, + GetCFStrValue(fmt, kCMFormatDescriptionExtension_TransferFunction)); + EXPECT_EQ(kCMFormatDescriptionYCbCrMatrix_ITU_R_709_2, + GetCFStrValue(fmt, kCMFormatDescriptionExtension_YCbCrMatrix)); + EXPECT_FALSE(GetBoolValue(fmt, kCMFormatDescriptionExtension_FullRangeVideo)); + + if (__builtin_available(macos 10.13, *)) { + EXPECT_TRUE( + GetDataValue(fmt, + kCMFormatDescriptionExtension_MasteringDisplayColorVolume) + .empty()); + EXPECT_TRUE( + GetDataValue(fmt, kCMFormatDescriptionExtension_ContentLightLevelInfo) + .empty()); + } +} + +TEST(VTConfigUtil, CreateFormatExtensions_H264_BT2020_PQ) { + base::ScopedCFTypeRef<CFMutableDictionaryRef> fmt(CreateFormatExtensions( + kCMVideoCodecType_H264, H264PROFILE_MAIN, + VideoColorSpace(VideoColorSpace::PrimaryID::BT2020, + VideoColorSpace::TransferID::SMPTEST2084, + VideoColorSpace::MatrixID::BT2020_NCL, + gfx::ColorSpace::RangeID::FULL), + HDRMetadata())); + EXPECT_EQ("avc1", GetStrValue(fmt, kCMFormatDescriptionExtension_FormatName)); + EXPECT_EQ(24, GetIntValue(fmt, kCMFormatDescriptionExtension_Depth)); + + if (__builtin_available(macos 10.13, *)) { + EXPECT_EQ(kCMFormatDescriptionColorPrimaries_ITU_R_2020, + GetCFStrValue(fmt, kCMFormatDescriptionExtension_ColorPrimaries)); + EXPECT_EQ( + kCMFormatDescriptionTransferFunction_SMPTE_ST_2084_PQ, + GetCFStrValue(fmt, kCMFormatDescriptionExtension_TransferFunction)); + EXPECT_EQ(kCMFormatDescriptionYCbCrMatrix_ITU_R_2020, + GetCFStrValue(fmt, kCMFormatDescriptionExtension_YCbCrMatrix)); + } + EXPECT_TRUE(GetBoolValue(fmt, kCMFormatDescriptionExtension_FullRangeVideo)); + AssertHasEmptyHDRMetadata(fmt); +} + +TEST(VTConfigUtil, CreateFormatExtensions_H264_BT2020_HLG) { + base::ScopedCFTypeRef<CFMutableDictionaryRef> fmt(CreateFormatExtensions( + kCMVideoCodecType_H264, H264PROFILE_MAIN, + VideoColorSpace(VideoColorSpace::PrimaryID::BT2020, + VideoColorSpace::TransferID::ARIB_STD_B67, + VideoColorSpace::MatrixID::BT2020_NCL, + gfx::ColorSpace::RangeID::FULL), + HDRMetadata())); + EXPECT_EQ("avc1", GetStrValue(fmt, kCMFormatDescriptionExtension_FormatName)); + EXPECT_EQ(24, GetIntValue(fmt, kCMFormatDescriptionExtension_Depth)); + + if (__builtin_available(macos 10.13, *)) { + EXPECT_EQ(kCMFormatDescriptionColorPrimaries_ITU_R_2020, + GetCFStrValue(fmt, kCMFormatDescriptionExtension_ColorPrimaries)); + EXPECT_EQ( + kCMFormatDescriptionTransferFunction_ITU_R_2100_HLG, + GetCFStrValue(fmt, kCMFormatDescriptionExtension_TransferFunction)); + EXPECT_EQ(kCMFormatDescriptionYCbCrMatrix_ITU_R_2020, + GetCFStrValue(fmt, kCMFormatDescriptionExtension_YCbCrMatrix)); + } + EXPECT_TRUE(GetBoolValue(fmt, kCMFormatDescriptionExtension_FullRangeVideo)); + AssertHasEmptyHDRMetadata(fmt); +} + +TEST(VTConfigUtil, CreateFormatExtensions_HDRMetadata) { + // Values from real YouTube HDR content. + HDRMetadata hdr_meta; + hdr_meta.max_content_light_level = 1000; + hdr_meta.max_frame_average_light_level = 600; + auto& mastering = hdr_meta.mastering_metadata; + mastering.luminance_min = 0; + mastering.luminance_max = 1000; + mastering.primary_r = gfx::PointF(0.68, 0.32); + mastering.primary_g = gfx::PointF(0.2649, 0.69); + mastering.primary_b = gfx::PointF(0.15, 0.06); + mastering.white_point = gfx::PointF(0.3127, 0.3290); + + base::ScopedCFTypeRef<CFMutableDictionaryRef> fmt(CreateFormatExtensions( + kCMVideoCodecType_H264, H264PROFILE_MAIN, + VideoColorSpace(VideoColorSpace::PrimaryID::BT2020, + VideoColorSpace::TransferID::SMPTEST2084, + VideoColorSpace::MatrixID::BT2020_NCL, + gfx::ColorSpace::RangeID::FULL), + hdr_meta)); + if (__builtin_available(macos 10.13, *)) { + { + auto mdcv = GetDataValue( + fmt, kCMFormatDescriptionExtension_MasteringDisplayColorVolume); + ASSERT_EQ(24u, mdcv.size()); + std::unique_ptr<mp4::BoxReader> box_reader( + mp4::BoxReader::ReadConcatentatedBoxes(mdcv.data(), mdcv.size(), + nullptr)); + mp4::MasteringDisplayColorVolume mdcv_box; + ASSERT_TRUE(mdcv_box.Parse(box_reader.get())); + EXPECT_EQ(mdcv_box.display_primaries_gx, mastering.primary_g.x()); + EXPECT_EQ(mdcv_box.display_primaries_gy, mastering.primary_g.y()); + EXPECT_EQ(mdcv_box.display_primaries_bx, mastering.primary_b.x()); + EXPECT_EQ(mdcv_box.display_primaries_by, mastering.primary_b.y()); + EXPECT_EQ(mdcv_box.display_primaries_rx, mastering.primary_r.x()); + EXPECT_EQ(mdcv_box.display_primaries_ry, mastering.primary_r.y()); + EXPECT_EQ(mdcv_box.white_point_x, mastering.white_point.x()); + EXPECT_EQ(mdcv_box.white_point_y, mastering.white_point.y()); + EXPECT_EQ(mdcv_box.max_display_mastering_luminance, + mastering.luminance_max); + EXPECT_EQ(mdcv_box.min_display_mastering_luminance, + mastering.luminance_min); + } + + { + auto clli = GetDataValue( + fmt, kCMFormatDescriptionExtension_ContentLightLevelInfo); + ASSERT_EQ(4u, clli.size()); + std::unique_ptr<mp4::BoxReader> box_reader( + mp4::BoxReader::ReadConcatentatedBoxes(clli.data(), clli.size(), + nullptr)); + mp4::ContentLightLevelInformation clli_box; + ASSERT_TRUE(clli_box.Parse(box_reader.get())); + EXPECT_EQ(clli_box.max_content_light_level, + hdr_meta.max_content_light_level); + EXPECT_EQ(clli_box.max_pic_average_light_level, + hdr_meta.max_frame_average_light_level); + } + } +} + +TEST(VTConfigUtil, GetImageBufferColorSpace_BT601) { + auto cs = VideoColorSpace::REC601(); + auto image_buffer = CreateCVImageBuffer(cs); + ASSERT_TRUE(image_buffer); + + // macOS doesn't have a SMPTE170M transfer function, apps are supposed to use + // kCMFormatDescriptionTransferFunction_ITU_R_709_2 instead for SDR content. + cs.primaries = VideoColorSpace::PrimaryID::SMPTE240M; + auto expected_cs = ToBT709_APPLE(cs.ToGfxColorSpace()); + EXPECT_EQ(expected_cs, GetImageBufferColorSpace(image_buffer)); +} + +TEST(VTConfigUtil, GetImageBufferColorSpace_BT709) { + auto cs = VideoColorSpace::REC709(); + auto image_buffer = CreateCVImageBuffer(cs); + ASSERT_TRUE(image_buffer); + + // macOS returns a special BT709_APPLE transfer function since it doesn't use + // the same gamma level as is standardized. + auto expected_cs = ToBT709_APPLE(cs.ToGfxColorSpace()); + EXPECT_EQ(expected_cs, GetImageBufferColorSpace(image_buffer)); +} + +TEST(VTConfigUtil, GetImageBufferColorSpace_GAMMA22) { + auto cs = VideoColorSpace(VideoColorSpace::PrimaryID::SMPTE240M, + VideoColorSpace::TransferID::GAMMA22, + VideoColorSpace::MatrixID::SMPTE240M, + gfx::ColorSpace::RangeID::LIMITED); + auto image_buffer = CreateCVImageBuffer(cs); + ASSERT_TRUE(image_buffer); + EXPECT_EQ(cs.ToGfxColorSpace(), GetImageBufferColorSpace(image_buffer)); +} + +TEST(VTConfigUtil, GetImageBufferColorSpace_GAMMA28) { + auto cs = VideoColorSpace(VideoColorSpace::PrimaryID::SMPTE240M, + VideoColorSpace::TransferID::GAMMA28, + VideoColorSpace::MatrixID::SMPTE240M, + gfx::ColorSpace::RangeID::LIMITED); + auto image_buffer = CreateCVImageBuffer(cs); + ASSERT_TRUE(image_buffer); + EXPECT_EQ(cs.ToGfxColorSpace(), GetImageBufferColorSpace(image_buffer)); +} + +TEST(VTConfigUtil, GetImageBufferColorSpace_BT2020_PQ) { + auto cs = VideoColorSpace(VideoColorSpace::PrimaryID::BT2020, + VideoColorSpace::TransferID::SMPTEST2084, + VideoColorSpace::MatrixID::BT2020_NCL, + gfx::ColorSpace::RangeID::LIMITED); + auto image_buffer = CreateCVImageBuffer(cs); + ASSERT_TRUE(image_buffer); + EXPECT_EQ(cs.ToGfxColorSpace(), GetImageBufferColorSpace(image_buffer)); +} + +TEST(VTConfigUtil, GetImageBufferColorSpace_BT2020_HLG) { + auto cs = VideoColorSpace(VideoColorSpace::PrimaryID::BT2020, + VideoColorSpace::TransferID::ARIB_STD_B67, + VideoColorSpace::MatrixID::BT2020_NCL, + gfx::ColorSpace::RangeID::LIMITED); + auto image_buffer = CreateCVImageBuffer(cs); + ASSERT_TRUE(image_buffer); + EXPECT_EQ(cs.ToGfxColorSpace(), GetImageBufferColorSpace(image_buffer)); +} + +} // namespace media
diff --git a/media/gpu/mac/vt_video_decode_accelerator_mac.cc b/media/gpu/mac/vt_video_decode_accelerator_mac.cc index fe2a8cb..7d51dbe9 100644 --- a/media/gpu/mac/vt_video_decode_accelerator_mac.cc +++ b/media/gpu/mac/vt_video_decode_accelerator_mac.cc
@@ -18,6 +18,7 @@ #include "base/bind.h" #include "base/logging.h" #include "base/mac/mac_logging.h" +#include "base/mac/mac_util.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/stl_util.h" @@ -32,6 +33,8 @@ #include "base/version.h" #include "components/crash/core/common/crash_key.h" #include "media/base/limits.h" +#include "media/base/media_switches.h" +#include "media/gpu/mac/vt_config_util.h" #include "ui/gfx/geometry/rect.h" #include "ui/gl/gl_context.h" #include "ui/gl/gl_image_io_surface.h" @@ -110,6 +113,9 @@ // Note that 4:2:0 textures cannot be used directly as RGBA in OpenGL, but are // lower power than 4:2:2 when composited directly by CoreAnimation. + // + // TODO(crbug.com/1103432): Based on other > 8-bit codecs, we'll likely need + // to add kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange here. int32_t pixel_format = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; #define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i) base::ScopedCFTypeRef<CFNumberRef> cf_pixel_format(CFINT(pixel_format)); @@ -297,155 +303,6 @@ vda->Output(source_frame_refcon, status, image_buffer); } -// Read the value for the key in |key| to CFString and convert it to IdType. -// Use the list of pairs in |cfstr_id_pairs| to do the conversion (by doing a -// linear lookup). -template <typename IdType, typename StringIdPair> -bool GetImageBufferProperty(CVImageBufferRef image_buffer, - CFStringRef key, - const StringIdPair* cfstr_id_pairs, - size_t cfstr_id_pairs_size, - IdType* value_as_id) { - CFStringRef value_as_string = reinterpret_cast<CFStringRef>( - CVBufferGetAttachment(image_buffer, key, nullptr)); - if (!value_as_string) - return false; - - for (size_t i = 0; i < cfstr_id_pairs_size; ++i) { - if (!CFStringCompare(value_as_string, cfstr_id_pairs[i].cfstr, 0)) { - *value_as_id = cfstr_id_pairs[i].id; - return true; - } - } - - return false; -} - -// Use a matrix id that is coherent with a primary id. Useful when we fail to -// parse the matrix. Previously it was always defaulting to MatrixID::BT709 -// See http://crbug.com/788236. -gfx::ColorSpace::MatrixID GetDefaultMatrixID( - const gfx::ColorSpace::PrimaryID primary_id) { - gfx::ColorSpace::MatrixID matrix_id = gfx::ColorSpace::MatrixID::BT709; - - switch (primary_id) { - case gfx::ColorSpace::PrimaryID::BT709: - matrix_id = gfx::ColorSpace::MatrixID::BT709; - break; - case gfx::ColorSpace::PrimaryID::BT470BG: - matrix_id = gfx::ColorSpace::MatrixID::BT470BG; - break; - default: - break; - } - - return matrix_id; -} - -gfx::ColorSpace GetImageBufferColorSpace(CVImageBufferRef image_buffer) { - // The named primaries. Default to BT709. - gfx::ColorSpace::PrimaryID primary_id = gfx::ColorSpace::PrimaryID::BT709; - struct { - const CFStringRef cfstr; - const gfx::ColorSpace::PrimaryID id; - } primaries[] = { - { - kCVImageBufferColorPrimaries_ITU_R_709_2, - gfx::ColorSpace::PrimaryID::BT709, - }, - { - kCVImageBufferColorPrimaries_EBU_3213, - gfx::ColorSpace::PrimaryID::BT470BG, - }, - { - kCVImageBufferColorPrimaries_SMPTE_C, - gfx::ColorSpace::PrimaryID::SMPTE240M, - }, - }; - if (!GetImageBufferProperty(image_buffer, kCVImageBufferColorPrimariesKey, - primaries, base::size(primaries), &primary_id)) { - DLOG(ERROR) << "Failed to find CVImageBufferRef primaries."; - } - - // The named transfer function. - gfx::ColorSpace::TransferID transfer_id = gfx::ColorSpace::TransferID::BT709; - skcms_TransferFunction custom_tr_fn = {2.2f, 1, 0, 1, 0, 0, 0}; - struct { - const CFStringRef cfstr; - gfx::ColorSpace::TransferID id; - } transfers[] = { - { - kCVImageBufferTransferFunction_ITU_R_709_2, - gfx::ColorSpace::TransferID::BT709_APPLE, - }, - { - kCVImageBufferTransferFunction_SMPTE_240M_1995, - gfx::ColorSpace::TransferID::SMPTE240M, - }, - { - kCVImageBufferTransferFunction_UseGamma, - gfx::ColorSpace::TransferID::CUSTOM, - }, - }; - if (!GetImageBufferProperty(image_buffer, kCVImageBufferTransferFunctionKey, - transfers, base::size(transfers), &transfer_id)) { - DLOG(ERROR) << "Failed to find CVImageBufferRef transfer."; - } - - // Transfer functions can also be specified as a gamma value. - if (transfer_id == gfx::ColorSpace::TransferID::CUSTOM) { - // If we fail to find the custom transfer function parameters, fall back to - // BT709. - transfer_id = gfx::ColorSpace::TransferID::BT709; - CFNumberRef gamma_number = - reinterpret_cast<CFNumberRef>(CVBufferGetAttachment( - image_buffer, kCVImageBufferGammaLevelKey, nullptr)); - if (gamma_number) { - CGFloat gamma_float = 0; - if (CFNumberGetValue(gamma_number, kCFNumberCGFloatType, &gamma_float)) { - transfer_id = gfx::ColorSpace::TransferID::CUSTOM; - custom_tr_fn.g = gamma_float; - } else { - DLOG(ERROR) << "Failed to get CVImageBufferRef gamma level as float."; - } - } else { - DLOG(ERROR) << "Failed to get CVImageBufferRef gamma level."; - } - } - - // Read the RGB to YUV matrix ID. - gfx::ColorSpace::MatrixID matrix_id = GetDefaultMatrixID(primary_id); - struct { - const CFStringRef cfstr; - gfx::ColorSpace::MatrixID id; - } matrices[] = {{ - kCVImageBufferYCbCrMatrix_ITU_R_709_2, - gfx::ColorSpace::MatrixID::BT709, - }, - { - kCVImageBufferYCbCrMatrix_ITU_R_601_4, - gfx::ColorSpace::MatrixID::SMPTE170M, - }, - { - kCVImageBufferYCbCrMatrix_SMPTE_240M_1995, - gfx::ColorSpace::MatrixID::SMPTE240M, - }}; - if (!GetImageBufferProperty(image_buffer, kCVImageBufferYCbCrMatrixKey, - matrices, base::size(matrices), &matrix_id)) { - DLOG(ERROR) << "Failed to find CVImageBufferRef YUV matrix."; - } - - // It is specified to the decoder to use luma=[16,235] chroma=[16,240] via - // the kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange. - gfx::ColorSpace::RangeID range_id = gfx::ColorSpace::RangeID::LIMITED; - - if (transfer_id == gfx::ColorSpace::TransferID::CUSTOM) { - return gfx::ColorSpace(primary_id, gfx::ColorSpace::TransferID::CUSTOM, - matrix_id, range_id, nullptr, &custom_tr_fn); - } - return gfx::ColorSpace(primary_id, transfer_id, matrix_id, range_id); -} - } // namespace bool InitializeVideoToolbox() { @@ -615,6 +472,7 @@ client_ = client; // Spawn a thread to handle parsing and calling VideoToolbox. + // TODO(sandersd): This should probably use a base::ThreadPool thread instead. if (!decoder_thread_.Start()) { DLOG(ERROR) << "Failed to start decoder thread"; return false;
diff --git a/media/gpu/test/fake_command_buffer_helper.cc b/media/gpu/test/fake_command_buffer_helper.cc index 4e7a92cb..2d282ee 100644 --- a/media/gpu/test/fake_command_buffer_helper.cc +++ b/media/gpu/test/fake_command_buffer_helper.cc
@@ -3,6 +3,8 @@ // found in the LICENSE file. #include "media/gpu/test/fake_command_buffer_helper.h" +#include "gpu/command_buffer/service/shared_image_backing.h" +#include "gpu/command_buffer/service/shared_image_representation.h" #include "base/logging.h" @@ -75,6 +77,21 @@ return is_context_current_; } +std::unique_ptr<gpu::SharedImageRepresentationFactoryRef> +FakeCommandBufferHelper::Register( + std::unique_ptr<gpu::SharedImageBacking> backing) { + DVLOG(2) << __func__; + DCHECK(task_runner_->BelongsToCurrentThread()); + return nullptr; +} + +gpu::TextureBase* FakeCommandBufferHelper::GetTexture(GLuint service_id) const { + DVLOG(2) << __func__ << "(" << service_id << ")"; + DCHECK(task_runner_->BelongsToCurrentThread()); + DCHECK(service_ids_.count(service_id)); + return nullptr; +} + GLuint FakeCommandBufferHelper::CreateTexture(GLenum target, GLenum internal_format, GLsizei width,
diff --git a/media/gpu/test/fake_command_buffer_helper.h b/media/gpu/test/fake_command_buffer_helper.h index e136974..1e019ce1 100644 --- a/media/gpu/test/fake_command_buffer_helper.h +++ b/media/gpu/test/fake_command_buffer_helper.h
@@ -41,6 +41,9 @@ gl::GLContext* GetGLContext() override; bool HasStub() override; bool MakeContextCurrent() override; + std::unique_ptr<gpu::SharedImageRepresentationFactoryRef> Register( + std::unique_ptr<gpu::SharedImageBacking> backing) override; + gpu::TextureBase* GetTexture(GLuint service_id) const override; GLuint CreateTexture(GLenum target, GLenum internal_format, GLsizei width,
diff --git a/media/gpu/windows/d3d11_copying_texture_wrapper.cc b/media/gpu/windows/d3d11_copying_texture_wrapper.cc index 9157fe8..cbe44cf 100644 --- a/media/gpu/windows/d3d11_copying_texture_wrapper.cc +++ b/media/gpu/windows/d3d11_copying_texture_wrapper.cc
@@ -30,8 +30,6 @@ CopyingTexture2DWrapper::~CopyingTexture2DWrapper() = default; Status CopyingTexture2DWrapper::ProcessTexture( - ComD3D11Texture2D texture, - size_t array_slice, const gfx::ColorSpace& input_color_space, MailboxHolderArray* mailbox_dest, gfx::ColorSpace* output_color_space) { @@ -48,11 +46,11 @@ D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC input_view_desc = {0}; input_view_desc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D; - input_view_desc.Texture2D.ArraySlice = array_slice; + input_view_desc.Texture2D.ArraySlice = array_slice_; input_view_desc.Texture2D.MipSlice = 0; ComD3D11VideoProcessorInputView input_view; hr = video_processor_->CreateVideoProcessorInputView( - texture.Get(), &input_view_desc, &input_view); + texture_.Get(), &input_view_desc, &input_view); if (!SUCCEEDED(hr)) { return Status(StatusCode::kCreateVideoProcessorInputViewFailed) .AddCause(HresultToStatus(hr)); @@ -85,19 +83,27 @@ .AddCause(HresultToStatus(hr)); } - return output_texture_wrapper_->ProcessTexture( - output_texture_, 0, copy_color_space, mailbox_dest, output_color_space); + return output_texture_wrapper_->ProcessTexture(copy_color_space, mailbox_dest, + output_color_space); } Status CopyingTexture2DWrapper::Init( scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, - GetCommandBufferHelperCB get_helper_cb) { + GetCommandBufferHelperCB get_helper_cb, + ComD3D11Texture2D texture, + size_t array_slice) { auto result = video_processor_->Init(size_.width(), size_.height()); if (!result.is_ok()) return std::move(result).AddHere(); + // Remember the texture + array_slice so later, ProcessTexture can still use + // it. + texture_ = texture; + array_slice_ = array_slice; + return output_texture_wrapper_->Init(std::move(gpu_task_runner), - std::move(get_helper_cb)); + std::move(get_helper_cb), + output_texture_, /*array_slice=*/0); } void CopyingTexture2DWrapper::SetStreamHDRMetadata(
diff --git a/media/gpu/windows/d3d11_copying_texture_wrapper.h b/media/gpu/windows/d3d11_copying_texture_wrapper.h index e62d1c7..1583f8b 100644 --- a/media/gpu/windows/d3d11_copying_texture_wrapper.h +++ b/media/gpu/windows/d3d11_copying_texture_wrapper.h
@@ -31,14 +31,14 @@ base::Optional<gfx::ColorSpace> output_color_space); ~CopyingTexture2DWrapper() override; - Status ProcessTexture(ComD3D11Texture2D texture, - size_t array_slice, - const gfx::ColorSpace& input_color_space, + Status ProcessTexture(const gfx::ColorSpace& input_color_space, MailboxHolderArray* mailbox_dest, gfx::ColorSpace* output_color_space) override; Status Init(scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, - GetCommandBufferHelperCB get_helper_cb) override; + GetCommandBufferHelperCB get_helper_cb, + ComD3D11Texture2D texture, + size_t array_slice) override; void SetStreamHDRMetadata(const HDRMetadata& stream_metadata) override; void SetDisplayHDRMetadata( @@ -54,6 +54,9 @@ // If set, this is the color space that we last saw in ProcessTexture. base::Optional<gfx::ColorSpace> previous_input_color_space_; + + ComD3D11Texture2D texture_; + size_t array_slice_ = 0; }; } // namespace media
diff --git a/media/gpu/windows/d3d11_copying_texture_wrapper_unittest.cc b/media/gpu/windows/d3d11_copying_texture_wrapper_unittest.cc index 9677dcc6..e6058c9 100644 --- a/media/gpu/windows/d3d11_copying_texture_wrapper_unittest.cc +++ b/media/gpu/windows/d3d11_copying_texture_wrapper_unittest.cc
@@ -86,9 +86,7 @@ public: MockTexture2DWrapper() {} - Status ProcessTexture(ComD3D11Texture2D texture, - size_t array_slice, - const gfx::ColorSpace& input_color_space, + Status ProcessTexture(const gfx::ColorSpace& input_color_space, MailboxHolderArray* mailbox_dest, gfx::ColorSpace* output_color_space) override { // Pretend we created an arbitrary color space, so that we're sure that it @@ -98,7 +96,9 @@ } Status Init(scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, - GetCommandBufferHelperCB get_helper_cb) override { + GetCommandBufferHelperCB get_helper_cb, + ComD3D11Texture2D in_texture, + size_t array_slice) override { gpu_task_runner_ = std::move(gpu_task_runner); return MockInit(); } @@ -222,16 +222,19 @@ MailboxHolderArray mailboxes; gfx::ColorSpace input_color_space = gfx::ColorSpace::CreateSCRGBLinear(); gfx::ColorSpace output_color_space; - EXPECT_EQ(wrapper->Init(gpu_task_runner_, CreateMockHelperCB()).is_ok(), + EXPECT_EQ(wrapper + ->Init(gpu_task_runner_, CreateMockHelperCB(), + /*texture_d3d=*/nullptr, /*array_slice=*/0) + .is_ok(), InitSucceeds()); task_environment_.RunUntilIdle(); if (GetProcessorProxyInit()) EXPECT_EQ(texture_wrapper_raw->gpu_task_runner_, gpu_task_runner_); - EXPECT_EQ(wrapper - ->ProcessTexture(nullptr, 0, input_color_space, &mailboxes, - &output_color_space) - .is_ok(), - ProcessTextureSucceeds()); + EXPECT_EQ( + wrapper + ->ProcessTexture(input_color_space, &mailboxes, &output_color_space) + .is_ok(), + ProcessTextureSucceeds()); if (ProcessTextureSucceeds()) { // Regardless of what the input space is, the output should be provided by
diff --git a/media/gpu/windows/d3d11_picture_buffer.cc b/media/gpu/windows/d3d11_picture_buffer.cc index ee56553..aeb0d40 100644 --- a/media/gpu/windows/d3d11_picture_buffer.cc +++ b/media/gpu/windows/d3d11_picture_buffer.cc
@@ -52,8 +52,9 @@ view_desc.ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D; view_desc.Texture2D.ArraySlice = array_slice_; - Status result = texture_wrapper_->Init(std::move(gpu_task_runner), - std::move(get_helper_cb)); + Status result = + texture_wrapper_->Init(std::move(gpu_task_runner), + std::move(get_helper_cb), texture_, array_slice_); if (!result.is_ok()) { MEDIA_LOG(ERROR, media_log) << "Failed to Initialize the wrapper"; return result; @@ -75,8 +76,7 @@ const gfx::ColorSpace& input_color_space, MailboxHolderArray* mailbox_dest, gfx::ColorSpace* output_color_space) { - return texture_wrapper_->ProcessTexture(Texture(), array_slice_, - input_color_space, mailbox_dest, + return texture_wrapper_->ProcessTexture(input_color_space, mailbox_dest, output_color_space); }
diff --git a/media/gpu/windows/d3d11_texture_wrapper.cc b/media/gpu/windows/d3d11_texture_wrapper.cc index 80612a2f..fccbc0a 100644 --- a/media/gpu/windows/d3d11_texture_wrapper.cc +++ b/media/gpu/windows/d3d11_texture_wrapper.cc
@@ -9,13 +9,38 @@ #include <utility> #include <vector> +#include "components/viz/common/resources/resource_format_utils.h" +#include "gpu/command_buffer/common/shared_image_usage.h" #include "gpu/command_buffer/service/mailbox_manager.h" +#include "gpu/command_buffer/service/shared_image_backing_d3d.h" #include "media/base/bind_to_current_loop.h" #include "media/base/win/mf_helpers.h" +#include "mojo/public/cpp/bindings/callback_helpers.h" #include "ui/gl/gl_image.h" namespace media { +namespace { + +base::Optional<viz::ResourceFormat> DXGIFormatToVizFormat( + DXGI_FORMAT dxgi_format) { + switch (dxgi_format) { + case DXGI_FORMAT_NV12: + return viz::YUV_420_BIPLANAR; + case DXGI_FORMAT_P010: + return viz::P010; + case DXGI_FORMAT_B8G8R8A8_UNORM: + return viz::BGRA_8888; + case DXGI_FORMAT_R16G16B16A16_FLOAT: + return viz::RGBA_F16; + default: + NOTREACHED(); + return {}; + } +} + +} // anonymous namespace + // Handy structure so that we can activate / bind one or two textures. struct ScopedTextureEverything { ScopedTextureEverything(GLenum unit, GLuint service_id) @@ -63,8 +88,6 @@ DefaultTexture2DWrapper::~DefaultTexture2DWrapper() = default; Status DefaultTexture2DWrapper::ProcessTexture( - ComD3D11Texture2D texture, - size_t array_slice, const gfx::ColorSpace& input_color_space, MailboxHolderArray* mailbox_dest, gfx::ColorSpace* output_color_space) { @@ -75,14 +98,6 @@ return Status(StatusCode::kProcessTextureFailed) .AddCause(std::move(*received_error_)); - // Temporary check to track down https://crbug.com/1077645 - CHECK(texture); - - // It's okay to post and forget this call, since it'll be ordered correctly - // with respect to any access on the gpu main thread. - gpu_resources_.Post(FROM_HERE, &GpuResources::PushNewTexture, - std::move(texture), array_slice); - // TODO(liberato): make sure that |mailbox_holders_| is zero-initialized in // case we don't use all the planes. for (size_t i = 0; i < VideoFrame::kMaxPlanes; i++) @@ -96,7 +111,9 @@ Status DefaultTexture2DWrapper::Init( scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, - GetCommandBufferHelperCB get_helper_cb) { + GetCommandBufferHelperCB get_helper_cb, + ComD3D11Texture2D texture, + size_t array_slice) { gpu_resources_ = base::SequenceBound<GpuResources>( std::move(gpu_task_runner), BindToCurrentLoop(base::BindOnce(&DefaultTexture2DWrapper::OnError, @@ -123,11 +140,18 @@ // The current implementation is. std::vector<gpu::Mailbox> mailboxes; for (int texture_idx = 0; texture_idx < textures_per_picture; texture_idx++) { - mailboxes.push_back(gpu::Mailbox::Generate()); + mailboxes.push_back(gpu::Mailbox::GenerateForSharedImage()); mailbox_holders_[texture_idx] = gpu::MailboxHolder( mailboxes[texture_idx], gpu::SyncToken(), GL_TEXTURE_EXTERNAL_OES); } + const base::Optional<viz::ResourceFormat> viz_format = + DXGIFormatToVizFormat(dxgi_format_); + if (!viz_format.has_value()) { + return Status(StatusCode::kUnsupportedTextureFormatForBind) + .WithData("dxgi_format", dxgi_format_); + } + // Start construction of the GpuResources. // We send the texture itself, since we assume that we're using the angle // device for decoding. Sharing seems not to work very well. Otherwise, we @@ -135,7 +159,8 @@ // a handle that we get from |texture| as an IDXGIResource1. gpu_resources_.Post(FROM_HERE, &GpuResources::Init, std::move(get_helper_cb), std::move(mailboxes), GL_TEXTURE_EXTERNAL_OES, size_, - textures_per_picture); + textures_per_picture, viz_format.value(), texture, + array_slice); return OkStatus(); } @@ -165,7 +190,10 @@ const std::vector<gpu::Mailbox> mailboxes, GLenum target, gfx::Size size, - int textures_per_picture) { + int textures_per_picture, + viz::ResourceFormat format, + ComD3D11Texture2D texture, + size_t array_slice) { helper_ = get_helper_cb.Run(); if (!helper_ || !helper_->MakeContextCurrent()) { @@ -173,17 +201,6 @@ return; } - // Create the textures and attach them to the mailboxes. - // TODO(liberato): Should we use GL_FLOAT for an fp16 texture? It doesn't - // really seem to matter so far as I can tell. - for (int texture_idx = 0; texture_idx < textures_per_picture; texture_idx++) { - uint32_t service_id = - helper_->CreateTexture(target, GL_RGBA, size.width(), size.height(), - GL_RGBA, GL_UNSIGNED_BYTE); - service_ids_.push_back(service_id); - helper_->ProduceTexture(mailboxes[texture_idx], service_id); - } - // Create the stream for zero-copy use by gl. EGLDisplay egl_display = gl::GLSurfaceEGL::GetHardwareDisplay(); const EGLint stream_attributes[] = { @@ -205,6 +222,60 @@ // have a FakeCommandBufferHelper since the service IDs aren't meaningful. gl_image_ = base::MakeRefCounted<gl::GLImageDXGI>(size, stream); + const GLenum internal_format = viz::GLInternalFormat(format); + const GLenum data_type = viz::GLDataType(format); + const GLenum data_format = viz::GLDataFormat(format); + + // Create the textures and attach them to the mailboxes. + // TODO(liberato): Should we use GL_FLOAT for an fp16 texture? It doesn't + // really seem to matter so far as I can tell. + for (int texture_idx = 0; texture_idx < textures_per_picture; texture_idx++) { + // TODO(crbug.com/1011555): CreateTexture allocates a GL texture, figure out + // if this can be removed. + const uint32_t service_id = + helper_->CreateTexture(target, internal_format, size.width(), + size.height(), data_format, data_type); + + const auto& mailbox = mailboxes[texture_idx]; + + // Shared image does not need to store the colorspace since it is already + // stored on the VideoFrame which is provided upon presenting the overlay. + // To prevent the developer from mistakenly using it, provide the invalid + // value from default-construction. + const gfx::ColorSpace kInvalidColorSpace; + + // Usage flags to allow the display compositor to draw from it, video to + // decode, and allow webgl/canvas access. + const uint32_t shared_image_usage = + gpu::SHARED_IMAGE_USAGE_VIDEO_DECODE | gpu::SHARED_IMAGE_USAGE_GLES2 | + gpu::SHARED_IMAGE_USAGE_RASTER | gpu::SHARED_IMAGE_USAGE_DISPLAY | + gpu::SHARED_IMAGE_USAGE_SCANOUT; + + // Create a shared image + // TODO(crbug.com/1011555): Need key shared mutex if shared image is ever + // used by another device. + scoped_refptr<gpu::gles2::TexturePassthrough> gl_texture = + gpu::gles2::TexturePassthrough::CheckedCast( + helper_->GetTexture(service_id)); + + auto shared_image = std::make_unique<gpu::SharedImageBackingD3D>( + mailbox, format, size, kInvalidColorSpace, kTopLeft_GrSurfaceOrigin, + kPremul_SkAlphaType, shared_image_usage, + /*swap_chain=*/nullptr, std::move(gl_texture), gl_image_, + /*buffer_index=*/0, texture, base::win::ScopedHandle(), + /*dxgi_key_mutex=*/nullptr); + + // Caller is assumed to provide cleared d3d textures. + shared_image->SetCleared(); + + // Shared images will be destroyed when this wrapper goes away. + // Only GpuResource can be used to safely destroy the shared images on the + // gpu main thread. + shared_images_.push_back(helper_->Register(std::move(shared_image))); + + service_ids_.push_back(service_id); + } + // Bind all the textures so that the stream can find them. OrderedDestructionList texture_everythings; for (int i = 0; i < textures_per_picture; i++) @@ -259,11 +330,15 @@ helper_->BindImage(service_ids_[texture_idx], gl_image_.get(), false /* client_managed */); } + + // Specify the texture so ProcessTexture knows how to process it using a GL + // image. + gl_image_->SetTexture(texture, array_slice); + + PushNewTexture(); } -void DefaultTexture2DWrapper::GpuResources::PushNewTexture( - ComD3D11Texture2D texture, - size_t array_slice) { +void DefaultTexture2DWrapper::GpuResources::PushNewTexture() { // If init didn't complete, then signal (another) error that will probably be // ignored in favor of whatever we signalled earlier. if (!gl_image_ || !stream_) { @@ -271,12 +346,6 @@ return; } - // Notify |gl_image_| that it has a new texture. Do this unconditionally, so - // hat we can guarantee that the image isn't null. Nobody expects it to be, - // and failures will be noticed only asynchronously. - // https://crbug.com/1077645 - gl_image_->SetTexture(texture, array_slice); - if (!helper_ || !helper_->MakeContextCurrent()) { NotifyError(StatusCode::kMakeContextCurrentFailed); return; @@ -285,14 +354,14 @@ // Notify angle that it has a new texture. EGLAttrib frame_attributes[] = { EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE, - array_slice, + gl_image_->level(), EGL_NONE, }; EGLDisplay egl_display = gl::GLSurfaceEGL::GetHardwareDisplay(); - if (!eglStreamPostD3DTextureANGLE(egl_display, stream_, - static_cast<void*>(texture.Get()), - frame_attributes)) { + if (!eglStreamPostD3DTextureANGLE( + egl_display, stream_, static_cast<void*>(gl_image_->texture().Get()), + frame_attributes)) { NotifyError(StatusCode::kPostTextureFailed); return; }
diff --git a/media/gpu/windows/d3d11_texture_wrapper.h b/media/gpu/windows/d3d11_texture_wrapper.h index 3878f4fd9..0d2684d2 100644 --- a/media/gpu/windows/d3d11_texture_wrapper.h +++ b/media/gpu/windows/d3d11_texture_wrapper.h
@@ -47,13 +47,13 @@ // Initialize the wrapper. virtual Status Init( scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, - GetCommandBufferHelperCB get_helper_cb) = 0; + GetCommandBufferHelperCB get_helper_cb, + ComD3D11Texture2D texture, + size_t array_size) = 0; // Import |texture|, |array_slice| and return the mailbox(es) that can be // used to refer to it. - virtual Status ProcessTexture(ComD3D11Texture2D texture, - size_t array_slice, - const gfx::ColorSpace& input_color_space, + virtual Status ProcessTexture(const gfx::ColorSpace& input_color_space, MailboxHolderArray* mailbox_dest_out, gfx::ColorSpace* output_color_space) = 0; @@ -77,11 +77,11 @@ ~DefaultTexture2DWrapper() override; Status Init(scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, - GetCommandBufferHelperCB get_helper_cb) override; + GetCommandBufferHelperCB get_helper_cb, + ComD3D11Texture2D in_texture, + size_t array_slice) override; - Status ProcessTexture(ComD3D11Texture2D texture, - size_t array_slice, - const gfx::ColorSpace& input_color_space, + Status ProcessTexture(const gfx::ColorSpace& input_color_space, MailboxHolderArray* mailbox_dest, gfx::ColorSpace* output_color_space) override; @@ -103,14 +103,18 @@ const std::vector<gpu::Mailbox> mailboxes, GLenum target, gfx::Size size, - int textures_per_picture); - - // Push a new |texture|, |array_slice| to |gl_image_|. - void PushNewTexture(ComD3D11Texture2D texture, size_t array_slice); + int textures_per_picture, + viz::ResourceFormat format, + ComD3D11Texture2D texture, + size_t array_slice); std::vector<uint32_t> service_ids_; private: + // Push a new |texture|, |array_slice| to |gl_image_|. + // Both |texture| and |array_slice| were set by Init. + void PushNewTexture(); + // Notify our wrapper about |status|, if we haven't before. void NotifyError(Status status); @@ -121,6 +125,9 @@ scoped_refptr<gl::GLImageDXGI> gl_image_; EGLStreamKHR stream_; + std::vector<std::unique_ptr<gpu::SharedImageRepresentationFactoryRef>> + shared_images_; + DISALLOW_COPY_AND_ASSIGN(GpuResources); };
diff --git a/media/gpu/windows/d3d11_texture_wrapper_unittest.cc b/media/gpu/windows/d3d11_texture_wrapper_unittest.cc index fe7b9179..ef9e777 100644 --- a/media/gpu/windows/d3d11_texture_wrapper_unittest.cc +++ b/media/gpu/windows/d3d11_texture_wrapper_unittest.cc
@@ -90,7 +90,8 @@ const DXGI_FORMAT dxgi_format = DXGI_FORMAT_NV12; auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format); - const Status init_result = wrapper->Init(task_runner_, get_helper_cb_); + const Status init_result = wrapper->Init( + task_runner_, get_helper_cb_, /*texture_d3d=*/nullptr, /*array_slice=*/0); EXPECT_TRUE(init_result.is_ok()); // TODO: verify that ProcessTexture processes both textures. @@ -101,7 +102,8 @@ const DXGI_FORMAT dxgi_format = DXGI_FORMAT_B8G8R8A8_UNORM; auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format); - const Status init_result = wrapper->Init(task_runner_, get_helper_cb_); + const Status init_result = wrapper->Init( + task_runner_, get_helper_cb_, /*texture_d3d=*/nullptr, /*array_slice=*/0); EXPECT_TRUE(init_result.is_ok()); } @@ -110,7 +112,8 @@ const DXGI_FORMAT dxgi_format = DXGI_FORMAT_R16G16B16A16_FLOAT; auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format); - const Status init_result = wrapper->Init(task_runner_, get_helper_cb_); + const Status init_result = wrapper->Init( + task_runner_, get_helper_cb_, /*texture_d3d=*/nullptr, /*array_slice=*/0); EXPECT_TRUE(init_result.is_ok()); } @@ -119,8 +122,19 @@ const DXGI_FORMAT dxgi_format = DXGI_FORMAT_P010; auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format); - const Status init_result = wrapper->Init(task_runner_, get_helper_cb_); + const Status init_result = wrapper->Init( + task_runner_, get_helper_cb_, /*texture_d3d=*/nullptr, /*array_slice=*/0); EXPECT_TRUE(init_result.is_ok()); } +TEST_F(D3D11TextureWrapperUnittest, UnknownInitFails) { + STOP_IF_WIN7(); + const DXGI_FORMAT dxgi_format = DXGI_FORMAT_UNKNOWN; + + auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format); + const Status init_result = wrapper->Init( + task_runner_, get_helper_cb_, /*texture_d3d=*/nullptr, /*array_slice=*/0); + EXPECT_FALSE(init_result.is_ok()); +} + } // namespace media \ No newline at end of file
diff --git a/media/remoting/stream_provider.cc b/media/remoting/stream_provider.cc index d2df3a0..72aeb9b7 100644 --- a/media/remoting/stream_provider.cc +++ b/media/remoting/stream_provider.cc
@@ -199,12 +199,20 @@ callback_message.has_audio_decoder_config()) { const pb::AudioDecoderConfig audio_message = callback_message.audio_decoder_config(); - UpdateAudioConfig(audio_message); + ConvertProtoToAudioDecoderConfig(audio_message, &audio_decoder_config_); + if (!audio_decoder_config_.IsValidConfig()) { + OnError("Invalid audio config"); + return; + } } else if (type_ == DemuxerStream::VIDEO && callback_message.has_video_decoder_config()) { const pb::VideoDecoderConfig video_message = callback_message.video_decoder_config(); - UpdateVideoConfig(video_message); + ConvertProtoToVideoDecoderConfig(video_message, &video_decoder_config_); + if (!video_decoder_config_.IsValidConfig()) { + OnError("Invalid video config"); + return; + } } else { OnError("Config missing"); return; @@ -244,8 +252,6 @@ total_received_frame_count_ = callback_message.count(); if (ToDemuxerStreamStatus(callback_message.status()) == kConfigChanged) { - config_changed_ = true; - if (callback_message.has_audio_decoder_config()) { const pb::AudioDecoderConfig audio_message = callback_message.audio_decoder_config(); @@ -277,14 +283,7 @@ OnError("Invalid audio config"); return; } - if (config_changed_) { - DCHECK(audio_decoder_config_.IsValidConfig()); - DCHECK(!next_audio_decoder_config_.IsValidConfig()); - next_audio_decoder_config_ = audio_config; - } else { - DCHECK(!audio_decoder_config_.IsValidConfig()); - audio_decoder_config_ = audio_config; - } + next_audio_decoder_config_ = audio_config; } void StreamProvider::MediaStream::UpdateVideoConfig( @@ -296,14 +295,7 @@ OnError("Invalid video config"); return; } - if (config_changed_) { - DCHECK(video_decoder_config_.IsValidConfig()); - DCHECK(!next_video_decoder_config_.IsValidConfig()); - next_video_decoder_config_ = video_config; - } else { - DCHECK(!video_decoder_config_.IsValidConfig()); - video_decoder_config_ = video_config; - } + next_video_decoder_config_ = video_config; } void StreamProvider::MediaStream::SendReadUntil() { @@ -326,7 +318,8 @@ DCHECK(read_cb); read_complete_callback_ = std::move(read_cb); - if (buffers_.empty() && config_changed_) { + if (buffers_.empty() && (next_audio_decoder_config_.IsValidConfig() || + next_video_decoder_config_.IsValidConfig())) { CompleteRead(DemuxerStream::kConfigChanged); return; } @@ -345,14 +338,14 @@ switch (status) { case DemuxerStream::kConfigChanged: - if (type_ == AUDIO) { - DCHECK(next_audio_decoder_config_.IsValidConfig()); + if (next_audio_decoder_config_.IsValidConfig()) { audio_decoder_config_ = next_audio_decoder_config_; - } else { - DCHECK(next_video_decoder_config_.IsValidConfig()); - video_decoder_config_ = next_video_decoder_config_; + next_audio_decoder_config_ = media::AudioDecoderConfig(); } - config_changed_ = false; + if (next_video_decoder_config_.IsValidConfig()) { + video_decoder_config_ = next_video_decoder_config_; + next_video_decoder_config_ = media::VideoDecoderConfig(); + } std::move(read_complete_callback_).Run(status, nullptr); return; case DemuxerStream::kAborted:
diff --git a/media/remoting/stream_provider.h b/media/remoting/stream_provider.h index 3b18c47..bd2630b5 100644 --- a/media/remoting/stream_provider.h +++ b/media/remoting/stream_provider.h
@@ -184,11 +184,6 @@ // contains how many frames are sent. uint32_t total_received_frame_count_ = 0; - // Indicates whether Audio/VideoDecoderConfig changed and the frames with - // the old config are not yet consumed. The new config is stored in the end - // of |audio/video_decoder_config_|. - bool config_changed_ = false; - // Indicates whether a ReadUntil RPC message was sent without receiving the // ReadUntilCallback message yet. bool read_until_sent_ = false;
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index 26d16a20..b883035 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc
@@ -460,7 +460,8 @@ // other argument should be NULL. void PreconnectErrorResendRequestTest(const MockWrite* write_failure, const MockRead* read_failure, - bool use_spdy); + bool use_spdy, + bool upload = false); SimpleGetHelperResult SimpleGetHelperForData( base::span<StaticSocketDataProvider*> providers) { @@ -1883,13 +1884,24 @@ void HttpNetworkTransactionTest::PreconnectErrorResendRequestTest( const MockWrite* write_failure, const MockRead* read_failure, - bool use_spdy) { + bool use_spdy, + bool chunked_upload) { + SpdyTestUtil spdy_util; HttpRequestInfo request; request.method = "GET"; request.url = GURL("https://www.foo.com/"); request.traffic_annotation = net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); + const char upload_data[] = "foobar"; + ChunkedUploadDataStream upload_data_stream(0); + if (chunked_upload) { + request.method = "POST"; + upload_data_stream.AppendData(upload_data, base::size(upload_data) - 1, + true); + request.upload_data_stream = &upload_data_stream; + } + RecordingTestNetLog net_log; session_deps_.net_log = &net_log; std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -1904,17 +1916,32 @@ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl2); // SPDY versions of the request and response. - spdy::SpdySerializedFrame spdy_request(spdy_util_.ConstructSpdyGet( - request.url.spec().c_str(), 1, DEFAULT_PRIORITY)); + + spdy::SpdyHeaderBlock spdy_post_header_block; + spdy_post_header_block[spdy::kHttp2MethodHeader] = "POST"; + spdy_util.AddUrlToHeaderBlock(request.url.spec(), &spdy_post_header_block); + spdy::SpdySerializedFrame spdy_request( + chunked_upload + ? spdy_util.ConstructSpdyHeaders(1, std::move(spdy_post_header_block), + DEFAULT_PRIORITY, false) + : spdy_util.ConstructSpdyGet(request.url.spec().c_str(), 1, + DEFAULT_PRIORITY)); + + spdy::SpdySerializedFrame spdy_request_body( + spdy_util.ConstructSpdyDataFrame(1, "foobar", true)); spdy::SpdySerializedFrame spdy_response( - spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); + spdy_util.ConstructSpdyGetReply(nullptr, 0, 1)); spdy::SpdySerializedFrame spdy_data( - spdy_util_.ConstructSpdyDataFrame(1, "hello", true)); + spdy_util.ConstructSpdyDataFrame(1, "hello", true)); // HTTP/1.1 versions of the request and response. - const char kHttpRequest[] = "GET / HTTP/1.1\r\n" + const std::string http_request = + std::string(chunked_upload ? "POST" : "GET") + + " / HTTP/1.1\r\n" "Host: www.foo.com\r\n" - "Connection: keep-alive\r\n\r\n"; + "Connection: keep-alive\r\n" + + (chunked_upload ? "Transfer-Encoding: chunked\r\n\r\n" : "\r\n"); + const char* kHttpRequest = http_request.c_str(); const char kHttpResponse[] = "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\n"; const char kHttpData[] = "hello"; @@ -1928,8 +1955,14 @@ ASSERT_TRUE(read_failure); if (use_spdy) { data1_writes.push_back(CreateMockWrite(spdy_request)); + if (chunked_upload) + data1_writes.push_back(CreateMockWrite(spdy_request_body)); } else { data1_writes.push_back(MockWrite(kHttpRequest)); + if (chunked_upload) { + data1_writes.push_back(MockWrite("6\r\nfoobar\r\n")); + data1_writes.push_back(MockWrite("0\r\n\r\n")); + } } data1_reads.push_back(*read_failure); } @@ -1941,19 +1974,25 @@ std::vector<MockWrite> data2_writes; if (use_spdy) { - data2_writes.push_back(CreateMockWrite(spdy_request, 0, ASYNC)); - - data2_reads.push_back(CreateMockRead(spdy_response, 1, ASYNC)); - data2_reads.push_back(CreateMockRead(spdy_data, 2, ASYNC)); - data2_reads.push_back(MockRead(ASYNC, OK, 3)); + int seq = 0; + data2_writes.push_back(CreateMockWrite(spdy_request, seq++, ASYNC)); + if (chunked_upload) + data2_writes.push_back(CreateMockWrite(spdy_request_body, seq++, ASYNC)); + data2_reads.push_back(CreateMockRead(spdy_response, seq++, ASYNC)); + data2_reads.push_back(CreateMockRead(spdy_data, seq++, ASYNC)); + data2_reads.push_back(MockRead(ASYNC, OK, seq++)); } else { + int seq = 0; data2_writes.push_back( - MockWrite(ASYNC, kHttpRequest, strlen(kHttpRequest), 0)); - + MockWrite(ASYNC, kHttpRequest, strlen(kHttpRequest), seq++)); + if (chunked_upload) { + data2_writes.push_back(MockWrite(ASYNC, "6\r\nfoobar\r\n", 11, seq++)); + data2_writes.push_back(MockWrite(ASYNC, "0\r\n\r\n", 5, seq++)); + } data2_reads.push_back( - MockRead(ASYNC, kHttpResponse, strlen(kHttpResponse), 1)); - data2_reads.push_back(MockRead(ASYNC, kHttpData, strlen(kHttpData), 2)); - data2_reads.push_back(MockRead(ASYNC, OK, 3)); + MockRead(ASYNC, kHttpResponse, strlen(kHttpResponse), seq++)); + data2_reads.push_back(MockRead(ASYNC, kHttpData, strlen(kHttpData), seq++)); + data2_reads.push_back(MockRead(ASYNC, OK, seq++)); } SequencedSocketData data2(data2_reads, data2_writes); session_deps_.socket_factory->AddSocketDataProvider(&data2); @@ -2118,22 +2157,34 @@ TEST_F(HttpNetworkTransactionTest, PreconnectErrorNotConnectedOnWrite) { MockWrite write_failure(ASYNC, ERR_SOCKET_NOT_CONNECTED); - PreconnectErrorResendRequestTest(&write_failure, nullptr, false); + PreconnectErrorResendRequestTest(&write_failure, nullptr, + false /* use_spdy */); + PreconnectErrorResendRequestTest( + &write_failure, nullptr, false /* use_spdy */, true /* chunked_upload */); } TEST_F(HttpNetworkTransactionTest, PreconnectErrorReset) { MockRead read_failure(ASYNC, ERR_CONNECTION_RESET); - PreconnectErrorResendRequestTest(nullptr, &read_failure, false); + PreconnectErrorResendRequestTest(nullptr, &read_failure, + false /* use_spdy */); + PreconnectErrorResendRequestTest(nullptr, &read_failure, false /* use_spdy */, + true /* chunked_upload */); } TEST_F(HttpNetworkTransactionTest, PreconnectErrorEOF) { MockRead read_failure(SYNCHRONOUS, OK); // EOF - PreconnectErrorResendRequestTest(nullptr, &read_failure, false); + PreconnectErrorResendRequestTest(nullptr, &read_failure, + false /* use_spdy */); + PreconnectErrorResendRequestTest(nullptr, &read_failure, false /* use_spdy */, + true /* chunked_upload */); } TEST_F(HttpNetworkTransactionTest, PreconnectErrorAsyncEOF) { MockRead read_failure(ASYNC, OK); // EOF - PreconnectErrorResendRequestTest(nullptr, &read_failure, false); + PreconnectErrorResendRequestTest(nullptr, &read_failure, + false /* use_spdy */); + PreconnectErrorResendRequestTest(nullptr, &read_failure, false /* use_spdy */, + true /* chunked_upload */); } // Make sure that on a 408 response (Request Timeout), the request is retried, @@ -2145,27 +2196,39 @@ "Content-Length: 6\r\n\r\n" "Pickle"); KeepAliveConnectionResendRequestTest(nullptr, &read_failure); - PreconnectErrorResendRequestTest(nullptr, &read_failure, false); + PreconnectErrorResendRequestTest(nullptr, &read_failure, + false /* use_spdy */); + PreconnectErrorResendRequestTest(nullptr, &read_failure, false /* use_spdy */, + true /* chunked_upload */); } TEST_F(HttpNetworkTransactionTest, SpdyPreconnectErrorNotConnectedOnWrite) { MockWrite write_failure(ASYNC, ERR_SOCKET_NOT_CONNECTED); - PreconnectErrorResendRequestTest(&write_failure, nullptr, true); + PreconnectErrorResendRequestTest(&write_failure, nullptr, + true /* use_spdy */); + PreconnectErrorResendRequestTest(&write_failure, nullptr, true /* use_spdy */, + true /* chunked_upload */); } TEST_F(HttpNetworkTransactionTest, SpdyPreconnectErrorReset) { MockRead read_failure(ASYNC, ERR_CONNECTION_RESET); - PreconnectErrorResendRequestTest(nullptr, &read_failure, true); + PreconnectErrorResendRequestTest(nullptr, &read_failure, true /* use_spdy */); + PreconnectErrorResendRequestTest(nullptr, &read_failure, true /* use_spdy */, + true /* chunked_upload */); } TEST_F(HttpNetworkTransactionTest, SpdyPreconnectErrorEOF) { MockRead read_failure(SYNCHRONOUS, OK); // EOF - PreconnectErrorResendRequestTest(nullptr, &read_failure, true); + PreconnectErrorResendRequestTest(nullptr, &read_failure, true /* use_spdy */); + PreconnectErrorResendRequestTest(nullptr, &read_failure, true /* use_spdy */, + true /* chunked_upload */); } TEST_F(HttpNetworkTransactionTest, SpdyPreconnectErrorAsyncEOF) { MockRead read_failure(ASYNC, OK); // EOF - PreconnectErrorResendRequestTest(nullptr, &read_failure, true); + PreconnectErrorResendRequestTest(nullptr, &read_failure, true /* use_spdy */); + PreconnectErrorResendRequestTest(nullptr, &read_failure, true /* use_spdy */, + true /* chunked_upload */); } TEST_F(HttpNetworkTransactionTest, NonKeepAliveConnectionReset) {
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h index e8d0b0f..e2b4b67 100644 --- a/net/quic/quic_flags_list.h +++ b/net/quic/quic_flags_list.h
@@ -449,3 +449,14 @@ QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_check_encryption_level_in_fast_path, true) + +// If true, gQUIC will only consult stream_map in +// QuicSession::GetNumActiveStreams(). +QUIC_FLAG( + bool, + FLAGS_quic_reloadable_flag_quic_get_stream_information_from_stream_map, + false) + +// If true, QuicSession does not keep a separate zombie_streams. Instead, all +// streams are stored in stream_map_. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_remove_zombie_streams, false)
diff --git a/pdf/BUILD.gn b/pdf/BUILD.gn index 04fffbb..974f69a 100644 --- a/pdf/BUILD.gn +++ b/pdf/BUILD.gn
@@ -196,6 +196,8 @@ configs += [ ":pdf_common_config" ] + public_deps = [ "//third_party/abseil-cpp:absl" ] + deps = [ "//base", "//ppapi/cpp:objects", @@ -214,6 +216,8 @@ configs += [ ":pdf_common_config" ] + public_deps = [ "//third_party/abseil-cpp:absl" ] + deps = [ ":features", ":internal",
diff --git a/pdf/ppapi_migration/image.cc b/pdf/ppapi_migration/image.cc index ee74906..f1fc88fa 100644 --- a/pdf/ppapi_migration/image.cc +++ b/pdf/ppapi_migration/image.cc
@@ -5,13 +5,14 @@ #include "pdf/ppapi_migration/image.h" #include "ppapi/cpp/image_data.h" +#include "third_party/abseil-cpp/absl/types/variant.h" #include "third_party/skia/include/core/SkBitmap.h" namespace chrome_pdf { -Image::Image(const pp::ImageData& pepper_image) : pepper_image_(pepper_image) {} +Image::Image(const pp::ImageData& pepper_image) : image_(pepper_image) {} -Image::Image(const SkBitmap& skia_image) : skia_image_(skia_image) {} +Image::Image(const SkBitmap& skia_image) : image_(skia_image) {} Image::Image(const Image& other) = default;
diff --git a/pdf/ppapi_migration/image.h b/pdf/ppapi_migration/image.h index d9dfec5..35c2ffb 100644 --- a/pdf/ppapi_migration/image.h +++ b/pdf/ppapi_migration/image.h
@@ -5,8 +5,8 @@ #ifndef PDF_PPAPI_MIGRATION_IMAGE_H_ #define PDF_PPAPI_MIGRATION_IMAGE_H_ -#include "base/check.h" #include "ppapi/cpp/image_data.h" +#include "third_party/abseil-cpp/absl/types/variant.h" #include "third_party/skia/include/core/SkBitmap.h" namespace chrome_pdf { @@ -24,18 +24,13 @@ ~Image(); const pp::ImageData& pepper_image() const { - DCHECK(skia_image_.isNull()); - return pepper_image_; + return absl::get<pp::ImageData>(image_); } - const SkBitmap& skia_image() const { - DCHECK(pepper_image_.is_null()); - return skia_image_; - } + const SkBitmap& skia_image() const { return absl::get<SkBitmap>(image_); } private: - pp::ImageData pepper_image_; - SkBitmap skia_image_; + absl::variant<pp::ImageData, SkBitmap> image_; }; } // namespace chrome_pdf
diff --git a/services/device/usb/BUILD.gn b/services/device/usb/BUILD.gn index d2941e4..5b7ea62 100644 --- a/services/device/usb/BUILD.gn +++ b/services/device/usb/BUILD.gn
@@ -32,6 +32,8 @@ "usb_device_handle.h", "usb_device_handle_android.cc", "usb_device_handle_android.h", + "usb_device_handle_mac.cc", + "usb_device_handle_mac.h", "usb_device_handle_win.cc", "usb_device_handle_win.h", "usb_device_linux.cc",
diff --git a/services/device/usb/usb_device.h b/services/device/usb/usb_device.h index 6851cb69..15d7430e 100644 --- a/services/device/usb/usb_device.h +++ b/services/device/usb/usb_device.h
@@ -145,6 +145,7 @@ private: friend class base::RefCountedThreadSafe<UsbDevice>; friend class UsbDeviceHandleImpl; + friend class UsbDeviceHandleMac; friend class UsbDeviceHandleUsbfs; friend class UsbDeviceHandleWin; friend class UsbServiceAndroid;
diff --git a/services/device/usb/usb_device_handle_mac.cc b/services/device/usb/usb_device_handle_mac.cc new file mode 100644 index 0000000..2bba7c61 --- /dev/null +++ b/services/device/usb/usb_device_handle_mac.cc
@@ -0,0 +1,123 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/device/usb/usb_device_handle_mac.h" + +#include <IOKit/IOCFPlugIn.h> +#include <IOKit/IOKitLib.h> +#include <IOKit/IOReturn.h> +#include <IOKit/usb/IOUSBLib.h> + +#include <utility> + +#include "base/mac/scoped_ioplugininterface.h" +#include "services/device/usb/usb_device_mac.h" + +namespace device { + +UsbDeviceHandleMac::UsbDeviceHandleMac( + scoped_refptr<UsbDeviceMac> device, + base::mac::ScopedIOPluginInterface<IOUSBDeviceInterface182> + device_interface) + : device_interface_(std::move(device_interface)), + device_(std::move(device)) {} + +UsbDeviceHandleMac::~UsbDeviceHandleMac() {} + +scoped_refptr<UsbDevice> UsbDeviceHandleMac::GetDevice() const { + return device_; +} + +void UsbDeviceHandleMac::Close() { + GetDevice()->HandleClosed(this); + device_ = nullptr; +} + +void UsbDeviceHandleMac::SetConfiguration(int configuration_value, + ResultCallback callback) { + NOTIMPLEMENTED(); + return; +} + +void UsbDeviceHandleMac::ClaimInterface(int interface_number, + ResultCallback callback) { + NOTIMPLEMENTED(); + return; +} + +void UsbDeviceHandleMac::ReleaseInterface(int interface_number, + ResultCallback callback) { + NOTIMPLEMENTED(); + return; +} + +void UsbDeviceHandleMac::SetInterfaceAlternateSetting(int interface_number, + int alternate_setting, + ResultCallback callback) { + NOTIMPLEMENTED(); + return; +} + +void UsbDeviceHandleMac::ResetDevice(ResultCallback callback) { + NOTIMPLEMENTED(); + return; +} + +void UsbDeviceHandleMac::ClearHalt(mojom::UsbTransferDirection direction, + uint8_t endpoint_number, + ResultCallback callback) { + NOTIMPLEMENTED(); + return; +} + +void UsbDeviceHandleMac::ControlTransfer( + mojom::UsbTransferDirection direction, + mojom::UsbControlTransferType request_type, + mojom::UsbControlTransferRecipient recipient, + uint8_t request, + uint16_t value, + uint16_t index, + scoped_refptr<base::RefCountedBytes> buffer, + unsigned int timeout, + TransferCallback callback) { + NOTIMPLEMENTED(); + return; +} + +void UsbDeviceHandleMac::IsochronousTransferIn( + uint8_t endpoint, + const std::vector<uint32_t>& packet_lengths, + unsigned int timeout, + IsochronousTransferCallback callback) { + NOTIMPLEMENTED(); + return; +} + +void UsbDeviceHandleMac::IsochronousTransferOut( + uint8_t endpoint, + scoped_refptr<base::RefCountedBytes> buffer, + const std::vector<uint32_t>& packet_lengths, + unsigned int timeout, + IsochronousTransferCallback callback) { + NOTIMPLEMENTED(); + return; +} + +void UsbDeviceHandleMac::GenericTransfer( + mojom::UsbTransferDirection direction, + uint8_t endpoint_number, + scoped_refptr<base::RefCountedBytes> buffer, + unsigned int timeout, + TransferCallback callback) { + NOTIMPLEMENTED(); + return; +} + +const mojom::UsbInterfaceInfo* UsbDeviceHandleMac::FindInterfaceByEndpoint( + uint8_t endpoint_address) { + NOTIMPLEMENTED(); + return nullptr; +} + +} // namespace device
diff --git a/services/device/usb/usb_device_handle_mac.h b/services/device/usb/usb_device_handle_mac.h new file mode 100644 index 0000000..02a2b0e --- /dev/null +++ b/services/device/usb/usb_device_handle_mac.h
@@ -0,0 +1,85 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_DEVICE_USB_USB_DEVICE_HANDLE_MAC_H_ +#define SERVICES_DEVICE_USB_USB_DEVICE_HANDLE_MAC_H_ + +#include "services/device/usb/usb_device_handle.h" + +#include <IOKit/IOCFPlugIn.h> +#include <IOKit/IOKitLib.h> +#include <IOKit/IOReturn.h> +#include <IOKit/usb/IOUSBLib.h> + +#include <vector> + +#include "base/mac/scoped_ioplugininterface.h" +#include "base/memory/ref_counted.h" +#include "services/device/public/mojom/usb_device.mojom.h" + +namespace device { + +class UsbDeviceMac; + +class UsbDeviceHandleMac : public UsbDeviceHandle { + public: + UsbDeviceHandleMac(const UsbDeviceHandleMac&) = delete; + UsbDeviceHandleMac& operator=(const UsbDeviceHandleMac&) = delete; + + // UsbDeviceHandle implementation: + scoped_refptr<UsbDevice> GetDevice() const override; + void Close() override; + void SetConfiguration(int configuration_value, + ResultCallback callback) override; + void ClaimInterface(int interface_number, ResultCallback callback) override; + void ReleaseInterface(int interface_number, ResultCallback callback) override; + void SetInterfaceAlternateSetting(int interface_number, + int alternate_setting, + ResultCallback callback) override; + void ResetDevice(ResultCallback callback) override; + void ClearHalt(mojom::UsbTransferDirection direction, + uint8_t endpoint_number, + ResultCallback callback) override; + void ControlTransfer(mojom::UsbTransferDirection direction, + mojom::UsbControlTransferType request_type, + mojom::UsbControlTransferRecipient recipient, + uint8_t request, + uint16_t value, + uint16_t index, + scoped_refptr<base::RefCountedBytes> buffer, + unsigned int timeout, + TransferCallback callback) override; + void IsochronousTransferIn(uint8_t endpoint, + const std::vector<uint32_t>& packet_lengths, + unsigned int timeout, + IsochronousTransferCallback callback) override; + void IsochronousTransferOut(uint8_t endpoint, + scoped_refptr<base::RefCountedBytes> buffer, + const std::vector<uint32_t>& packet_lengths, + unsigned int timeout, + IsochronousTransferCallback callback) override; + void GenericTransfer(mojom::UsbTransferDirection direction, + uint8_t endpoint_number, + scoped_refptr<base::RefCountedBytes> buffer, + unsigned int timeout, + TransferCallback callback) override; + const mojom::UsbInterfaceInfo* FindInterfaceByEndpoint( + uint8_t endpoint_address) override; + + UsbDeviceHandleMac(scoped_refptr<UsbDeviceMac> device, + base::mac::ScopedIOPluginInterface<IOUSBDeviceInterface182> + device_interface); + + protected: + ~UsbDeviceHandleMac() override; + friend class UsbDeviceMac; + + private: + base::mac::ScopedIOPluginInterface<IOUSBDeviceInterface182> device_interface_; + scoped_refptr<UsbDeviceMac> device_; +}; + +} // namespace device + +#endif // SERVICES_DEVICE_USB_USB_DEVICE_HANDLE_MAC_H_
diff --git a/services/device/usb/usb_device_mac.cc b/services/device/usb/usb_device_mac.cc index 40667a3f..8371d10 100644 --- a/services/device/usb/usb_device_mac.cc +++ b/services/device/usb/usb_device_mac.cc
@@ -4,10 +4,21 @@ #include "services/device/usb/usb_device_mac.h" +#include <IOKit/IOCFPlugIn.h> +#include <IOKit/IOKitLib.h> +#include <IOKit/IOReturn.h> +#include <IOKit/usb/IOUSBLib.h> + #include <utility> +#include "base/mac/foundation_util.h" +#include "base/mac/scoped_ionotificationportref.h" +#include "base/mac/scoped_ioobject.h" +#include "base/mac/scoped_ioplugininterface.h" +#include "components/device_event_log/device_event_log.h" #include "services/device/usb/usb_descriptors.h" #include "services/device/usb/usb_device_handle.h" +#include "services/device/usb/usb_device_handle_mac.h" namespace device { @@ -18,6 +29,61 @@ UsbDeviceMac::~UsbDeviceMac() = default; void UsbDeviceMac::Open(OpenCallback callback) { + base::ScopedCFTypeRef<CFDictionaryRef> matching_dict( + IORegistryEntryIDMatching(entry_id())); + if (!matching_dict.get()) { + USB_LOG(ERROR) << "Failed to create matching dictionary for ID."; + std::move(callback).Run(nullptr); + return; + } + + // IOServiceGetMatchingService consumes a reference to the matching dictionary + // passed to it. + base::mac::ScopedIOObject<io_service_t> usb_device( + IOServiceGetMatchingService(kIOMasterPortDefault, + matching_dict.release())); + if (!usb_device.get()) { + USB_LOG(ERROR) << "IOService not found for ID."; + std::move(callback).Run(nullptr); + return; + } + + base::mac::ScopedIOPluginInterface<IOCFPlugInInterface> plugin_interface; + int32_t score; + IOReturn kr = IOCreatePlugInInterfaceForService( + usb_device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, + plugin_interface.InitializeInto(), &score); + if ((kr != kIOReturnSuccess) || !plugin_interface) { + USB_LOG(ERROR) << "Unable to create a plug-in: " << std::hex << kr; + std::move(callback).Run(nullptr); + return; + } + + base::mac::ScopedIOPluginInterface<IOUSBDeviceInterface182> device_interface; + IOReturn result = + (*plugin_interface) + ->QueryInterface( + plugin_interface.get(), + CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), + reinterpret_cast<LPVOID*>(device_interface.InitializeInto())); + if (result || !device_interface) { + USB_LOG(ERROR) << "Couldn’t create a device interface."; + std::move(callback).Run(nullptr); + return; + } + + kr = (*device_interface)->USBDeviceOpen(device_interface); + if (kr != kIOReturnSuccess) { + USB_LOG(ERROR) << "Failed to open device: " << std::hex << kr; + std::move(callback).Run(nullptr); + return; + } + + auto device_handle = base::MakeRefCounted<UsbDeviceHandleMac>( + this, std::move(device_interface)); + + handles().push_back(device_handle.get()); + std::move(callback).Run(nullptr); }
diff --git a/services/device/usb/usb_device_mac.h b/services/device/usb/usb_device_mac.h index 1d45ea56..f27b56b 100644 --- a/services/device/usb/usb_device_mac.h +++ b/services/device/usb/usb_device_mac.h
@@ -21,8 +21,6 @@ uint64_t entry_id() const { return entry_id_; } protected: - friend class UsbServiceMac; - ~UsbDeviceMac() override; private:
diff --git a/services/device/usb/usb_service_mac.cc b/services/device/usb/usb_service_mac.cc index a968239..4de2e82 100644 --- a/services/device/usb/usb_service_mac.cc +++ b/services/device/usb/usb_service_mac.cc
@@ -320,6 +320,7 @@ auto by_guid_it = devices().find(mac_device->guid()); devices().erase(by_guid_it); NotifyDeviceRemoved(mac_device); + mac_device->OnDisconnect(); } }
diff --git a/services/network/public/cpp/BUILD.gn b/services/network/public/cpp/BUILD.gn index bef82fa..083f90f 100644 --- a/services/network/public/cpp/BUILD.gn +++ b/services/network/public/cpp/BUILD.gn
@@ -239,15 +239,6 @@ "url_request_mojom_traits.cc", "url_request_mojom_traits.h", ] - jumbo_excluded_sources = [ - # IPC/Params code generators are based on macros and multiple - # inclusion of headers using those macros. That is not - # compatible with jumbo compiling all source, generators and - # users, together, so exclude those files from jumbo compilation. - "network_ipc_param_traits.cc", - "p2p_param_traits.cc", - "url_request_mojom_traits.cc", - ] configs += [ "//build/config/compiler:wexit_time_destructors" ] @@ -258,10 +249,10 @@ "//services/network/public/mojom:data_pipe_interfaces", "//services/network/public/mojom:mutable_network_traffic_annotation_interface", "//services/network/public/mojom:trust_tokens_interface", + "//third_party/webrtc_overrides:webrtc_component", "//url/ipc:url_ipc", "//url/mojom:url_mojom_gurl", "//url/mojom:url_mojom_origin", - "//third_party/webrtc_overrides:webrtc_component", ] deps = [ "//base",
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json index e0a7dea..b3b8b9f 100644 --- a/testing/buildbot/chromium.chromiumos.json +++ b/testing/buildbot/chromium.chromiumos.json
@@ -4181,5 +4181,728 @@ "test_id_prefix": "ninja://third_party/blink/renderer/platform/wtf:wtf_unittests/" } ] + }, + "linux-lacros-tester-rel": { + "additional_compile_targets": [ + "chrome", + "chrome_sandbox", + "linux_symbols", + "symupload" + ], + "gtest_tests": [ + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "absl_hardening_tests", + "test_id_prefix": "ninja://third_party/abseil-cpp:absl_hardening_tests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "accessibility_unittests", + "test_id_prefix": "ninja://ui/accessibility:accessibility_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "angle_unittests", + "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "base_unittests", + "test_id_prefix": "ninja://base:base_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "base_util_unittests", + "test_id_prefix": "ninja://base/util:base_util_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "blink_common_unittests", + "test_id_prefix": "ninja://third_party/blink/common:blink_common_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "blink_fuzzer_unittests", + "test_id_prefix": "ninja://third_party/blink/renderer/platform:blink_fuzzer_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "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/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "blink_platform_unittests", + "test_id_prefix": "ninja://third_party/blink/renderer/platform:blink_platform_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "webkit_unit_tests", + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "blink_unittests", + "test_id_prefix": "ninja://third_party/blink/renderer/controller:blink_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "boringssl_crypto_tests", + "test_id_prefix": "ninja://third_party/boringssl:boringssl_crypto_tests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "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": [ + "--gtest_filter=-*UsingRealWebcam*" + ], + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "capture_unittests", + "test_id_prefix": "ninja://media/capture:capture_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "cast_unittests", + "test_id_prefix": "ninja://media/cast:cast_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "cc_unittests", + "test_id_prefix": "ninja://cc:cc_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "chrome_app_unittests", + "test_id_prefix": "ninja://chrome/test:chrome_app_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "chromedriver_unittests", + "test_id_prefix": "ninja://chrome/test/chromedriver:chromedriver_unittests/" + }, + { + "experiment_percentage": 100, + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "content_unittests", + "test_id_prefix": "ninja://content/test:content_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "crypto_unittests", + "test_id_prefix": "ninja://crypto:crypto_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "device_unittests", + "test_id_prefix": "ninja://device:device_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "display_unittests", + "test_id_prefix": "ninja://ui/display:display_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "events_unittests", + "test_id_prefix": "ninja://ui/events:events_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "filesystem_service_unittests", + "test_id_prefix": "ninja://components/services/filesystem:filesystem_service_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "gcm_unit_tests", + "test_id_prefix": "ninja://google_apis/gcm:gcm_unit_tests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "gfx_unittests", + "test_id_prefix": "ninja://ui/gfx:gfx_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "gin_unittests", + "test_id_prefix": "ninja://gin:gin_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "google_apis_unittests", + "test_id_prefix": "ninja://google_apis:google_apis_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "gpu_unittests", + "test_id_prefix": "ninja://gpu:gpu_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "gwp_asan_unittests", + "test_id_prefix": "ninja://components/gwp_asan:gwp_asan_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "ipc_tests", + "test_id_prefix": "ninja://ipc:ipc_tests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "jingle_unittests", + "test_id_prefix": "ninja://jingle:jingle_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "latency_unittests", + "test_id_prefix": "ninja://ui/latency:latency_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "libjingle_xmpp_unittests", + "test_id_prefix": "ninja://third_party/libjingle_xmpp:libjingle_xmpp_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "media_blink_unittests", + "test_id_prefix": "ninja://media/blink:media_blink_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "media_unittests", + "test_id_prefix": "ninja://media:media_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "midi_unittests", + "test_id_prefix": "ninja://media/midi:midi_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "mojo_core_unittests", + "test_id_prefix": "ninja://mojo/core:mojo_core_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "mojo_unittests", + "test_id_prefix": "ninja://mojo:mojo_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "nacl_helper_nonsfi_unittests", + "test_id_prefix": "ninja://components/nacl/loader:nacl_helper_nonsfi_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "nacl_loader_unittests", + "test_id_prefix": "ninja://components/nacl/loader:nacl_loader_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "native_theme_unittests", + "test_id_prefix": "ninja://ui/native_theme:native_theme_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "net_unittests", + "test_id_prefix": "ninja://net:net_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "pdf_unittests", + "test_id_prefix": "ninja://pdf:pdf_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "perfetto_unittests", + "test_id_prefix": "ninja://third_party/perfetto:perfetto_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "ppapi_unittests", + "test_id_prefix": "ninja://ppapi:ppapi_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "printing_unittests", + "test_id_prefix": "ninja://printing:printing_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "remoting_unittests", + "test_id_prefix": "ninja://remoting:remoting_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "sandbox_linux_unittests", + "test_id_prefix": "ninja://sandbox/linux:sandbox_linux_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "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/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "services_unittests", + "test_id_prefix": "ninja://services:services_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "shell_dialogs_unittests", + "test_id_prefix": "ninja://ui/shell_dialogs:shell_dialogs_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "skia_unittests", + "test_id_prefix": "ninja://skia:skia_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "sql_unittests", + "test_id_prefix": "ninja://sql:sql_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "storage_unittests", + "test_id_prefix": "ninja://storage:storage_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "traffic_annotation_auditor_unittests", + "test_id_prefix": "ninja://tools/traffic_annotation/auditor:traffic_annotation_auditor_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "ui_base_unittests", + "test_id_prefix": "ninja://ui/base:ui_base_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "ui_touch_selection_unittests", + "test_id_prefix": "ninja://ui/touch_selection:ui_touch_selection_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "viz_unittests", + "test_id_prefix": "ninja://components/viz:viz_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "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/" + } + ] } }
diff --git a/testing/buildbot/chromium.ci.json b/testing/buildbot/chromium.ci.json index 69993b83..b8735f8 100644 --- a/testing/buildbot/chromium.ci.json +++ b/testing/buildbot/chromium.ci.json
@@ -255898,8 +255898,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -256107,8 +256106,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -256316,8 +256314,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -256525,8 +256522,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -256734,8 +256730,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -256943,8 +256938,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -266256,8 +266250,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -266465,8 +266458,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -266674,8 +266666,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -266883,8 +266874,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -267092,8 +267082,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -267301,8 +267290,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/"
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index 0687dcc..4f9b558 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -61072,729 +61072,6 @@ } ] }, - "linux-lacros-tester-rel": { - "additional_compile_targets": [ - "chrome", - "chrome_sandbox", - "linux_symbols", - "symupload" - ], - "gtest_tests": [ - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "absl_hardening_tests", - "test_id_prefix": "ninja://third_party/abseil-cpp:absl_hardening_tests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "accessibility_unittests", - "test_id_prefix": "ninja://ui/accessibility:accessibility_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "angle_unittests", - "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "base_unittests", - "test_id_prefix": "ninja://base:base_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "base_util_unittests", - "test_id_prefix": "ninja://base/util:base_util_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "blink_common_unittests", - "test_id_prefix": "ninja://third_party/blink/common:blink_common_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "blink_fuzzer_unittests", - "test_id_prefix": "ninja://third_party/blink/renderer/platform:blink_fuzzer_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "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/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "blink_platform_unittests", - "test_id_prefix": "ninja://third_party/blink/renderer/platform:blink_platform_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "name": "webkit_unit_tests", - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "blink_unittests", - "test_id_prefix": "ninja://third_party/blink/renderer/controller:blink_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "boringssl_crypto_tests", - "test_id_prefix": "ninja://third_party/boringssl:boringssl_crypto_tests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "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": [ - "--gtest_filter=-*UsingRealWebcam*" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "capture_unittests", - "test_id_prefix": "ninja://media/capture:capture_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "cast_unittests", - "test_id_prefix": "ninja://media/cast:cast_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "cc_unittests", - "test_id_prefix": "ninja://cc:cc_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "chrome_app_unittests", - "test_id_prefix": "ninja://chrome/test:chrome_app_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "chromedriver_unittests", - "test_id_prefix": "ninja://chrome/test/chromedriver:chromedriver_unittests/" - }, - { - "experiment_percentage": 100, - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "content_unittests", - "test_id_prefix": "ninja://content/test:content_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "crypto_unittests", - "test_id_prefix": "ninja://crypto:crypto_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "device_unittests", - "test_id_prefix": "ninja://device:device_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "display_unittests", - "test_id_prefix": "ninja://ui/display:display_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "events_unittests", - "test_id_prefix": "ninja://ui/events:events_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "filesystem_service_unittests", - "test_id_prefix": "ninja://components/services/filesystem:filesystem_service_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gcm_unit_tests", - "test_id_prefix": "ninja://google_apis/gcm:gcm_unit_tests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gfx_unittests", - "test_id_prefix": "ninja://ui/gfx:gfx_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gin_unittests", - "test_id_prefix": "ninja://gin:gin_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "google_apis_unittests", - "test_id_prefix": "ninja://google_apis:google_apis_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gpu_unittests", - "test_id_prefix": "ninja://gpu:gpu_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gwp_asan_unittests", - "test_id_prefix": "ninja://components/gwp_asan:gwp_asan_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "ipc_tests", - "test_id_prefix": "ninja://ipc:ipc_tests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "jingle_unittests", - "test_id_prefix": "ninja://jingle:jingle_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "latency_unittests", - "test_id_prefix": "ninja://ui/latency:latency_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "libjingle_xmpp_unittests", - "test_id_prefix": "ninja://third_party/libjingle_xmpp:libjingle_xmpp_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "media_blink_unittests", - "test_id_prefix": "ninja://media/blink:media_blink_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "media_unittests", - "test_id_prefix": "ninja://media:media_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "midi_unittests", - "test_id_prefix": "ninja://media/midi:midi_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "mojo_core_unittests", - "test_id_prefix": "ninja://mojo/core:mojo_core_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "mojo_unittests", - "test_id_prefix": "ninja://mojo:mojo_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "nacl_helper_nonsfi_unittests", - "test_id_prefix": "ninja://components/nacl/loader:nacl_helper_nonsfi_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "nacl_loader_unittests", - "test_id_prefix": "ninja://components/nacl/loader:nacl_loader_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "native_theme_unittests", - "test_id_prefix": "ninja://ui/native_theme:native_theme_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "net_unittests", - "test_id_prefix": "ninja://net:net_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "pdf_unittests", - "test_id_prefix": "ninja://pdf:pdf_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "perfetto_unittests", - "test_id_prefix": "ninja://third_party/perfetto:perfetto_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "ppapi_unittests", - "test_id_prefix": "ninja://ppapi:ppapi_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "printing_unittests", - "test_id_prefix": "ninja://printing:printing_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "remoting_unittests", - "test_id_prefix": "ninja://remoting:remoting_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "sandbox_linux_unittests", - "test_id_prefix": "ninja://sandbox/linux:sandbox_linux_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "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/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "services_unittests", - "test_id_prefix": "ninja://services:services_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "shell_dialogs_unittests", - "test_id_prefix": "ninja://ui/shell_dialogs:shell_dialogs_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "skia_unittests", - "test_id_prefix": "ninja://skia:skia_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "sql_unittests", - "test_id_prefix": "ninja://sql:sql_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "storage_unittests", - "test_id_prefix": "ninja://storage:storage_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "traffic_annotation_auditor_unittests", - "test_id_prefix": "ninja://tools/traffic_annotation/auditor:traffic_annotation_auditor_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "ui_base_unittests", - "test_id_prefix": "ninja://ui/base:ui_base_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "ui_touch_selection_unittests", - "test_id_prefix": "ninja://ui/touch_selection:ui_touch_selection_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "viz_unittests", - "test_id_prefix": "ninja://components/viz:viz_unittests/" - }, - { - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "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/" - } - ] - }, "linux-perfetto-rel": { "additional_compile_targets": [ "chrome"
diff --git a/testing/buildbot/chromium.swangle.json b/testing/buildbot/chromium.swangle.json index 07e92f9..30024a9 100644 --- a/testing/buildbot/chromium.swangle.json +++ b/testing/buildbot/chromium.swangle.json
@@ -87,8 +87,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -296,8 +295,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -505,8 +503,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -714,8 +711,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -923,8 +919,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -1132,8 +1127,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -1473,8 +1467,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -1682,8 +1675,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -1891,8 +1883,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -2100,8 +2091,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -2309,8 +2299,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/" @@ -2518,8 +2507,7 @@ ], "hard_timeout": 900, "io_timeout": 900, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "angle_deqp_egl_tests", "test_id_prefix": "ninja://third_party/angle/src/tests:angle_deqp_egl_tests/"
diff --git a/testing/buildbot/filters/android.emulator_m.chrome_public_test_apk.filter b/testing/buildbot/filters/android.emulator_m.chrome_public_test_apk.filter index 7a8a8b0..0065bff 100644 --- a/testing/buildbot/filters/android.emulator_m.chrome_public_test_apk.filter +++ b/testing/buildbot/filters/android.emulator_m.chrome_public_test_apk.filter
@@ -65,7 +65,11 @@ -org.chromium.chrome.features.start_surface.StartSurfaceLayoutTest.testIncognitoToggle_thumbnailFetchCount -org.chromium.chrome.features.start_surface.StartSurfaceLayoutTest.testThumbnailFetchingResult_defaultAspectRatio -org.chromium.chrome.features.start_surface.StartSurfaceLayoutTest.testRecycling_aspectRatioPoint75 +-org.chromium.chrome.features.start_surface.StartSurfaceLayoutTest.testGridToTabToCurrentLiveDetached # crbug.com/1096295 -org.chromium.chrome.features.start_surface.StartSurfaceLayoutTest.testActivityCanBeGarbageCollectedAfterFinished +# crbug.com/1112812 +-org.chromium.chrome.browser.autofill_assistant.AutofillAssistantAutostartTest.testAutostart +-org.chromium.chrome.browser.autofill.AutofillUpstreamTest.testSaveCardInfoBarWithEmptyYear
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl index 5a06a78..4e4a057 100644 --- a/testing/buildbot/test_suites.pyl +++ b/testing/buildbot/test_suites.pyl
@@ -4198,9 +4198,6 @@ '--test-launcher-batch-limit=512', '--test-launcher-retry-limit=0', ], - 'swarming': { - 'shards': 2, - }, }, 'angle_deqp_gles2_tests': { 'args': [
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl index 4495481..0f70fb42 100644 --- a/testing/buildbot/waterfalls.pyl +++ b/testing/buildbot/waterfalls.pyl
@@ -957,6 +957,17 @@ 'gtest_tests': 'linux_chromeos_gtests', }, }, + 'linux-lacros-tester-rel': { + 'additional_compile_targets': [ + 'chrome', + 'chrome_sandbox', + 'linux_symbols', + 'symupload' + ], + 'test_suites': { + 'gtest_tests': 'linux_lacros_gtests', + }, + }, }, }, { @@ -2291,17 +2302,6 @@ 'gtest_tests': 'linux_lacros_gtests', }, }, - 'linux-lacros-tester-rel': { - 'additional_compile_targets': [ - 'chrome', - 'chrome_sandbox', - 'linux_symbols', - 'symupload' - ], - 'test_suites': { - 'gtest_tests': 'linux_lacros_gtests', - }, - }, 'linux-perfetto-rel': { 'additional_compile_targets': [ 'chrome' ], 'mixins': [
diff --git a/testing/test_env.py b/testing/test_env.py index 6e39aa4..7199826 100755 --- a/testing/test_env.py +++ b/testing/test_env.py
@@ -2,6 +2,7 @@ # Copyright (c) 2012 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +# Whitespace change for crbug.com/1112996. TODO: Delete me. """Sets environment variables needed to run a chromium unit test."""
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index c200eb2..d9a819a 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -2242,6 +2242,25 @@ ] } ], + "DisableLatencyRecoveryDesktop": [ + { + "platforms": [ + "chromeos", + "linux", + "mac", + "windows" + ], + "experiments": [ + { + "name": "DisabledImplDisabledMain", + "disable_features": [ + "ImplLatencyRecovery", + "MainLatencyRecovery" + ] + } + ] + } + ], "DisableMalwareExtensionsRemotely": [ { "platforms": [
diff --git a/third_party/blink/public/platform/media/webmediaplayer_delegate.h b/third_party/blink/public/platform/media/webmediaplayer_delegate.h index 3300bea4..fc8c71cb 100644 --- a/third_party/blink/public/platform/media/webmediaplayer_delegate.h +++ b/third_party/blink/public/platform/media/webmediaplayer_delegate.h
@@ -67,6 +67,7 @@ virtual void OnSeekBackward(double seconds) = 0; virtual void OnEnterPictureInPicture() = 0; virtual void OnExitPictureInPicture() = 0; + virtual void OnSetAudioSink(const std::string& sink_id) = 0; // Called to control audio ducking. Output volume should be set to // |player_volume| * |multiplier|. The range of |multiplier| is [0, 1], @@ -130,6 +131,11 @@ virtual void DidPictureInPictureAvailabilityChange(int delegate_id, bool available) = 0; + // Notify that the audio output sink has changed + virtual void DidAudioOutputSinkChange( + int delegate_id, + const std::string& hashed_device_id) = 0; + // Notify that a buffer underflow event happened for the media player. virtual void DidBufferUnderflow(int player_id) = 0;
diff --git a/third_party/blink/public/platform/scheduler/test/web_mock_thread_scheduler.h b/third_party/blink/public/platform/scheduler/test/web_mock_thread_scheduler.h index dd1333c..109b74e 100644 --- a/third_party/blink/public/platform/scheduler/test/web_mock_thread_scheduler.h +++ b/third_party/blink/public/platform/scheduler/test/web_mock_thread_scheduler.h
@@ -27,6 +27,8 @@ MOCK_METHOD0(DefaultTaskRunner, scoped_refptr<base::SingleThreadTaskRunner>()); + MOCK_METHOD0(DeprecatedDefaultTaskRunner, + scoped_refptr<base::SingleThreadTaskRunner>()); MOCK_METHOD0(CompositorTaskRunner, scoped_refptr<base::SingleThreadTaskRunner>()); MOCK_METHOD0(InputTaskRunner, scoped_refptr<base::SingleThreadTaskRunner>());
diff --git a/third_party/blink/public/platform/scheduler/web_thread_scheduler.h b/third_party/blink/public/platform/scheduler/web_thread_scheduler.h index 08f8ade..828b8f1 100644 --- a/third_party/blink/public/platform/scheduler/web_thread_scheduler.h +++ b/third_party/blink/public/platform/scheduler/web_thread_scheduler.h
@@ -82,9 +82,6 @@ virtual scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner(); - // Returns the cleanup task runner, which is for cleaning up. - virtual scoped_refptr<base::SingleThreadTaskRunner> CleanupTaskRunner(); - // Returns a default task runner. This is basically same as the default task // runner, but is explicitly allowed to run JavaScript. For the detail, see // the comment at blink::ThreadScheduler::DeprecatedDefaultTaskRunner.
diff --git a/third_party/blink/public/platform/task_type.h b/third_party/blink/public/platform/task_type.h index 9d2ac8d7..6dfc404 100644 --- a/third_party/blink/public/platform/task_type.h +++ b/third_party/blink/public/platform/task_type.h
@@ -260,7 +260,8 @@ kMainThreadTaskQueueIdle = 41, kMainThreadTaskQueueIPC = 42, kMainThreadTaskQueueControl = 43, - kMainThreadTaskQueueCleanup = 52, + // Removed: + // kMainThreadTaskQueueCleanup = 52, kMainThreadTaskQueueMemoryPurge = 62, kMainThreadTaskQueueNonWaking = 69, kCompositorThreadTaskQueueDefault = 45,
diff --git a/third_party/blink/public/web/modules/mediastream/webmediaplayer_ms.h b/third_party/blink/public/web/modules/mediastream/webmediaplayer_ms.h index a8ca9685..7166dbc4 100644 --- a/third_party/blink/public/web/modules/mediastream/webmediaplayer_ms.h +++ b/third_party/blink/public/web/modules/mediastream/webmediaplayer_ms.h
@@ -182,6 +182,7 @@ void OnSeekBackward(double seconds) override; void OnEnterPictureInPicture() override; void OnExitPictureInPicture() override; + void OnSetAudioSink(const std::string& sink_id) override; void OnVolumeMultiplierUpdate(double multiplier) override; void OnBecamePersistentVideo(bool value) override;
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h index 699eb37..a194c36 100644 --- a/third_party/blink/public/web/web_local_frame.h +++ b/third_party/blink/public/web/web_local_frame.h
@@ -544,15 +544,6 @@ // Iframe sandbox --------------------------------------------------------- - // TODO(ekaramad): This method is only exposed for testing for certain tests - // outside of blink/ that are interested in approximate value of the - // FrameReplicationState. This method should be replaced with one in content/ - // where the notion of FrameReplicationState is relevant to. - // Returns the effective sandbox flags which are inherited from their parent - // frame. - virtual network::mojom::WebSandboxFlags EffectiveSandboxFlagsForTesting() - const = 0; - // Returns false if this frame, or any parent frame is sandboxed and does not // have the flag "allow-downloads" set. virtual bool IsAllowedToDownload() const = 0;
diff --git a/third_party/blink/renderer/core/fetch/BUILD.gn b/third_party/blink/renderer/core/fetch/BUILD.gn index 5b229766..bd02a17 100644 --- a/third_party/blink/renderer/core/fetch/BUILD.gn +++ b/third_party/blink/renderer/core/fetch/BUILD.gn
@@ -48,13 +48,5 @@ "trust_token_to_mojom.h", ] - if (is_win && is_component_build) { - # Body.cpp exports a class (CORE_EXPORT) that inherits from - # PairIterable<String, String> that is also used as base class by an - # imported (CORE_EXPORT) class and that confuses the Windows - # linker/compiler. https://crbug.com/739340 - jumbo_excluded_sources = [ "body.cc" ] - } - public_deps = [ "//third_party/blink/renderer/platform" ] }
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc index 529612f..a99cecf 100644 --- a/third_party/blink/renderer/core/frame/local_dom_window.cc +++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -240,7 +240,8 @@ MakeGarbageCollected<TextSuggestionController>(*this)), isolated_world_csp_map_( MakeGarbageCollected< - HeapHashMap<int, Member<ContentSecurityPolicy>>>()) {} + HeapHashMap<int, Member<ContentSecurityPolicy>>>()), + token_(LocalFrameToken(frame.GetFrameToken())) {} void LocalDOMWindow::BindContentSecurityPolicy() { DCHECK(!GetContentSecurityPolicy()->IsBound());
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.h b/third_party/blink/renderer/core/frame/local_dom_window.h index 19937dd7..2019a18 100644 --- a/third_party/blink/renderer/core/frame/local_dom_window.h +++ b/third_party/blink/renderer/core/frame/local_dom_window.h
@@ -29,6 +29,7 @@ #include "services/metrics/public/cpp/ukm_recorder.h" #include "services/metrics/public/cpp/ukm_source_id.h" +#include "third_party/blink/public/common/tokens/tokens.h" #include "third_party/blink/renderer/bindings/core/v8/script_value.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/dom/document.h" @@ -104,9 +105,16 @@ static LocalDOMWindow* From(const ScriptState*); - explicit LocalDOMWindow(LocalFrame&, WindowAgent*); + LocalDOMWindow(LocalFrame&, WindowAgent*); ~LocalDOMWindow() override; + // Returns the token identifying the frame that this ExecutionContext was + // associated with at the moment of its creation. This remains valid even + // after the frame has been destroyed and the ExecutionContext is detached. + // This is used as a stable and persistent identifier for attributing detached + // context memory usage. + const LocalFrameToken& token() const { return token_; } + LocalFrame* GetFrame() const { return To<LocalFrame>(DOMWindow::GetFrame()); } void Initialize(); @@ -493,6 +501,11 @@ // document. This helps to count them only once per page load. // We don't use std::bitset to avoid to include feature_policy.mojom-blink.h. mutable Vector<bool> potentially_violated_features_; + + // Token identifying the LocalFrame that this window was associated with at + // creation. Remains valid even after the frame is destroyed and the context + // is detached. + const LocalFrameToken token_; }; template <>
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc index a3b74a6..9d069f8a 100644 --- a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc +++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
@@ -134,10 +134,11 @@ std::move(widget_host), std::move(widget))), client_(&client) { - frame_widget_host_.Bind(std::move(frame_widget_host), - ThreadScheduler::Current()->IPCTaskRunner()); + frame_widget_host_.Bind( + std::move(frame_widget_host), + ThreadScheduler::Current()->DeprecatedDefaultTaskRunner()); receiver_.Bind(std::move(frame_widget), - ThreadScheduler::Current()->IPCTaskRunner()); + ThreadScheduler::Current()->DeprecatedDefaultTaskRunner()); } WebFrameWidgetBase::~WebFrameWidgetBase() = default;
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc index f5d4b39..7783dfe 100644 --- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc +++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -2324,31 +2324,6 @@ GetFrame()->CopyImageAtViewportPoint(IntPoint(pos_in_viewport)); } -network::mojom::blink::WebSandboxFlags -WebLocalFrameImpl::EffectiveSandboxFlagsForTesting() const { - if (!GetFrame()) - return network::mojom::blink::WebSandboxFlags::kNone; - network::mojom::blink::WebSandboxFlags flags = - GetFrame()->Loader().EffectiveSandboxFlags(); - if (RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled()) { - // When some of sandbox flags set in the 'sandbox' attribute are implemented - // as policies they are removed form the FrameOwner's sandbox flags to avoid - // being considered again as part of inherited or CSP sandbox. - // Note: if the FrameOwner is remote then the effective flags would miss the - // part of sandbox converted to FeaturePolicies. That said, with - // FeaturePolicyForSandbox all such flags should be part of the document's - // FeaturePolicy. For certain flags such as "downloads", dedicated API - // should be used (see IsAllowedToDownload()). - auto* local_owner = GetFrame()->DeprecatedLocalOwner(); - if (local_owner && local_owner->OwnerType() == - mojom::blink::FrameOwnerElementType::kIframe) { - flags |= To<HTMLIFrameElement>(local_owner) - ->sandbox_flags_converted_to_feature_policies(); - } - } - return flags; -} - bool WebLocalFrameImpl::IsAllowedToDownload() const { if (!GetFrame()) return true;
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h index 94a26b4f..0e18107 100644 --- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h +++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -248,8 +248,6 @@ const WebVector<WebString>& words) override; void SetContentSettingsClient(WebContentSettingsClient*) override; void ReloadImage(const WebNode&) override; - network::mojom::blink::WebSandboxFlags EffectiveSandboxFlagsForTesting() - const override; bool IsAllowedToDownload() const override; bool FindForTesting(int identifier, const WebString& search_text,
diff --git a/third_party/blink/renderer/core/html/BUILD.gn b/third_party/blink/renderer/core/html/BUILD.gn index 77e74b1..e8e37235 100644 --- a/third_party/blink/renderer/core/html/BUILD.gn +++ b/third_party/blink/renderer/core/html/BUILD.gn
@@ -593,9 +593,6 @@ "window_name_collection.h", ] - jumbo_excluded_sources = - [ "canvas/canvas_rendering_context.cc" ] # https://crbug.com/716395 - deps = [ "//services/metrics/public/cpp:metrics_cpp", "//skia:skcms",
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc index bef569d..a46f98f 100644 --- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc +++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc
@@ -13,8 +13,44 @@ namespace blink { +namespace { + using ::testing::ElementsAre; +String ToDebugString(const NGInlineCursor& cursor) { + if (cursor.Current().IsLineBox()) + return "#linebox"; + + if (cursor.Current().IsLayoutGeneratedText()) { + StringBuilder result; + result.Append("#'"); + result.Append(cursor.CurrentText()); + result.Append("'"); + return result.ToString(); + } + + if (cursor.Current().IsText()) + return cursor.CurrentText().ToString().StripWhiteSpace(); + + if (const LayoutObject* layout_object = cursor.Current().GetLayoutObject()) { + if (const Element* element = DynamicTo<Element>(layout_object->GetNode())) { + if (const AtomicString& id = element->GetIdAttribute()) + return "#" + id; + } + + return layout_object->DebugName(); + } + + return "#null"; +} + +Vector<String> LayoutObjectToDebugStringList(NGInlineCursor cursor) { + Vector<String> list; + for (; cursor; cursor.MoveToNextForSameLayoutObject()) + list.push_back(ToDebugString(cursor)); + return list; +} + class NGInlineCursorTest : public NGLayoutTest, private ScopedLayoutNGFragmentItemForTest, public testing::WithParamInterface<bool> { @@ -73,35 +109,6 @@ EXPECT_THAT(backwards, forwards); } - String ToDebugString(const NGInlineCursor& cursor) { - if (cursor.Current().IsLineBox()) - return "#linebox"; - - if (cursor.Current().IsLayoutGeneratedText()) { - StringBuilder result; - result.Append("#'"); - result.Append(cursor.CurrentText()); - result.Append("'"); - return result.ToString(); - } - - if (cursor.Current().IsText()) - return cursor.CurrentText().ToString().StripWhiteSpace(); - - if (const LayoutObject* layout_object = - cursor.Current().GetLayoutObject()) { - if (const Element* element = - DynamicTo<Element>(layout_object->GetNode())) { - if (const AtomicString& id = element->GetIdAttribute()) - return "#" + id; - } - - return layout_object->DebugName(); - } - - return "#null"; - } - Vector<String> ToDebugStringListWithBidiLevel(const NGInlineCursor& start) { Vector<String> list; for (NGInlineCursor cursor(start); cursor; cursor.MoveToNext()) { @@ -215,12 +222,8 @@ "</div>"); NGInlineCursor cursor; cursor.MoveToIncludingCulledInline(*GetLayoutObjectByElementId("culled")); - Vector<String> list; - while (cursor) { - list.push_back(ToDebugString(cursor)); - cursor.MoveToNextForSameLayoutObject(); - } - EXPECT_THAT(list, ElementsAre("abc", "ABC", "", "XYZ", "xyz")); + EXPECT_THAT(LayoutObjectToDebugStringList(cursor), + ElementsAre("abc", "ABC", "", "XYZ", "xyz")); } // We should not have float:right fragment, because it isn't in-flow in @@ -233,12 +236,43 @@ "</div>"); NGInlineCursor cursor; cursor.MoveToIncludingCulledInline(*GetLayoutObjectByElementId("culled")); - Vector<String> list; - while (cursor) { - list.push_back(ToDebugString(cursor)); - cursor.MoveToNextForSameLayoutObject(); - } - EXPECT_THAT(list, ElementsAre("abc", "xyz")); + EXPECT_THAT(LayoutObjectToDebugStringList(cursor), ElementsAre("abc", "xyz")); +} + +TEST_P(NGInlineCursorTest, CulledInlineWithOOF) { + SetBodyInnerHTML(R"HTML( + <div id=root> + <b id=culled>abc<span style="position:absolute"></span>xyz</b> + </div> + )HTML"); + NGInlineCursor cursor; + cursor.MoveToIncludingCulledInline(*GetLayoutObjectByElementId("culled")); + EXPECT_THAT(LayoutObjectToDebugStringList(cursor), ElementsAre("abc", "xyz")); +} + +TEST_P(NGInlineCursorTest, CulledInlineNested) { + SetBodyInnerHTML(R"HTML( + <div id=root> + <b id=culled><span>abc</span> xyz</b> + </div> + )HTML"); + NGInlineCursor cursor; + cursor.MoveToIncludingCulledInline(*GetLayoutObjectByElementId("culled")); + EXPECT_THAT(LayoutObjectToDebugStringList(cursor), ElementsAre("abc", "xyz")); +} + +TEST_P(NGInlineCursorTest, CulledInlineBlockChild) { + SetBodyInnerHTML(R"HTML( + <div id=root> + <b id=culled> + <div>block</div> + <span>abc</span> xyz + </b> + </div> + )HTML"); + NGInlineCursor cursor; + cursor.MoveToIncludingCulledInline(*GetLayoutObjectByElementId("culled")); + EXPECT_THAT(LayoutObjectToDebugStringList(cursor), ElementsAre("#culled")); } TEST_P(NGInlineCursorTest, CulledInlineWithRoot) { @@ -247,12 +281,8 @@ )HTML"); const LayoutObject* layout_inline_a = GetLayoutObjectByElementId("a"); cursor.MoveToIncludingCulledInline(*layout_inline_a); - Vector<String> list; - while (cursor) { - list.push_back(ToDebugString(cursor)); - cursor.MoveToNextForSameLayoutObject(); - } - EXPECT_THAT(list, ElementsAre("abc", "", "xyz")); + EXPECT_THAT(LayoutObjectToDebugStringList(cursor), + ElementsAre("abc", "", "xyz")); } TEST_P(NGInlineCursorTest, CulledInlineWithoutRoot) { @@ -262,12 +292,8 @@ const LayoutObject* layout_inline_a = GetLayoutObjectByElementId("a"); NGInlineCursor cursor; cursor.MoveToIncludingCulledInline(*layout_inline_a); - Vector<String> list; - while (cursor) { - list.push_back(ToDebugString(cursor)); - cursor.MoveToNextForSameLayoutObject(); - } - EXPECT_THAT(list, ElementsAre("abc", "", "xyz")); + EXPECT_THAT(LayoutObjectToDebugStringList(cursor), + ElementsAre("abc", "", "xyz")); } TEST_P(NGInlineCursorTest, FirstChild) { @@ -620,12 +646,8 @@ TEST_P(NGInlineCursorTest, NextForSameLayoutObject) { NGInlineCursor cursor = SetupCursor("<pre id=root>abc\ndef\nghi</pre>"); cursor.MoveTo(*GetLayoutObjectByElementId("root")->SlowFirstChild()); - Vector<String> list; - while (cursor) { - list.push_back(ToDebugString(cursor)); - cursor.MoveToNextForSameLayoutObject(); - } - EXPECT_THAT(list, ElementsAre("abc", "", "def", "", "ghi")); + EXPECT_THAT(LayoutObjectToDebugStringList(cursor), + ElementsAre("abc", "", "def", "", "ghi")); } // Test |NextForSameLayoutObject| with limit range set. @@ -660,12 +682,8 @@ // Now |line2| is limited to the 2nd line. There should be only one framgnet // for `<span>` if we search using `line2`. LayoutObject* span1 = GetLayoutObjectByElementId("span1"); - wtf_size_t count = 0; - for (line2.MoveTo(*span1); line2; line2.MoveToNextForSameLayoutObject()) { - DCHECK_EQ(line2.Current().GetLayoutObject(), span1); - ++count; - } - EXPECT_EQ(count, 1u); + line2.MoveTo(*span1); + EXPECT_THAT(LayoutObjectToDebugStringList(line2), ElementsAre("#span1")); } TEST_P(NGInlineCursorTest, Sibling) { @@ -1038,4 +1056,6 @@ ElementsAre("text3")); } +} // namespace + } // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc index a8688cd..277a5bd 100644 --- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -885,7 +885,7 @@ unsigned line_text_offset = item_result.StartOffset() - line_info->StartOffset(); DCHECK_EQ(kObjectReplacementCharacter, line_text[line_text_offset]); - float space = spacing.ComputeSpacing(line_text_offset, offset); + float space = spacing.ComputeSpacing(line_text_offset, 0.0, offset); item_result.inline_size += space; // |offset| is non-zero only before CJK characters. DCHECK_EQ(offset, 0.f);
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc index aa09812f..84574b9 100644 --- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc +++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
@@ -1109,7 +1109,7 @@ scoped_refptr<ShapeResult> shape_result = shaper.Shape(start_item, end_offset); - if (UNLIKELY(spacing.SetSpacing(font.GetFontDescription()))) + if (UNLIKELY(spacing.SetSpacing(font))) shape_result->ApplySpacing(spacing); // If the text is from one item, use the ShapeResult as is.
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc index fd91a99f..f38e03084 100644 --- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc +++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
@@ -2087,7 +2087,7 @@ DCHECK_EQ(break_iterator_.Locale(), style.LocaleForLineBreakIterator()); } ShapeResultSpacing<String> spacing(spacing_.Text()); - spacing.SetSpacing(style.GetFontDescription()); + spacing.SetSpacing(style.GetFont()); DCHECK_EQ(spacing.LetterSpacing(), spacing_.LetterSpacing()); DCHECK_EQ(spacing.WordSpacing(), spacing_.WordSpacing()); #endif @@ -2136,7 +2136,7 @@ break_iterator_.SetLocale(style.LocaleForLineBreakIterator()); } - spacing_.SetSpacing(style.GetFontDescription()); + spacing_.SetSpacing(style.GetFont()); } void NGLineBreaker::MoveToNextOf(const NGInlineItem& item) {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc index f57b41b..9e59e6cb 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc
@@ -19,6 +19,43 @@ namespace blink { +namespace { + +enum class LegendBlockAlignment { + kStart, + kCenter, + kEnd, +}; + +// This function is very similar to BlockAlignment() in ng_length_utils.cc, but +// it supports text-align:left/center/right. +inline LegendBlockAlignment ComputeLegendBlockAlignment( + const ComputedStyle& legend_style, + const ComputedStyle& fieldset_style) { + bool start_auto = legend_style.MarginStartUsing(fieldset_style).IsAuto(); + bool end_auto = legend_style.MarginEndUsing(fieldset_style).IsAuto(); + if (start_auto || end_auto) { + if (start_auto) { + return end_auto ? LegendBlockAlignment::kCenter + : LegendBlockAlignment::kEnd; + } + return LegendBlockAlignment::kStart; + } + const bool is_ltr = fieldset_style.IsLeftToRightDirection(); + switch (legend_style.GetTextAlign()) { + case ETextAlign::kLeft: + return is_ltr ? LegendBlockAlignment::kStart : LegendBlockAlignment::kEnd; + case ETextAlign::kRight: + return is_ltr ? LegendBlockAlignment::kEnd : LegendBlockAlignment::kStart; + case ETextAlign::kCenter: + return LegendBlockAlignment::kCenter; + default: + return LegendBlockAlignment::kStart; + } +} + +} // namespace + NGFieldsetLayoutAlgorithm::NGFieldsetLayoutAlgorithm( const NGLayoutAlgorithmParams& params) : NGLayoutAlgorithm(params), @@ -329,7 +366,7 @@ LayoutUnit legend_inline_start = ComputeLegendInlineOffset( legend.Style(), NGFragment(writing_mode_, result->PhysicalFragment()).InlineSize(), - legend_margins, BorderScrollbarPadding().inline_start, + legend_margins, Style(), BorderScrollbarPadding().inline_start, ChildAvailableSize().inline_size); // NOTE: For painting purposes, this must be kept in sync with: @@ -344,22 +381,21 @@ const ComputedStyle& legend_style, LayoutUnit legend_border_box_inline_size, const NGBoxStrut& legend_margins, + const ComputedStyle& fieldset_style, LayoutUnit fieldset_border_padding_inline_start, LayoutUnit fieldset_content_inline_size) { - const ETextAlign align = legend_style.GetTextAlign(); - const ETextAlign align_end = legend_style.IsLeftToRightDirection() - ? ETextAlign::kRight - : ETextAlign::kLeft; - LayoutUnit legend_inline_start = fieldset_border_padding_inline_start; - if (align == ETextAlign::kCenter) { - legend_inline_start += - (fieldset_content_inline_size - legend_border_box_inline_size) / 2; - } else if (align == align_end) { - legend_inline_start += fieldset_content_inline_size - - legend_border_box_inline_size - - legend_margins.inline_end; - } else { - legend_inline_start += legend_margins.inline_start; + LayoutUnit legend_inline_start = + fieldset_border_padding_inline_start + legend_margins.inline_start; + // The following logic is very similar to ResolveInlineMargins(), but it uses + // ComputeLegendBlockAlignment(). + const LayoutUnit available_space = + fieldset_content_inline_size - legend_border_box_inline_size; + if (available_space > LayoutUnit()) { + auto alignment = ComputeLegendBlockAlignment(legend_style, fieldset_style); + if (alignment == LegendBlockAlignment::kCenter) + legend_inline_start += available_space / 2; + else if (alignment == LegendBlockAlignment::kEnd) + legend_inline_start += available_space - legend_margins.inline_end; } return legend_inline_start; }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h index ac41aa7c..76c61a3 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h +++ b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h
@@ -31,6 +31,7 @@ const ComputedStyle& legend_style, LayoutUnit legend_border_box_inline_size, const NGBoxStrut& legend_margins, + const ComputedStyle& fieldset_style, LayoutUnit fieldset_border_padding_inline_start, LayoutUnit fieldset_content_inline_size);
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc index 3139b88..4a711f3 100644 --- a/third_party/blink/renderer/core/loader/document_loader.cc +++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -1501,6 +1501,30 @@ } } +network::mojom::blink::WebSandboxFlags DocumentLoader::CalculateSandboxFlags() { + auto sandbox_flags = GetFrameLoader().GetForcedSandboxFlags() | + content_security_policy_->GetSandboxMask() | + frame_policy_.sandbox_flags; + if (archive_) { + // The URL of a Document loaded from a MHTML archive is controlled by + // the Content-Location header. This would allow UXSS, since + // Content-Location can be arbitrarily controlled to control the + // Document's URL and origin. Instead, force a Document loaded from a + // MHTML archive to be sandboxed, providing exceptions only for creating + // new windows. + DCHECK(commit_reason_ == CommitReason::kRegular || + commit_reason_ == CommitReason::kInitialization); + sandbox_flags |= (network::mojom::blink::WebSandboxFlags::kAll & + ~(network::mojom::blink::WebSandboxFlags::kPopups | + network::mojom::blink::WebSandboxFlags:: + kPropagatesToAuxiliaryBrowsingContexts)); + } else if (commit_reason_ == CommitReason::kXSLT) { + // An XSLT document inherits sandbox flags from the document that create it. + sandbox_flags |= frame_->DomWindow()->GetSandboxFlags(); + } + return sandbox_flags; +} + scoped_refptr<SecurityOrigin> DocumentLoader::CalculateOrigin( Document* owner_document, network::mojom::blink::WebSandboxFlags sandbox_flags) { @@ -1635,31 +1659,7 @@ .BoolValue()); } - // Make the snapshot value of sandbox flags from the beginning of navigation - // available in frame loader, so that the value could be further used to - // initialize sandbox flags in security context. crbug.com/1026627 - GetFrameLoader().SetFrameOwnerSandboxFlags(frame_policy_.sandbox_flags); - - network::mojom::blink::WebSandboxFlags sandbox_flags = - GetFrameLoader().EffectiveSandboxFlags() | - content_security_policy_->GetSandboxMask(); - if (archive_) { - // The URL of a Document loaded from a MHTML archive is controlled by - // the Content-Location header. This would allow UXSS, since - // Content-Location can be arbitrarily controlled to control the - // Document's URL and origin. Instead, force a Document loaded from a - // MHTML archive to be sandboxed, providing exceptions only for creating - // new windows. - DCHECK(commit_reason_ == CommitReason::kRegular || - commit_reason_ == CommitReason::kInitialization); - sandbox_flags |= (network::mojom::blink::WebSandboxFlags::kAll & - ~(network::mojom::blink::WebSandboxFlags::kPopups | - network::mojom::blink::WebSandboxFlags:: - kPropagatesToAuxiliaryBrowsingContexts)); - } else if (commit_reason_ == CommitReason::kXSLT) { - sandbox_flags |= frame_->DomWindow()->GetSandboxFlags(); - } - + auto sandbox_flags = CalculateSandboxFlags(); auto security_origin = CalculateOrigin(owner_document, sandbox_flags); GlobalObjectReusePolicy global_object_reuse_policy =
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h index e682741..5ba686a 100644 --- a/third_party/blink/renderer/core/loader/document_loader.h +++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -339,6 +339,7 @@ mojom::MHTMLLoadResult::kSuccess; private: + network::mojom::blink::WebSandboxFlags CalculateSandboxFlags(); scoped_refptr<SecurityOrigin> CalculateOrigin( Document* owner_document, network::mojom::blink::WebSandboxFlags);
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc index ee724b0..b9b4ead3 100644 --- a/third_party/blink/renderer/core/loader/frame_loader.cc +++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -214,7 +214,14 @@ FrameLoader::FrameLoader(LocalFrame* frame) : frame_(frame), progress_tracker_(MakeGarbageCollected<ProgressTracker>(frame)), - forced_sandbox_flags_(network::mojom::blink::WebSandboxFlags::kNone), + // Frames need to inherit the sandbox flags of their parent frame. + // These can be fixed at construction time, because the only actions that + // trigger a sandbox flags change in the parent will necessarily detach + // this frame. + forced_sandbox_flags_( + frame_->Tree().Parent() + ? frame_->Tree().Parent()->GetSecurityContext()->GetSandboxFlags() + : network::mojom::blink::WebSandboxFlags::kNone), dispatching_did_clear_window_object_in_main_world_(false), detached_(false), virtual_time_pauser_( @@ -1494,11 +1501,6 @@ forced_sandbox_flags_ |= flags; } -void FrameLoader::SetFrameOwnerSandboxFlags( - network::mojom::blink::WebSandboxFlags flags) { - frame_owner_sandbox_flags_ = flags; -} - void FrameLoader::DispatchDidClearDocumentOfWindowObject() { if (state_machine_.CreatingInitialEmptyDocument()) return; @@ -1532,32 +1534,11 @@ Client()->DispatchDidClearWindowObjectInMainWorld(); } -network::mojom::blink::WebSandboxFlags FrameLoader::EffectiveSandboxFlags() - const { - network::mojom::blink::WebSandboxFlags flags = forced_sandbox_flags_; - if (frame_->Owner()) { - // Cannot use flags in frame_owner->GetFramePolicy().sandbox_flags, because - // frame_owner's frame policy is volatile and can be changed by javascript - // before navigation commits. Uses a snapshot - // value(frame_owner_sandbox_flags_) which is set in - // DocumentInit::WithFramePolicy instead. crbug.com/1026627 - DCHECK(frame_owner_sandbox_flags_.has_value()); - flags |= frame_owner_sandbox_flags_.value(); - } - // Frames need to inherit the sandbox flags of their parent frame. - if (Frame* parent_frame = frame_->Tree().Parent()) - flags |= parent_frame->GetSecurityContext()->GetSandboxFlags(); - return flags; -} - network::mojom::blink::WebSandboxFlags FrameLoader::PendingEffectiveSandboxFlags() const { network::mojom::blink::WebSandboxFlags flags = forced_sandbox_flags_; if (FrameOwner* frame_owner = frame_->Owner()) flags |= frame_owner->GetFramePolicy().sandbox_flags; - // Frames need to inherit the sandbox flags of their parent frame. - if (Frame* parent_frame = frame_->Tree().Parent()) - flags |= parent_frame->GetSecurityContext()->GetSandboxFlags(); return flags; }
diff --git a/third_party/blink/renderer/core/loader/frame_loader.h b/third_party/blink/renderer/core/loader/frame_loader.h index d73aa96..484feb4 100644 --- a/third_party/blink/renderer/core/loader/frame_loader.h +++ b/third_party/blink/renderer/core/loader/frame_loader.h
@@ -148,16 +148,9 @@ // sandbox attribute of any parent frames. void ForceSandboxFlags(network::mojom::blink::WebSandboxFlags flags); - // Set frame_owner's effective sandbox flags, which are sandbox flags value - // at the beginning of navigation. - void SetFrameOwnerSandboxFlags(network::mojom::blink::WebSandboxFlags flags); - - // Includes the collection of forced, inherited, and FrameOwner's sandbox - // flags, where the FrameOwner's flag is snapshotted from the last committed - // navigation. Note: with FeaturePolicyForSandbox the frame owner's sandbox - // flags only includes the flags which are *not* implemented as feature - // policies already present in the FrameOwner's ContainerPolicy. - network::mojom::blink::WebSandboxFlags EffectiveSandboxFlags() const; + network::mojom::blink::WebSandboxFlags GetForcedSandboxFlags() const { + return forced_sandbox_flags_; + } // Includes the collection of forced, inherited, and FrameOwner's sandbox // flags. Note: with FeaturePolicyForSandbox the frame owner's sandbox flags @@ -310,14 +303,6 @@ std::unique_ptr<ClientNavigationState> client_navigation_; network::mojom::blink::WebSandboxFlags forced_sandbox_flags_; - // A snapshot value of frame_owner's sandbox flags states at the beginning of - // navigation. For main frame which does not have a frame owner, the value is - // base::nullopt. - // The snapshot value is needed because of potential racing conditions on - // sandbox attribute on iframe element. - // crbug.com/1026627 - base::Optional<network::mojom::blink::WebSandboxFlags> - frame_owner_sandbox_flags_ = base::nullopt; bool dispatching_did_clear_window_object_in_main_world_; bool detached_;
diff --git a/third_party/blink/renderer/core/paint/ng/ng_fieldset_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_fieldset_painter.cc index c1c55ee..c23d9e82 100644 --- a/third_party/blink/renderer/core/paint/ng/ng_fieldset_painter.cc +++ b/third_party/blink/renderer/core/paint/ng/ng_fieldset_painter.cc
@@ -86,7 +86,7 @@ NGFieldsetLayoutAlgorithm::ComputeLegendInlineOffset( (*legend)->Style(), legend_border_box.size.ConvertToLogical(writing_mode).inline_size, - legend_margins, border_padding.inline_start, + legend_margins, style, border_padding.inline_start, fieldset_content_inline_size), legend_margins.block_start}; legend_border_box.offset = offset.ConvertToPhysical(
diff --git a/third_party/blink/renderer/core/script/BUILD.gn b/third_party/blink/renderer/core/script/BUILD.gn index 3b08884..7007485 100644 --- a/third_party/blink/renderer/core/script/BUILD.gn +++ b/third_party/blink/renderer/core/script/BUILD.gn
@@ -67,6 +67,4 @@ ] deps = [ "//third_party/blink/public:resources" ] - - jumbo_excluded_sources = [ "modulator.cc" ] # https://crbug.com/716395 }
diff --git a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc index 96b32f75..efd9d8a 100644 --- a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc +++ b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc
@@ -11,6 +11,7 @@ #include "base/bind.h" #include "base/callback.h" +#include "base/optional.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" #include "cc/layers/video_frame_provider_client_impl.h" @@ -805,7 +806,9 @@ media::OutputDeviceStatusCB callback = ConvertToOutputDeviceStatusCB(std::move(completion_callback)); if (audio_renderer_) { - audio_renderer_->SwitchOutputDevice(sink_id.Utf8(), std::move(callback)); + auto sink_id_utf8 = sink_id.Utf8(); + audio_renderer_->SwitchOutputDevice(sink_id_utf8, std::move(callback)); + delegate_->DidAudioOutputSinkChange(delegate_id_, sink_id_utf8); } else { std::move(callback).Run(media::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL); SendLogMessage(String::Format( @@ -1081,6 +1084,11 @@ client_->RequestExitPictureInPicture(); } +void WebMediaPlayerMS::OnSetAudioSink(const std::string& sink_id) { + SetSinkId(WebString::FromASCII(sink_id), + base::DoNothing::Once<base::Optional<blink::WebSetSinkIdError>>()); +} + void WebMediaPlayerMS::OnVolumeMultiplierUpdate(double multiplier) { // TODO(perkj, magjed): See TODO in OnPlay(). }
diff --git a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_test.cc b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_test.cc index e514cfc..03e2af5 100644 --- a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_test.cc +++ b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_test.cc
@@ -183,6 +183,11 @@ EXPECT_EQ(delegate_id_, delegate_id); } + void DidAudioOutputSinkChange(int delegate_id, + const std::string& hashed_device_id) override { + EXPECT_EQ(delegate_id_, delegate_id); + } + private: int delegate_id_ = 1234; Observer* observer_ = nullptr; @@ -609,7 +614,6 @@ void RequestExitPictureInPicture() override {} Features GetFeatures() override { return Features(); } - // Implementation of cc::VideoFrameProvider::Client void StopUsingProvider() override; void StartRendering() override;
diff --git a/third_party/blink/renderer/modules/storage/cached_storage_area.cc b/third_party/blink/renderer/modules/storage/cached_storage_area.cc index 1497131..e8d5a4d 100644 --- a/third_party/blink/renderer/modules/storage/cached_storage_area.cc +++ b/third_party/blink/renderer/modules/storage/cached_storage_area.cc
@@ -181,12 +181,12 @@ CachedStorageArea::CachedStorageArea( AreaType type, scoped_refptr<const SecurityOrigin> origin, - scoped_refptr<base::SingleThreadTaskRunner> ipc_runner, + scoped_refptr<base::SingleThreadTaskRunner> task_runner, StorageNamespace* storage_namespace) : type_(type), origin_(std::move(origin)), storage_namespace_(storage_namespace), - ipc_task_runner_(std::move(ipc_runner)), + task_runner_(std::move(task_runner)), areas_(MakeGarbageCollected<HeapHashMap<WeakMember<Source>, String>>()) { BindStorageArea(); base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( @@ -204,17 +204,16 @@ // Some tests may not provide a StorageNamespace. DCHECK(!remote_area_); if (new_area) { - remote_area_.Bind(std::move(new_area), ipc_task_runner_); + remote_area_.Bind(std::move(new_area), task_runner_); } else if (storage_namespace_) { storage_namespace_->BindStorageArea( - origin_, remote_area_.BindNewPipeAndPassReceiver(ipc_task_runner_)); + origin_, remote_area_.BindNewPipeAndPassReceiver(task_runner_)); } else { return; } receiver_.reset(); - remote_area_->AddObserver( - receiver_.BindNewPipeAndPassRemote(ipc_task_runner_)); + remote_area_->AddObserver(receiver_.BindNewPipeAndPassRemote(task_runner_)); } void CachedStorageArea::ResetConnection(
diff --git a/third_party/blink/renderer/modules/storage/cached_storage_area.h b/third_party/blink/renderer/modules/storage/cached_storage_area.h index b4e046bd..28c9c3d0 100644 --- a/third_party/blink/renderer/modules/storage/cached_storage_area.h +++ b/third_party/blink/renderer/modules/storage/cached_storage_area.h
@@ -177,7 +177,7 @@ const AreaType type_; const scoped_refptr<const SecurityOrigin> origin_; const WeakPersistent<StorageNamespace> storage_namespace_; - const scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner_; + const scoped_refptr<base::SingleThreadTaskRunner> task_runner_; std::unique_ptr<StorageAreaMap> map_;
diff --git a/third_party/blink/renderer/modules/storage/storage_controller.cc b/third_party/blink/renderer/modules/storage/storage_controller.cc index 995be27..6c1590b 100644 --- a/third_party/blink/renderer/modules/storage/storage_controller.cc +++ b/third_party/blink/renderer/modules/storage/storage_controller.cc
@@ -42,12 +42,13 @@ // static StorageController* StorageController::GetInstance() { - DEFINE_STATIC_LOCAL(StorageController, gCachedStorageAreaController, - (GetDomStorageConnection(), - Thread::MainThread()->Scheduler()->IPCTaskRunner(), - base::SysInfo::IsLowEndDevice() - ? kStorageControllerTotalCacheLimitInBytesLowEnd - : kStorageControllerTotalCacheLimitInBytes)); + DEFINE_STATIC_LOCAL( + StorageController, gCachedStorageAreaController, + (GetDomStorageConnection(), + Thread::MainThread()->Scheduler()->DeprecatedDefaultTaskRunner(), + base::SysInfo::IsLowEndDevice() + ? kStorageControllerTotalCacheLimitInBytesLowEnd + : kStorageControllerTotalCacheLimitInBytes)); return &gCachedStorageAreaController; } @@ -63,9 +64,9 @@ StorageController::StorageController( DomStorageConnection connection, - scoped_refptr<base::SingleThreadTaskRunner> ipc_runner, + scoped_refptr<base::SingleThreadTaskRunner> task_runner, size_t total_cache_limit) - : ipc_runner_(std::move(ipc_runner)), + : task_runner_(std::move(task_runner)), namespaces_(MakeGarbageCollected< HeapHashMap<String, WeakMember<StorageNamespace>>>()), total_cache_limit_(total_cache_limit),
diff --git a/third_party/blink/renderer/modules/storage/storage_controller.h b/third_party/blink/renderer/modules/storage/storage_controller.h index 3deb45c0..554f6cb 100644 --- a/third_party/blink/renderer/modules/storage/storage_controller.h +++ b/third_party/blink/renderer/modules/storage/storage_controller.h
@@ -62,7 +62,7 @@ mojo::PendingReceiver<mojom::blink::DomStorageClient> client_receiver; }; StorageController(DomStorageConnection connection, - scoped_refptr<base::SingleThreadTaskRunner> ipc_runner, + scoped_refptr<base::SingleThreadTaskRunner> task_runner, size_t total_cache_limit); // Creates a MakeGarbageCollected<StorageNamespace> for Session storage, and @@ -92,8 +92,8 @@ return dom_storage_remote_.get(); } - scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() { - return ipc_runner_; + scoped_refptr<base::SingleThreadTaskRunner> TaskRunner() { + return task_runner_; } private: @@ -102,7 +102,7 @@ // mojom::blink::DomStorageClient: void ResetStorageAreaAndNamespaceConnections() override; - scoped_refptr<base::SingleThreadTaskRunner> ipc_runner_; + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; Persistent<HeapHashMap<String, WeakMember<StorageNamespace>>> namespaces_; Persistent<StorageNamespace> local_storage_namespace_; size_t total_cache_limit_;
diff --git a/third_party/blink/renderer/modules/storage/storage_namespace.cc b/third_party/blink/renderer/modules/storage/storage_namespace.cc index 0f53d5f..bb5c9a0 100644 --- a/third_party/blink/renderer/modules/storage/storage_namespace.cc +++ b/third_party/blink/renderer/modules/storage/storage_namespace.cc
@@ -101,7 +101,7 @@ result = base::MakeRefCounted<CachedStorageArea>( IsSessionStorage() ? CachedStorageArea::AreaType::kSessionStorage : CachedStorageArea::AreaType::kLocalStorage, - origin, controller_->IPCTaskRunner(), this); + origin, controller_->TaskRunner(), this); cached_areas_.insert(std::move(origin), result); return result; } @@ -212,7 +212,7 @@ return; controller_->dom_storage()->BindSessionStorageNamespace( namespace_id_, - namespace_.BindNewPipeAndPassReceiver(controller_->IPCTaskRunner())); + namespace_.BindNewPipeAndPassReceiver(controller_->TaskRunner())); } } // namespace blink
diff --git a/third_party/blink/renderer/modules/webaudio/periodic_wave.cc b/third_party/blink/renderer/modules/webaudio/periodic_wave.cc index 2ea0d53..9efa189d 100644 --- a/third_party/blink/renderer/modules/webaudio/periodic_wave.cc +++ b/third_party/blink/renderer/modules/webaudio/periodic_wave.cc
@@ -36,6 +36,7 @@ #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" #include "third_party/blink/renderer/platform/bindings/exception_state.h" #if defined(CPU_ARM_NEON) @@ -70,6 +71,22 @@ return nullptr; } + if (real.size() < 2) { + exception_state.ThrowDOMException( + DOMExceptionCode::kIndexSizeError, + ExceptionMessages::IndexExceedsMinimumBound("length of the real array", + real.size(), 2u)); + return nullptr; + } + + if (imag.size() < 2) { + exception_state.ThrowDOMException( + DOMExceptionCode::kIndexSizeError, + ExceptionMessages::IndexExceedsMinimumBound("length of the imag array", + imag.size(), 2u)); + return nullptr; + } + PeriodicWave* periodic_wave = MakeGarbageCollected<PeriodicWave>(context.sampleRate()); periodic_wave->CreateBandLimitedTables(real.data(), imag.data(), real.size(),
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_shader_module.cc b/third_party/blink/renderer/modules/webgpu/gpu_shader_module.cc index 8e40d48..2733049 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_shader_module.cc +++ b/third_party/blink/renderer/modules/webgpu/gpu_shader_module.cc
@@ -21,13 +21,14 @@ WGPUShaderModuleDescriptor dawn_desc = {}; WGPUShaderModuleWGSLDescriptor wgsl_desc = {}; WGPUShaderModuleSPIRVDescriptor spirv_desc = {}; + std::string wgsl_code; auto wgsl_or_spirv = webgpu_desc->code(); if (wgsl_or_spirv.IsUSVString()) { - std::string code = wgsl_or_spirv.GetAsUSVString().Utf8(); + wgsl_code = wgsl_or_spirv.GetAsUSVString().Utf8(); wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor; - wgsl_desc.source = code.c_str(); + wgsl_desc.source = wgsl_code.c_str(); dawn_desc.nextInChain = reinterpret_cast<WGPUChainedStruct*>(&wgsl_desc); } else { DCHECK(wgsl_or_spirv.IsUint32Array()); @@ -48,8 +49,10 @@ dawn_desc.nextInChain = reinterpret_cast<WGPUChainedStruct*>(&spirv_desc); } + std::string label; if (webgpu_desc->hasLabel()) { - dawn_desc.label = webgpu_desc->label().Utf8().data(); + label = webgpu_desc->label().Utf8(); + dawn_desc.label = label.c_str(); } return MakeGarbageCollected<GPUShaderModule>(
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn index 557c4ab..e0653b5 100644 --- a/third_party/blink/renderer/platform/BUILD.gn +++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1533,27 +1533,6 @@ ] } - if (is_win) { - jumbo_excluded_sources = [ - # https://crbug.com/775979 - Uses libjpeg_turbo which uses a - # "boolean" typedef which is different (int) from the Windows - # standard "boolean" typedef (unsigned char), resulting in - # compilation errors when both are joined in a translation unit. - "image-decoders/jpeg/jpeg_image_decoder.cc", - ] - - if (is_component_build) { - # https://crbug.com/764823 - Mixing certain //url/ headers and - # using url::RawCanonOutputT<char> in one translation unit breaks - # the Windows component build. These files use RawCanonOutput. - jumbo_excluded_sources += [ - "link_hash.cc", - "weborigin/kurl.cc", - "weborigin/origin_access_entry.cc", - "weborigin/security_origin.cc", - ] - } - } configs += [ ":blink_platform_pch", ":blink_platform_config",
diff --git a/third_party/blink/renderer/platform/fonts/font.h b/third_party/blink/renderer/platform/fonts/font.h index 8864a71..3e0aeec6 100644 --- a/third_party/blink/renderer/platform/fonts/font.h +++ b/third_party/blink/renderer/platform/fonts/font.h
@@ -241,6 +241,13 @@ return EnsureFontFallbackList()->ShouldSkipDrawing(); } + // Returns true if any of the matched @font-face rules has set a + // letter-spacing-override value. + bool HasLetterSpacingOverride() const { + return font_fallback_list_ && + font_fallback_list_->HasLetterSpacingOverride(); + } + private: // TODO(xiaochengh): The function not only initializes null FontFallbackList, // but also syncs invalid FontFallbackList. Rename it for better readability.
diff --git a/third_party/blink/renderer/platform/fonts/font_data.h b/third_party/blink/renderer/platform/fonts/font_data.h index d79e7fd..579b4fd7 100644 --- a/third_party/blink/renderer/platform/fonts/font_data.h +++ b/third_party/blink/renderer/platform/fonts/font_data.h
@@ -52,6 +52,7 @@ virtual bool IsLoadingFallback() const = 0; virtual bool IsSegmented() const = 0; virtual bool ShouldSkipDrawing() const = 0; + virtual bool HasLetterSpacingOverride() const = 0; private: DISALLOW_COPY_AND_ASSIGN(FontData);
diff --git a/third_party/blink/renderer/platform/fonts/font_fallback_list.cc b/third_party/blink/renderer/platform/fonts/font_fallback_list.cc index 6cb1dd0..8512b83 100644 --- a/third_party/blink/renderer/platform/fonts/font_fallback_list.cc +++ b/third_party/blink/renderer/platform/fonts/font_fallback_list.cc
@@ -47,6 +47,7 @@ generation_(FontCache::GetFontCache()->Generation()), has_loading_fallback_(false), has_custom_font_(false), + has_letter_spacing_override_(false), can_shape_word_by_word_(false), can_shape_word_by_word_computed_(false), is_invalid_(false) {} @@ -61,6 +62,7 @@ family_index_ = 0; has_loading_fallback_ = false; has_custom_font_ = false; + has_letter_spacing_override_ = false; can_shape_word_by_word_ = false; can_shape_word_by_word_computed_ = false; font_selector_version_ = font_selector_ ? font_selector_->Version() : 0; @@ -280,6 +282,8 @@ has_loading_fallback_ = true; if (result->IsCustomFont()) has_custom_font_ = true; + if (result->HasLetterSpacingOverride()) + has_letter_spacing_override_ = true; } return result.get(); }
diff --git a/third_party/blink/renderer/platform/fonts/font_fallback_list.h b/third_party/blink/renderer/platform/fonts/font_fallback_list.h index ebda8686..d2a56aff 100644 --- a/third_party/blink/renderer/platform/fonts/font_fallback_list.h +++ b/third_party/blink/renderer/platform/fonts/font_fallback_list.h
@@ -111,6 +111,7 @@ bool HasLoadingFallback() const { return has_loading_fallback_; } bool HasCustomFont() const { return has_custom_font_; } + bool HasLetterSpacingOverride() const { return has_letter_spacing_override_; } private: explicit FontFallbackList(FontSelector* font_selector); @@ -132,6 +133,7 @@ uint16_t generation_; bool has_loading_fallback_ : 1; bool has_custom_font_ : 1; + bool has_letter_spacing_override_ : 1; bool can_shape_word_by_word_ : 1; bool can_shape_word_by_word_computed_ : 1; bool is_invalid_ : 1;
diff --git a/third_party/blink/renderer/platform/fonts/segmented_font_data.cc b/third_party/blink/renderer/platform/fonts/segmented_font_data.cc index f68af86..a9a0bd1 100644 --- a/third_party/blink/renderer/platform/fonts/segmented_font_data.cc +++ b/third_party/blink/renderer/platform/fonts/segmented_font_data.cc
@@ -86,4 +86,13 @@ return false; } +bool SegmentedFontData::HasLetterSpacingOverride() const { + auto* end = faces_.end(); + for (auto* it = faces_.begin(); it != end; ++it) { + if ((*it)->FontData()->HasLetterSpacingOverride()) + return true; + } + return false; +} + } // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/segmented_font_data.h b/third_party/blink/renderer/platform/fonts/segmented_font_data.h index 17cc3ed..0b237e5 100644 --- a/third_party/blink/renderer/platform/fonts/segmented_font_data.h +++ b/third_party/blink/renderer/platform/fonts/segmented_font_data.h
@@ -58,6 +58,7 @@ bool IsLoadingFallback() const override; bool IsSegmented() const override; bool ShouldSkipDrawing() const override; + bool HasLetterSpacingOverride() const override; Vector<scoped_refptr<FontDataForRangeSet>, 1> faces_; };
diff --git a/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h b/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h index af68756..b0c2647 100644 --- a/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h +++ b/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h
@@ -58,7 +58,7 @@ // SVG sets SpacingDisabled because it handles spacing by themselves. if (!run.SpacingDisabled()) - spacing_.SetSpacingAndExpansion(font->GetFontDescription()); + spacing_.SetSpacingAndExpansion(*font); } bool Next(scoped_refptr<const ShapeResult>* word_result) {
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc index 1bbc062..f220ea8 100644 --- a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc +++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc
@@ -714,7 +714,7 @@ ShapeResultSpacing<String> spacing(string); FontDescription font_description; font_description.SetLetterSpacing(-5); - spacing.SetSpacing(font_description); + spacing.SetSpacing(Font(font_description)); result->ApplySpacing(spacing); EXPECT_EQ(5 * 5, width - result->Width()); @@ -729,7 +729,7 @@ ShapeResultSpacing<String> spacing(string); FontDescription font_description; font_description.SetLetterSpacing(-char_width); - spacing.SetSpacing(font_description); + spacing.SetSpacing(Font(font_description)); result->ApplySpacing(spacing); // EXPECT_EQ(0.0f, result->Width()); @@ -744,7 +744,7 @@ ShapeResultSpacing<String> spacing(string); FontDescription font_description; font_description.SetLetterSpacing(-2 * char_width); - spacing.SetSpacing(font_description); + spacing.SetSpacing(Font(font_description)); result->ApplySpacing(spacing); // CSS does not allow negative width, it should be clampled to 0.
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc index 7b1c08b..972fdc4a 100644 --- a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc +++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
@@ -854,7 +854,8 @@ } space = spacing.ComputeSpacing( - run_start_index + glyph_data.character_index, offset); + run_start_index + glyph_data.character_index, + run->font_data_->GetLetterSpacingOverride(), offset); glyph_data.advance += space; total_space_for_run += space;
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.cc index ad24510..349ae05 100644 --- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.cc +++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.cc
@@ -4,15 +4,17 @@ #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h" +#include "third_party/blink/renderer/platform/fonts/font.h" #include "third_party/blink/renderer/platform/fonts/font_description.h" #include "third_party/blink/renderer/platform/text/text_run.h" namespace blink { template <typename TextContainerType> -bool ShapeResultSpacing<TextContainerType>::SetSpacing( - const FontDescription& font_description) { - if (!font_description.LetterSpacing() && !font_description.WordSpacing()) { +bool ShapeResultSpacing<TextContainerType>::SetSpacing(const Font& font) { + const FontDescription& font_description = font.GetFontDescription(); + if (!font_description.LetterSpacing() && !font_description.WordSpacing() && + !font.HasLetterSpacingOverride()) { has_spacing_ = false; return false; } @@ -41,18 +43,19 @@ template <typename TextContainerType> void ShapeResultSpacing<TextContainerType>::SetSpacingAndExpansion( - const FontDescription& font_description) { + const Font& font) { // Available only for TextRun since it has expansion data. NOTREACHED(); } template <> -void ShapeResultSpacing<TextRun>::SetSpacingAndExpansion( - const FontDescription& font_description) { +void ShapeResultSpacing<TextRun>::SetSpacingAndExpansion(const Font& font) { + const FontDescription& font_description = font.GetFontDescription(); letter_spacing_ = font_description.LetterSpacing(); word_spacing_ = font_description.WordSpacing(); expansion_ = text_.Expansion(); - has_spacing_ = letter_spacing_ || word_spacing_ || expansion_; + has_spacing_ = letter_spacing_ || word_spacing_ || expansion_ || + font.HasLetterSpacingOverride(); if (!has_spacing_) return; @@ -118,8 +121,10 @@ } template <typename TextContainerType> -float ShapeResultSpacing<TextContainerType>::ComputeSpacing(unsigned index, - float& offset) { +float ShapeResultSpacing<TextContainerType>::ComputeSpacing( + unsigned index, + float letter_spacing_override, + float& offset) { DCHECK(has_spacing_); UChar32 character = text_[index]; bool treat_as_space = @@ -131,8 +136,10 @@ character = kSpaceCharacter; float spacing = 0; - if (letter_spacing_ && !Character::TreatAsZeroWidthSpace(character)) - spacing += letter_spacing_; + + bool has_letter_spacing = letter_spacing_ || letter_spacing_override; + if (has_letter_spacing && !Character::TreatAsZeroWidthSpace(character)) + spacing += letter_spacing_ + letter_spacing_override; if (treat_as_space && (index || character == kNoBreakSpaceCharacter)) spacing += word_spacing_;
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h b/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h index a3cbc71..582c803 100644 --- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h +++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h
@@ -11,7 +11,7 @@ namespace blink { -class FontDescription; +class Font; // A context object to apply letter-spacing, word-spacing, and justification to // ShapeResult. @@ -42,8 +42,10 @@ return expansion_opportunity_count_; } - // Set letter-spacing and word-spacing. - bool SetSpacing(const FontDescription&); + // Set letter-spacing, word-spacing, and letter-spacing-override. Uses a Font + // argument instead of FontDescription as letter-spacing-override is retrieved + // from CSS @font-face, not from style like word-spacing and letter-spacing. + bool SetSpacing(const Font&); // Set the expansion for the justification. void SetExpansion(float expansion, @@ -52,15 +54,17 @@ bool allows_leading_expansion = false, bool allows_trailing_expansion = false); - // Set letter-spacing, word-spacing, and justification. - // Available only for TextRun. - void SetSpacingAndExpansion(const FontDescription&); + // Set letter-spacing, word-spacing, letter-spacing-override and + // justification. Available only for TextRun. + void SetSpacingAndExpansion(const Font&); // Compute the sum of all spacings for the specified |index|. // The |index| is for the |TextContainerType| given in the constructor. // For justification, this function must be called incrementally since it // keeps states and counts consumed justification opportunities. - float ComputeSpacing(unsigned index, float& offset); + float ComputeSpacing(unsigned index, + float letter_spacing_override, + float& offset); private: bool IsAfterExpansion() const { return is_after_expansion_; } @@ -88,8 +92,7 @@ // Forward declare so no implicit instantiations happen before the // first explicit instantiation (which would be a C++ violation). template <> -void ShapeResultSpacing<TextRun>::SetSpacingAndExpansion( - const FontDescription&); +void ShapeResultSpacing<TextRun>::SetSpacingAndExpansion(const Font&); } // namespace blink #endif
diff --git a/third_party/blink/renderer/platform/fonts/simple_font_data.cc b/third_party/blink/renderer/platform/fonts/simple_font_data.cc index 46cda19..5b4749c 100644 --- a/third_party/blink/renderer/platform/fonts/simple_font_data.cc +++ b/third_party/blink/renderer/platform/fonts/simple_font_data.cc
@@ -173,6 +173,11 @@ DCHECK(face); if (int units_per_em = face->getUnitsPerEm()) font_metrics_.SetUnitsPerEm(units_per_em); + + if (metrics_override.letter_spacing_override) { + letter_spacing_override_ = + *metrics_override.letter_spacing_override * platform_data_.size(); + } } void SimpleFontData::PlatformGlyphInit() {
diff --git a/third_party/blink/renderer/platform/fonts/simple_font_data.h b/third_party/blink/renderer/platform/fonts/simple_font_data.h index cf2e191..07cf76b 100644 --- a/third_party/blink/renderer/platform/fonts/simple_font_data.h +++ b/third_party/blink/renderer/platform/fonts/simple_font_data.h
@@ -153,6 +153,14 @@ return visual_overflow_inflation_for_descent_; } + bool HasLetterSpacingOverride() const override { + return letter_spacing_override_.has_value(); + } + + float GetLetterSpacingOverride() const { + return letter_spacing_override_.value_or(0); + } + protected: SimpleFontData( const FontPlatformData&, @@ -203,6 +211,10 @@ unsigned visual_overflow_inflation_for_ascent_; unsigned visual_overflow_inflation_for_descent_; + // The additional spacing between letters as defined by the + // letter-spacing-override value in @font-face. + base::Optional<float> letter_spacing_override_; + mutable LayoutUnit em_height_ascent_; mutable LayoutUnit em_height_descent_;
diff --git a/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc index 8714be68..7771ffdb 100644 --- a/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc +++ b/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc
@@ -216,8 +216,6 @@ void XRWebGLDrawingBuffer::UseSharedBuffer( const gpu::MailboxHolder& buffer_mailbox_holder) { - DVLOG(3) << __FUNCTION__; - gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL(); // Ensure that the mailbox holder is ready to use, the following actions need @@ -228,6 +226,10 @@ // recovery for cases where these assumptions may not be accurate. DCHECK(buffer_mailbox_holder.sync_token.HasData()); DCHECK(!buffer_mailbox_holder.mailbox.IsZero()); + DVLOG(3) << __func__ + << ": mailbox=" << buffer_mailbox_holder.mailbox.ToDebugString() + << ", SyncToken=" + << buffer_mailbox_holder.sync_token.ToDebugString(); gl->WaitSyncTokenCHROMIUM(buffer_mailbox_holder.sync_token.GetConstData()); // Create a texture backed by the shared buffer image.
diff --git a/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc b/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc index 8f5bde3..38188f7 100644 --- a/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc +++ b/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc
@@ -243,11 +243,6 @@ return base::ThreadTaskRunnerHandle::Get(); } - scoped_refptr<base::SingleThreadTaskRunner> CleanupTaskRunner() override { - DCHECK(WTF::IsMainThread()); - return base::ThreadTaskRunnerHandle::Get(); - } - scoped_refptr<base::SingleThreadTaskRunner> V8TaskRunner() override { DCHECK(WTF::IsMainThread()); return base::ThreadTaskRunnerHandle::Get();
diff --git a/third_party/blink/renderer/platform/scheduler/common/web_thread_scheduler.cc b/third_party/blink/renderer/platform/scheduler/common/web_thread_scheduler.cc index 641f84c..d6f37ee 100644 --- a/third_party/blink/renderer/platform/scheduler/common/web_thread_scheduler.cc +++ b/third_party/blink/renderer/platform/scheduler/common/web_thread_scheduler.cc
@@ -79,12 +79,6 @@ } scoped_refptr<base::SingleThreadTaskRunner> -WebThreadScheduler::CleanupTaskRunner() { - NOTREACHED(); - return nullptr; -} - -scoped_refptr<base::SingleThreadTaskRunner> WebThreadScheduler::DeprecatedDefaultTaskRunner() { NOTREACHED(); return nullptr;
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc index 537aebf..d95f96ca 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
@@ -473,7 +473,6 @@ case TaskType::kMainThreadTaskQueueIdle: case TaskType::kMainThreadTaskQueueIPC: case TaskType::kMainThreadTaskQueueControl: - case TaskType::kMainThreadTaskQueueCleanup: case TaskType::kMainThreadTaskQueueMemoryPurge: case TaskType::kCompositorThreadTaskQueueDefault: case TaskType::kCompositorThreadTaskQueueInput:
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc index c3cb873..a3188441 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -234,8 +234,6 @@ MainThreadTaskQueue::QueueType::kV8)); ipc_task_queue_ = NewTaskQueue(MainThreadTaskQueue::QueueCreationParams( MainThreadTaskQueue::QueueType::kIPC)); - cleanup_task_queue_ = NewTaskQueue(MainThreadTaskQueue::QueueCreationParams( - MainThreadTaskQueue::QueueType::kCleanup)); non_waking_task_queue_ = NewTaskQueue(MainThreadTaskQueue::QueueCreationParams( MainThreadTaskQueue::QueueType::kNonWaking) @@ -249,8 +247,6 @@ TaskType::kMainThreadTaskQueueControl); ipc_task_runner_ = ipc_task_queue_->CreateTaskRunner(TaskType::kMainThreadTaskQueueIPC); - cleanup_task_runner_ = cleanup_task_queue_->CreateTaskRunner( - TaskType::kMainThreadTaskQueueCleanup); non_waking_task_runner_ = non_waking_task_queue_->CreateTaskRunner( TaskType::kMainThreadTaskQueueNonWaking); @@ -660,11 +656,6 @@ } scoped_refptr<base::SingleThreadTaskRunner> -MainThreadSchedulerImpl::CleanupTaskRunner() { - return cleanup_task_runner_; -} - -scoped_refptr<base::SingleThreadTaskRunner> MainThreadSchedulerImpl::DeprecatedDefaultTaskRunner() { return helper_.DeprecatedDefaultTaskRunner(); }
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h index dc65f93..b2b7914a 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h +++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
@@ -161,7 +161,6 @@ std::unique_ptr<WebWidgetScheduler> CreateWidgetScheduler() override; // Note: this is also shared by the ThreadScheduler interface. scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() override; - scoped_refptr<base::SingleThreadTaskRunner> CleanupTaskRunner() override; scoped_refptr<base::SingleThreadTaskRunner> NonWakingTaskRunner() override; scoped_refptr<base::SingleThreadTaskRunner> DeprecatedDefaultTaskRunner() override; @@ -839,7 +838,6 @@ scoped_refptr<MainThreadTaskQueue> v8_task_queue_; scoped_refptr<MainThreadTaskQueue> ipc_task_queue_; - scoped_refptr<MainThreadTaskQueue> cleanup_task_queue_; scoped_refptr<MainThreadTaskQueue> memory_purge_task_queue_; scoped_refptr<MainThreadTaskQueue> non_waking_task_queue_; @@ -847,7 +845,6 @@ scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_; scoped_refptr<base::SingleThreadTaskRunner> control_task_runner_; scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner_; - scoped_refptr<base::SingleThreadTaskRunner> cleanup_task_runner_; scoped_refptr<base::SingleThreadTaskRunner> non_waking_task_runner_; MemoryPurgeManager memory_purge_manager_;
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc index 4c8ed578..2c8614e 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
@@ -55,8 +55,6 @@ return "input_tq"; case MainThreadTaskQueue::QueueType::kDetached: return "detached_tq"; - case MainThreadTaskQueue::QueueType::kCleanup: - return "cleanup_tq"; case MainThreadTaskQueue::QueueType::kOther: return "other_tq"; case MainThreadTaskQueue::QueueType::kWebScheduling: @@ -93,7 +91,6 @@ case MainThreadTaskQueue::QueueType::kIPC: case MainThreadTaskQueue::QueueType::kInput: case MainThreadTaskQueue::QueueType::kDetached: - case MainThreadTaskQueue::QueueType::kCleanup: case MainThreadTaskQueue::QueueType::kNonWaking: case MainThreadTaskQueue::QueueType::kOther: return false; @@ -115,7 +112,6 @@ case QueueType::kV8: case QueueType::kIPC: case QueueType::kNonWaking: - case QueueType::kCleanup: return QueueClass::kNone; case QueueType::kFrameLoading: case QueueType::kFrameLoadingControl:
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h index 5689e50..1b6b45a 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h +++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
@@ -71,8 +71,7 @@ // TODO(altimin): Move to the top when histogram is renumbered. kDetached = 19, - kCleanup = 20, - + // 20 : kCleanup, obsolete. // 21 : kWebSchedulingUserInteraction, obsolete. // 22 : kWebSchedulingBestEffort, obsolete.
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc b/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc index f462aab..c62b378 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc
@@ -105,8 +105,6 @@ return "MainThreadTaskQueueIPC"; case TaskType::kMainThreadTaskQueueControl: return "MainThreadTaskQueueControl"; - case TaskType::kMainThreadTaskQueueCleanup: - return "MainThreadTaskQueueCleanup"; case TaskType::kMainThreadTaskQueueMemoryPurge: return "MainThreadTaskQueueMemoryPurge"; case TaskType::kMainThreadTaskQueueNonWaking:
diff --git a/third_party/blink/renderer/platform/scheduler/test/renderer_scheduler_test_support.cc b/third_party/blink/renderer/platform/scheduler/test/renderer_scheduler_test_support.cc index 95619cc..d99f517 100644 --- a/third_party/blink/renderer/platform/scheduler/test/renderer_scheduler_test_support.cc +++ b/third_party/blink/renderer/platform/scheduler/test/renderer_scheduler_test_support.cc
@@ -32,10 +32,6 @@ return base::ThreadTaskRunnerHandle::Get(); } - scoped_refptr<base::SingleThreadTaskRunner> CleanupTaskRunner() override { - return base::ThreadTaskRunnerHandle::Get(); - } - std::unique_ptr<Thread> CreateMainThread() override { return simple_thread_scheduler_->CreateMainThread(); }
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc index 04fa08a..b3274af 100644 --- a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc +++ b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc
@@ -196,7 +196,6 @@ case TaskType::kMainThreadTaskQueueIdle: case TaskType::kMainThreadTaskQueueIPC: case TaskType::kMainThreadTaskQueueControl: - case TaskType::kMainThreadTaskQueueCleanup: case TaskType::kMainThreadTaskQueueMemoryPurge: case TaskType::kMainThreadTaskQueueNonWaking: case TaskType::kCompositorThreadTaskQueueDefault:
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 23a00432..1d5a611 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -1307,9 +1307,6 @@ # Fieldset in NG # -crbug.com/875235 virtual/layout_ng_fieldset/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align.html [ Failure ] -crbug.com/875235 virtual/layout_ng_fieldset/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-auto-margins.html [ Failure ] -crbug.com/875235 virtual/layout_ng_fieldset/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-margin-inline.html [ Failure ] ### virtual/layout_ng_fieldset/fast/forms/fieldset/ crbug.com/875235 virtual/layout_ng_fieldset/fast/forms/fieldset/legend-small-after-margin-before-border-horizontal-mode.html [ Failure ] @@ -6851,9 +6848,6 @@ crbug.com/1111410 http/tests/serviceworker/service-worker-gc.html [ Pass Failure ] -# Sheriff 2020-08-01 -crbug.com/1112111 fast/forms/month/month-picker-appearance-step.html [ Pass Failure ] - # Sheriff 2020-08-03 crbug.com/1108423 external/wpt/referrer-policy/gen/worker-classic.http-rp/unset/worker-classic.http.html [ Pass Timeout ] crbug.com/1106429 virtual/percent-based-scrolling/max-percent-delta-page-zoom.html [ Pass Failure ] @@ -6867,3 +6861,6 @@ crbug.com/1112771 external/wpt/css/css-grid/layout-algorithm/grid-flex-track-intrinsic-sizes-001.html [ Failure ] crbug.com/1112771 external/wpt/webhid/idlharness.https.window.html [ Failure ] crbug.com/1112771 external/wpt/css/css-grid/grid-definition/grid-inline-template-columns-rows-resolved-values-001.tentative.html [ Failure ] + +# Sheriff 2020-08-05 +crbug.com/1113050 fast/borders/border-radius-mask-video-ratio.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-periodicwave-interface/periodicWave-expected.txt b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-periodicwave-interface/periodicWave-expected.txt deleted file mode 100644 index 8d26f7e..0000000 --- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-periodicwave-interface/periodicWave-expected.txt +++ /dev/null
@@ -1,34 +0,0 @@ -This is a testharness.js-based test. -PASS # AUDIT TASK RUNNER STARTED. -PASS Executing "create with factory method" -PASS Executing "different length with factory method" -PASS Executing "too small with factory method" -PASS Executing "create with constructor" -PASS Executing "different length with constructor" -PASS Executing "too small with constructor" -PASS Executing "output test" -PASS Audit report -PASS > [create with factory method] -PASS context.createPeriodicWave(new Float32Array(4096), new Float32Array(4096)) did not throw an exception. -PASS < [create with factory method] All assertions passed. (total 1 assertions) -PASS > [different length with factory method] -PASS context.createPeriodicWave(new Float32Array(512), new Float32Array(4)) threw IndexSizeError: "Failed to execute 'createPeriodicWave' on 'BaseAudioContext': length of real array (512) and length of imaginary array (4) must match.". -PASS < [different length with factory method] All assertions passed. (total 1 assertions) -PASS > [too small with factory method] -FAIL X context.createPeriodicWave(new Float32Array(1), new Float32Array(1)) did not throw an exception. assert_true: expected true got false -FAIL < [too small with factory method] 1 out of 1 assertions were failed. assert_true: expected true got false -PASS > [create with constructor] -PASS new PeriodicWave(context, { real : new Float32Array(4096), imag : new Float32Array(4096) }) did not throw an exception. -PASS < [create with constructor] All assertions passed. (total 1 assertions) -PASS > [different length with constructor] -PASS new PeriodicWave(context, { real : new Float32Array(4096), imag : new Float32Array(4) }) threw IndexSizeError: "Failed to construct 'PeriodicWave': length of real array (4096) and length of imaginary array (4) must match.". -PASS < [different length with constructor] All assertions passed. (total 1 assertions) -PASS > [too small with constructor] -FAIL X new PeriodicWave(context, { real : new Float32Array(1), imag : new Float32Array(1) }) did not throw an exception. assert_true: expected true got false -FAIL < [too small with constructor] 1 out of 1 assertions were failed. assert_true: expected true got false -PASS > [output test] -PASS rendering PeriodicWave is identical to the array AudioBuffer. -PASS < [output test] All assertions passed. (total 1 assertions) -FAIL # AUDIT TASK RUNNER FINISHED: 2 out of 7 tasks were failed. assert_true: expected true got false -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/fast/forms/month/month-picker-appearance-step.html b/third_party/blink/web_tests/fast/forms/month/month-picker-appearance-step.html index 055551c2..fc7291b 100644 --- a/third_party/blink/web_tests/fast/forms/month/month-picker-appearance-step.html +++ b/third_party/blink/web_tests/fast/forms/month/month-picker-appearance-step.html
@@ -3,7 +3,7 @@ testRunner.waitUntilDone(); </script> <script src="../../../fast/forms/resources/picker-common.js"></script> -<input type=month id=month value="2019-08" step="3"> +<input type="month" id="month" value="2019-08" step="3" max="2020-01"> <script> openPicker(document.getElementById('month'), () => testRunner.notifyDone()); </script>
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/month/month-picker-appearance-step-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/month/month-picker-appearance-step-expected.png index 009b9c3..c53df59 100644 --- a/third_party/blink/web_tests/platform/linux/fast/forms/month/month-picker-appearance-step-expected.png +++ b/third_party/blink/web_tests/platform/linux/fast/forms/month/month-picker-appearance-step-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fieldset/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fieldset/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align-expected.txt new file mode 100644 index 0000000..2c1519b --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fieldset/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align-expected.txt
@@ -0,0 +1,13 @@ +This is a testharness.js-based test. +PASS div[align=left] legend +PASS div[align=center] legend +PASS div[align=right] legend +PASS div[align=justify] legend +FAIL div[style="text-align: center"] legend assert_equals: expected legend[align=left] expected 24 but got 394 +FAIL div[style="text-align: center"][align=center] legend assert_equals: expected legend[align=left] expected 24 but got 394 +PASS legend[style="margin: 0 auto"] +PASS legend[style="margin: 0 0 0 auto"] +PASS fieldset[dir=rtl] legend +FAIL fieldset[dir=rtl] legend[style="text-align: left"] assert_equals: expected legend[align=right] expected 764 but got 24 +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/forms/month/month-picker-appearance-step-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/forms/month/month-picker-appearance-step-expected.png index 33a7cba..35f5be9a 100644 --- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/forms/month/month-picker-appearance-step-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/forms/month/month-picker-appearance-step-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/fast/forms/month/month-picker-appearance-step-expected.png b/third_party/blink/web_tests/platform/mac-retina/fast/forms/month/month-picker-appearance-step-expected.png new file mode 100644 index 0000000..cc91228 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/fast/forms/month/month-picker-appearance-step-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/month/month-picker-appearance-step-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/month/month-picker-appearance-step-expected.png index cc91228..9426e5de 100644 --- a/third_party/blink/web_tests/platform/mac/fast/forms/month/month-picker-appearance-step-expected.png +++ b/third_party/blink/web_tests/platform/mac/fast/forms/month/month-picker-appearance-step-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/layout_ng_fieldset/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/layout_ng_fieldset/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align-expected.txt new file mode 100644 index 0000000..2c1519b --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/layout_ng_fieldset/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align-expected.txt
@@ -0,0 +1,13 @@ +This is a testharness.js-based test. +PASS div[align=left] legend +PASS div[align=center] legend +PASS div[align=right] legend +PASS div[align=justify] legend +FAIL div[style="text-align: center"] legend assert_equals: expected legend[align=left] expected 24 but got 394 +FAIL div[style="text-align: center"][align=center] legend assert_equals: expected legend[align=left] expected 24 but got 394 +PASS legend[style="margin: 0 auto"] +PASS legend[style="margin: 0 0 0 auto"] +PASS fieldset[dir=rtl] legend +FAIL fieldset[dir=rtl] legend[style="text-align: left"] assert_equals: expected legend[align=right] expected 764 but got 24 +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/month/month-picker-appearance-step-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/month/month-picker-appearance-step-expected.png index a9ced9f9..6a21cf5f 100644 --- a/third_party/blink/web_tests/platform/win/fast/forms/month/month-picker-appearance-step-expected.png +++ b/third_party/blink/web_tests/platform/win/fast/forms/month/month-picker-appearance-step-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/layout_ng_fieldset/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align-expected.txt b/third_party/blink/web_tests/platform/win/virtual/layout_ng_fieldset/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align-expected.txt new file mode 100644 index 0000000..7493d20 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/layout_ng_fieldset/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align-expected.txt
@@ -0,0 +1,13 @@ +This is a testharness.js-based test. +PASS div[align=left] legend +PASS div[align=center] legend +PASS div[align=right] legend +PASS div[align=justify] legend +FAIL div[style="text-align: center"] legend assert_equals: expected legend[align=left] expected 24 but got 395 +FAIL div[style="text-align: center"][align=center] legend assert_equals: expected legend[align=left] expected 24 but got 395 +PASS legend[style="margin: 0 auto"] +PASS legend[style="margin: 0 0 0 auto"] +PASS fieldset[dir=rtl] legend +FAIL fieldset[dir=rtl] legend[style="text-align: left"] assert_equals: expected legend[align=right] expected 765 but got 24 +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/platform/win7/fast/forms/month/month-picker-appearance-step-expected.png b/third_party/blink/web_tests/platform/win7/fast/forms/month/month-picker-appearance-step-expected.png index 2a2d0954a..6d25f2a 100644 --- a/third_party/blink/web_tests/platform/win7/fast/forms/month/month-picker-appearance-step-expected.png +++ b/third_party/blink/web_tests/platform/win7/fast/forms/month/month-picker-appearance-step-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/webaudio/internals/audiocontext-close.html b/third_party/blink/web_tests/webaudio/internals/audiocontext-close.html index a495261e..91b6449 100644 --- a/third_party/blink/web_tests/webaudio/internals/audiocontext-close.html +++ b/third_party/blink/web_tests/webaudio/internals/audiocontext-close.html
@@ -17,7 +17,7 @@ let osc; let gain; let offlinePromise; - let wave = new Float32Array(1); + let wave = new Float32Array(5); let audit = Audit.createTaskRunner();
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-fonts/letter-spacing-override-ref.html b/third_party/blink/web_tests/wpt_internal/css/css-fonts/letter-spacing-override-ref.html new file mode 100644 index 0000000..6fdd7f1a --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-fonts/letter-spacing-override-ref.html
@@ -0,0 +1,34 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/4792"> +<link rel="assert" title="Tests that letter-spacing-override sets letter spacing to characters rendered with the font face only"> +<title>Tests the letter-spacing-override descriptor of @font-face</title> +<style> +@font-face { + font-family: custom-font; + src: local(Ahem), url(/fonts/Ahem.ttf); + unicode-range: U+0-7F; /* ASCII only */ +} + +.target { + font: 20px custom-font, sans-serif; +} + +.letter-spacing { + letter-spacing: 1em; +} + +</style> + +<p>letter-spacing-override should affect Ahem characters only.</p> + +<div class="target"> + <span class="letter-spacing">XXX</span>一二三<span class="letter-spacing">XXX</span> +</div> + +<p>letter-spacing-override: 0 should be the same as no override.</p> + +<div class="target"> + XXX一二三XXX +</div>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-fonts/letter-spacing-override.html b/third_party/blink/web_tests/wpt_internal/css/css-fonts/letter-spacing-override.html new file mode 100644 index 0000000..b6bd045 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-fonts/letter-spacing-override.html
@@ -0,0 +1,43 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/4792"> +<link rel="match" href="letter-spacing-override-ref.html"> +<link rel="assert" title="Tests that letter-spacing-override sets letter spacing to characters rendered with the font face only"> +<title>Tests the letter-spacing-override descriptor of @font-face</title> +<style> +@font-face { + font-family: custom-font; + src: local(Ahem), url(/fonts/Ahem.ttf); + letter-spacing-override: 1.0; + unicode-range: U+0-7F; /* ASCII only */ +} + +.target { + font: 20px custom-font, sans-serif; +} + +@font-face { + font-family: reference-font; + src: local(Ahem), url(/fonts/Ahem.ttf); + letter-spacing-override: 0; + unicode-range: U+0-7F; /* ASCII only */ +} + +.reference { + font: 20px reference-font, sans-serif; +} + +</style> + +<p>letter-spacing-override should affect Ahem characters only.</p> + +<div class="target"> + XXX一二三XXX +</div> + +<p>letter-spacing-override: 0 should be the same as no override.</p> + +<div class="reference"> + XXX一二三XXX +</div>
diff --git a/third_party/closure_compiler/externs/file_manager_private.js b/third_party/closure_compiler/externs/file_manager_private.js index 8e040f3..299b523 100644 --- a/third_party/closure_compiler/externs/file_manager_private.js +++ b/third_party/closure_compiler/externs/file_manager_private.js
@@ -1056,6 +1056,14 @@ */ chrome.fileManagerPrivate.sharesheetHasTargets = function(entries, callback) {}; +/** + * Invoke Sharesheet for selected files. If not possible, then returns + * an error via chrome.runtime.lastError. |entries| Array of selected entries. + * @param {!Array<!Entry>} entries + * @param {function()} callback + */ +chrome.fileManagerPrivate.invokeSharesheet = function(entries, callback) {}; + /** @type {!ChromeEvent} */ chrome.fileManagerPrivate.onMountCompleted;
diff --git a/tools/generate_stubs/generate_stubs.py b/tools/generate_stubs/generate_stubs.py index 27a11ed..9cf65c7 100755 --- a/tools/generate_stubs/generate_stubs.py +++ b/tools/generate_stubs/generate_stubs.py
@@ -311,8 +311,8 @@ module_opened = true; opened_libraries[cur_module] = handle; } else { - %(logging_function)s << "dlopen(" << dso_path->c_str() << ") failed, " - << "dlerror() says:\\n" << dlerror(); + %(logging_function)s << "dlopen(" << dso_path->c_str() << ") failed."; + %(logging_function)s << "dlerror() says:\\n" << dlerror(); } }
diff --git a/tools/json_schema_compiler/cpp_bundle_generator.py b/tools/json_schema_compiler/cpp_bundle_generator.py index 3c7ad43b..4e4fbf3 100644 --- a/tools/json_schema_compiler/cpp_bundle_generator.py +++ b/tools/json_schema_compiler/cpp_bundle_generator.py
@@ -143,7 +143,13 @@ ifdefs = [] for platform in model_object.platforms: if platform == Platforms.CHROMEOS: - ifdefs.append('defined(OS_CHROMEOS)') + # TODO(https://crbug.com/1052397): For readability, this should become + # defined(OS_CHROMEOS) && BUILDFLAG(IS_ASH). + ifdefs.append('(defined(OS_CHROMEOS) && !BUILDFLAG(IS_LACROS))') + elif platform == Platforms.LACROS: + # TODO(https://crbug.com/1052397): For readability, this should become + # defined(OS_CHROMEOS) && BUILDFLAG(IS_LACROS). + ifdefs.append('BUILDFLAG(IS_LACROS)') elif platform == Platforms.LINUX: ifdefs.append('(defined(OS_LINUX) && !defined(OS_CHROMEOS))') elif platform == Platforms.MAC: @@ -252,6 +258,9 @@ os.path.join(self._bundle._impl_dir, 'generated_api_registration.h'))) c.Append() + c.Append('#include "build/build_config.h"') + c.Append('#include "build/lacros_buildflags.h"') + c.Append() for namespace in self._bundle._model.namespaces.values(): namespace_name = namespace.unix_name.replace("experimental_", "") implementation_header = namespace.compiler_options.get(
diff --git a/tools/json_schema_compiler/cpp_bundle_generator_test.py b/tools/json_schema_compiler/cpp_bundle_generator_test.py index 2241ecd..0994d06 100755 --- a/tools/json_schema_compiler/cpp_bundle_generator_test.py +++ b/tools/json_schema_compiler/cpp_bundle_generator_test.py
@@ -36,15 +36,15 @@ 'test/function_platform_all.json') self.assertEquals( 'defined(OS_WIN) || (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || ' - 'defined(OS_CHROMEOS)', + '(defined(OS_CHROMEOS) && !BUILDFLAG(IS_LACROS))', _getPlatformIfdefs(cpp_bundle_generator, model)) def testIfDefsForChromeOS(self): cpp_bundle_generator, model = _createCppBundleGenerator( 'test/function_platform_chromeos.json') - self.assertEquals( - 'defined(OS_CHROMEOS)', - _getPlatformIfdefs(cpp_bundle_generator, model)) + self.assertEquals('(defined(OS_CHROMEOS) && !BUILDFLAG(IS_LACROS))', + _getPlatformIfdefs(cpp_bundle_generator, model)) + if __name__ == '__main__': unittest.main()
diff --git a/tools/json_schema_compiler/feature_compiler.py b/tools/json_schema_compiler/feature_compiler.py index fbdeaf6..acb4e45 100644 --- a/tools/json_schema_compiler/feature_compiler.py +++ b/tools/json_schema_compiler/feature_compiler.py
@@ -240,6 +240,7 @@ list: { 'enum_map': { 'chromeos': 'Feature::CHROMEOS_PLATFORM', + 'lacros': 'Feature::LACROS_PLATFORM', 'linux': 'Feature::LINUX_PLATFORM', 'mac': 'Feature::MACOSX_PLATFORM', 'win': 'Feature::WIN_PLATFORM',
diff --git a/tools/json_schema_compiler/model.py b/tools/json_schema_compiler/model.py index cafd0e1d..49f2855 100644 --- a/tools/json_schema_compiler/model.py +++ b/tools/json_schema_compiler/model.py
@@ -605,6 +605,7 @@ """ CHROMEOS = _PlatformInfo("chromeos") CHROMEOS_TOUCH = _PlatformInfo("chromeos_touch") + LACROS = _PlatformInfo("lacros") LINUX = _PlatformInfo("linux") MAC = _PlatformInfo("mac") WIN = _PlatformInfo("win")
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index 2f4569c6..1bb02656 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -135,6 +135,7 @@ 'linux-chromeos-rel': 'chromeos_with_codecs_release_bot', 'linux-chromeos-dbg': 'chromeos_with_codecs_debug_bot', 'linux-lacros-builder-rel': 'lacros_on_linux_release_bot', + 'linux-lacros-compile-rel': 'lacros_on_linux_release_bot', 'linux-lacros-tester-rel': 'lacros_on_linux_release_bot', }, @@ -888,7 +889,6 @@ 'linux-clang-tidy-rel': 'release_trybot', 'linux-dcheck-off-rel': 'release_trybot_dcheck_off', 'linux-gcc-rel': 'release_bot_x86_minimal_symbols_no_clang_cxx11', - 'linux-lacros-compile-rel': 'lacros_on_linux_release_bot', 'linux-lacros-fyi-rel': 'lacros_on_linux_release_bot', 'linux-libfuzzer-asan-rel': 'libfuzzer_asan_release_trybot', 'linux-ozone-rel': 'ozone_linux_release_trybot', @@ -2854,7 +2854,7 @@ }, 'official_optimize': { - 'gn_args': 'is_official_build=true is_debug=false', + 'gn_args': 'is_official_build=true', }, 'official_optimize_goma': {
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 84b1147..7247cf7f 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -24126,6 +24126,7 @@ label="AUTOTESTPRIVATE_STOPTHROUGHPUTTRACKERDATACOLLECTION"/> <int value="1494" label="INPUTMETHODPRIVATE_GETAUTOCORRECTRANGE"/> <int value="1495" label="FILEMANAGERPRIVATEINTERNAL_SHARESHEETHASTARGETS"/> + <int value="1496" label="FILEMANAGERPRIVATEINTERNAL_INVOKESHARESHEET"/> </enum> <enum name="ExtensionIconState"> @@ -39509,6 +39510,12 @@ <int value="3" label="Holdback"/> </enum> +<enum name="LiteVideoThrottleResult"> + <int value="0" label="None"/> + <int value="1" label="Throttled without stopping"/> + <int value="2" label="Throttled but stopped due to rebuffer"/> +</enum> + <enum name="LiveCaptionsSessionEvent"> <int value="0" label="Stream started"/> <int value="1" label="Stream finished"/> @@ -39928,6 +39935,7 @@ <int value="-2020721975" label="smart-virtual-keyboard"/> <int value="-2020024440" label="scroll-end-effect"/> <int value="-2017953534" label="enable-hosted-app-shim-creation"/> + <int value="-2017778637" label="PrintSaveToDrive:disabled"/> <int value="-2015293660" label="AccessibilityExposeDisplayNone:disabled"/> <int value="-2013551096" label="ViewsSimplifiedFullscreenUI:disabled"/> <int value="-2013124655" label="EnableEphemeralFlashPermission:disabled"/> @@ -41427,6 +41435,7 @@ <int value="-470247915" label="AutofillUpstreamEditableExpirationDate:enabled"/> <int value="-468697885" label="ArcInputMethod:enabled"/> + <int value="-467792766" label="ReengagementNotification:enabled"/> <int value="-466704882" label="webview-log-js-console-messages"/> <int value="-465381408" label="Sharesheet:disabled"/> <int value="-462554210" @@ -42006,6 +42015,7 @@ <int value="173288154" label="PrintPdfAsImage:enabled"/> <int value="173339199" label="SmsReceiverCrossDevice:disabled"/> <int value="174759256" label="LockScreenMediaControls:enabled"/> + <int value="174917935" label="ReengagementNotification:disabled"/> <int value="178337215" label="enable-md-history"/> <int value="178693406" label="LockScreenMediaControls:disabled"/> <int value="180074362" label="memory-pressure-thresholds"/> @@ -42391,6 +42401,7 @@ <int value="606723570" label="SharingUseDeviceInfo:disabled"/> <int value="606834606" label="force-color-profile"/> <int value="606969417" label="DiscoverApp:enabled"/> + <int value="608761130" label="PrintSaveToDrive:enabled"/> <int value="609112512" label="touch-selection-strategy"/> <int value="609580715" label="ArcCupsApi:disabled"/> <int value="610545308" label="enable-potentially-annoying-security-features"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index f7cfe42..b3e1b0ea 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -4056,7 +4056,7 @@ </histogram> <histogram name="Android.Omnibox.InvalidMatch" enum="MatchResult" - expires_after="2020-08-30"> + expires_after="2021-02-27"> <owner>ender@chromium.org</owner> <owner>tedchoc@chromium.org</owner> <owner>mpearson@chromium.org</owner> @@ -116687,6 +116687,9 @@ <histogram name="Omnibox.HistoryQuickHistoryIDSetFromWords" units="ms" expires_after="M86"> + <obsolete> + Removed in August 2020. + </obsolete> <owner>mpearson@chromium.org</owner> <owner>jdonnelly@chromium.org</owner> <summary>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml index e4b994d7..b15bc23 100644 --- a/tools/metrics/ukm/ukm.xml +++ b/tools/metrics/ukm/ukm.xml
@@ -5343,6 +5343,12 @@ requests throttled. </summary> </metric> + <metric name="ThrottlingResult" enum="LiteVideoThrottleResult"> + <summary> + The result for whether media requests associated with this navigation were + successfully throttled or stopped due to an opt-out event. + </summary> + </metric> <metric name="ThrottlingStartDecision" enum="LiteVideoDecision"> <summary> The decision for whether media requests associated with this frame will be
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index 4454f1e..57980c6c 100644 --- a/tools/perf/core/perfetto_binary_roller/binary_deps.json +++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -1,12 +1,12 @@ { "trace_processor_shell": { "win": { - "hash": "c3f650eb5d76f8320c288295bcb161c6cfb2c66e", - "remote_path": "perfetto_binaries/trace_processor_shell/win/46b0d8780f6664c080dbec70c09bdd207cff12e9/trace_processor_shell.exe" + "hash": "80b5e25a6e577a2be1fc35689406db65b821a568", + "remote_path": "perfetto_binaries/trace_processor_shell/win/b156b016f22bbd24370ff2dc1bcc0a7b3edf7ee4/trace_processor_shell.exe" }, "mac": { - "hash": "a0bf2c8901f2ad304e539e0d2543dd652461e536", - "remote_path": "perfetto_binaries/trace_processor_shell/mac/7176f54720e6f5ff087e1fe6407191b5692a7aa2/trace_processor_shell" + "hash": "f2ababab87ba7a0db590374ad04a1d08325604aa", + "remote_path": "perfetto_binaries/trace_processor_shell/mac/b156b016f22bbd24370ff2dc1bcc0a7b3edf7ee4/trace_processor_shell" }, "linux": { "hash": "1f57c64fd764e42c7fa3bd16d9313ca091d7358b",
diff --git a/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm b/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm index f6600264..e67729e 100644 --- a/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm +++ b/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
@@ -447,6 +447,7 @@ if (has_hdr_color_space) type = CALayerType::kHDRCopier; break; + // TODO(crbug.com/1103432): We'll likely need YpCbCr10 here for HDR. case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: // Only allow 4:2:0 frames which fill the layer's contents to be // promoted to AV layers.
diff --git a/ui/accessibility/ax_range.h b/ui/accessibility/ax_range.h index 62db9d3..2d263e1c 100644 --- a/ui/accessibility/ax_range.h +++ b/ui/accessibility/ax_range.h
@@ -131,6 +131,12 @@ : AXRange(anchor_->Clone(), focus_->Clone()); } + AXRange AsBackwardRange() const { + return (CompareEndpoints(anchor(), focus()).value_or(0) < 0) + ? AXRange(focus_->Clone(), anchor_->Clone()) + : AXRange(anchor_->Clone(), focus_->Clone()); + } + bool IsCollapsed() const { return !IsNull() && *anchor_ == *focus_; } // We define a "leaf text range" as an AXRange whose endpoints are leaf text
diff --git a/ui/base/ime/win/BUILD.gn b/ui/base/ime/win/BUILD.gn index 5a7c0c7d..8121777 100644 --- a/ui/base/ime/win/BUILD.gn +++ b/ui/base/ime/win/BUILD.gn
@@ -45,11 +45,4 @@ libs = [ "imm32.lib" ] ldflags = [ "/DELAYLOAD:imm32.dll" ] - - jumbo_excluded_sources = [ - # tsf_text_store.cc needs INITGUID to be defined before - # including any header to properly generate GUID objects. That - # is not guaranteed when included in a jumbo build. - "tsf_text_store.cc", - ] }
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js index 7cfb2a1..d5301e82 100644 --- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js +++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -1574,7 +1574,13 @@ */ CommandHandler.COMMANDS_['invoke-sharesheet'] = new class extends Command { execute(event, fileManager) { - // TODO(crbug.com/1097623): Implement this. + const entries = fileManager.selectionHandler.selection.entries; + chrome.fileManagerPrivate.invokeSharesheet(entries, () => { + if (chrome.runtime.lastError) { + console.error(chrome.runtime.lastError.message); + return; + } + }); } /** @override */
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js index 7067e53..a04ab700 100644 --- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js +++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
@@ -64,10 +64,10 @@ * * @param {!DirectoryEntry|!FilesAppDirEntry} entry The entry to be searched * for. Can be a fake. - * @return {boolean} True if the parent item is found. + * @return {!Promise<boolean>} True if the parent item is found. * @this {(DirectoryItem|VolumeItem|DirectoryTree)} */ -DirectoryItemTreeBaseMethods.searchAndSelectByEntry = function(entry) { +DirectoryItemTreeBaseMethods.searchAndSelectByEntry = async function(entry) { for (let i = 0; i < this.items.length; i++) { const item = this.items[i]; if (!item.entry) { @@ -78,18 +78,18 @@ // When we looking for an item in team drives, recursively search inside the // "Google Drive" root item. if (util.isSharedDriveEntry(entry) && item instanceof DriveVolumeItem) { - item.selectByEntry(entry); + await item.selectByEntry(entry); return true; } if (util.isComputersEntry(entry) && item instanceof DriveVolumeItem) { - item.selectByEntry(entry); + await item.selectByEntry(entry); return true; } if (util.isDescendantEntry(item.entry, entry) || util.isSameEntry(item.entry, entry)) { - item.selectByEntry(entry); + await item.selectByEntry(entry); return true; } } @@ -508,10 +508,10 @@ * * @param {!DirectoryEntry|!FilesAppDirEntry} entry The entry to be searched * for. Can be a fake. - * @return {boolean} True if the parent item is found. + * @return {!Promise<boolean>} True if the parent item is found. */ - searchAndSelectByEntry(entry) { - return DirectoryItemTreeBaseMethods.searchAndSelectByEntry.call( + async searchAndSelectByEntry(entry) { + return await DirectoryItemTreeBaseMethods.searchAndSelectByEntry.call( this, entry); } @@ -746,20 +746,22 @@ * Select the item corresponding to the given {@code entry}. * @param {!DirectoryEntry|!FilesAppDirEntry} entry The entry to be selected. * Can be a fake. + * @return {!Promise<void>} */ - selectByEntry(entry) { + async selectByEntry(entry) { if (util.isSameEntry(entry, this.entry)) { this.selected = true; return; } - if (this.searchAndSelectByEntry(entry)) { + if (await this.searchAndSelectByEntry(entry)) { return; } // If the entry doesn't exist, updates sub directories and tries again. - this.updateSubDirectories( - false /* recursive */, this.searchAndSelectByEntry.bind(this, entry)); + await new Promise( + this.updateSubDirectories.bind(this, false /* recursive */)); + await this.searchAndSelectByEntry(entry); } /** @@ -1562,11 +1564,12 @@ * Select the item corresponding to the given entry. * @param {!DirectoryEntry|!FilesAppDirEntry} entry The directory entry to be * selected. Can be a fake. + * @return {!Promise<void>} * @override */ - selectByEntry(entry) { + async selectByEntry(entry) { // Find the item to be selected among children. - this.searchAndSelectByEntry(entry); + await this.searchAndSelectByEntry(entry); } /** @@ -2129,9 +2132,9 @@ * * @param {!DirectoryEntry|!FilesAppDirEntry} entry The entry to be searched * for. Can be a fake. - * @return {boolean} True if the parent item is found. + * @return {!Promise<boolean>} True if the parent item is found. */ - searchAndSelectByEntry(entry) { + async searchAndSelectByEntry(entry) { // If the |entry| is same as one of volumes or shortcuts, select it. for (let i = 0; i < this.items.length; i++) { // Skips the Drive root volume. For Drive entries, one of children of @@ -2142,13 +2145,14 @@ } if (util.isSameEntry(item.entry, entry)) { - item.selectByEntry(entry); + await item.selectByEntry(entry); return true; } } // Otherwise, search whole tree. const found = - DirectoryItemTreeBaseMethods.searchAndSelectByEntry.call(this, entry); + await DirectoryItemTreeBaseMethods.searchAndSelectByEntry.call( + this, entry); return found; } @@ -2189,13 +2193,14 @@ * Select the item corresponding to the given entry. * @param {!DirectoryEntry|!FilesAppDirEntry} entry The directory entry to be * selected. Can be a fake. + * @return {!Promise<void>} */ - selectByEntry(entry) { + async selectByEntry(entry) { if (this.selectedItem && util.isSameEntry(entry, this.selectedItem.entry)) { return; } - if (this.searchAndSelectByEntry(entry)) { + if (await this.searchAndSelectByEntry(entry)) { return; } @@ -2205,11 +2210,11 @@ if (!volumeInfo) { return; } - volumeInfo.resolveDisplayRoot(() => { + volumeInfo.resolveDisplayRoot(async () => { if (this.sequence_ !== currentSequence) { return; } - if (!this.searchAndSelectByEntry(entry)) { + if (!(await this.searchAndSelectByEntry(entry))) { this.selectedItem = null; } }); @@ -2335,8 +2340,8 @@ * @param {!Event} event Event. * @private */ - onCurrentDirectoryChanged_(event) { - this.selectByEntry(event.newDirEntry); + async onCurrentDirectoryChanged_(event) { + await this.selectByEntry(event.newDirEntry); const selectedItem = this.selectedItem;
diff --git a/ui/file_manager/integration_tests/file_manager/recents.js b/ui/file_manager/integration_tests/file_manager/recents.js index 935aefe..1a6eb08 100644 --- a/ui/file_manager/integration_tests/file_manager/recents.js +++ b/ui/file_manager/integration_tests/file_manager/recents.js
@@ -159,6 +159,31 @@ await verifyRecents(appId, RECENT_ENTRY_SET.concat(RECENT_ENTRY_SET)); }; +testcase.recentsNested = async () => { + // Populate downloads with nested folder structure. |desktop| is added to + // ensure Recents has different files to Downloads/A/B/C + const appId = await setupAndWaitUntilReady( + RootPath.DOWNLOADS, + NESTED_ENTRY_SET.concat([ENTRIES.deeplyBurriedSmallJpeg]), []); + + // Verifies file list in Recents. + await verifyRecents(appId, [ENTRIES.deeplyBurriedSmallJpeg]); + + // Tests that selecting "Go to file location" for a file navigates to + // Downloads/A/B/C since the file in Recents is from Downloads/A/B/C. + await goToFileLocation(appId, ENTRIES.deeplyBurriedSmallJpeg.nameText); + await remoteCall.waitForElement(appId, `[scan-completed="C"]`); + await remoteCall.waitForFiles( + appId, TestEntryInfo.getExpectedRows([ENTRIES.deeplyBurriedSmallJpeg])); + await verifyBreadcrumbsPath(appId, '/My files/Downloads/A/B/C'); + + // Check: The directory should be highlighted in the directory tree. + await remoteCall.waitForElement( + appId, + '.tree-item[full-path-for-testing="/Downloads/A/B/C"] > ' + + '.tree-row[selected][active]'); +}; + testcase.recentAudioDownloads = async () => { const appId = await setupAndWaitUntilReady( RootPath.DOWNLOADS, BASIC_LOCAL_ENTRY_SET, []); @@ -217,4 +242,4 @@ // from Drive should be shown too. await verifyRecentVideos( appId, [RECENTLY_MODIFIED_VIDEO, RECENTLY_MODIFIED_VIDEO]); -}; \ No newline at end of file +};
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc index 0ff5f56..c405f94 100644 --- a/ui/gfx/render_text_unittest.cc +++ b/ui/gfx/render_text_unittest.cc
@@ -38,6 +38,7 @@ #include "ui/gfx/font.h" #include "ui/gfx/font_names_testing.h" #include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/point_conversions.h" #include "ui/gfx/geometry/point_f.h" #include "ui/gfx/range/range.h" #include "ui/gfx/range/range_f.h" @@ -3567,12 +3568,11 @@ SCOPED_TRACE(base::StringPrintf("Testing case[%" PRIuS "]", i)); render_text->SetText(UTF8ToUTF16(kTestStrings[i])); for (size_t j = 0; j < render_text->text().length(); ++j) { - const Range range(render_text->GetCursorSpan(Range(j, j + 1)).Round()); + gfx::RangeF cursor_span = render_text->GetCursorSpan(Range(j, j + 1)); // Test a point just inside the leading edge of the glyph bounds. - int x = range.is_reversed() ? range.GetMax() - 1 : range.GetMin() + 1; - EXPECT_EQ( - j, render_text->FindCursorPosition(Point(x, GetCursorYForTesting())) - .caret_pos()); + float x = cursor_span.start() + (cursor_span.is_reversed() ? -1 : 1); + Point point = gfx::ToCeiledPoint(PointF(x, GetCursorYForTesting())); + EXPECT_EQ(j, render_text->FindCursorPosition(point).caret_pos()); } } } @@ -5994,8 +5994,9 @@ for (size_t j = 0; j < 4; ++j) { SCOPED_TRACE(base::StringPrintf("Case %" PRIuS ", char %" PRIuS, i, j)); - EXPECT_EQ(cases[i].bounds[j], - run.GetGraphemeBounds(render_text, j).Round()); + RangeF bounds_f = run.GetGraphemeBounds(render_text, j); + Range bounds(std::round(bounds_f.start()), std::round(bounds_f.end())); + EXPECT_EQ(cases[i].bounds[j], bounds); } } } @@ -6328,13 +6329,13 @@ run.range = Range(3, 8); run.shape.glyph_count = 0; - EXPECT_EQ(Range(0, 0), run.CharRangeToGlyphRange(Range(4, 5))); - EXPECT_EQ(Range(0, 0), run.GetGraphemeBounds(render_text, 4).Round()); + EXPECT_EQ(Range(), run.CharRangeToGlyphRange(Range(4, 5))); + EXPECT_EQ(RangeF(), run.GetGraphemeBounds(render_text, 4)); Range chars; Range glyphs; run.GetClusterAt(4, &chars, &glyphs); EXPECT_EQ(Range(3, 8), chars); - EXPECT_EQ(Range(0, 0), glyphs); + EXPECT_EQ(Range(), glyphs); } // Ensure the line breaker doesn't compute the word's width bigger than the
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java index df97a99..ad8b5eb 100644 --- a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java +++ b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
@@ -693,7 +693,9 @@ } private static void notifyWebViewRunningInProcess(ClassLoader webViewClassLoader) { - try { + // TODO(crbug.com/1112001): Investigate why loading classes causes strict mode + // violations in some situations. + try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) { Class<?> webViewChromiumFactoryProviderClass = Class.forName("com.android.webview.chromium.WebViewChromiumFactoryProvider", true, webViewClassLoader);