diff --git a/DEPS b/DEPS index c8ba096..d8dcfb8 100644 --- a/DEPS +++ b/DEPS
@@ -133,11 +133,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': '7597caf5efd5324e0d42f06c253266ffb6f5a291', + 'skia_revision': 'e9d0b9c9d9fb11d393dae81b1bc01493ccb35fa8', # 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': '6b24deba39421e11e2bb922a78a8f5f355838bc8', + 'v8_revision': '3c297f5939a69ae140f4f99ccfaa72476955e627', # 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. @@ -145,11 +145,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': '14126505b237ed3f56b7a3860fef43403e0accd9', + 'angle_revision': 'b6a2f6bcf65759b0ae37aacce762cc7fcf84e570', # 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': '13eba6ca91d7e49624736eda33adfd561184514c', + 'swiftshader_revision': '856ebf878c7109b932419fbf17079b876f19a6f7', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. @@ -248,7 +248,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'spv_headers_revision': '03a081524afabdde274d885880c2fef213e46a59', + 'spv_headers_revision': 'a946e7319a9eeb5391f35638c53ae51437b0f5ab', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -260,11 +260,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': '8c1a90199a43f04f86539141b394f05014cea77a', + 'dawn_revision': '9bf529ec9421dcd6a27b9d07fbe3edf6bea598d3', # 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': 'a6ef0a64fd597e53003c7846192790ed0e3642b5', + 'quiche_revision': '348de99ee1f13e6747da3695387a2c78610f5f62', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling clang format # and whatever else without interference from each other. @@ -435,7 +435,7 @@ }, 'src/ios/third_party/material_components_ios/src': { - 'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '4dc42cedf3ed6cc91ad1c99092d62991180bc0d1', + 'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'a64c49634b47b6f0608197a5020ce43ce4951b22', 'condition': 'checkout_ios', }, @@ -475,7 +475,7 @@ }, 'src/ios/third_party/motion_transitioning_objc/src': { - 'url': Var('chromium_git') + '/external/github.com/material-motion/motion-transitioning-objc.git' + '@' + '78ac32badf9ca9c1ad497a6131ce2bd539094812', + 'url': Var('chromium_git') + '/external/github.com/material-motion/motion-transitioning-objc.git' + '@' + '8f360fc6f016af373276f858796a5e9f73498af9', 'condition': 'checkout_ios', }, @@ -752,7 +752,7 @@ # Build tools for Chrome OS. Note: This depends on third_party/pyelftools. 'src/third_party/chromite': { - 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '4fa43115a03d6fa0fecab17ae170fbbb425dbdce', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '0e5424d46467bfe7e8246c429b87de5c6f216c1a', 'condition': 'checkout_linux', }, @@ -1293,7 +1293,7 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'a0f51b2e123f39c9ff12e621b0b47dd28dd64424', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + 'be7af9399ceb88171bf60b50419ff2dec8184fb9', + Var('webrtc_git') + '/src.git' + '@' + '184f6d5d75c198cb7b70b8f9b75e0b5096c6e577', 'src/third_party/xdg-utils': { 'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d', @@ -1334,7 +1334,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@3ceec3ecec70d61729dea6839861a6a92e5e7b42', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@76fbbeea67f5d1b1514d3246f2a5e823a6e160a6', 'condition': 'checkout_src_internal', },
diff --git a/android_webview/tools/cts_config/expected_failure_on_bot.json b/android_webview/tools/cts_config/expected_failure_on_bot.json index f835552..da1675964 100644 --- a/android_webview/tools/cts_config/expected_failure_on_bot.json +++ b/android_webview/tools/cts_config/expected_failure_on_bot.json
@@ -3,24 +3,6 @@ { "name": "testJavascriptInterfaceForClientPopup", "_bug_id": "crbug.com/698369" - }, - { - "name": "testRequestImageRef", - "_bug_id": "crbug.com/900915" - }, - { - "name": "testLoadDataWithBaseUrl", - "_bug_id": "crbug.com/900915" - } - ], - "android.webkit.cts.WebSettingsTest": [ - { - "name": "testLocalImageLoads", - "_bug_id": "crbug.com/680759" - }, - { - "name": "testAllowMixedMode", - "_bug_id": "crbug.com/777393" } ], "android.webkit.cts.ServiceWorkerClientTest": [
diff --git a/android_webview/tools/cts_config/webview_cts_gcs_path.json b/android_webview/tools/cts_config/webview_cts_gcs_path.json index ddf694c..be601da 100644 --- a/android_webview/tools/cts_config/webview_cts_gcs_path.json +++ b/android_webview/tools/cts_config/webview_cts_gcs_path.json
@@ -99,6 +99,10 @@ { "match": "android.webkit.cts.WebViewClientTest#testShouldOverrideUrlLoadingOnCreateWindow", "_bug_id": "crbug.com/896022" + }, + { + "match": "android.webkit.cts.WebViewTest#testLoadDataWithBaseUrl", + "_bug_id": "crbug.com/900915" } ] }, @@ -174,6 +178,10 @@ { "match": "android.webkit.cts.WebViewClientTest#testShouldOverrideUrlLoadingOnCreateWindow", "_bug_id": "crbug.com/896022" + }, + { + "match": "android.webkit.cts.WebViewTest#testLoadDataWithBaseUrl", + "_bug_id": "crbug.com/900915" } ] },
diff --git a/ash/BUILD.gn b/ash/BUILD.gn index 2e5ea4a..fd65797 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn
@@ -1060,8 +1060,6 @@ "wm/drag_window_resizer.cc", "wm/event_client_impl.cc", "wm/event_client_impl.h", - "wm/focus_rules.cc", - "wm/focus_rules.h", "wm/fullscreen_window_finder.cc", "wm/fullscreen_window_finder.h", "wm/gestures/overview_gesture_handler.cc",
diff --git a/ash/accelerators/debug_commands.cc b/ash/accelerators/debug_commands.cc index f0a4f396..9b716364e5 100644 --- a/ash/accelerators/debug_commands.cc +++ b/ash/accelerators/debug_commands.cc
@@ -12,9 +12,9 @@ #include "ash/system/toast/toast_manager.h" #include "ash/touch/touch_devices_controller.h" #include "ash/wallpaper/wallpaper_controller.h" -#include "ash/wm/focus_rules.h" #include "ash/wm/tablet_mode/tablet_mode_controller.h" #include "ash/wm/widget_finder.h" +#include "ash/wm/window_properties.h" #include "ash/wm/window_state.h" #include "ash/wm/window_util.h" #include "ash/ws/window_lookup.h" @@ -91,7 +91,7 @@ *out << " [proxy] id=" << window_service->GetIdForDebugging(window) << " "; *out << name << " (" << window << ")" << " type=" << window->type(); - if (ash::IsToplevelWindow(window)) + if (window->GetProperty(kWindowStateKey)) *out << " " << wm::GetWindowState(window)->GetStateType(); *out << ((window == active_window) ? " [active]" : "") << ((window == focused_window) ? " [focused]" : "")
diff --git a/ash/app_list/views/app_list_item_view.cc b/ash/app_list/views/app_list_item_view.cc index d55bc644..c3e353b 100644 --- a/ash/app_list/views/app_list_item_view.cc +++ b/ash/app_list/views/app_list_item_view.cc
@@ -476,9 +476,10 @@ apps_grid_view_->SetSelectedView(this); } -void AppListItemView::ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) { +void AppListItemView::ShowContextMenuForViewImpl( + views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) { if (context_menu_ && context_menu_->IsShowingMenu()) return; // Prevent multiple requests for context menus before the current request
diff --git a/ash/app_list/views/app_list_item_view.h b/ash/app_list/views/app_list_item_view.h index b38f4141..e2dba2e8 100644 --- a/ash/app_list/views/app_list_item_view.h +++ b/ash/app_list/views/app_list_item_view.h
@@ -170,9 +170,9 @@ std::vector<ash::mojom::MenuItemPtr> menu); // views::ContextMenuController overrides: - void ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; // views::Button overrides: bool ShouldEnterPushedState(const ui::Event& event) override;
diff --git a/ash/app_list/views/search_result_tile_item_view.cc b/ash/app_list/views/search_result_tile_item_view.cc index ecb334c..ffad6f52 100644 --- a/ash/app_list/views/search_result_tile_item_view.cc +++ b/ash/app_list/views/search_result_tile_item_view.cc
@@ -353,7 +353,7 @@ Layout(); } -void SearchResultTileItemView::ShowContextMenuForView( +void SearchResultTileItemView::ShowContextMenuForViewImpl( views::View* source, const gfx::Point& point, ui::MenuSourceType source_type) {
diff --git a/ash/app_list/views/search_result_tile_item_view.h b/ash/app_list/views/search_result_tile_item_view.h index 3c2ddec2..52139ae 100644 --- a/ash/app_list/views/search_result_tile_item_view.h +++ b/ash/app_list/views/search_result_tile_item_view.h
@@ -61,15 +61,15 @@ void OnMetadataChanged() override; // views::ContextMenuController overrides: - void ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; // AppListMenuModelAdapter::Delegate overrides: void ExecuteCommand(int command_id, int event_flags) override; private: - // Bound by ShowContextMenuForView(). + // Bound by ShowContextMenuForViewImpl(). void OnGetContextMenuModel(views::View* source, const gfx::Point& point, ui::MenuSourceType source_type,
diff --git a/ash/app_list/views/search_result_view.cc b/ash/app_list/views/search_result_view.cc index 9b5cfdf..7e5824b 100644 --- a/ash/app_list/views/search_result_view.cc +++ b/ash/app_list/views/search_result_view.cc
@@ -498,9 +498,10 @@ return IsMouseHovered() || selected(); } -void SearchResultView::ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) { +void SearchResultView::ShowContextMenuForViewImpl( + views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) { // |result()| could be NULL when result list is changing. if (!result()) return;
diff --git a/ash/app_list/views/search_result_view.h b/ash/app_list/views/search_result_view.h index 79130484..6a53b400 100644 --- a/ash/app_list/views/search_result_view.h +++ b/ash/app_list/views/search_result_view.h
@@ -116,11 +116,11 @@ void ButtonPressed(views::Button* sender, const ui::Event& event) override; // views::ContextMenuController overrides: - void ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; - // Bound by ShowContextMenuForView(). + // Bound by ShowContextMenuForViewImpl(). void OnGetContextMenu(views::View* source, const gfx::Point& point, ui::MenuSourceType source_type,
diff --git a/ash/frame/non_client_frame_view_ash.cc b/ash/frame/non_client_frame_view_ash.cc index 2d0589f..a436c98 100644 --- a/ash/frame/non_client_frame_view_ash.cc +++ b/ash/frame/non_client_frame_view_ash.cc
@@ -491,7 +491,7 @@ UpdateHeaderView(); } -void NonClientFrameViewAsh::ShowContextMenuForView( +void NonClientFrameViewAsh::ShowContextMenuForViewImpl( views::View* source, const gfx::Point& point, ui::MenuSourceType source_type) {
diff --git a/ash/frame/non_client_frame_view_ash.h b/ash/frame/non_client_frame_view_ash.h index 2affcad..ae4d1fc4 100644 --- a/ash/frame/non_client_frame_view_ash.h +++ b/ash/frame/non_client_frame_view_ash.h
@@ -125,9 +125,9 @@ SplitViewController::State state) override; // views::ContextMenuController: - void ShowContextMenuForView(View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; // ui::SimpleMenuModel::Delegate: bool IsCommandIdChecked(int command_id) const override;
diff --git a/ash/metrics/pip_uma.h b/ash/metrics/pip_uma.h index 80b27ec3..1c2d24a1 100644 --- a/ash/metrics/pip_uma.h +++ b/ash/metrics/pip_uma.h
@@ -14,6 +14,7 @@ "Ash.Pip.FreeResizeInitialArea"; constexpr char kAshPipFreeResizeFinishAreaHistogramName[] = "Ash.Pip.FreeResizeFinishArea"; +constexpr char kAshPipPositionHistogramName[] = "Ash.Pip.Position"; // This enum should be kept in sync with the AshPipEvents enum in // src/tools/metrics/histograms/enums.xml. @@ -25,7 +26,22 @@ CHROME_PIP_START = 4, CHROME_PIP_END = 5, FREE_RESIZE = 6, - COUNT = 7 + kMaxValue = FREE_RESIZE +}; + +// This enum should be kept in sync with the AshPipPosition enum in +// src/tools/metrics/histograms/enums.xml. +enum class AshPipPosition { + MIDDLE = 0, + TOP_MIDDLE = 1, + MIDDLE_LEFT = 2, + MIDDLE_RIGHT = 3, + BOTTOM_MIDDLE = 4, + TOP_LEFT = 5, + TOP_RIGHT = 6, + BOTTOM_LEFT = 7, + BOTTOM_RIGHT = 8, + kMaxValue = BOTTOM_RIGHT }; } // namespace ash
diff --git a/ash/public/cpp/shelf_struct_mojom_traits.h b/ash/public/cpp/shelf_struct_mojom_traits.h index 093f9e8..4760c56 100644 --- a/ash/public/cpp/shelf_struct_mojom_traits.h +++ b/ash/public/cpp/shelf_struct_mojom_traits.h
@@ -189,6 +189,43 @@ }; template <> +struct EnumTraits<ash::mojom::ShelfAutoHideBehavior, + ash::ShelfAutoHideBehavior> { + static ash::mojom::ShelfAutoHideBehavior ToMojom( + ash::ShelfAutoHideBehavior input) { + switch (input) { + case ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS: + return ash::mojom::ShelfAutoHideBehavior:: + SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS; + case ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER: + return ash::mojom::ShelfAutoHideBehavior:: + SHELF_AUTO_HIDE_BEHAVIOR_NEVER; + case ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN: + return ash::mojom::ShelfAutoHideBehavior::SHELF_AUTO_HIDE_ALWAYS_HIDDEN; + } + NOTREACHED(); + return ash::mojom::ShelfAutoHideBehavior::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS; + } + + static bool FromMojom(ash::mojom::ShelfAutoHideBehavior input, + ash::ShelfAutoHideBehavior* out) { + switch (input) { + case ash::mojom::ShelfAutoHideBehavior::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS: + *out = ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS; + return true; + case ash::mojom::ShelfAutoHideBehavior::SHELF_AUTO_HIDE_BEHAVIOR_NEVER: + *out = ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER; + return true; + case ash::mojom::ShelfAutoHideBehavior::SHELF_AUTO_HIDE_ALWAYS_HIDDEN: + *out = ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN; + return true; + } + NOTREACHED(); + return false; + } +}; + +template <> struct ASH_PUBLIC_EXPORT StructTraits<ash::mojom::ShelfIDDataView, ash::ShelfID> { static const std::string& app_id(const ash::ShelfID& i) { return i.app_id; }
diff --git a/ash/public/interfaces/shelf.mojom b/ash/public/interfaces/shelf.mojom index b048e03..9ca03f8 100644 --- a/ash/public/interfaces/shelf.mojom +++ b/ash/public/interfaces/shelf.mojom
@@ -9,6 +9,13 @@ import "ui/events/mojo/event.mojom"; import "ui/gfx/image/mojo/image.mojom"; +// These values match ash::ShelfAutoHideBehavior. +enum ShelfAutoHideBehavior { + SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, // Always auto-hide. + SHELF_AUTO_HIDE_BEHAVIOR_NEVER, // Never auto-hide. + SHELF_AUTO_HIDE_ALWAYS_HIDDEN, // Always hide. +}; + // The actions that may be performed when a shelf item is selected. // These values match ash::ShelfAction. enum ShelfAction { @@ -75,6 +82,17 @@ UpdateShelfItem(ShelfItem item); // Sets the |delegate| for the item with |id|. SetShelfItemDelegate(ShelfID id, ShelfItemDelegate delegate); + // Returns the auto hide behavior. For testing only. + // |display_id| represents the display that contains the shelf. |display_id| + // must be valid. + GetAutoHideBehaviorForTesting(int64 display_id) + => (ShelfAutoHideBehavior behavior); + // Sets the auto hide behavior. For testing only. + // |display_id| represents the display that contains the shelf. |display_id| + // must be valid. + // |behavior| is the new behavior. + SetAutoHideBehaviorForTesting(int64 display_id, + ShelfAutoHideBehavior behavior) => (); }; // A Shelf observer, used to persist profile settings and cache a ShelfModel.
diff --git a/ash/public/interfaces/wallpaper.mojom b/ash/public/interfaces/wallpaper.mojom index bb5df37..e04f2d2 100644 --- a/ash/public/interfaces/wallpaper.mojom +++ b/ash/public/interfaces/wallpaper.mojom
@@ -289,10 +289,10 @@ // able to decide whatever the first wallpaper it wants to display. OnReadyToSetWallpaper(); - // TODO(wzang|784495): Consider removing this after views-based login is - // enabled. - // Notifies the client that the animation of the first wallpaper since - // the controller initialization has completed. + // Notifies the client that the animation of the first wallpaper since the + // controller initialization has completed. + // TODO(crbug.com/875128): Remove this after web-ui login code is completely + // removed. OnFirstWallpaperAnimationFinished(); }; @@ -309,7 +309,8 @@ // are ordered and are referenced in wallpaper::ColorProfileType. OnWallpaperColorsChanged(array<uint32> prominent_colors); - // TODO(wzang|784495): Remove this after views-based login is enabled. // Invoked when the blur state of the wallpaper changes. + // TODO(crbug.com/875128): Remove this after web-ui login code is completely + // removed. OnWallpaperBlurChanged(bool blurred); };
diff --git a/ash/shelf/shelf_controller.cc b/ash/shelf/shelf_controller.cc index d49f4db..28d3915 100644 --- a/ash/shelf/shelf_controller.cc +++ b/ash/shelf/shelf_controller.cc
@@ -4,7 +4,8 @@ #include "ash/shelf/shelf_controller.h" -#include <memory> +#include <algorithm> +#include <utility> #include "ash/public/cpp/ash_pref_names.h" #include "ash/public/cpp/remote_shelf_item_delegate.h" @@ -262,6 +263,24 @@ model_.SetShelfItemDelegate(id, nullptr); } +void ShelfController::GetAutoHideBehaviorForTesting( + int64_t display_id, + GetAutoHideBehaviorForTestingCallback callback) { + Shelf* shelf = GetShelfForDisplay(display_id); + DCHECK(shelf); + std::move(callback).Run(shelf->auto_hide_behavior()); +} + +void ShelfController::SetAutoHideBehaviorForTesting( + int64_t display_id, + ShelfAutoHideBehavior behavior, + SetAutoHideBehaviorForTestingCallback callback) { + Shelf* shelf = GetShelfForDisplay(display_id); + DCHECK(shelf); + shelf->SetAutoHideBehavior(behavior); + std::move(callback).Run(); +} + void ShelfController::ShelfItemAdded(int index) { if (applying_remote_shelf_model_changes_) return;
diff --git a/ash/shelf/shelf_controller.h b/ash/shelf/shelf_controller.h index 453e46f..4afe6b1 100644 --- a/ash/shelf/shelf_controller.h +++ b/ash/shelf/shelf_controller.h
@@ -5,6 +5,9 @@ #ifndef ASH_SHELF_SHELF_CONTROLLER_H_ #define ASH_SHELF_SHELF_CONTROLLER_H_ +#include <memory> +#include <string> + #include "ash/ash_export.h" #include "ash/display/window_tree_host_manager.h" #include "ash/public/cpp/shelf_item.h" @@ -59,6 +62,13 @@ void UpdateShelfItem(const ShelfItem& item) override; void SetShelfItemDelegate(const ShelfID& id, mojom::ShelfItemDelegatePtr delegate) override; + void GetAutoHideBehaviorForTesting( + int64_t display_id, + GetAutoHideBehaviorForTestingCallback callback) override; + void SetAutoHideBehaviorForTesting( + int64_t display_id, + ShelfAutoHideBehavior behavior, + SetAutoHideBehaviorForTestingCallback callback) override; // ShelfModelObserver: void ShelfItemAdded(int index) override;
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc index afe2099..5880e16 100644 --- a/ash/shelf/shelf_view.cc +++ b/ash/shelf/shelf_view.cc
@@ -2229,9 +2229,9 @@ source_type); } -void ShelfView::ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) { +void ShelfView::ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) { // Prevent concurrent requests that may show application or context menus. // If a second request is sent before the first one can respond, the Chrome // side ShelfItemDelegate may become unresponsive: https://crbug.com/881886
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h index 4fac211..c36bf2e 100644 --- a/ash/shelf/shelf_view.h +++ b/ash/shelf/shelf_view.h
@@ -466,9 +466,9 @@ base::Optional<std::vector<mojom::MenuItemPtr>> menu_items); // Overridden from views::ContextMenuController: - void ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; // Show either a context or normal click menu of given |menu_model|. // If |context_menu| is set, the displayed menu is a context menu and not
diff --git a/ash/shell.cc b/ash/shell.cc index 1eee4d16..f2c7a239 100644 --- a/ash/shell.cc +++ b/ash/shell.cc
@@ -1066,7 +1066,7 @@ AddPreTargetHandler(env_filter_.get()); // FocusController takes ownership of AshFocusRules. - focus_rules_ = new wm::AshFocusRules(); + focus_rules_ = new AshFocusRules(); focus_controller_ = std::make_unique<::wm::FocusController>(focus_rules_); focus_controller_->AddObserver(this);
diff --git a/ash/shell.h b/ash/shell.h index 678c69d..70869c4 100644 --- a/ash/shell.h +++ b/ash/shell.h
@@ -78,7 +78,6 @@ class ActivationClient; class CompoundEventFilter; class FocusController; -class FocusRules; class ShadowController; class VisibilityController; class WindowModalityController; @@ -97,6 +96,7 @@ class ArcCustomTabController; class AshDBusServices; class AshDisplayController; +class AshFocusRules; class AppListControllerImpl; class NativeCursorManagerAsh; class AshTouchTransformController; @@ -412,7 +412,7 @@ } FirstRunHelper* first_run_helper() { return first_run_helper_.get(); } ::wm::FocusController* focus_controller() { return focus_controller_.get(); } - ::wm::FocusRules* focus_rules() { return focus_rules_; } + AshFocusRules* focus_rules() { return focus_rules_; } FocusCycler* focus_cycler() { return focus_cycler_.get(); } HighlighterController* highlighter_controller() { return highlighter_controller_.get(); @@ -774,7 +774,7 @@ std::unique_ptr<WindowCycleController> window_cycle_controller_; std::unique_ptr<OverviewController> overview_controller_; // Owned by |focus_controller_|. - ::wm::FocusRules* focus_rules_ = nullptr; + AshFocusRules* focus_rules_ = nullptr; std::unique_ptr<::wm::ShadowController> shadow_controller_; std::unique_ptr<::wm::VisibilityController> visibility_controller_; std::unique_ptr<::wm::WindowModalityController> window_modality_controller_;
diff --git a/ash/shell/window_type_launcher.cc b/ash/shell/window_type_launcher.cc index eefde08..113735b9 100644 --- a/ash/shell/window_type_launcher.cc +++ b/ash/shell/window_type_launcher.cc
@@ -326,7 +326,7 @@ } } -void WindowTypeLauncher::ShowContextMenuForView( +void WindowTypeLauncher::ShowContextMenuForViewImpl( views::View* source, const gfx::Point& point, ui::MenuSourceType source_type) {
diff --git a/ash/shell/window_type_launcher.h b/ash/shell/window_type_launcher.h index 15321c7..f11b444 100644 --- a/ash/shell/window_type_launcher.h +++ b/ash/shell/window_type_launcher.h
@@ -67,9 +67,9 @@ void ExecuteCommand(int id, int event_flags) override; // Override from views::ContextMenuController: - void ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; views::Button* create_button_; views::Button* create_nonresizable_button_;
diff --git a/ash/wallpaper/wallpaper_controller.cc b/ash/wallpaper/wallpaper_controller.cc index 7a5f073..45130f45 100644 --- a/ash/wallpaper/wallpaper_controller.cc +++ b/ash/wallpaper/wallpaper_controller.cc
@@ -616,8 +616,8 @@ } void WallpaperController::OnWallpaperAnimationFinished() { - // TODO(wzang|784495): This is used by a code path in web-UI login. Remove it - // if views-based login is not interested in this event. + // TODO(crbug.com/875128): Remove this after web-ui login code is completely + // removed. if (wallpaper_controller_client_ && is_first_wallpaper_) { wallpaper_controller_client_->OnFirstWallpaperAnimationFinished(); }
diff --git a/ash/wallpaper/wallpaper_view.cc b/ash/wallpaper/wallpaper_view.cc index 7d0cf4c..3672184 100644 --- a/ash/wallpaper/wallpaper_view.cc +++ b/ash/wallpaper/wallpaper_view.cc
@@ -194,9 +194,9 @@ return true; } -void WallpaperView::ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) { +void WallpaperView::ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) { Shell::Get()->ShowContextMenu(point, source_type); }
diff --git a/ash/wallpaper/wallpaper_view.h b/ash/wallpaper/wallpaper_view.h index f08a78c6..7b4e9c8 100644 --- a/ash/wallpaper/wallpaper_view.h +++ b/ash/wallpaper/wallpaper_view.h
@@ -27,9 +27,9 @@ bool OnMousePressed(const ui::MouseEvent& event) override; // views::ContextMenuController: - void ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; DISALLOW_COPY_AND_ASSIGN(WallpaperView); };
diff --git a/ash/wm/ash_focus_rules.cc b/ash/wm/ash_focus_rules.cc index 2fd7a70..db3773c7 100644 --- a/ash/wm/ash_focus_rules.cc +++ b/ash/wm/ash_focus_rules.cc
@@ -7,8 +7,8 @@ #include "ash/public/cpp/shell_window_ids.h" #include "ash/session/session_controller.h" #include "ash/shell.h" +#include "ash/shell_delegate.h" #include "ash/wm/container_finder.h" -#include "ash/wm/focus_rules.h" #include "ash/wm/mru_window_tracker.h" #include "ash/wm/window_state.h" #include "ui/aura/client/aura_constants.h" @@ -16,7 +16,6 @@ #include "ui/events/event.h" namespace ash { -namespace wm { namespace { bool BelongsToContainerWithEqualOrGreaterId(const aura::Window* window, @@ -49,7 +48,14 @@ // AshFocusRules, ::wm::FocusRules: bool AshFocusRules::IsToplevelWindow(const aura::Window* window) const { - return ash::IsToplevelWindow(window); + DCHECK(window); + // The window must be in a valid hierarchy. + if (!window->GetRootWindow() || !window->parent()) + return false; + + // The window must exist within a container that supports activation. + // The window cannot be blocked by a modal transient. + return IsActivatableShellWindowId(window->parent()->id()); } bool AshFocusRules::SupportsChildActivation(const aura::Window* window) const { @@ -58,7 +64,26 @@ bool AshFocusRules::IsWindowConsideredVisibleForActivation( const aura::Window* window) const { - return ash::IsWindowConsideredVisibleForActivation(window); + DCHECK(window); + // If the |window| doesn't belong to the current active user and also doesn't + // show for the current active user, then it should not be activated. + if (!Shell::Get()->shell_delegate()->CanShowWindowForUser(window)) + return false; + + if (window->IsVisible()) + return true; + + // Minimized windows are hidden in their minimized state, but they can always + // be activated. + if (wm::GetWindowState(window)->IsMinimized()) + return true; + + if (!window->TargetVisibility()) + return false; + + const int parent_shell_window_id = window->parent()->id(); + return parent_shell_window_id == kShellWindowId_DefaultContainer || + parent_shell_window_id == kShellWindowId_LockScreenContainer; } bool AshFocusRules::CanActivateWindow(const aura::Window* window) const { @@ -144,8 +169,8 @@ aura::Window* ignore) const { aura::Window* window = nullptr; aura::Window* root = ignore ? ignore->GetRootWindow() : nullptr; - aura::Window::Windows containers = - GetContainersFromAllRootWindows(kActivatableShellWindowIds[index], root); + aura::Window::Windows containers = wm::GetContainersFromAllRootWindows( + kActivatableShellWindowIds[index], root); for (aura::Window* container : containers) { window = GetTopmostWindowToActivateInContainer(container, ignore); if (window) @@ -160,7 +185,7 @@ for (aura::Window::Windows::const_reverse_iterator i = container->children().rbegin(); i != container->children().rend(); ++i) { - WindowState* window_state = GetWindowState(*i); + wm::WindowState* window_state = wm::GetWindowState(*i); if (*i != ignore && window_state->CanActivate() && !window_state->IsMinimized()) return *i; @@ -168,5 +193,4 @@ return nullptr; } -} // namespace wm } // namespace ash
diff --git a/ash/wm/ash_focus_rules.h b/ash/wm/ash_focus_rules.h index 03bdda8..24612efb 100644 --- a/ash/wm/ash_focus_rules.h +++ b/ash/wm/ash_focus_rules.h
@@ -6,19 +6,16 @@ #define ASH_WM_ASH_FOCUS_RULES_H_ #include "ash/ash_export.h" -#include "base/compiler_specific.h" #include "base/macros.h" #include "ui/wm/core/base_focus_rules.h" namespace ash { -namespace wm { class ASH_EXPORT AshFocusRules : public ::wm::BaseFocusRules { public: AshFocusRules(); ~AshFocusRules() override; - private: // ::wm::BaseFocusRules: bool IsToplevelWindow(const aura::Window* window) const override; bool SupportsChildActivation(const aura::Window* window) const override; @@ -29,6 +26,7 @@ const ui::Event* event) const override; aura::Window* GetNextActivatableWindow(aura::Window* ignore) const override; + private: aura::Window* GetTopmostWindowToActivateForContainerIndex( int index, aura::Window* ignore) const; @@ -39,7 +37,6 @@ DISALLOW_COPY_AND_ASSIGN(AshFocusRules); }; -} // namespace wm } // namespace ash #endif // ASH_WM_ASH_FOCUS_RULES_H_
diff --git a/ash/wm/focus_rules.cc b/ash/wm/focus_rules.cc deleted file mode 100644 index 12721b1..0000000 --- a/ash/wm/focus_rules.cc +++ /dev/null
@@ -1,67 +0,0 @@ -// Copyright 2016 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/wm/focus_rules.h" - -#include "ash/public/cpp/shell_window_ids.h" -#include "ash/shell.h" -#include "ash/shell_delegate.h" -#include "ash/wm/window_state.h" -#include "ui/aura/window.h" -#include "ui/wm/public/activation_delegate.h" - -namespace ash { - -bool IsToplevelWindow(const aura::Window* window) { - DCHECK(window); - // The window must in a valid hierarchy. - if (!window->GetRootWindow() || !window->parent()) - return false; - - // The window must exist within a container that supports activation. - // The window cannot be blocked by a modal transient. - return IsActivatableShellWindowId(window->parent()->id()); -} - -bool IsWindowConsideredActivatable(const aura::Window* window) { - DCHECK(window); - // Only toplevel windows can be activated. - if (!IsToplevelWindow(window)) - return false; - - if (!IsWindowConsideredVisibleForActivation(window)) - return false; - - if (::wm::GetActivationDelegate(window) && - !::wm::GetActivationDelegate(window)->ShouldActivate()) { - return false; - } - - return window->CanFocus(); -} - -bool IsWindowConsideredVisibleForActivation(const aura::Window* window) { - DCHECK(window); - // If the |window| doesn't belong to the current active user and also doesn't - // show for the current active user, then it should not be activated. - if (!Shell::Get()->shell_delegate()->CanShowWindowForUser(window)) - return false; - - if (window->IsVisible()) - return true; - - // Minimized windows are hidden in their minimized state, but they can always - // be activated. - if (wm::GetWindowState(window)->IsMinimized()) - return true; - - if (!window->TargetVisibility()) - return false; - - const int parent_shell_window_id = window->parent()->id(); - return parent_shell_window_id == kShellWindowId_DefaultContainer || - parent_shell_window_id == kShellWindowId_LockScreenContainer; -} - -} // namespace ash
diff --git a/ash/wm/focus_rules.h b/ash/wm/focus_rules.h deleted file mode 100644 index 90d607c..0000000 --- a/ash/wm/focus_rules.h +++ /dev/null
@@ -1,25 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef ASH_WM_FOCUS_RULES_H_ -#define ASH_WM_FOCUS_RULES_H_ - -#include "ash/ash_export.h" - -namespace aura { -class Window; -} - -namespace ash { - -// These functions provide the ash implementation wm::FocusRules. See -// description there for details. -ASH_EXPORT bool IsToplevelWindow(const aura::Window* window); -ASH_EXPORT bool IsWindowConsideredActivatable(const aura::Window* window); -ASH_EXPORT bool IsWindowConsideredVisibleForActivation( - const aura::Window* window); - -} // namespace ash - -#endif // ASH_WM_FOCUS_RULES_H_
diff --git a/ash/wm/mru_window_tracker.cc b/ash/wm/mru_window_tracker.cc index dd3d944..b9f596b 100644 --- a/ash/wm/mru_window_tracker.cc +++ b/ash/wm/mru_window_tracker.cc
@@ -10,7 +10,7 @@ #include "ash/public/cpp/window_properties.h" #include "ash/session/session_controller.h" #include "ash/shell.h" -#include "ash/wm/focus_rules.h" +#include "ash/wm/ash_focus_rules.h" #include "ash/wm/switchable_windows.h" #include "ash/wm/window_state.h" #include "ash/wm/window_util.h" @@ -19,11 +19,31 @@ #include "ui/aura/window.h" #include "ui/wm/core/window_util.h" #include "ui/wm/public/activation_client.h" +#include "ui/wm/public/activation_delegate.h" namespace ash { namespace { +bool IsWindowConsideredActivatable(const aura::Window* window) { + DCHECK(window); + AshFocusRules* focus_rules = Shell::Get()->focus_rules(); + + // Only toplevel windows can be activated. + if (!focus_rules->IsToplevelWindow(window)) + return false; + + if (!focus_rules->IsWindowConsideredVisibleForActivation(window)) + return false; + + if (::wm::GetActivationDelegate(window) && + !::wm::GetActivationDelegate(window)->ShouldActivate()) { + return false; + } + + return window->CanFocus(); +} + // A predicate that determines whether |window| can be included in the MRU // window list. bool CanIncludeWindowInMruList(aura::Window* window) {
diff --git a/ash/wm/pip/pip_window_resizer.cc b/ash/wm/pip/pip_window_resizer.cc index fb129d1e..6f25147d 100644 --- a/ash/wm/pip/pip_window_resizer.cc +++ b/ash/wm/pip/pip_window_resizer.cc
@@ -4,6 +4,9 @@ #include "ash/wm/pip/pip_window_resizer.h" +#include <algorithm> +#include <utility> + #include "ash/metrics/pip_uma.h" #include "ash/wm/pip/pip_positioner.h" #include "ash/wm/widget_finder.h" @@ -70,6 +73,66 @@ } } +int ComputeIntersectionArea(const gfx::Rect& ninth, const gfx::Rect& bounds) { + gfx::Rect intersection = ninth; + intersection.Intersect(bounds); + return intersection.width() * intersection.height(); +} + +gfx::Rect ScaleRect(const gfx::Rect& rect, int scale) { + return gfx::Rect(rect.x() * scale, rect.y() * scale, rect.width() * scale, + rect.height() * scale); +} + +void CollectPositionMetric(const gfx::Rect& bounds_in_screen, + const gfx::Rect& area_in_screen) { + const int width = area_in_screen.width(); + const int height = area_in_screen.height(); + // Scale by three to avoid truncation. + const gfx::Rect area = ScaleRect(area_in_screen, 3); + const gfx::Rect bounds = ScaleRect(bounds_in_screen, 3); + + // Choose corners first, then edges, and finally middle in the case of a tie. + // This is based on the enum integer values. + // For this to work, all of the 9 buckets need to have the same area. + std::pair<int, AshPipPosition> area_ninths[9] = { + {ComputeIntersectionArea( + gfx::Rect(area.x() + width, area.y() + height, width, height), + bounds), + AshPipPosition::MIDDLE}, + {ComputeIntersectionArea( + gfx::Rect(area.x() + width, area.y(), width, height), bounds), + AshPipPosition::TOP_MIDDLE}, + {ComputeIntersectionArea( + gfx::Rect(area.x(), area.y() + height, width, height), bounds), + AshPipPosition::MIDDLE_LEFT}, + {ComputeIntersectionArea( + gfx::Rect(area.x() + 2 * width, area.y() + height, width, height), + bounds), + AshPipPosition::MIDDLE_RIGHT}, + {ComputeIntersectionArea( + gfx::Rect(area.x() + width, area.y() + 2 * height, width, height), + bounds), + AshPipPosition::BOTTOM_MIDDLE}, + {ComputeIntersectionArea(gfx::Rect(area.x(), area.y(), width, height), + bounds), + AshPipPosition::TOP_LEFT}, + {ComputeIntersectionArea( + gfx::Rect(area.x() + 2 * width, area.y(), width, height), bounds), + AshPipPosition::TOP_RIGHT}, + {ComputeIntersectionArea( + gfx::Rect(area.x(), area.y() + 2 * height, width, height), bounds), + AshPipPosition::BOTTOM_LEFT}, + {ComputeIntersectionArea(gfx::Rect(area.x() + 2 * width, + area.y() + 2 * height, width, height), + bounds), + AshPipPosition::BOTTOM_RIGHT}}; + + std::sort(area_ninths, area_ninths + base::size(area_ninths)); + UMA_HISTOGRAM_ENUMERATION(kAshPipPositionHistogramName, + area_ninths[8].second); +} + } // namespace PipWindowResizer::PipWindowResizer(wm::WindowState* window_state) @@ -79,7 +142,7 @@ bool is_resize = details().bounds_change & kBoundsChange_Resizes; if (is_resize) { UMA_HISTOGRAM_ENUMERATION(kAshPipEventsHistogramName, - AshPipEvents::FREE_RESIZE, AshPipEvents::COUNT); + AshPipEvents::FREE_RESIZE); CollectFreeResizeAreaMetric(kAshPipFreeResizeInitialAreaHistogramName, GetTarget()); } else { @@ -191,6 +254,11 @@ if (details().bounds_change & kBoundsChange_Resizes) { CollectFreeResizeAreaMetric(kAshPipFreeResizeFinishAreaHistogramName, GetTarget()); + } else { + // Collect final position on drag-move. + display::Display display = window_state()->GetDisplay(); + gfx::Rect area = PipPositioner::GetMovementArea(display); + CollectPositionMetric(GetTarget()->GetBoundsInScreen(), area); } window_state()->OnCompleteDrag(last_location_in_screen_);
diff --git a/ash/wm/pip/pip_window_resizer_unittest.cc b/ash/wm/pip/pip_window_resizer_unittest.cc index 4a5c3a0c..978deb1 100644 --- a/ash/wm/pip/pip_window_resizer_unittest.cc +++ b/ash/wm/pip/pip_window_resizer_unittest.cc
@@ -5,6 +5,7 @@ #include "ash/wm/pip/pip_window_resizer.h" #include <string> +#include <tuple> #include <utility> #include "ash/metrics/pip_uma.h" @@ -622,5 +623,265 @@ std::make_tuple("400x400,400x400", 0u), std::make_tuple("400x400,400x400", 1u))); +using PipWindowResizerNonSquareAspectRatioTest = PipWindowResizerTest; + +TEST_P(PipWindowResizerNonSquareAspectRatioTest, PipPositionUmaMetrics) { + histograms().ExpectTotalCount(kAshPipPositionHistogramName, 0); + + { + // Check TOP_LEFT. + PreparePipWindow(gfx::Rect(0, 0, 100, 100)); + std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION)); + ASSERT_TRUE(resizer.get()); + resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0); + resizer->CompleteDrag(); + + EXPECT_EQ(1, histograms().GetBucketCount(kAshPipPositionHistogramName, + Sample(AshPipPosition::TOP_LEFT))); + histograms().ExpectTotalCount(kAshPipPositionHistogramName, 1); + } + + { + // Check TOP_MIDDLE. + PreparePipWindow(gfx::Rect(100, 0, 100, 100)); + std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION)); + ASSERT_TRUE(resizer.get()); + resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0); + resizer->CompleteDrag(); + + EXPECT_EQ(1, + histograms().GetBucketCount(kAshPipPositionHistogramName, + Sample(AshPipPosition::TOP_MIDDLE))); + histograms().ExpectTotalCount(kAshPipPositionHistogramName, 2); + } + + { + // Check TOP_RIGHT. + PreparePipWindow(gfx::Rect(250, 0, 100, 100)); + std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION)); + ASSERT_TRUE(resizer.get()); + resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0); + resizer->CompleteDrag(); + + EXPECT_EQ(1, + histograms().GetBucketCount(kAshPipPositionHistogramName, + Sample(AshPipPosition::TOP_RIGHT))); + histograms().ExpectTotalCount(kAshPipPositionHistogramName, 3); + } + + { + // Check MIDDLE_LEFT. + PreparePipWindow(gfx::Rect(0, 100, 100, 100)); + std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION)); + ASSERT_TRUE(resizer.get()); + resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0); + resizer->CompleteDrag(); + + EXPECT_EQ(1, + histograms().GetBucketCount(kAshPipPositionHistogramName, + Sample(AshPipPosition::MIDDLE_LEFT))); + histograms().ExpectTotalCount(kAshPipPositionHistogramName, 4); + } + + { + // Check MIDDLE. + PreparePipWindow(gfx::Rect(100, 100, 100, 100)); + std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION)); + ASSERT_TRUE(resizer.get()); + resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0); + resizer->CompleteDrag(); + + EXPECT_EQ(1, histograms().GetBucketCount(kAshPipPositionHistogramName, + Sample(AshPipPosition::MIDDLE))); + histograms().ExpectTotalCount(kAshPipPositionHistogramName, 5); + } + + { + // Check MIDDLE_RIGHT. + PreparePipWindow(gfx::Rect(250, 100, 100, 100)); + std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION)); + ASSERT_TRUE(resizer.get()); + resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0); + resizer->CompleteDrag(); + + EXPECT_EQ( + 1, histograms().GetBucketCount(kAshPipPositionHistogramName, + Sample(AshPipPosition::MIDDLE_RIGHT))); + histograms().ExpectTotalCount(kAshPipPositionHistogramName, 6); + } + + { + // Check BOTTOM_LEFT. + PreparePipWindow(gfx::Rect(0, 250, 100, 100)); + std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION)); + ASSERT_TRUE(resizer.get()); + resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0); + resizer->CompleteDrag(); + + EXPECT_EQ(1, + histograms().GetBucketCount(kAshPipPositionHistogramName, + Sample(AshPipPosition::BOTTOM_LEFT))); + histograms().ExpectTotalCount(kAshPipPositionHistogramName, 7); + } + + { + // Check BOTTOM_MIDDLE. + PreparePipWindow(gfx::Rect(100, 250, 100, 100)); + std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION)); + ASSERT_TRUE(resizer.get()); + resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0); + resizer->CompleteDrag(); + + EXPECT_EQ( + 1, histograms().GetBucketCount(kAshPipPositionHistogramName, + Sample(AshPipPosition::BOTTOM_MIDDLE))); + histograms().ExpectTotalCount(kAshPipPositionHistogramName, 8); + } + + { + // Check BOTTOM_RIGHT. + PreparePipWindow(gfx::Rect(250, 250, 100, 100)); + std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION)); + ASSERT_TRUE(resizer.get()); + resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0); + resizer->CompleteDrag(); + + EXPECT_EQ( + 1, histograms().GetBucketCount(kAshPipPositionHistogramName, + Sample(AshPipPosition::BOTTOM_RIGHT))); + histograms().ExpectTotalCount(kAshPipPositionHistogramName, 9); + } +} + +TEST_P(PipWindowResizerNonSquareAspectRatioTest, + PipPositionUmaMetricsCornerPriority) { + histograms().ExpectTotalCount(kAshPipPositionHistogramName, 0); + + // Check corners are priotised over edges and middle. + { + // Check TOP_LEFT. + PreparePipWindow(gfx::Rect(0, 0, 300, 210)); + std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION)); + ASSERT_TRUE(resizer.get()); + resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0); + resizer->CompleteDrag(); + + EXPECT_EQ(1, histograms().GetBucketCount(kAshPipPositionHistogramName, + Sample(AshPipPosition::TOP_LEFT))); + histograms().ExpectTotalCount(kAshPipPositionHistogramName, 1); + } + + { + // Check TOP_RIGHT. + PreparePipWindow(gfx::Rect(100, 0, 300, 210)); + std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION)); + ASSERT_TRUE(resizer.get()); + resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0); + resizer->CompleteDrag(); + + EXPECT_EQ(1, + histograms().GetBucketCount(kAshPipPositionHistogramName, + Sample(AshPipPosition::TOP_RIGHT))); + histograms().ExpectTotalCount(kAshPipPositionHistogramName, 2); + } + + { + // Check BOTTOM_LEFT. + PreparePipWindow(gfx::Rect(0, 190, 300, 210)); + std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION)); + ASSERT_TRUE(resizer.get()); + resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0); + resizer->CompleteDrag(); + + EXPECT_EQ(1, + histograms().GetBucketCount(kAshPipPositionHistogramName, + Sample(AshPipPosition::BOTTOM_LEFT))); + histograms().ExpectTotalCount(kAshPipPositionHistogramName, 3); + } + + { + // Check BOTTOM_RIGHT. + PreparePipWindow(gfx::Rect(100, 190, 300, 210)); + std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION)); + ASSERT_TRUE(resizer.get()); + resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0); + resizer->CompleteDrag(); + + EXPECT_EQ( + 1, histograms().GetBucketCount(kAshPipPositionHistogramName, + Sample(AshPipPosition::BOTTOM_RIGHT))); + histograms().ExpectTotalCount(kAshPipPositionHistogramName, 4); + } +} + +TEST_P(PipWindowResizerNonSquareAspectRatioTest, + PipPositionUmaMetricsEdgePriority) { + histograms().ExpectTotalCount(kAshPipPositionHistogramName, 0); + + // Test that edges are prioritised over middle. + { + // Check TOP_MIDDLE. + PreparePipWindow(gfx::Rect(100, 0, 200, 220)); + std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION)); + ASSERT_TRUE(resizer.get()); + resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0); + resizer->CompleteDrag(); + + EXPECT_EQ(1, + histograms().GetBucketCount(kAshPipPositionHistogramName, + Sample(AshPipPosition::TOP_MIDDLE))); + histograms().ExpectTotalCount(kAshPipPositionHistogramName, 1); + } + + { + // Check BOTTOM_MIDDLE. + PreparePipWindow(gfx::Rect(100, 80, 200, 220)); + std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION)); + ASSERT_TRUE(resizer.get()); + resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0); + resizer->CompleteDrag(); + + EXPECT_EQ( + 1, histograms().GetBucketCount(kAshPipPositionHistogramName, + Sample(AshPipPosition::BOTTOM_MIDDLE))); + histograms().ExpectTotalCount(kAshPipPositionHistogramName, 2); + } + + { + // Check MIDDLE_LEFT. + PreparePipWindow(gfx::Rect(0, 90, 300, 120)); + std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION)); + ASSERT_TRUE(resizer.get()); + resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0); + resizer->CompleteDrag(); + + EXPECT_EQ(1, + histograms().GetBucketCount(kAshPipPositionHistogramName, + Sample(AshPipPosition::MIDDLE_LEFT))); + histograms().ExpectTotalCount(kAshPipPositionHistogramName, 3); + } + + { + // Check MIDDLE_RIGHT. + PreparePipWindow(gfx::Rect(100, 90, 300, 120)); + std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION)); + ASSERT_TRUE(resizer.get()); + resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0); + resizer->CompleteDrag(); + + EXPECT_EQ( + 1, histograms().GetBucketCount(kAshPipPositionHistogramName, + Sample(AshPipPosition::MIDDLE_RIGHT))); + histograms().ExpectTotalCount(kAshPipPositionHistogramName, 4); + } +} + +INSTANTIATE_TEST_SUITE_P( + /* no prefix */, + PipWindowResizerNonSquareAspectRatioTest, + testing::Values(std::make_tuple("400x300", 0u), + std::make_tuple("400x300,4000x3000", 0u), + std::make_tuple("4000x3000,400x300", 1u))); + } // namespace wm } // namespace ash
diff --git a/ash/wm/splitview/split_view_drag_indicators.cc b/ash/wm/splitview/split_view_drag_indicators.cc index 151acf4..85a8795 100644 --- a/ash/wm/splitview/split_view_drag_indicators.cc +++ b/ash/wm/splitview/split_view_drag_indicators.cc
@@ -287,7 +287,7 @@ ~SplitViewDragIndicatorsView() override {} // Called by parent widget when the state machine changes. Handles setting the - // opacity of the highlights and labels based on the state. + // opacity and bounds of the highlights and labels based on the state. void OnIndicatorTypeChanged(IndicatorState indicator_state) { DCHECK_NE(indicator_state_, indicator_state); @@ -303,7 +303,8 @@ right_highlight_view_->OnIndicatorTypeChanged(indicator_state, previous_indicator_state_); - if (indicator_state != IndicatorState::kNone) + if (indicator_state != IndicatorState::kNone || + IsPreviewAreaState(previous_indicator_state_)) Layout(previous_indicator_state_ != IndicatorState::kNone); } @@ -356,9 +357,17 @@ left_highlight_bounds.Transpose(); right_highlight_bounds.Transpose(); } - const bool preview_left = - (indicator_state_ == IndicatorState::kPreviewAreaLeft); - if (IsPreviewAreaState(indicator_state_)) { + // While the preview area fades out, its inset animates to zero. + const bool nix_preview_inset = + indicator_state_ == IndicatorState::kNone && + IsPreviewAreaState(previous_indicator_state_); + // For positioning purposes, we need |IndicatorState::kPreviewAreaLeft| or + // |IndicatorState::kPreviewAreaRight|, even if |nix_preview_inset| is true + // and |indicator_state_| has already been set to |IndicatorState::kNone|. + const IndicatorState preview_state = + nix_preview_inset ? previous_indicator_state_ : indicator_state_; + const bool preview_left = preview_state == IndicatorState::kPreviewAreaLeft; + if (IsPreviewAreaState(indicator_state_) || nix_preview_inset) { // Get the preview area bounds from the split view controller. gfx::Rect preview_area_bounds = Shell::Get()->split_view_controller()->GetSnappedWindowBoundsInScreen( @@ -375,8 +384,10 @@ const gfx::Rect work_area_bounds = GetWorkAreaBoundsNoOverlapWithShelf(root_window); preview_area_bounds.set_y(preview_area_bounds.y() - work_area_bounds.y()); - preview_area_bounds.Inset(kHighlightScreenEdgePaddingDp, - kHighlightScreenEdgePaddingDp); + if (!nix_preview_inset) { + preview_area_bounds.Inset(kHighlightScreenEdgePaddingDp, + kHighlightScreenEdgePaddingDp); + } // Calculate the bounds of the other highlight, which is the one that // shrinks and fades away, while the other one, the preview area, expands @@ -389,7 +400,7 @@ if (!landscape) other_bounds.Transpose(); - if (IsPreviewAreaOnLeftTopOfScreen(indicator_state_)) { + if (IsPreviewAreaOnLeftTopOfScreen(preview_state)) { left_highlight_bounds = preview_area_bounds; right_highlight_bounds = other_bounds; } else { @@ -400,9 +411,9 @@ } left_highlight_view_->SetBounds(GetMirroredRect(left_highlight_bounds), - landscape, animate); + landscape, animate, nix_preview_inset); right_highlight_view_->SetBounds(GetMirroredRect(right_highlight_bounds), - landscape, animate); + landscape, animate, nix_preview_inset); // Calculate the bounds of the views which contain the guidance text and // icon. Rotate the two views in landscape mode.
diff --git a/ash/wm/splitview/split_view_highlight_view.cc b/ash/wm/splitview/split_view_highlight_view.cc index 06e5dfb..c54ea5f 100644 --- a/ash/wm/splitview/split_view_highlight_view.cc +++ b/ash/wm/splitview/split_view_highlight_view.cc
@@ -80,7 +80,8 @@ void SplitViewHighlightView::SetBounds(const gfx::Rect& bounds, bool landscape, - bool animate) { + bool animate, + bool nixing_preview_inset) { if (bounds == this->bounds() && landscape == landscape_) return; @@ -94,7 +95,8 @@ const bool slides_from_right = base::i18n::IsRTL() && landscape ? !is_right_or_bottom_ : is_right_or_bottom_; - if (slides_from_right && animate && !offset.IsZero()) { + if ((slides_from_right || nixing_preview_inset) && animate && + !offset.IsZero()) { gfx::Rect old_left_top_bounds = left_top_->bounds(); gfx::Rect old_right_middle_bounds = right_bottom_->bounds(); gfx::Rect old_middle_bounds = middle_->bounds(); @@ -141,16 +143,19 @@ // and apply it. Otherwise set the new bounds and reset the transforms on all // items. if (animate) { + const SplitviewAnimationType animation_type = + nixing_preview_inset ? SPLITVIEW_ANIMATION_PREVIEW_AREA_NIX_INSET + : SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN_OUT; DoSplitviewTransformAnimation( - middle_->layer(), SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN_OUT, + middle_->layer(), animation_type, CalculateTransformFromRects(middle_->bounds(), middle_bounds, landscape)); DoSplitviewTransformAnimation( - left_top_->layer(), SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN_OUT, + left_top_->layer(), animation_type, CalculateTransformFromRects(left_top_->bounds(), left_top_bounds, landscape)); DoSplitviewTransformAnimation( - right_bottom_->layer(), SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN_OUT, + right_bottom_->layer(), animation_type, CalculateTransformFromRects(right_bottom_->bounds(), right_bottom_bounds, landscape)); } else {
diff --git a/ash/wm/splitview/split_view_highlight_view.h b/ash/wm/splitview/split_view_highlight_view.h index c8c0f1ab..f1a5d5b 100644 --- a/ash/wm/splitview/split_view_highlight_view.h +++ b/ash/wm/splitview/split_view_highlight_view.h
@@ -22,12 +22,23 @@ // rounded corners remain the same during the duration of an animation. // (Transforming a rounded rect will stretch the corners, and having to repaint // every animation tick is expensive.) +// +// Although rounded corners are prevented from stretching along one axis, there +// is one animation where the rounded corners will stretch along the +// perpendicular axis. Specifically, the preview area has a small inset (on all +// four sides) until you actually snap the window, and then the preview area +// animates to nix that inset while fading out. So the rounded corners will +// stretch by an amount depending on the dimensions of the work area, but it is +// unlikely to be noticeable under normal circumstances. class ASH_EXPORT SplitViewHighlightView : public views::View { public: explicit SplitViewHighlightView(bool is_right_or_bottom); ~SplitViewHighlightView() override; - void SetBounds(const gfx::Rect& bounds, bool landscape, bool animate); + void SetBounds(const gfx::Rect& bounds, + bool landscape, + bool animate, + bool nixing_preview_inset = false); void SetColor(SkColor color);
diff --git a/ash/wm/splitview/split_view_utils.cc b/ash/wm/splitview/split_view_utils.cc index 115c838f..44bf8f6 100644 --- a/ash/wm/splitview/split_view_utils.cc +++ b/ash/wm/splitview/split_view_utils.cc
@@ -96,6 +96,7 @@ ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET; return; case SPLITVIEW_ANIMATION_PREVIEW_AREA_FADE_OUT: + case SPLITVIEW_ANIMATION_PREVIEW_AREA_NIX_INSET: *out_duration = kPreviewAreaFadeOutMs; *out_tween_type = gfx::Tween::FAST_OUT_LINEAR_IN; return; @@ -181,6 +182,7 @@ switch (type) { case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_SLIDE_IN: case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_SLIDE_OUT: + case SPLITVIEW_ANIMATION_PREVIEW_AREA_NIX_INSET: case SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN_OUT: case SPLITVIEW_ANIMATION_SET_WINDOW_TRANSFORM: case SPLITVIEW_ANIMATION_TEXT_SLIDE_IN:
diff --git a/ash/wm/splitview/split_view_utils.h b/ash/wm/splitview/split_view_utils.h index fe5d8a6c7..865bbcf 100644 --- a/ash/wm/splitview/split_view_utils.h +++ b/ash/wm/splitview/split_view_utils.h
@@ -59,6 +59,8 @@ // Used to slide in the text labels. SPLITVIEW_ANIMATION_TEXT_SLIDE_IN, SPLITVIEW_ANIMATION_TEXT_SLIDE_OUT, + // Used to animate the inset of the preview area to nothing. + SPLITVIEW_ANIMATION_PREVIEW_AREA_NIX_INSET, // Used to apply window transform on the selector item after it gets snapped // or on the dragged window after the drag ends. SPLITVIEW_ANIMATION_SET_WINDOW_TRANSFORM,
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc index b828bac..48c6a51 100644 --- a/ash/wm/window_state.cc +++ b/ash/wm/window_state.cc
@@ -165,18 +165,16 @@ static_cast<int>(ash::AppType::ARC_APP); if (enter) { UMA_HISTOGRAM_ENUMERATION(kAshPipEventsHistogramName, - AshPipEvents::PIP_START, AshPipEvents::COUNT); + AshPipEvents::PIP_START); UMA_HISTOGRAM_ENUMERATION(kAshPipEventsHistogramName, is_android ? AshPipEvents::ANDROID_PIP_START - : AshPipEvents::CHROME_PIP_START, - AshPipEvents::COUNT); + : AshPipEvents::CHROME_PIP_START); } else { - UMA_HISTOGRAM_ENUMERATION(kAshPipEventsHistogramName, AshPipEvents::PIP_END, - AshPipEvents::COUNT); + UMA_HISTOGRAM_ENUMERATION(kAshPipEventsHistogramName, + AshPipEvents::PIP_END); UMA_HISTOGRAM_ENUMERATION(kAshPipEventsHistogramName, is_android ? AshPipEvents::ANDROID_PIP_END - : AshPipEvents::CHROME_PIP_END, - AshPipEvents::COUNT); + : AshPipEvents::CHROME_PIP_END); } }
diff --git a/base/process/process_handle_win.cc b/base/process/process_handle_win.cc index 67986cd2..fca342b 100644 --- a/base/process/process_handle_win.cc +++ b/base/process/process_handle_win.cc
@@ -21,8 +21,15 @@ } ProcessId GetProcId(ProcessHandle process) { + if (process == base::kNullProcessHandle) + return 0; // This returns 0 if we have insufficient rights to query the process handle. - return GetProcessId(process); + // Invalid handles or non-process handles will cause a hard failure. + ProcessId result = GetProcessId(process); + // TODO(davidbienvenu): Change to CHECK once we don't get reports of DCHECKs. + DCHECK(result != 0 || GetLastError() != ERROR_INVALID_HANDLE) + << "process handle = " << process; + return result; } ProcessId GetParentProcessId(ProcessHandle process) {
diff --git a/base/profiler/native_stack_sampler.h b/base/profiler/native_stack_sampler.h index 0ff7992..48526a1 100644 --- a/base/profiler/native_stack_sampler.h +++ b/base/profiler/native_stack_sampler.h
@@ -62,7 +62,7 @@ // thread being sampled). // Records a set of frames and returns them. - virtual std::vector<StackSamplingProfiler::Frame> RecordStackFrames( + virtual void RecordStackFrames( StackBuffer* stackbuffer, StackSamplingProfiler::ProfileBuilder* profile_builder) = 0;
diff --git a/base/profiler/native_stack_sampler_mac.cc b/base/profiler/native_stack_sampler_mac.cc index 7d74166e..0cb439126 100644 --- a/base/profiler/native_stack_sampler_mac.cc +++ b/base/profiler/native_stack_sampler_mac.cc
@@ -127,11 +127,9 @@ (((1 << __builtin_popcount(UNWIND_X86_64_RBP_FRAME_OFFSET))) - 1)); } -} // namespace - -// True if the unwind from |leaf_frame_rip| may trigger a crash bug in +// True if the unwind from |leaf_frame_module| may trigger a crash bug in // unw_init_local. If so, the stack walk should be aborted at the leaf frame. -bool MayTriggerUnwInitLocalCrash(uint64_t leaf_frame_rip) { +bool MayTriggerUnwInitLocalCrash(const ModuleCache::Module* leaf_frame_module) { // The issue here is a bug in unw_init_local that, in some unwinds, results in // attempts to access memory at the address immediately following the address // range of the library. When the library is the last of the mapped libraries @@ -150,20 +148,15 @@ // // TODO(lgrey): Add references above to LLVM/Radar bugs on unw_init_local once // filed. - Dl_info info; - if (dladdr(reinterpret_cast<const void*>(leaf_frame_rip), &info) == 0) - return false; uint64_t unused; vm_size_t size = sizeof(unused); - return vm_read_overwrite(current_task(), - reinterpret_cast<vm_address_t>(info.dli_fbase) + - ModuleCache::GetModuleTextSize(info.dli_fbase), - sizeof(unused), - reinterpret_cast<vm_address_t>(&unused), &size) != 0; + return vm_read_overwrite( + current_task(), + leaf_frame_module->GetBaseAddress() + leaf_frame_module->GetSize(), + sizeof(unused), reinterpret_cast<vm_address_t>(&unused), + &size) != 0; } -namespace { - // Check if the cursor contains a valid-looking frame pointer for frame pointer // unwinds. If the stack frame has a frame pointer, stepping the cursor will // involve indexing memory access off of that pointer. In that case, @@ -269,9 +262,8 @@ ~NativeStackSamplerMac() override; // StackSamplingProfiler::NativeStackSampler: - std::vector<Frame> RecordStackFrames( - StackBuffer* stack_buffer, - ProfileBuilder* profile_builder) override; + void RecordStackFrames(StackBuffer* stack_buffer, + ProfileBuilder* profile_builder) override; private: // Walks the stack represented by |unwind_context|, calling back to the @@ -328,13 +320,10 @@ NativeStackSamplerMac::~NativeStackSamplerMac() {} -std::vector<Frame> NativeStackSamplerMac::RecordStackFrames( - StackBuffer* stack_buffer, - ProfileBuilder* profile_builder) { +void NativeStackSamplerMac::RecordStackFrames(StackBuffer* stack_buffer, + ProfileBuilder* profile_builder) { x86_thread_state64_t thread_state; - const std::vector<Frame> empty_frames; - // Copy the stack. uintptr_t new_stack_top = 0; @@ -345,19 +334,19 @@ // default heap acquired by the target thread before it was suspended. ScopedSuspendThread suspend_thread(thread_port_); if (!suspend_thread.was_successful()) - return empty_frames; + return; if (!GetThreadState(thread_port_, &thread_state)) - return empty_frames; + return; auto stack_top = reinterpret_cast<uintptr_t>(thread_stack_base_address_); uintptr_t stack_bottom = thread_state.__rsp; if (stack_bottom >= stack_top) - return empty_frames; + return; uintptr_t stack_size = stack_top - stack_bottom; if (stack_size > stack_buffer->size()) - return empty_frames; + return; profile_builder->RecordMetadata(); @@ -375,18 +364,15 @@ // Walk the stack and record it. - // Reserve enough memory for most stacks, to avoid repeated allocations. - // Approximately 99.9% of recorded stacks are 128 frames or fewer. - std::vector<Frame> frames; - frames.reserve(128); - // Avoid an out-of-bounds read bug in libunwind that can crash us in some // circumstances. If we're subject to that case, just record the first frame // and bail. See MayTriggerUnwInitLocalCrash for details. uintptr_t rip = thread_state.__rip; - if (MayTriggerUnwInitLocalCrash(rip)) { - frames.emplace_back(rip, module_cache_->GetModuleForAddress(rip)); - return frames; + const ModuleCache::Module* leaf_frame_module = + module_cache_->GetModuleForAddress(rip); + if (leaf_frame_module && MayTriggerUnwInitLocalCrash(leaf_frame_module)) { + profile_builder->OnSampleCompleted({Frame(rip, leaf_frame_module)}); + return; } const auto continue_predicate = [this, @@ -405,6 +391,11 @@ return HasValidRbp(unwind_cursor, new_stack_top); }; + // Reserve enough memory for most stacks, to avoid repeated allocations. + // Approximately 99.9% of recorded stacks are 128 frames or fewer. + std::vector<Frame> frames; + frames.reserve(128); + WalkStack( thread_state, [&frames](uintptr_t frame_ip, const ModuleCache::Module* module) { @@ -412,7 +403,7 @@ }, continue_predicate); - return frames; + profile_builder->OnSampleCompleted(frames); } template <typename StackFrameCallback, typename ContinueUnwindPredicate>
diff --git a/base/profiler/native_stack_sampler_win.cc b/base/profiler/native_stack_sampler_win.cc index 4a23310..c0e39f9 100644 --- a/base/profiler/native_stack_sampler_win.cc +++ b/base/profiler/native_stack_sampler_win.cc
@@ -285,11 +285,8 @@ void* stack_copy_buffer, size_t stack_copy_buffer_size, ModuleCache* module_cache, - std::vector<Frame>* stack, ProfileBuilder* profile_builder, NativeStackSamplerTestDelegate* test_delegate) { - DCHECK(stack->empty()); - CONTEXT thread_context = {0}; thread_context.ContextFlags = CONTEXT_FULL; // The stack bounds are saved to uintptr_ts for use outside @@ -344,7 +341,8 @@ RewritePointersToStackMemory(top, bottom, &thread_context, stack_copy_buffer); - *stack = RecordStack(module_cache, &thread_context); + profile_builder->OnSampleCompleted( + RecordStack(module_cache, &thread_context)); } } @@ -360,9 +358,8 @@ ~NativeStackSamplerWin() override; // StackSamplingProfiler::NativeStackSampler: - std::vector<Frame> RecordStackFrames( - StackBuffer* stack_buffer, - ProfileBuilder* profile_builder) override; + void RecordStackFrames(StackBuffer* stack_buffer, + ProfileBuilder* profile_builder) override; private: win::ScopedHandle thread_handle_; @@ -389,20 +386,15 @@ NativeStackSamplerWin::~NativeStackSamplerWin() {} -std::vector<Frame> NativeStackSamplerWin::RecordStackFrames( - StackBuffer* stack_buffer, - ProfileBuilder* profile_builder) { +void NativeStackSamplerWin::RecordStackFrames(StackBuffer* stack_buffer, + ProfileBuilder* profile_builder) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler.debug"), "NativeStackSamplerWin::RecordStackFrames"); DCHECK(stack_buffer); - std::vector<Frame> stack; SuspendThreadAndRecordStack(thread_handle_.Get(), thread_stack_base_address_, stack_buffer->buffer(), stack_buffer->size(), - module_cache_, &stack, profile_builder, - test_delegate_); - - return stack; + module_cache_, profile_builder, test_delegate_); } // NativeStackSampler ---------------------------------------------------------
diff --git a/base/profiler/stack_sampling_profiler.cc b/base/profiler/stack_sampling_profiler.cc index 5a0acd9..944f0bf8 100644 --- a/base/profiler/stack_sampling_profiler.cc +++ b/base/profiler/stack_sampling_profiler.cc
@@ -526,9 +526,8 @@ } // Record a single sample. - collection->profile_builder->OnSampleCompleted( - collection->native_sampler->RecordStackFrames( - stack_buffer_.get(), collection->profile_builder.get())); + collection->native_sampler->RecordStackFrames( + stack_buffer_.get(), collection->profile_builder.get()); // Schedule the next sample recording if there is one. if (++collection->sample_count < collection->params.samples_per_profile) {
diff --git a/base/sampling_heap_profiler/module_cache.h b/base/sampling_heap_profiler/module_cache.h index 45cee33..e559a0d 100644 --- a/base/sampling_heap_profiler/module_cache.h +++ b/base/sampling_heap_profiler/module_cache.h
@@ -72,20 +72,9 @@ std::vector<const Module*> GetModules() const; private: - // TODO(alph): Refactor corresponding functions to use public API instead, - // and drop friends. - // Creates a Module object for the specified memory address. Returns null if // the address does not belong to a module. static std::unique_ptr<Module> CreateModuleForAddress(uintptr_t address); - friend class NativeStackSamplerMac; - -#if defined(OS_MACOSX) - // Returns the size of the _TEXT segment of the module loaded - // at |module_addr|. - static size_t GetModuleTextSize(const void* module_addr); - friend bool MayTriggerUnwInitLocalCrash(uint64_t); -#endif std::map<uintptr_t, std::unique_ptr<Module>> modules_cache_map_; };
diff --git a/base/sampling_heap_profiler/module_cache_mac.cc b/base/sampling_heap_profiler/module_cache_mac.cc index 0a9b7a2..bcfa89a 100644 --- a/base/sampling_heap_profiler/module_cache_mac.cc +++ b/base/sampling_heap_profiler/module_cache_mac.cc
@@ -61,18 +61,25 @@ return std::string(); } +// Returns the size of the _TEXT segment of the module loaded at |module_addr|. +size_t GetModuleTextSize(const void* module_addr) { + const mach_header_64* mach_header = + reinterpret_cast<const mach_header_64*>(module_addr); + DCHECK_EQ(MH_MAGIC_64, mach_header->magic); + unsigned long module_size; + getsegmentdata(mach_header, SEG_TEXT, &module_size); + return module_size; +} + } // namespace class MacModule : public ModuleCache::Module { public: - MacModule(uintptr_t base_address, - const std::string& id, - const FilePath& debug_basename, - size_t size) - : base_address_(base_address), - id_(id), - debug_basename_(debug_basename), - size_(size) {} + MacModule(const Dl_info& dl_info) + : base_address_(reinterpret_cast<uintptr_t>(dl_info.dli_fbase)), + id_(GetUniqueId(dl_info.dli_fbase)), + debug_basename_(FilePath(dl_info.dli_fname).BaseName()), + size_(GetModuleTextSize(dl_info.dli_fbase)) {} MacModule(const MacModule&) = delete; MacModule& operator=(const MacModule&) = delete; @@ -93,22 +100,10 @@ // static std::unique_ptr<ModuleCache::Module> ModuleCache::CreateModuleForAddress( uintptr_t address) { - Dl_info inf; - if (!dladdr(reinterpret_cast<const void*>(address), &inf)) + Dl_info info; + if (!dladdr(reinterpret_cast<const void*>(address), &info)) return nullptr; - auto base_module_address = reinterpret_cast<uintptr_t>(inf.dli_fbase); - return std::make_unique<MacModule>( - base_module_address, GetUniqueId(inf.dli_fbase), - FilePath(inf.dli_fname).BaseName(), GetModuleTextSize(inf.dli_fbase)); -} - -size_t ModuleCache::GetModuleTextSize(const void* module_addr) { - const mach_header_64* mach_header = - reinterpret_cast<const mach_header_64*>(module_addr); - DCHECK_EQ(MH_MAGIC_64, mach_header->magic); - unsigned long module_size; - getsegmentdata(mach_header, SEG_TEXT, &module_size); - return module_size; + return std::make_unique<MacModule>(info); } } // namespace base
diff --git a/base/sampling_heap_profiler/poisson_allocation_sampler.cc b/base/sampling_heap_profiler/poisson_allocation_sampler.cc index 3522e328..f169c7d 100644 --- a/base/sampling_heap_profiler/poisson_allocation_sampler.cc +++ b/base/sampling_heap_profiler/poisson_allocation_sampler.cc
@@ -475,6 +475,10 @@ void PoissonAllocationSampler::DoRecordFree(void* address) { if (UNLIKELY(ScopedMuteThreadSamples::IsMuted())) return; + // There is a rare case on macOS and Android when the very first thread_local + // access in ScopedMuteThreadSamples constructor may allocate and + // thus reenter DoRecordAlloc. However the call chain won't build up further + // as RecordAlloc accesses are guarded with pthread TLS-based ReentryGuard. ScopedMuteThreadSamples no_reentrancy_scope; AutoLock lock(mutex_); for (auto* observer : observers_)
diff --git a/base/sampling_heap_profiler/sampling_heap_profiler.cc b/base/sampling_heap_profiler/sampling_heap_profiler.cc index 3d5e35a..5b08dcc 100644 --- a/base/sampling_heap_profiler/sampling_heap_profiler.cc +++ b/base/sampling_heap_profiler/sampling_heap_profiler.cc
@@ -168,6 +168,10 @@ size_t total, PoissonAllocationSampler::AllocatorType type, const char* context) { + // CaptureStack and allocation context tracking may use TLS. + // Bail out if it has been destroyed. + if (UNLIKELY(base::ThreadLocalStorage::HasBeenDestroyed())) + return; DCHECK(PoissonAllocationSampler::ScopedMuteThreadSamples::IsMuted()); AutoLock lock(mutex_); Sample sample(size, total, ++last_sample_ordinal_); @@ -187,9 +191,6 @@ void SamplingHeapProfiler::CaptureMixedStack(const char* context, Sample* sample) { - // Allocation context is tracked in TLS. Return nothing if TLS was destroyed. - if (UNLIKELY(base::ThreadLocalStorage::HasBeenDestroyed())) - return; auto* tracker = trace_event::AllocationContextTracker::GetInstanceForCurrentThread(); if (!tracker) @@ -229,10 +230,6 @@ if (record_thread_names_) sample->thread_name = CachedThreadName(); - // Task context require access to TLS. - if (UNLIKELY(base::ThreadLocalStorage::HasBeenDestroyed())) - return; - if (!context) { const auto* tracker = trace_event::AllocationContextTracker::GetInstanceForCurrentThread();
diff --git a/base/sequence_checker.h b/base/sequence_checker.h index 48b593b..0fccffe 100644 --- a/base/sequence_checker.h +++ b/base/sequence_checker.h
@@ -54,7 +54,13 @@ DCHECK((name).CalledOnValidSequence()) #define DETACH_FROM_SEQUENCE(name) (name).DetachFromSequence() #else // DCHECK_IS_ON() +#if __OBJC__ && defined(OS_IOS) +// TODO(https://crbug.com/936856): clang currently doesn't support static_assert +// in Objective-C classes. #define SEQUENCE_CHECKER(name) +#else +#define SEQUENCE_CHECKER(name) static_assert(true, "") +#endif #define DCHECK_CALLED_ON_VALID_SEQUENCE(name) EAT_STREAM_PARAMETERS #define DETACH_FROM_SEQUENCE(name) #endif // DCHECK_IS_ON()
diff --git a/base/threading/thread_checker.h b/base/threading/thread_checker.h index 6799e258..0d744683 100644 --- a/base/threading/thread_checker.h +++ b/base/threading/thread_checker.h
@@ -65,7 +65,7 @@ #define DCHECK_CALLED_ON_VALID_THREAD(name) DCHECK((name).CalledOnValidThread()) #define DETACH_FROM_THREAD(name) (name).DetachFromThread() #else // DCHECK_IS_ON() -#define THREAD_CHECKER(name) +#define THREAD_CHECKER(name) static_assert(true, "") #define DCHECK_CALLED_ON_VALID_THREAD(name) EAT_STREAM_PARAMETERS #define DETACH_FROM_THREAD(name) #endif // DCHECK_IS_ON()
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h index a8748af..d1b00361 100644 --- a/base/threading/thread_restrictions.h +++ b/base/threading/thread_restrictions.h
@@ -278,8 +278,13 @@ #define EMPTY_BODY_IF_DCHECK_IS_OFF #else #define INLINE_IF_DCHECK_IS_OFF inline + +// The static_assert() eats follow-on semicolons. `= default` would work +// too, but it makes clang realize that all the Scoped classes are no-ops in +// non-dcheck builds and it starts emitting many -Wunused-variable warnings. #define EMPTY_BODY_IF_DCHECK_IS_OFF \ - {} + {} \ + static_assert(true, "") #endif namespace internal { @@ -598,6 +603,9 @@ DISALLOW_IMPLICIT_CONSTRUCTORS(ThreadRestrictions); }; +#undef INLINE_IF_DCHECK_IS_OFF +#undef EMPTY_BODY_IF_DCHECK_IS_OFF + } // namespace base #endif // BASE_THREADING_THREAD_RESTRICTIONS_H_
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index 7aa7046..c0d0ff1 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn
@@ -1580,7 +1580,12 @@ # TODO(thakis): Enable this for more platforms, https://crbug.com/926235 # Fuchsia: https://crbug.com/935588 has_dchecks = is_debug || dcheck_always_on - if (has_dchecks && !is_fuchsia) { + if (!has_dchecks && is_ios) { + # clang currently doesn't support static_assert()s in Obj-C classes, + # https://crbug.com/936856 + cflags_c = [ "-Wextra-semi" ] + cflags_cc = [ "-Wextra-semi" ] + } else if (!is_fuchsia) { cflags += [ "-Wextra-semi" ] } }
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1 index 2f2aa1f..0183fca 100644 --- a/build/fuchsia/linux.sdk.sha1 +++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@ -1dc5fa95d6cf9874bb56250199cdf49cbe92b501 \ No newline at end of file +9622b5e49a17bc72966697339c9cad3ed56e07c5 \ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1 index acf3cd87..97573a42 100644 --- a/build/fuchsia/mac.sdk.sha1 +++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@ -5fd7f2fc6f9501d959f3b94d71cd43cc9c663996 \ No newline at end of file +f80a3a8d25335a8121173e65b280d018fe7cb615 \ No newline at end of file
diff --git a/cc/input/main_thread_scrolling_reason.cc b/cc/input/main_thread_scrolling_reason.cc index 5d1a6b9..4b5cac23 100644 --- a/cc/input/main_thread_scrolling_reason.cc +++ b/cc/input/main_thread_scrolling_reason.cc
@@ -39,8 +39,6 @@ traced_value.AppendString("Frame overlay"); if (reasons & kHandlingScrollFromMainThread) traced_value.AppendString("Handling scroll from main thread"); - if (reasons & kCustomScrollbarScrolling) - traced_value.AppendString("Custom scrollbar scrolling"); if (reasons & kHasOpacityAndLCDText) traced_value.AppendString("Has opacity and LCD text"); if (reasons & kHasTransformAndLCDText)
diff --git a/cc/input/main_thread_scrolling_reason.h b/cc/input/main_thread_scrolling_reason.h index 6878928..cf7e840 100644 --- a/cc/input/main_thread_scrolling_reason.h +++ b/cc/input/main_thread_scrolling_reason.h
@@ -36,7 +36,6 @@ // animation. Note that a scroll handled by the main thread can result in an // animation running on the main thread or on the compositor thread. kHandlingScrollFromMainThread = 1 << 13, - kCustomScrollbarScrolling = 1 << 15, // Style-related scrolling on main reasons. // These *AndLCDText reasons are due to subpixel text rendering which can @@ -80,8 +79,7 @@ uint32_t reasons_set_by_main_thread = kNotScrollingOnMain | kHasBackgroundAttachmentFixedObjects | kHasNonLayerViewportConstrainedObjects | kThreadedScrollingDisabled | - kScrollbarScrolling | kFrameOverlay | kHandlingScrollFromMainThread | - kCustomScrollbarScrolling; + kScrollbarScrolling | kFrameOverlay | kHandlingScrollFromMainThread; return (reasons & reasons_set_by_main_thread) == reasons; }
diff --git a/cc/input/main_thread_scrolling_reason_unittest.cc b/cc/input/main_thread_scrolling_reason_unittest.cc index c6ab9e4a..3970af2e 100644 --- a/cc/input/main_thread_scrolling_reason_unittest.cc +++ b/cc/input/main_thread_scrolling_reason_unittest.cc
@@ -19,7 +19,6 @@ "Scrollbar scrolling," "Frame overlay," "Handling scroll from main thread," - "Custom scrollbar scrolling," "Has opacity and LCD text," "Has transform and LCD text," "Background is not opaque in rect and LCD text,"
diff --git a/cc/layers/draw_properties.h b/cc/layers/draw_properties.h index f5c7a6cf..1ffc742 100644 --- a/cc/layers/draw_properties.h +++ b/cc/layers/draw_properties.h
@@ -11,6 +11,7 @@ #include "cc/trees/occlusion.h" #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/rrect_f.h" #include "ui/gfx/transform.h" namespace cc { @@ -55,6 +56,10 @@ // In target surface space, the original rect that clipped this layer. This // value is used to avoid unnecessarily changing GL scissor state. gfx::Rect clip_rect; + + // Contains a rounded corner rect to clip this layer when drawing. This rrect + // is in the target space of the layer. + gfx::RRectF rounded_corner_bounds; }; } // namespace cc
diff --git a/cc/layers/layer.h b/cc/layers/layer.h index ace3acc..0bfbffc 100644 --- a/cc/layers/layer.h +++ b/cc/layers/layer.h
@@ -247,6 +247,12 @@ return inputs_.corner_radii; } + // Returns true if any of the corner has a non-zero radius set. + bool HasRoundedCorner() const { + return corner_radii()[0] + corner_radii()[1] + corner_radii()[2] + + corner_radii()[3]; + } + // Set or get the opacity which should be applied to the contents of the layer // and its subtree (together as a single composited entity) when blending them // into their target. Note that this does not speak to the contents of this @@ -890,12 +896,6 @@ // they are marked as needing to be rebuilt. void UpdateScrollOffset(const gfx::ScrollOffset&); - // Returns true if any of the corner has a non-zero radius set. - bool HasRoundedCorner() const { - return corner_radii()[0] + corner_radii()[1] + corner_radii()[2] + - corner_radii()[3]; - } - // Encapsulates all data, callbacks or interfaces received from the embedder. struct Inputs { explicit Inputs(int layer_id);
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc index 2b5c098..8cc972a 100644 --- a/cc/layers/layer_unittest.cc +++ b/cc/layers/layer_unittest.cc
@@ -879,7 +879,6 @@ reasons |= MainThreadScrollingReason::kScrollbarScrolling; reasons_to_clear |= MainThreadScrollingReason::kHandlingScrollFromMainThread; - reasons_to_clear |= MainThreadScrollingReason::kCustomScrollbarScrolling; reasons_after_clearing |= MainThreadScrollingReason::kThreadedScrollingDisabled; @@ -905,7 +904,6 @@ // Check that clearing non-set reasons doesn't set needs commit. reasons_to_clear = 0; - reasons_to_clear |= MainThreadScrollingReason::kCustomScrollbarScrolling; reasons_to_clear |= MainThreadScrollingReason::kFrameOverlay; EXPECT_SET_NEEDS_COMMIT( 0, test_layer->ClearMainThreadScrollingReasons(reasons_to_clear));
diff --git a/cc/layers/render_surface_impl.h b/cc/layers/render_surface_impl.h index 58ae7f8..b63fc7f 100644 --- a/cc/layers/render_surface_impl.h +++ b/cc/layers/render_surface_impl.h
@@ -51,6 +51,13 @@ } float draw_opacity() const { return draw_properties_.draw_opacity; } + void SetRoundedCornerRRect(const gfx::RRectF& rounded_corner_bounds) { + draw_properties_.rounded_corner_bounds = rounded_corner_bounds; + } + const gfx::RRectF& rounded_corner_bounds() const { + return draw_properties_.rounded_corner_bounds; + } + SkBlendMode BlendMode() const; bool UsesDefaultBlendMode() const; @@ -218,6 +225,11 @@ // True if the surface needs to be clipped by clip_rect. bool is_clipped : 1; + + // Contains a rounded corner rect to clip this render surface by when + // drawing. This rrect is in the target space of the render surface. The + // root render surface will never have this set. + gfx::RRectF rounded_corner_bounds; }; DrawProperties draw_properties_;
diff --git a/cc/tiles/image_controller.cc b/cc/tiles/image_controller.cc index 844f8c42..919be166e5 100644 --- a/cc/tiles/image_controller.cc +++ b/cc/tiles/image_controller.cc
@@ -163,8 +163,8 @@ } scoped_refptr<TileTask> result = paint_worklet_image_cache_.GetTaskForPaintWorkletImage(*it); - DCHECK(result); - tasks->push_back(std::move(result)); + if (result) + tasks->push_back(std::move(result)); // Remove it so that there is no need to check whether an image is // PaintWorklet generated or not in TileManager's // work_to_schedule->extra_prepaint_images.insert.
diff --git a/cc/tiles/paint_worklet_image_cache.cc b/cc/tiles/paint_worklet_image_cache.cc index 1b9052f..fc37da4a 100644 --- a/cc/tiles/paint_worklet_image_cache.cc +++ b/cc/tiles/paint_worklet_image_cache.cc
@@ -40,27 +40,59 @@ void PaintWorkletImageCache::SetPaintWorkletLayerPainter( std::unique_ptr<PaintWorkletLayerPainter> painter) { + DCHECK(!painter_); painter_ = std::move(painter); } scoped_refptr<TileTask> PaintWorkletImageCache::GetTaskForPaintWorkletImage( const DrawImage& image) { + // As described in crbug.com/939192, the |painter_| could be null, and we + // should not create any raster task. + if (!painter_) + return nullptr; + DCHECK(image.paint_image().IsPaintWorklet()); return base::MakeRefCounted<PaintWorkletTaskImpl>(this, image.paint_image()); } -// TODO(xidachen): dispatch the work to a worklet thread, invoke JS callback. -// Do check the cache first. If there is already a cache entry for this input, -// then there is no need to call the Paint() function. +// TODO(xidachen): we might need to consider the animated property value and the +// PaintWorkletInput to decide whether we need to call Paint() function or not. void PaintWorkletImageCache::PaintImageInTask(const PaintImage& paint_image) { + // TODO(crbug.com/939009): When creating a TileTask for a given PaintImage at + // GetTaskForPaintWorkletImage, we should not create a new TileTask if there + // is already a TileTask for this PaintImage. + { + base::AutoLock hold(records_lock_); + if (records_.find(paint_image.paint_worklet_input()) != records_.end()) + return; + } + // Because the compositor could be waiting on the lock in NotifyPrepareTiles, + // we unlock here such that the compositor won't be blocked on potentially + // slow Paint function. // TODO(xidachen): ensure that the canvas operations in the PaintRecord // matches the PaintGeneratedImage::Draw. sk_sp<PaintRecord> record = painter_->Paint(); - records_[paint_image.paint_worklet_input()] = - PaintWorkletImageCacheValue(std::move(record), 0); + { + base::AutoLock hold(records_lock_); + // It is possible for two or more threads to both pass through the first + // lock and arrive here. To avoid ref-count issues caused by potential + // racing among threads, we use insert such that if an entry already exists + // for a particular key, the value won't be overridden. + records_.insert( + std::make_pair(paint_image.paint_worklet_input(), + PaintWorkletImageCacheValue(std::move(record), 0))); + } } std::pair<PaintRecord*, base::OnceCallback<void()>> PaintWorkletImageCache::GetPaintRecordAndRef(PaintWorkletInput* input) { + base::AutoLock hold(records_lock_); + // If the |painter_| is null, then GetTaskForPaintWorkletImage will return a + // null TileTask, and hence there will be no cache entry for this input. + if (!painter_) { + return std::make_pair(sk_make_sp<PaintOpBuffer>().get(), + base::OnceCallback<void()>()); + } + DCHECK(records_.find(input) != records_.end()); records_[input].used_ref_count++; records_[input].num_of_frames_not_accessed = 0u; // The PaintWorkletImageCache object lives as long as the LayerTreeHostImpl, @@ -78,6 +110,7 @@ } void PaintWorkletImageCache::DecrementCacheRefCount(PaintWorkletInput* input) { + base::AutoLock hold(records_lock_); auto it = records_.find(input); DCHECK(it != records_.end()); @@ -87,12 +120,14 @@ } void PaintWorkletImageCache::NotifyDidPrepareTiles() { + base::AutoLock hold(records_lock_); base::EraseIf( records_, [this]( const std::pair<PaintWorkletInput*, PaintWorkletImageCacheValue>& t) { return t.second.num_of_frames_not_accessed >= - num_of_frames_to_purge_cache_entry_; + num_of_frames_to_purge_cache_entry_ && + t.second.used_ref_count == 0; }); for (auto& pair : records_) pair.second.num_of_frames_not_accessed++;
diff --git a/cc/tiles/paint_worklet_image_cache.h b/cc/tiles/paint_worklet_image_cache.h index 749233a..e5ef233a 100644 --- a/cc/tiles/paint_worklet_image_cache.h +++ b/cc/tiles/paint_worklet_image_cache.h
@@ -8,6 +8,7 @@ #include <utility> #include "base/containers/flat_map.h" +#include "base/synchronization/lock.h" #include "cc/cc_export.h" #include "cc/paint/draw_image.h" #include "cc/paint/paint_record.h" @@ -62,12 +63,17 @@ private: void DecrementCacheRefCount(PaintWorkletInput* input); + // This is a map of paint worklet inputs to a pair of paint record and a // reference count. The paint record is the representation of the worklet // output based on the input, and the reference count is the number of times // that it is used for tile rasterization. - // TODO(xidachen): use a struct instead of std::pair. base::flat_map<PaintWorkletInput*, PaintWorkletImageCacheValue> records_; + + // The |records_| can be accessed from compositor and raster worker threads at + // the same time. To prevent race, we need to lock on it. + base::Lock records_lock_; + // The PaintWorkletImageCache is owned by ImageController, which has the same // life time as the LayerTreeHostImpl, that guarantees that the painter will // live as long as the LayerTreeHostImpl.
diff --git a/cc/tiles/paint_worklet_image_cache_unittest.cc b/cc/tiles/paint_worklet_image_cache_unittest.cc index e046b03..4ee6b0d 100644 --- a/cc/tiles/paint_worklet_image_cache_unittest.cc +++ b/cc/tiles/paint_worklet_image_cache_unittest.cc
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <memory> #include <utility> #include "cc/tiles/paint_worklet_image_cache.h" @@ -47,9 +48,6 @@ paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()), kNone_SkFilterQuality, CreateMatrix(SkSize::Make(1.f, 1.f), true), PaintImage::kDefaultFrameIndex); - std::unique_ptr<TestPaintWorkletLayerPainter> painter = - std::make_unique<TestPaintWorkletLayerPainter>(); - cache->SetPaintWorkletLayerPainter(std::move(painter)); return cache->GetTaskForPaintWorkletImage(draw_image); } @@ -64,6 +62,9 @@ TEST(PaintWorkletImageCacheTest, GetTaskForImage) { TestPaintWorkletImageCache cache; + std::unique_ptr<TestPaintWorkletLayerPainter> painter = + std::make_unique<TestPaintWorkletLayerPainter>(); + cache.SetPaintWorkletLayerPainter(std::move(painter)); PaintImage paint_image = CreatePaintImage(100, 100); scoped_refptr<TileTask> task = GetTaskForPaintWorkletImage(paint_image, &cache); @@ -116,8 +117,39 @@ EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 0u); } +TEST(PaintWorkletImageCacheTest, EntryWithNonZeroRefCountNotPurged) { + TestPaintWorkletImageCache cache; + std::unique_ptr<TestPaintWorkletLayerPainter> painter = + std::make_unique<TestPaintWorkletLayerPainter>(); + cache.SetPaintWorkletLayerPainter(std::move(painter)); + PaintImage paint_image = CreatePaintImage(100, 100); + scoped_refptr<TileTask> task = + GetTaskForPaintWorkletImage(paint_image, &cache); + EXPECT_TRUE(task); + + TestTileTaskRunner::ProcessTask(task.get()); + + PaintWorkletImageProvider provider(&cache); + ImageProvider::ScopedResult result = + provider.GetPaintRecordResult(paint_image.paint_worklet_input()); + base::flat_map<PaintWorkletInput*, + PaintWorkletImageCache::PaintWorkletImageCacheValue> + records = cache.GetRecordsForTest(); + EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u); + + cache.NotifyDidPrepareTiles(); + cache.NotifyDidPrepareTiles(); + cache.NotifyDidPrepareTiles(); + + records = cache.GetRecordsForTest(); + EXPECT_EQ(records.size(), 1u); +} + TEST(PaintWorkletImageCacheTest, MultipleRecordsInCache) { TestPaintWorkletImageCache cache; + std::unique_ptr<TestPaintWorkletLayerPainter> painter = + std::make_unique<TestPaintWorkletLayerPainter>(); + cache.SetPaintWorkletLayerPainter(std::move(painter)); PaintImage paint_image1 = CreatePaintImage(100, 100); scoped_refptr<TileTask> task1 = GetTaskForPaintWorkletImage(paint_image1, &cache); @@ -186,5 +218,44 @@ 2u); } +// This test ensures that if an entry already exist, then the PaintImageInTask +// will not replace it with a new entry and reset its ref count. +TEST(PaintWorkletImageCacheTest, CacheEntryLookup) { + TestPaintWorkletImageCache cache; + std::unique_ptr<TestPaintWorkletLayerPainter> painter = + std::make_unique<TestPaintWorkletLayerPainter>(); + cache.SetPaintWorkletLayerPainter(std::move(painter)); + PaintImage paint_image = CreatePaintImage(100, 100); + scoped_refptr<TileTask> task = + GetTaskForPaintWorkletImage(paint_image, &cache); + EXPECT_TRUE(task); + PaintWorkletImageProvider provider(&cache); + + TestTileTaskRunner::ProcessTask(task.get()); + + { + ImageProvider::ScopedResult result = + provider.GetPaintRecordResult(paint_image.paint_worklet_input()); + EXPECT_TRUE(result.paint_record()); + TestPaintRecord(result.paint_record()); + + base::flat_map<PaintWorkletInput*, + PaintWorkletImageCache::PaintWorkletImageCacheValue> + records = cache.GetRecordsForTest(); + // Test the ref count. + EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u); + + // Create a new task with the same PaintWorkletInput as the previous task. + // Then ProcessTask will invoke PaintWorkletImageCache::PaintImageInTask, + // and it should early exit, without replacing the existing PaintRecord and + // resetting the ref count. + scoped_refptr<TileTask> task_with_the_same_input = + GetTaskForPaintWorkletImage(paint_image, &cache); + EXPECT_TRUE(task); + TestTileTaskRunner::ProcessTask(task_with_the_same_input.get()); + EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u); + } +} + } // namespace } // namespace cc
diff --git a/cc/trees/draw_property_utils.cc b/cc/trees/draw_property_utils.cc index d719d9c5..17b0fc1 100644 --- a/cc/trees/draw_property_utils.cc +++ b/cc/trees/draw_property_utils.cc
@@ -664,6 +664,57 @@ layer->clip_tree_index(), target_node->id); } +static gfx::RRectF GetRoundedCornerRRect(const PropertyTrees* property_trees, + int effect_tree_index, + bool for_render_surface) { + const EffectTree* effect_tree = &property_trees->effect_tree; + const EffectNode* effect_node = effect_tree->Node(effect_tree_index); + const int target_id = effect_node->target_id; + + // Return empty rrect if this node has a render surface but the function call + // was made for a non render surface. + if (effect_node->has_render_surface && !for_render_surface) + return gfx::RRectF(); + + // Traverse the parent chain up to the render target to find a node which has + // a rounded corner bounds set. + const EffectNode* node = effect_node; + bool found_rounded_corner = false; + while (node) { + if (!node->rounded_corner_bounds.IsEmpty()) { + found_rounded_corner = true; + break; + } + + // Simply break if we reached a node that has a render surface or is the + // render target. + if (node->has_render_surface || node->id == target_id) + break; + + node = effect_tree->parent(node); + } + + // While traversing up the parent chain we did not find any node with a + // rounded corner. + if (!node || !found_rounded_corner) + return gfx::RRectF(); + + gfx::Transform to_target; + if (!property_trees->GetToTarget(node->transform_id, target_id, &to_target)) + return gfx::RRectF(); + + DCHECK(to_target.Preserves2dAxisAlignment()); + + const gfx::Vector2dF& translate = to_target.To2dTranslation(); + const gfx::Vector2dF& scale = to_target.Scale2d(); + + gfx::RRectF bounds = node->rounded_corner_bounds; + bounds.Scale(scale.x(), scale.y()); + bounds.Offset(translate); + + return bounds; +} + static void UpdateRenderTarget(EffectTree* effect_tree) { for (int i = EffectTree::kContentsRootNodeId; i < static_cast<int>(effect_tree->size()); ++i) { @@ -897,6 +948,9 @@ layer, property_trees->transform_tree, property_trees->effect_tree); layer->draw_properties().screen_space_transform_is_animating = transform_node->to_screen_is_potentially_animated; + layer->draw_properties().rounded_corner_bounds = + GetRoundedCornerRRect(property_trees, layer->effect_tree_index(), + /*from_render_surface*/ false); } // Compute effects and determine if render surfaces have contributing layers @@ -967,6 +1021,10 @@ SetSurfaceIsClipped(property_trees->clip_tree, render_surface); SetSurfaceDrawOpacity(property_trees->effect_tree, render_surface); SetSurfaceDrawTransform(property_trees, render_surface); + + render_surface->SetRoundedCornerRRect( + GetRoundedCornerRRect(property_trees, render_surface->EffectTreeIndex(), + /*for_render_surface*/ true)); render_surface->SetScreenSpaceTransform( property_trees->ToScreenSpaceTransformWithoutSurfaceContentsScale( render_surface->TransformTreeIndex(),
diff --git a/cc/trees/effect_node.h b/cc/trees/effect_node.h index b184461b..8ac75b1 100644 --- a/cc/trees/effect_node.h +++ b/cc/trees/effect_node.h
@@ -45,6 +45,10 @@ float backdrop_filter_quality; gfx::PointF filters_origin; + // Bounds of rounded corner rrect in the space of the transform node + // associated with this effect node. + gfx::RRectF rounded_corner_bounds; + SkBlendMode blend_mode; gfx::Vector2dF surface_contents_scale;
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc index 3718d87e..e13870d 100644 --- a/cc/trees/layer_tree_host_common_unittest.cc +++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -51,6 +51,8 @@ #include "components/viz/common/frame_sinks/copy_output_result.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/geometry/quad_f.h" +#include "ui/gfx/geometry/rect_conversions.h" +#include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/geometry/vector2d_conversions.h" #include "ui/gfx/transform.h" @@ -113,6 +115,10 @@ gfx::Size(root_layer->bounds().width() * device_scale_factor, root_layer->bounds().height() * device_scale_factor); + root_layer->layer_tree_host()->SetViewportSizeAndScale( + device_viewport_size, device_scale_factor, + viz::LocalSurfaceIdAllocation()); + // We are probably not testing what is intended if the root_layer bounds are // empty. DCHECK(!root_layer->bounds().IsEmpty()); @@ -10523,5 +10529,254 @@ EXPECT_EQ(gfx::Rect(root_layer_size), root->drawable_content_rect()); } +TEST_F(LayerTreeHostCommonTest, RoundedCornerBounds) { + // Layer Tree: + // +root + // +--render surface + // +----rounded corner layer 1 [should trigger render surface] + // +----layer 1 + // +--rounded corner layer 2 [should trigger render surface] + // +----layer 2 + // +------rounded corner layer 3 [should trigger render surface] + // +--------rounded corner layer 4 [should trigger render surface] + + constexpr int kRoundedCorner1Radius = 2; + constexpr int kRoundedCorner2Radius = 5; + constexpr int kRoundedCorner3Radius = 1; + constexpr int kRoundedCorner4Radius = 1; + + constexpr gfx::RectF kRoundedCornerLayer1Bound(15.f, 15.f, 20.f, 20.f); + constexpr gfx::RectF kRoundedCornerLayer2Bound(40.f, 40.f, 60.f, 60.f); + constexpr gfx::RectF kRoundedCornerLayer3Bound(0.f, 15.f, 5.f, 5.f); + constexpr gfx::RectF kRoundedCornerLayer4Bound(1.f, 1.f, 3.f, 3.f); + + constexpr float kDeviceScale = 1.6f; + + scoped_refptr<Layer> root = Layer::Create(); + scoped_refptr<Layer> render_surface = Layer::Create(); + scoped_refptr<Layer> rounded_corner_layer_1 = Layer::Create(); + scoped_refptr<Layer> layer_1 = Layer::Create(); + scoped_refptr<Layer> rounded_corner_layer_2 = Layer::Create(); + scoped_refptr<Layer> layer_2 = Layer::Create(); + scoped_refptr<Layer> rounded_corner_layer_3 = Layer::Create(); + scoped_refptr<Layer> rounded_corner_layer_4 = Layer::Create(); + + // Set up layer tree + root->AddChild(render_surface); + root->AddChild(rounded_corner_layer_2); + + render_surface->AddChild(rounded_corner_layer_1); + render_surface->AddChild(layer_1); + + rounded_corner_layer_2->AddChild(layer_2); + + layer_2->AddChild(rounded_corner_layer_3); + + rounded_corner_layer_3->AddChild(rounded_corner_layer_4); + + // Set the root layer on host. + host()->SetRootLayer(root); + + // Set layer positions. + render_surface->SetPosition(gfx::PointF(0, 0)); + rounded_corner_layer_1->SetPosition(kRoundedCornerLayer1Bound.origin()); + layer_1->SetPosition(gfx::PointF(10.f, 10.f)); + rounded_corner_layer_2->SetPosition(kRoundedCornerLayer2Bound.origin()); + layer_2->SetPosition(gfx::PointF(30.f, 30.f)); + rounded_corner_layer_3->SetPosition(kRoundedCornerLayer3Bound.origin()); + rounded_corner_layer_4->SetPosition(kRoundedCornerLayer4Bound.origin()); + + // Set up layer bounds. + root->SetBounds(gfx::Size(100, 100)); + render_surface->SetBounds(gfx::Size(50, 50)); + rounded_corner_layer_1->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer1Bound.size())); + layer_1->SetBounds(gfx::Size(10, 10)); + rounded_corner_layer_2->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer2Bound.size())); + layer_2->SetBounds(gfx::Size(25, 25)); + rounded_corner_layer_3->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer3Bound.size())); + rounded_corner_layer_4->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer4Bound.size())); + + // Add Layer transforms. + gfx::Transform layer_2_transform; + constexpr gfx::Vector2dF kLayer2Translation(10.f, 10.f); + layer_2_transform.Translate(kLayer2Translation); + layer_2->SetTransform(layer_2_transform); + + gfx::Transform rounded_corner_layer_3_transform; + constexpr float kRoundedCorner3Scale = 2.f; + rounded_corner_layer_3_transform.Scale(kRoundedCorner3Scale, + kRoundedCorner3Scale); + rounded_corner_layer_3->SetTransform(rounded_corner_layer_3_transform); + + // Set the layer properties + render_surface->SetForceRenderSurfaceForTesting(true); + + root->SetIsDrawable(true); + render_surface->SetIsDrawable(true); + rounded_corner_layer_1->SetIsDrawable(true); + layer_1->SetIsDrawable(true); + rounded_corner_layer_2->SetIsDrawable(true); + layer_2->SetIsDrawable(true); + rounded_corner_layer_3->SetIsDrawable(true); + rounded_corner_layer_4->SetIsDrawable(true); + + // Set Rounded corners + rounded_corner_layer_1->SetRoundedCorner( + {kRoundedCorner1Radius, kRoundedCorner1Radius, kRoundedCorner1Radius, + kRoundedCorner1Radius}); + rounded_corner_layer_2->SetRoundedCorner( + {kRoundedCorner2Radius, kRoundedCorner2Radius, kRoundedCorner2Radius, + kRoundedCorner2Radius}); + rounded_corner_layer_3->SetRoundedCorner( + {kRoundedCorner3Radius, kRoundedCorner3Radius, kRoundedCorner3Radius, + kRoundedCorner3Radius}); + rounded_corner_layer_4->SetRoundedCorner( + {kRoundedCorner4Radius, kRoundedCorner4Radius, kRoundedCorner4Radius, + kRoundedCorner4Radius}); + + ExecuteCalculateDrawProperties(root.get(), kDeviceScale); + + const EffectTree& effect_tree = + root->layer_tree_host()->property_trees()->effect_tree; + + // Each effect node should have a render surface as rounded corner triggers + // a render surface. + const EffectNode* effect_node = + effect_tree.Node(rounded_corner_layer_1->effect_tree_index()); + gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds; + EXPECT_TRUE(effect_node->has_render_surface); + EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(), + kRoundedCorner1Radius); + EXPECT_EQ(rounded_corner_bounds_1.rect(), + gfx::RectF(kRoundedCornerLayer1Bound.size())); + + effect_node = effect_tree.Node(rounded_corner_layer_2->effect_tree_index()); + gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds; + EXPECT_TRUE(effect_node->has_render_surface); + EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(), + kRoundedCorner2Radius); + EXPECT_EQ(rounded_corner_bounds_2.rect(), + gfx::RectF(kRoundedCornerLayer2Bound.size())); + + effect_node = effect_tree.Node(rounded_corner_layer_3->effect_tree_index()); + gfx::RRectF rounded_corner_bounds_3 = effect_node->rounded_corner_bounds; + EXPECT_TRUE(effect_node->has_render_surface); + EXPECT_FLOAT_EQ(rounded_corner_bounds_3.GetSimpleRadius(), + kRoundedCorner3Radius); + EXPECT_EQ(rounded_corner_bounds_3.rect(), + gfx::RectF(kRoundedCornerLayer3Bound.size())); + + effect_node = effect_tree.Node(rounded_corner_layer_4->effect_tree_index()); + gfx::RRectF rounded_corner_bounds_4 = effect_node->rounded_corner_bounds; + EXPECT_TRUE(effect_node->has_render_surface); + EXPECT_FLOAT_EQ(rounded_corner_bounds_4.GetSimpleRadius(), + kRoundedCorner4Radius); + EXPECT_EQ(rounded_corner_bounds_4.rect(), + gfx::RectF(kRoundedCornerLayer4Bound.size())); + + host()->host_impl()->CreatePendingTree(); + host()->CommitAndCreatePendingTree(); + // TODO(https://crbug.com/939968) This call should be handled by + // FakeLayerTreeHost instead of manually pushing the properties from the + // layer tree host to the pending tree. + root->layer_tree_host()->PushLayerTreePropertiesTo(host()->pending_tree()); + host()->host_impl()->ActivateSyncTree(); + LayerTreeImpl* layer_tree_impl = host()->host_impl()->active_tree(); + + // Get the layer impl for each Layer. + LayerImpl* root_impl = layer_tree_impl->LayerById(root->id()); + LayerImpl* rounded_corner_layer_1_impl = + layer_tree_impl->LayerById(rounded_corner_layer_1->id()); + LayerImpl* rounded_corner_layer_2_impl = + layer_tree_impl->LayerById(rounded_corner_layer_2->id()); + LayerImpl* rounded_corner_layer_3_impl = + layer_tree_impl->LayerById(rounded_corner_layer_3->id()); + LayerImpl* rounded_corner_layer_4_impl = + layer_tree_impl->LayerById(rounded_corner_layer_4->id()); + + // Set the root layer on host. + ExecuteCalculateDrawProperties(root_impl, kDeviceScale); + + // Rounded corner layer 1 + // The render target for this layer is |render_surface|, hence its target + // bounds are relative to |render_surface|. + // The offset from the origin of the render target is [15, 15] and the device + // scale factor is 1.6 thus giving the target space origin of [24, 24]. The + // corner radius is also scaled by a factor of 1.6. + const gfx::RRectF actual_self_rrect_1 = + rounded_corner_layer_1_impl->draw_properties().rounded_corner_bounds; + EXPECT_TRUE(actual_self_rrect_1.IsEmpty()); + + gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound; + bounds_in_target_space.Scale(kDeviceScale); + const gfx::RRectF actual_render_target_rrect_1 = + rounded_corner_layer_1_impl->render_target()->rounded_corner_bounds(); + EXPECT_EQ(actual_render_target_rrect_1.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_render_target_rrect_1.GetSimpleRadius(), + kRoundedCorner1Radius * kDeviceScale); + + // Rounded corner layer 2 + // The render target for this layer is |root|. + // The offset from the origin of the render target is [40, 40] and the device + // scale factor is 1.6 thus giving the target space origin of [64, 64]. The + // corner radius is also scaled by a factor of 1.6. + const gfx::RRectF actual_self_rrect_2 = + rounded_corner_layer_2_impl->draw_properties().rounded_corner_bounds; + EXPECT_TRUE(actual_self_rrect_2.IsEmpty()); + + bounds_in_target_space = kRoundedCornerLayer2Bound; + bounds_in_target_space.Scale(kDeviceScale); + const gfx::RRectF actual_render_target_rrect_2 = + rounded_corner_layer_2_impl->render_target()->rounded_corner_bounds(); + EXPECT_EQ(actual_render_target_rrect_2.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_render_target_rrect_2.GetSimpleRadius(), + kRoundedCorner2Radius * kDeviceScale); + + // Rounded corner layer 3 + // The render target for this layer is |rounded_corner_2|. + // The net offset from the origin of the render target is [40, 55] and the + // device scale factor is 1.6 thus giving the target space origin of [64, 88]. + // The corner radius is also scaled by a factor of 1.6 * transform scale. + const gfx::RRectF actual_self_rrect_3 = + rounded_corner_layer_3_impl->draw_properties().rounded_corner_bounds; + EXPECT_TRUE(actual_self_rrect_3.IsEmpty()); + + bounds_in_target_space = kRoundedCornerLayer3Bound; + bounds_in_target_space += + layer_2->position().OffsetFromOrigin() + kLayer2Translation; + bounds_in_target_space.Scale(kDeviceScale); + gfx::SizeF transformed_size = bounds_in_target_space.size(); + transformed_size.Scale(kRoundedCorner3Scale); + bounds_in_target_space.set_size(transformed_size); + + const gfx::RRectF actual_render_target_rrect_3 = + rounded_corner_layer_3_impl->render_target()->rounded_corner_bounds(); + EXPECT_EQ(actual_render_target_rrect_3.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_render_target_rrect_3.GetSimpleRadius(), + kRoundedCorner3Radius * kDeviceScale * kRoundedCorner3Scale); + + // Rounded corner layer 4 + // The render target for this layer is |rounded_corner_3|. + // The net offset from the origin of the render target is [1, 1] and the + // net scale is 1.6 * transform scale = 3.2 thus giving the target space o + // rigin of [3.2, 3.2]. + // The corner radius is also scaled by a factor of 3.2. + const gfx::RRectF actual_self_rrect_4 = + rounded_corner_layer_4_impl->draw_properties().rounded_corner_bounds; + EXPECT_TRUE(actual_self_rrect_4.IsEmpty()); + + bounds_in_target_space = kRoundedCornerLayer4Bound; + bounds_in_target_space.Scale(kDeviceScale * kRoundedCorner3Scale); + const gfx::RRectF actual_render_target_rrect_4 = + rounded_corner_layer_4_impl->render_target()->rounded_corner_bounds(); + EXPECT_EQ(actual_render_target_rrect_4.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_render_target_rrect_4.GetSimpleRadius(), + kRoundedCorner4Radius * kDeviceScale * kRoundedCorner3Scale); +} + } // namespace } // namespace cc
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index 51e5f965c..85a9802 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc
@@ -3755,7 +3755,12 @@ } // Initial scroll hit testing can be unreliable in the presence of squashed -// layers. In this case, we fall back to main thread scrolling. +// layers. In this case, we fall back to main thread scrolling. This function +// compares |layer_impl| returned from a regular hit test to the layer +// returned from a hit test performed only on scrollers and scrollbars. If the +// closest scrolling ancestor of |layer_impl| is not the other layer, then the +// layer_impl must be a squasing layer overtop of some other scroller and we +// must rely on the main thread. bool LayerTreeHostImpl::IsInitialScrollHitTestReliable( LayerImpl* layer_impl, LayerImpl* first_scrolling_layer_or_scrollbar) { @@ -3783,9 +3788,14 @@ first_scrolling_layer_or_scrollbar->scroll_tree_index(); } - // If |first_scrolling_layer_or_scrollbar| is not scrollable, it must - // be a drawn scrollbar. It may hit the squashing layer at the same time. - // These hit tests require falling back to main-thread scrolling. + // If |first_scrolling_layer_or_scrollbar| is not scrollable, it must be a + // scrollbar. It may hit the squashing layer at the same time. These hit + // tests require falling back to main-thread scrolling. + // TODO(bokan): This causes us to fallback to main anytime we hit a + // scrollbar. If |layer_impl == first_scrolling_layer_or_scrollbar| then we + // truly hit a scrollbar and the hit test is reliable. Only if they're + // different we might have a squashing layer above the scrollbar. + // https://crbug.com/939195. DCHECK(first_scrolling_layer_or_scrollbar->is_scrollbar()); return false; }
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc index 3b0833daf..21243003 100644 --- a/cc/trees/property_tree_builder.cc +++ b/cc/trees/property_tree_builder.cc
@@ -102,7 +102,7 @@ bool created_transform_node, DataForRecursion<LayerType>* data_for_children) const; - bool AddEffectNodeIfNeeded( + EffectNode* AddEffectNodeIfNeeded( const DataForRecursion<LayerType>& data_from_ancestor, LayerType* layer, DataForRecursion<LayerType>* data_for_children) const; @@ -186,6 +186,25 @@ return layer->test_properties()->filters; } +static bool HasRoundedCorner(Layer* layer) { + return layer->HasRoundedCorner(); +} + +static bool HasRoundedCorner(LayerImpl* layer) { + return false; +} + +static gfx::RRectF RoundedCornerBounds(Layer* layer) { + const std::array<uint32_t, 4> radii = layer->corner_radii(); + return gfx::RRectF(gfx::RectF(gfx::Rect(layer->bounds())), radii[0], radii[0], + radii[1], radii[1], radii[2], radii[2], radii[3], + radii[3]); +} + +static gfx::RRectF RoundedCornerBounds(LayerImpl* layer) { + return gfx::RRectF(); +} + static PictureLayer* MaskLayer(Layer* layer) { return layer->mask_layer(); } @@ -281,7 +300,8 @@ template <typename LayerType> static bool LayerClipsSubtree(LayerType* layer) { - return layer->masks_to_bounds() || MaskLayer(layer); + return layer->masks_to_bounds() || MaskLayer(layer) || + HasRoundedCorner(layer); } template <typename LayerType> @@ -847,6 +867,9 @@ return true; } + if (HasRoundedCorner(layer)) + return true; + // If the layer has blending. // TODO(rosca): this is temporary, until blending is implemented for other // types of quads than viz::RenderPassDrawQuad. Layers having descendants that @@ -962,7 +985,7 @@ } template <typename LayerType> -bool PropertyTreeBuilderContext<LayerType>::AddEffectNodeIfNeeded( +EffectNode* PropertyTreeBuilderContext<LayerType>::AddEffectNodeIfNeeded( const DataForRecursion<LayerType>& data_from_ancestor, LayerType* layer, DataForRecursion<LayerType>* data_for_children) const { @@ -1001,7 +1024,7 @@ if (!requires_node) { layer->SetEffectTreeIndex(parent_id); data_for_children->effect_tree_parent = parent_id; - return false; + return nullptr; } int node_id = effect_tree_.Insert(EffectNode(), parent_id); @@ -1043,6 +1066,13 @@ effect_tree_.AddMaskLayerId(node->mask_layer_id); } + if (HasRoundedCorner(layer)) { + // This is currently in the local space of the layer and hence in an invalid + // space. Once we have the associated transform node for this effect node, + // we will update this to the transform node's coordinate space. + node->rounded_corner_bounds = RoundedCornerBounds(layer); + } + if (!is_root) { // The effect node's transform id is used only when we create a render // surface. So, we can leave the default value when we don't create a render @@ -1091,7 +1121,7 @@ gfx::Transform(); data_for_children->animation_axis_aligned_since_render_target = true; } - return should_create_render_surface; + return node; } static inline bool UserScrollableHorizontal(Layer* layer) { @@ -1266,11 +1296,12 @@ DataForRecursion<LayerType> data_for_children(data_from_parent); - bool created_render_surface = + EffectNode* effect_node = AddEffectNodeIfNeeded(data_from_parent, layer, &data_for_children); bool created_transform_node = AddTransformNodeIfNeeded( - data_from_parent, layer, created_render_surface, &data_for_children); + data_from_parent, layer, effect_node && effect_node->has_render_surface, + &data_for_children); SetHasTransformNode(layer, created_transform_node); AddClipNodeIfNeeded(data_from_parent, layer, created_transform_node, &data_for_children); @@ -1280,6 +1311,22 @@ SetBackfaceVisibilityTransform(layer, created_transform_node); SetSafeOpaqueBackgroundColor(data_from_parent, layer, &data_for_children); + // Update |effect_node| based on changes to the transform tree. + if (effect_node) { + if (!effect_node->has_render_surface) { + effect_node->transform_id = + created_transform_node ? data_for_children.transform_tree_parent + : GetTransformParent(data_from_parent, layer); + } + + // The rounded corner bounds needs to be in the space of the transform node + // associated with this effect node. + if (!effect_node->rounded_corner_bounds.IsEmpty()) { + effect_node->rounded_corner_bounds.Offset( + layer->offset_to_transform_parent()); + } + } + bool not_axis_aligned_since_last_clip = data_from_parent.not_axis_aligned_since_last_clip ? true
diff --git a/chrome/VERSION b/chrome/VERSION index da3bc62..81c9992 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=75 MINOR=0 -BUILD=3730 +BUILD=3731 PATCH=0
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml index 657250f8..2ea2fd9 100644 --- a/chrome/android/java/AndroidManifest.xml +++ b/chrome/android/java/AndroidManifest.xml
@@ -473,7 +473,12 @@ <!-- ChromeTabbedActivity related --> <activity android:name="org.chromium.chrome.browser.ChromeTabbedActivity" android:theme="@style/Theme.Chromium.TabbedMode" + {% set notouch_build = notouch_build|default(0) %} + {% if notouch_build == "true" %} + android:exported="false" + {% else %} android:exported="true" + {% endif %} android:launchMode="singleTask" {# We can only use blocks once in Jinja, for future substitutions we use self.supports_video_persistence(). #} @@ -505,7 +510,9 @@ </activity> <!-- TODO(crbug.com/780674): retarget .Main back to CTA for non-modern APK --> <activity-alias android:name="com.google.android.apps.chrome.Main" - {% if min_sdk_version|int < 21 %} + {% if notouch_build == "true" %} + android:targetActivity="org.chromium.chrome.browser.touchless.NoTouchActivity" + {% elif min_sdk_version|int < 21 %} android:targetActivity="org.chromium.chrome.browser.document.ChromeLauncherActivity" {% else %} android:targetActivity="org.chromium.chrome.browser.ChromeTabbedActivity" @@ -545,7 +552,6 @@ {{ self.chrome_activity_common() }}> {{ self.extra_web_rendering_activity_definitions() }} </activity> - {% set notouch_build = notouch_build|default(0) %} {% if notouch_build == "true" %} <activity android:name="org.chromium.chrome.browser.touchless.NoTouchActivity" android:theme="@style/Theme.Chromium.Activity"
diff --git a/chrome/android/java/res/layout/bottom_tab_strip_toolbar.xml b/chrome/android/java/res/layout/bottom_tab_strip_toolbar.xml index f682754..01d8599 100644 --- a/chrome/android/java/res/layout/bottom_tab_strip_toolbar.xml +++ b/chrome/android/java/res/layout/bottom_tab_strip_toolbar.xml
@@ -11,7 +11,7 @@ <LinearLayout android:id="@+id/main_content" android:layout_width="match_parent" - android:layout_height="@dimen/bottom_sheet_peek_height" + android:layout_height="match_parent" android:orientation="horizontal" android:gravity="center_vertical" android:background="@color/modern_primary_color">
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java index edfc51c..90e71f3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -1472,21 +1472,11 @@ // Create after native initialization so subclasses that override this method have a chance // to setup. mPageViewTimer = createPageViewTimer(); - - if (shouldInitializeBottomSheet()) { - ViewGroup coordinator = findViewById(R.id.coordinator); - getLayoutInflater().inflate(R.layout.bottom_sheet, coordinator); - mBottomSheet = coordinator.findViewById(R.id.bottom_sheet); - mBottomSheet.init(coordinator, this); - - ((BottomContainer) findViewById(R.id.bottom_container)).setBottomSheet(mBottomSheet); - - mBottomSheetController = new BottomSheetController(this, mActivityTabProvider, - mScrimView, mBottomSheet, - getCompositorViewHolder().getLayoutManager().getOverlayPanelManager(), + if (shouldInitializeBottomSheet() + && FeatureUtilities.areContextualSuggestionsEnabled(this)) { + initializeBottomSheet( !ChromeFeatureList.isEnabled(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON)); - - mComponent.resolveContextualSuggestionsCoordinator(); + getComponent().resolveContextualSuggestionsCoordinator(); } } @@ -1498,6 +1488,24 @@ } /** + * Initializes the {@link BottomSheet} and {@link BottomSheetController} for use. + * @param suppressSheetForContextualSearch Whether the sheet should be suppressed when + * Contextual search is showing. + */ + protected void initializeBottomSheet(boolean suppressSheetForContextualSearch) { + ViewGroup coordinator = findViewById(R.id.coordinator); + getLayoutInflater().inflate(R.layout.bottom_sheet, coordinator); + mBottomSheet = coordinator.findViewById(R.id.bottom_sheet); + mBottomSheet.init(coordinator, this); + + ((BottomContainer) findViewById(R.id.bottom_container)).setBottomSheet(mBottomSheet); + + mBottomSheetController = new BottomSheetController(this, mActivityTabProvider, mScrimView, + mBottomSheet, getCompositorViewHolder().getLayoutManager().getOverlayPanelManager(), + suppressSheetForContextualSearch); + } + + /** * @return Whether native initialization has been completed for this activity. */ public boolean didFinishNativeInitialization() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java index e490163c..8f9ada75 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -203,6 +203,8 @@ public static final String CONTEXTUAL_SEARCH_DEFINITIONS = "ContextualSearchDefinitions"; public static final String CONTEXTUAL_SEARCH_ML_TAP_SUPPRESSION = "ContextualSearchMlTapSuppression"; + public static final String CONTEXTUAL_SEARCH_SIMPLIFIED_SERVER = + "ContextualSearchSimplifiedServer"; public static final String CONTEXTUAL_SEARCH_SECOND_TAP = "ContextualSearchSecondTap"; public static final String CONTEXTUAL_SEARCH_TAP_DISABLE_OVERRIDE = "ContextualSearchTapDisableOverride";
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 5f1f5a0a..0b66e5f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -972,6 +972,10 @@ }; OnClickListener bookmarkClickHandler = v -> addOrEditBookmark(getActivityTab()); + if (shouldInitializeBottomSheet() && FeatureUtilities.isTabGroupsAndroidEnabled()) { + initializeBottomSheet(false); + } + getToolbarManager().initializeWithNative(mTabModelSelectorImpl, getFullscreenManager().getBrowserVisibilityDelegate(), getFindToolbarManager(), mOverviewModeController, mLayoutManager, tabSwitcherClickHandler, @@ -1580,7 +1584,9 @@ @Override protected void initializeToolbar() { super.initializeToolbar(); - if (!isTablet() && FeatureUtilities.isBottomToolbarEnabled()) { + if (!isTablet() + && (FeatureUtilities.isBottomToolbarEnabled() + || FeatureUtilities.isTabGroupsAndroidEnabled())) { getToolbarManager().enableBottomToolbar(); } } @@ -2584,7 +2590,8 @@ @Override protected boolean shouldInitializeBottomSheet() { - return FeatureUtilities.areContextualSuggestionsEnabled(this); + return FeatureUtilities.areContextualSuggestionsEnabled(this) + || FeatureUtilities.isTabGroupsAndroidEnabled(); } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadController.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadController.java index cb70925..9aca7fb 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadController.java
@@ -24,6 +24,7 @@ import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.util.FeatureUtilities; +import org.chromium.components.download.DownloadCollectionBridge; import org.chromium.content_public.browser.BrowserStartupController; import org.chromium.content_public.browser.WebContents; import org.chromium.ui.base.AndroidPermissionDelegate; @@ -125,6 +126,7 @@ */ @CalledByNative private static boolean hasFileAccess() { + if (DownloadCollectionBridge.supportsDownloadCollection()) return true; Activity activity = ApplicationStatus.getLastTrackedFocusedActivity(); if (activity instanceof ChromeActivity) { return ((ChromeActivity) activity)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSection.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSection.java index 59e9d94..df2fdec 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSection.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSection.java
@@ -17,6 +17,7 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.explore_sites.ExploreSitesCategory.CategoryType; import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate; +import org.chromium.chrome.browser.ntp.NewTabPageUma; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.suggestions.SuggestionsConfig.TileStyle; import org.chromium.chrome.browser.suggestions.TileGridLayout; @@ -215,12 +216,18 @@ } private void onClicked(int tileIndex, ExploreSitesCategory category, View v) { - RecordHistogram.recordLinearCountHistogram( - "ExploreSites.ClickedNTPCategoryIndex", tileIndex, 1, 100, 100); + recordOpenedEsp(tileIndex); mNavigationDelegate.openUrl(WindowOpenDisposition.CURRENT_TAB, new LoadUrlParams(category.getUrl(), PageTransition.AUTO_BOOKMARK)); } + private void recordOpenedEsp(int tileIndex) { + RecordHistogram.recordLinearCountHistogram( + "ExploreSites.ClickedNTPCategoryIndex", tileIndex, 1, 100, 100); + NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_EXPLORE_SITES_TILE); + RecordUserAction.record("MobileNTPExploreSites"); + } + @VisibleForTesting static int compareCategoryPriority(ExploreSitesCategory cat1, ExploreSitesCategory cat2) { // First sort by activity count. Most used categories first.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageUma.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageUma.java index e63dfde..597c667 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageUma.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageUma.java
@@ -34,7 +34,8 @@ private NewTabPageUma() {} // Possible actions taken by the user on the NTP. These values are also defined in - // histograms.xml. WARNING: these values must stay in sync with histograms.xml. + // enums.xml as NewTabPageActionAndroid2. + // WARNING: these values must stay in sync with enums.xml. /** User performed a search using the omnibox. */ private static final int ACTION_SEARCHED_USING_OMNIBOX = 0; @@ -69,8 +70,11 @@ /** User clicked on the "Refresh" button in the "all dismissed" state. */ public static final int ACTION_CLICKED_ALL_DISMISSED_REFRESH = 10; + /** User opened an explore sites tile. */ + public static final int ACTION_OPENED_EXPLORE_SITES_TILE = 11; + /** The number of possible actions. */ - private static final int NUM_ACTIONS = 11; + private static final int NUM_ACTIONS = 12; /** User navigated to a page using the omnibox. */ private static final int RAPPOR_ACTION_NAVIGATED_USING_OMNIBOX = 0; @@ -93,7 +97,7 @@ /** * Possible results when updating content suggestions list in the UI. Keep in sync with the - * ContentSuggestionsUIUpdateResult enum in histograms.xml. Do not remove or change existing + * ContentSuggestionsUIUpdateResult2 enum in enums.xml. Do not remove or change existing * values other than NUM_UI_UPDATE_RESULTS. */ @IntDef({ContentSuggestionsUIUpdateResult.SUCCESS_APPENDED,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoController.java b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoController.java index a88de0d..866e686c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoController.java
@@ -358,7 +358,8 @@ * Whether to show a 'Details' link to the connection info popup. */ private boolean isConnectionDetailsLinkVisible() { - return mContentPublisher == null && !isShowingOfflinePage() && !isShowingPreview(); + return mContentPublisher == null && !isShowingOfflinePage() && !isShowingPreview() + && !mIsInternalPage; } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionSavingsMilestonePromo.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionSavingsMilestonePromo.java index ba22c37..f68b67c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionSavingsMilestonePromo.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionSavingsMilestonePromo.java
@@ -20,10 +20,8 @@ /** * A semi-colon delimited list of data savings values in MB that the promo should be shown * for. - * Note: The value is misnamed for historical reasons. This promo used to be displayed as a - * snackbar but was moved to an IPH in M74. */ - public static final String PROMO_PARAM_NAME = "snackbar_promo_data_savings_in_megabytes"; + public static final String PROMO_PARAM_NAME = "x_milestone_promo_data_savings_in_megabytes"; public static final String PROMO_FIELD_TRIAL_NAME = "DataCompressionProxyPromoVisibility"; private static final String ENABLE_DATA_REDUCTION_PROXY_SAVINGS_PROMO_SWITCH =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabStripBottomToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabStripBottomToolbarCoordinator.java index 609225c5..a0e856b5 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabStripBottomToolbarCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabStripBottomToolbarCoordinator.java
@@ -34,8 +34,8 @@ /** * Creates a new {@link TabStripBottomToolbarCoordinator} */ - public TabStripBottomToolbarCoordinator(Context context, ViewGroup parentView) { - mContext = context; + public TabStripBottomToolbarCoordinator(ViewGroup parentView) { + mContext = parentView.getContext(); mTabStripToolbarModel = new PropertyModel(TabStripToolbarViewProperties.ALL_KEYS); mTabStripToolbarCoordinator =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonInProductHelpController.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonInProductHelpController.java index e5c10e2..023c552 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonInProductHelpController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonInProductHelpController.java
@@ -19,6 +19,7 @@ import org.chromium.chrome.browser.lifecycle.Destroyable; import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings; import org.chromium.chrome.browser.ntp.NewTabPage; +import org.chromium.chrome.browser.preferences.datareduction.DataReductionSavingsMilestonePromo; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver; @@ -85,7 +86,8 @@ if (tab.isPreview()) tracker.notifyEvent(EventConstants.PREVIEWS_PAGE_LOADED); if (tab == activity.getActivityTabProvider().getActivityTab() && tab.isUserInteractable()) { - maybeShowDataSaver(activity); + maybeShowDataSaverDetail(activity); + maybeShowDataSaverMilestonePromo(activity); maybeShowPreviewVerboseStatus(activity); } mPageLoadTab = null; @@ -98,15 +100,34 @@ mPageLoadObserver.destroy(); } - // Attempts to show an IPH text bubble for data saver. - private static void maybeShowDataSaver(ChromeActivity activity) { + // Attempts to show an IPH text bubble for data saver detail. + private static void maybeShowDataSaverDetail(ChromeActivity activity) { View anchorView = activity.getToolbarManager().getMenuButton(); if (anchorView == null) return; setupAndMaybeShowIPHForFeature(FeatureConstants.DATA_SAVER_DETAIL_FEATURE, R.id.data_reduction_menu_item, false, R.string.iph_data_saver_detail_text, R.string.iph_data_saver_detail_accessibility_text, anchorView, - activity.getAppMenuHandler(), Profile.getLastUsedProfile(), activity); + activity.getAppMenuHandler(), Profile.getLastUsedProfile(), activity, null); + } + + // Attempts to show an IPH text bubble for data saver milestone promo. + private static void maybeShowDataSaverMilestonePromo(ChromeActivity activity) { + View anchorView = activity.getToolbarManager().getMenuButton(); + if (anchorView == null) return; + + final DataReductionSavingsMilestonePromo promo = + new DataReductionSavingsMilestonePromo(activity, + DataReductionProxySettings.getInstance().getTotalHttpContentLengthSaved()); + if (!promo.shouldShowPromo()) return; + + final Runnable dismissCallback = () -> { + promo.onPromoTextSeen(); + }; + setupAndMaybeShowIPHForFeature(FeatureConstants.DATA_SAVER_MILESTONE_PROMO_FEATURE, + R.id.data_reduction_menu_item, false, promo.getPromoText(), promo.getPromoText(), + anchorView, activity.getAppMenuHandler(), Profile.getLastUsedProfile(), activity, + dismissCallback); } // Attempts to show an IPH text bubble for page in preview mode. @@ -119,7 +140,7 @@ setupAndMaybeShowIPHForFeature(FeatureConstants.PREVIEWS_OMNIBOX_UI_FEATURE, null, true, R.string.iph_previews_omnibox_ui_text, R.string.iph_previews_omnibox_ui_accessibility_text, anchorView, null, - Profile.getLastUsedProfile(), activity); + Profile.getLastUsedProfile(), activity, null); } /** @@ -136,7 +157,7 @@ R.id.downloads_menu_id, true, R.string.iph_download_home_text, R.string.iph_download_home_accessibility_text, mActivity.getToolbarManager().getMenuButton(), mActivity.getAppMenuHandler(), - Profile.getLastUsedProfile(), mActivity); + Profile.getLastUsedProfile(), mActivity, null); } private void maybeShowNTPButtonIPH() { @@ -146,7 +167,7 @@ R.string.iph_ntp_button_text_home_text, R.string.iph_ntp_button_text_home_accessibility_text, mActivity.findViewById(R.id.home_button), null, Profile.getLastUsedProfile(), - mActivity); + mActivity, null); } /** @@ -162,17 +183,32 @@ R.string.iph_download_infobar_download_continuing_text, R.string.iph_download_infobar_download_continuing_text, activity.getToolbarManager().getMenuButton(), activity.getAppMenuHandler(), profile, - activity); + activity, null); } private static void setupAndMaybeShowIPHForFeature(String featureName, Integer highlightMenuItemId, boolean circleHighlight, @StringRes int stringId, @StringRes int accessibilityStringId, View anchorView, - @Nullable AppMenuHandler appMenuHandler, Profile profile, ChromeActivity activity) { + @Nullable AppMenuHandler appMenuHandler, Profile profile, ChromeActivity activity, + @Nullable Runnable onDismissCallback) { + final String contentString = activity.getString(stringId); + final String accessibilityString = activity.getString(accessibilityStringId); final Tracker tracker = TrackerFactory.getTrackerForProfile(profile); tracker.addOnInitializedCallback((Callback<Boolean>) success -> maybeShowIPH(tracker, featureName, highlightMenuItemId, circleHighlight, - stringId, accessibilityStringId, anchorView, appMenuHandler, activity)); + contentString, accessibilityString, anchorView, appMenuHandler, activity, + onDismissCallback)); + } + + private static void setupAndMaybeShowIPHForFeature(String featureName, + Integer highlightMenuItemId, boolean circleHighlight, String contentString, + String accessibilityString, View anchorView, @Nullable AppMenuHandler appMenuHandler, + Profile profile, ChromeActivity activity, @Nullable Runnable onDismissCallback) { + final Tracker tracker = TrackerFactory.getTrackerForProfile(profile); + tracker.addOnInitializedCallback((Callback<Boolean>) success + -> maybeShowIPH(tracker, featureName, highlightMenuItemId, circleHighlight, + contentString, accessibilityString, anchorView, appMenuHandler, activity, + onDismissCallback)); } private static boolean shouldHighlightForIPH(String featureName) { @@ -185,13 +221,14 @@ } private static void maybeShowIPH(Tracker tracker, String featureName, - Integer highlightMenuItemId, boolean circleHighlight, @StringRes int stringId, - @StringRes int accessibilityStringId, View anchorView, AppMenuHandler appMenuHandler, - ChromeActivity activity) { + Integer highlightMenuItemId, boolean circleHighlight, String contentString, + String accessibilityString, View anchorView, AppMenuHandler appMenuHandler, + ChromeActivity activity, @Nullable Runnable onDismissCallback) { // Activity was destroyed; don't show IPH. if (activity.isActivityFinishingOrDestroyed() || anchorView == null) return; - assert(stringId != 0 && accessibilityStringId != 0); + assert (contentString.length() > 0); + assert (accessibilityString.length() > 0); // Post a request to show the IPH bubble to allow time for a layout pass. Since the bubble // is shown on startup, the anchor view may not have a height initially see @@ -208,10 +245,13 @@ ViewRectProvider rectProvider = new ViewRectProvider(anchorView); TextBubble textBubble = new TextBubble( - activity, anchorView, stringId, accessibilityStringId, rectProvider); + activity, anchorView, contentString, accessibilityString, true, rectProvider); textBubble.setDismissOnTouchInteraction(true); textBubble.addOnDismissListener(() -> anchorView.getHandler().postDelayed(() -> { tracker.dismissed(featureName); + if (onDismissCallback != null) { + onDismissCallback.run(); + } if (shouldHighlightForIPH(featureName)) { turnOffHighlightForTextBubble(appMenuHandler, anchorView); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java index 7f51fa2..b7a64eb 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -978,7 +978,7 @@ mTabModelSelector.getModel(isIncognito).closeAllTabs(); }; mAppMenuButtonHelper.setOnClickRunnable(() -> recordBottomToolbarUseForIPH()); - mBottomControlsCoordinator.initializeWithNative( + mBottomControlsCoordinator.initializeWithNative(mActivity, mActivity.getCompositorViewHolder().getResourceManager(), mActivity.getCompositorViewHolder().getLayoutManager(), wrapBottomToolbarClickListenerForIPH(tabSwitcherClickHandler),
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java index 284d42d..e93ec6b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java
@@ -4,21 +4,25 @@ package org.chromium.chrome.browser.toolbar.bottom; +import android.support.annotation.Nullable; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.ViewStub; import org.chromium.chrome.R; import org.chromium.chrome.browser.ActivityTabProvider; +import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.appmenu.AppMenuButtonHelper; import org.chromium.chrome.browser.compositor.layouts.LayoutManager; import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior; import org.chromium.chrome.browser.compositor.layouts.ToolbarSwipeLayout; import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager; +import org.chromium.chrome.browser.tasks.tab_list_ui.TabStripBottomToolbarCoordinator; import org.chromium.chrome.browser.toolbar.IncognitoStateProvider; import org.chromium.chrome.browser.toolbar.MenuButton; import org.chromium.chrome.browser.toolbar.TabCountProvider; import org.chromium.chrome.browser.toolbar.bottom.BottomControlsViewBinder.ViewHolder; +import org.chromium.chrome.browser.util.FeatureUtilities; import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModelChangeProcessor; @@ -36,7 +40,8 @@ private final BottomControlsMediator mMediator; /** The coordinator for the split toolbar's bottom toolbar component. */ - private final BottomToolbarCoordinator mBottomToolbarCoordinator; + private @Nullable BottomToolbarCoordinator mBottomToolbarCoordinator; + private @Nullable TabStripBottomToolbarCoordinator mTabStripCoordinator; /** * Build the coordinator that manages the bottom controls. @@ -63,9 +68,14 @@ mMediator = new BottomControlsMediator(model, fullscreenManager, root.getResources().getDimensionPixelOffset(R.dimen.bottom_toolbar_height)); - mBottomToolbarCoordinator = new BottomToolbarCoordinator( - root.findViewById(R.id.bottom_toolbar_stub), tabProvider, homeButtonListener, - searchAcceleratorListener, shareButtonListener); + if (FeatureUtilities.isTabGroupsAndroidEnabled()) { + mTabStripCoordinator = new TabStripBottomToolbarCoordinator( + root.findViewById(R.id.bottom_container_slot)); + } else { + mBottomToolbarCoordinator = new BottomToolbarCoordinator( + root.findViewById(R.id.bottom_toolbar_stub), tabProvider, homeButtonListener, + searchAcceleratorListener, shareButtonListener); + } } /** @@ -73,6 +83,7 @@ * dependencies. * <p> * Calling this must occur after the native library have completely loaded. + * @param chromeActivity ChromeActivity instance to use. * @param resourceManager A {@link ResourceManager} for loading textures into the compositor. * @param layoutManager A {@link LayoutManager} to attach overlays to. * @param tabSwitcherListener An {@link OnClickListener} that is triggered when the @@ -88,56 +99,80 @@ * @param incognitoStateProvider Notifies components when incognito mode is entered or exited. * @param topToolbarRoot The root {@link ViewGroup} of the top toolbar. */ - public void initializeWithNative(ResourceManager resourceManager, LayoutManager layoutManager, - OnClickListener tabSwitcherListener, OnClickListener newTabClickListener, - OnClickListener closeTabsClickListener, AppMenuButtonHelper menuButtonHelper, - OverviewModeBehavior overviewModeBehavior, WindowAndroid windowAndroid, - TabCountProvider tabCountProvider, IncognitoStateProvider incognitoStateProvider, - ViewGroup topToolbarRoot) { + public void initializeWithNative(ChromeActivity chromeActivity, ResourceManager resourceManager, + LayoutManager layoutManager, OnClickListener tabSwitcherListener, + OnClickListener newTabClickListener, OnClickListener closeTabsClickListener, + AppMenuButtonHelper menuButtonHelper, OverviewModeBehavior overviewModeBehavior, + WindowAndroid windowAndroid, TabCountProvider tabCountProvider, + IncognitoStateProvider incognitoStateProvider, ViewGroup topToolbarRoot) { mMediator.setLayoutManager(layoutManager); mMediator.setResourceManager(resourceManager); mMediator.setToolbarSwipeHandler(layoutManager.getToolbarSwipeHandler()); mMediator.setWindowAndroid(windowAndroid); - mBottomToolbarCoordinator.initializeWithNative(tabSwitcherListener, newTabClickListener, - closeTabsClickListener, menuButtonHelper, overviewModeBehavior, tabCountProvider, - incognitoStateProvider, topToolbarRoot); + if (mBottomToolbarCoordinator != null) { + mBottomToolbarCoordinator.initializeWithNative(tabSwitcherListener, newTabClickListener, + closeTabsClickListener, menuButtonHelper, overviewModeBehavior, + tabCountProvider, incognitoStateProvider, topToolbarRoot); + } + + if (mTabStripCoordinator != null) { + mTabStripCoordinator.initializeWithNative(chromeActivity.getTabModelSelector(), + chromeActivity.getTabContentManager(), chromeActivity, + chromeActivity.getBottomSheetController()); + mMediator.setBottomControlsVisible(true); + } } /** * @param isVisible Whether the bottom control is visible. */ public void setBottomControlsVisible(boolean isVisible) { + // TabStripCoordinator manages its own visibility + if (mTabStripCoordinator != null) return; + mMediator.setBottomControlsVisible(isVisible); - mBottomToolbarCoordinator.setBottomToolbarVisible(isVisible); + if (mBottomToolbarCoordinator != null) { + mBottomToolbarCoordinator.setBottomToolbarVisible(isVisible); + } } /** * Show the update badge over the bottom toolbar's app menu. */ public void showAppMenuUpdateBadge() { - mBottomToolbarCoordinator.showAppMenuUpdateBadge(); + if (mBottomToolbarCoordinator != null) { + mBottomToolbarCoordinator.showAppMenuUpdateBadge(); + } } /** * Remove the update badge. */ public void removeAppMenuUpdateBadge() { - mBottomToolbarCoordinator.removeAppMenuUpdateBadge(); + if (mBottomToolbarCoordinator != null) { + mBottomToolbarCoordinator.removeAppMenuUpdateBadge(); + } } /** * @return Whether the update badge is showing. */ public boolean isShowingAppMenuUpdateBadge() { - return mBottomToolbarCoordinator.isShowingAppMenuUpdateBadge(); + if (mBottomToolbarCoordinator != null) { + return mBottomToolbarCoordinator.isShowingAppMenuUpdateBadge(); + } + return false; } /** * @return The wrapper for the browsing mode toolbar's app menu button. */ public MenuButton getMenuButtonWrapper() { - return mBottomToolbarCoordinator.getMenuButtonWrapper(); + if (mBottomToolbarCoordinator != null) { + return mBottomToolbarCoordinator.getMenuButtonWrapper(); + } + return null; } /** @@ -153,7 +188,8 @@ * Clean up any state when the bottom controls component is destroyed. */ public void destroy() { - mBottomToolbarCoordinator.destroy(); + if (mBottomToolbarCoordinator != null) mBottomToolbarCoordinator.destroy(); + if (mTabStripCoordinator != null) mTabStripCoordinator.destroy(); mMediator.destroy(); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java index be4df4d..37f1df9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java
@@ -44,8 +44,10 @@ final boolean showCompositedView = model.get(BottomControlsProperties.COMPOSITED_VIEW_VISIBLE); view.sceneLayer.setIsVisible(showCompositedView); - model.get(BottomControlsProperties.TOOLBAR_SWIPE_LAYOUT) - .setBottomToolbarSceneLayersVisibility(showCompositedView); + if (model.get(BottomControlsProperties.TOOLBAR_SWIPE_LAYOUT) != null) { + model.get(BottomControlsProperties.TOOLBAR_SWIPE_LAYOUT) + .setBottomToolbarSceneLayersVisibility(showCompositedView); + } model.get(BottomControlsProperties.LAYOUT_MANAGER).requestUpdate(); } else if (BottomControlsProperties.LAYOUT_MANAGER == propertyKey) { assert view.sceneLayer == null; @@ -57,6 +59,7 @@ .addSceneOverlayToBack(view.sceneLayer); } else if (BottomControlsProperties.TOOLBAR_SWIPE_LAYOUT == propertyKey) { assert view.sceneLayer != null; + assert model.get(BottomControlsProperties.TOOLBAR_SWIPE_LAYOUT) != null; model.get(BottomControlsProperties.TOOLBAR_SWIPE_LAYOUT) .setBottomToolbarSceneLayers(new ScrollingBottomViewSceneLayer(view.sceneLayer), new ScrollingBottomViewSceneLayer(view.sceneLayer),
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java index 4861197..9c198ab7f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java
@@ -304,6 +304,7 @@ @Override public boolean shouldRecognizeSwipe(MotionEvent e1, MotionEvent e2) { + if (FeatureUtilities.isTabGroupsAndroidEnabled()) return false; if (FeatureUtilities.isGridTabSwitcherEnabled(getContext())) return false; if (isOnTabStrip(e1)) return false; if (mToolbar != null && mToolbar.shouldIgnoreSwipeGesture()) return false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java index ef4cd55..72e3768 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -1535,7 +1535,7 @@ if (mForceTextureCapture) { return true; } - return !(urlHasFocus() || mUrlFocusChangeInProgress || mNtpSearchBoxScrollPercent > 0); + return !(urlHasFocus() || mUrlFocusChangeInProgress); } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java index 7c48fae..3d12cad 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
@@ -536,7 +536,8 @@ private static void cacheTabGroupsAndroidEnabled() { ChromePreferenceManager.getInstance().writeBoolean( ChromePreferenceManager.TAB_GROUPS_ANDROID_ENABLED_KEY, - ChromeFeatureList.isEnabled(ChromeFeatureList.TAB_GROUPS_ANDROID)); + !DeviceClassManager.enableAccessibilityLayout() + && ChromeFeatureList.isEnabled(ChromeFeatureList.TAB_GROUPS_ANDROID)); } /** @@ -558,8 +559,7 @@ private static boolean isDeviceEligibleForTabGroups() { return !SysUtils.isLowEndDevice() && !DeviceFormFactor.isNonMultiDisplayContextOnTablet( - ContextUtils.getApplicationContext()) - && !DeviceClassManager.enableAccessibilityLayout(); + ContextUtils.getApplicationContext()); } /**
diff --git a/chrome/app/DEPS b/chrome/app/DEPS index 5b690da..9491a52 100644 --- a/chrome/app/DEPS +++ b/chrome/app/DEPS
@@ -67,7 +67,7 @@ "+services/image_annotation/public", "+services/resource_coordinator/public", "+services/ws/common", - "+third_party/blink/public/mojom/input", + "+third_party/blink/public/mojom", ], "chrome_content_gpu_overlay_manifest\.cc": [ "+components/arc/common",
diff --git a/chrome/app/chrome_content_browser_overlay_manifest.cc b/chrome/app/chrome_content_browser_overlay_manifest.cc index 2cb99f1b..c9ca37b6 100644 --- a/chrome/app/chrome_content_browser_overlay_manifest.cc +++ b/chrome/app/chrome_content_browser_overlay_manifest.cc
@@ -42,8 +42,8 @@ #include "services/image_annotation/public/mojom/image_annotation.mojom.h" #include "services/preferences/public/cpp/manifest.h" #include "services/service_manager/public/cpp/manifest_builder.h" +#include "third_party/blink/public/mojom/badging/badging.mojom.h" #include "third_party/blink/public/mojom/input/input_host.mojom.h" -#include "third_party/blink/public/platform/modules/badging/badging.mojom.h" #include "third_party/blink/public/platform/modules/credentialmanager/credential_manager.mojom.h" #include "third_party/blink/public/platform/modules/installedapp/installed_app_provider.mojom.h" #include "third_party/blink/public/platform/modules/webshare/webshare.mojom.h"
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS index fcfd911..009f198d 100644 --- a/chrome/browser/DEPS +++ b/chrome/browser/DEPS
@@ -193,7 +193,6 @@ # Allow mojo generated files in WebKit. These files use STL types and # don't use WTF types. - "+third_party/blink/public/platform/modules/badging/badging.mojom.h", "+third_party/blink/public/platform/modules/budget_service/budget_service.mojom.h", "+third_party/blink/public/platform/modules/insecure_input/insecure_input_service.mojom.h", "+third_party/blink/public/platform/modules/installation/installation.mojom.h",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 975d252..b695888 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -50,6 +50,7 @@ #include "components/autofill_assistant/browser/features.h" #include "components/browser_sync/browser_sync_switches.h" #include "components/cloud_devices/common/cloud_devices_switches.h" +#include "components/contextual_search/core/browser/public.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h" #include "components/dom_distiller/core/dom_distiller_switches.h" @@ -987,6 +988,12 @@ const FeatureEntry::FeatureVariation kExploreSitesVariations[] = { {"Experimental", &kExploreSitesExperimental, 1, nullptr}, {"Personalized", &kExploreSitesPersonalized, 1, nullptr}}; + +const FeatureEntry::FeatureParam kSimplifiedServerAllCocaCards = { + contextual_search::kContextualCardsVersionParamName, + contextual_search::kContextualCardsSimplifiedServerWithDiagnosticChar}; +const FeatureEntry::FeatureVariation kSimplifiedServerVariations[] = { + {"and allow all CoCa cards", &kSimplifiedServerAllCocaCards, 1, nullptr}}; #endif // defined(OS_ANDROID) const FeatureEntry::FeatureParam @@ -1218,6 +1225,14 @@ flag_descriptions::kContextualSearchSecondTapName, flag_descriptions::kContextualSearchSecondTapDescription, kOsAndroid, FEATURE_VALUE_TYPE(chrome::android::kContextualSearchSecondTap)}, + {"contextual-search-simplified-server", + flag_descriptions::kContextualSearchSimplifiedServerName, + flag_descriptions::kContextualSearchSimplifiedServerDescription, + kOsAndroid, + FEATURE_WITH_PARAMS_VALUE_TYPE( + chrome::android::kContextualSearchSimplifiedServer, + kSimplifiedServerVariations, + "ContextualSearchSimplifiedServer")}, {"contextual-search-unity-integration", flag_descriptions::kContextualSearchUnityIntegrationName, flag_descriptions::kContextualSearchUnityIntegrationDescription,
diff --git a/chrome/browser/accessibility/accessibility_extension_api.cc b/chrome/browser/accessibility/accessibility_extension_api.cc index 6970785..22257b0 100644 --- a/chrome/browser/accessibility/accessibility_extension_api.cc +++ b/chrome/browser/accessibility/accessibility_extension_api.cc
@@ -130,15 +130,12 @@ ExtensionFunction::ResponseAction AccessibilityPrivateSetHighlightsFunction::Run() { #if defined(OS_CHROMEOS) - std::unique_ptr<extensions::api::accessibility_private::SetHighlights::Params> - params( - extensions::api::accessibility_private::SetHighlights::Params::Create( - *args_)); + std::unique_ptr<accessibility_private::SetHighlights::Params> params( + accessibility_private::SetHighlights::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params); std::vector<gfx::Rect> rects; - for (const extensions::api::accessibility_private::ScreenRect& rect : - params->rects) { + for (const accessibility_private::ScreenRect& rect : params->rects) { rects.push_back(gfx::Rect(rect.left, rect.top, rect.width, rect.height)); } @@ -411,8 +408,7 @@ int width = padding + (item_width * cols); int height = padding + (item_height * rows); - extensions::api::accessibility_private::ScreenRect elem = - std::move(params->element_bounds); + accessibility_private::ScreenRect elem = std::move(params->element_bounds); gfx::Rect element_bounds(elem.left, elem.top, elem.width, elem.height);
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc index a72e0d7..bfbccb23 100644 --- a/chrome/browser/android/chrome_feature_list.cc +++ b/chrome/browser/android/chrome_feature_list.cc
@@ -120,6 +120,7 @@ &kContextualSearchDefinitions, &kContextualSearchMlTapSuppression, &kContextualSearchSecondTap, + &kContextualSearchSimplifiedServer, &kContextualSearchTapDisableOverride, &kContextualSearchUnityIntegration, &kCustomContextMenu, @@ -328,6 +329,9 @@ const base::Feature kContextualSearchSecondTap{ "ContextualSearchSecondTap", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kContextualSearchSimplifiedServer{ + "ContextualSearchSimplifiedServer", base::FEATURE_DISABLED_BY_DEFAULT}; + const base::Feature kContextualSearchTapDisableOverride{ "ContextualSearchTapDisableOverride", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h index d7ef866..f8cee11 100644 --- a/chrome/browser/android/chrome_feature_list.h +++ b/chrome/browser/android/chrome_feature_list.h
@@ -48,6 +48,7 @@ extern const base::Feature kContextualSearchDefinitions; extern const base::Feature kContextualSearchMlTapSuppression; extern const base::Feature kContextualSearchSecondTap; +extern const base::Feature kContextualSearchSimplifiedServer; extern const base::Feature kContextualSearchTapDisableOverride; extern const base::Feature kContextualSearchUnityIntegration; extern const base::Feature kCustomContextMenu;
diff --git a/chrome/browser/android/contextualsearch/contextual_search_delegate.cc b/chrome/browser/android/contextualsearch/contextual_search_delegate.cc index 4104345..b9c9f22 100644 --- a/chrome/browser/android/contextualsearch/contextual_search_delegate.cc +++ b/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
@@ -24,6 +24,7 @@ #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/sync/profile_sync_service_factory.h" #include "chrome/browser/translate/translate_service.h" +#include "components/contextual_search/core/browser/public.h" #include "components/language/core/browser/language_model.h" #include "components/language/core/browser/language_model_manager.h" #include "components/language/core/browser/pref_names.h" @@ -75,10 +76,6 @@ const char kDiscourseContextHeaderPrefix[] = "X-Additional-Discourse-Context: "; const char kDoPreventPreloadValue[] = "1"; -// The version of the Contextual Cards API that we want to invoke. -const int kContextualCardsUrlActions = 3; -const int kContextualCardsDefinitions = 4; - const int kResponseCodeUninitialized = -1; } // namespace @@ -265,16 +262,29 @@ TemplateURLRef::SearchTermsArgs search_terms_args = TemplateURLRef::SearchTermsArgs(base::string16()); - // Set the Coca-integration version based on our current active feature, - // or an override param from our field trial. - int contextual_cards_version = kContextualCardsUrlActions; + // Set the Coca-integration version. + // This is based on our current active feature, or an override param from a + // field trial, possibly augmented by using simplified server logic. + int contextual_cards_version = + contextual_search::kContextualCardsUrlActionsIntegration; if (base::FeatureList::IsEnabled( chrome::android::kContextualSearchDefinitions)) { - contextual_cards_version = kContextualCardsDefinitions; + contextual_cards_version = + contextual_search::kContextualCardsDefinitionsIntegration; } + // Let the field-trial override. if (field_trial_->GetContextualCardsVersion() != 0) { contextual_cards_version = field_trial_->GetContextualCardsVersion(); } + // Add the simplified-server mixin, if enabled. + if (base::FeatureList::IsEnabled( + chrome::android::kContextualSearchSimplifiedServer) && + contextual_cards_version < + contextual_search::kContextualCardsSimplifiedServerMixin) { + contextual_cards_version = + contextual_cards_version + + contextual_search::kContextualCardsSimplifiedServerMixin; + } TemplateURLRef::SearchTermsArgs::ContextualSearchParams params( kContextualSearchRequestVersion, contextual_cards_version,
diff --git a/chrome/browser/android/contextualsearch/contextual_search_field_trial.cc b/chrome/browser/android/contextualsearch/contextual_search_field_trial.cc index 4b41395..113f469 100644 --- a/chrome/browser/android/contextualsearch/contextual_search_field_trial.cc +++ b/chrome/browser/android/contextualsearch/contextual_search_field_trial.cc
@@ -8,11 +8,11 @@ #include "base/metrics/field_trial_params.h" #include "base/strings/string_number_conversions.h" #include "chrome/browser/android/chrome_feature_list.h" +#include "components/contextual_search/core/browser/public.h" #include "components/variations/variations_associated_data.h" namespace { -const char kContextualSearchFieldTrialName[] = "ContextualSearch"; const char kFalseValue[] = "false"; const char kAnyNonEmptyValue[] = "1"; const char kContextualSearchResolverUrl[] = "contextual-search-resolver-url"; @@ -22,7 +22,6 @@ const char kContextualSearchSendURLDisabledParamName[] = "disable_send_url"; const char kContextualSearchDecodeMentionsDisabledParamName[] = "disable_decode_mentions"; -const char kContextualCardsVersionParamName[] = "contextual_cards_version"; // The default size of the content surrounding the selection to gather, allowing // room for other parameters. @@ -87,9 +86,9 @@ } int ContextualSearchFieldTrial::GetContextualCardsVersion() { - return GetIntParamValueOrDefault(kContextualCardsVersionParamName, 0, - &is_contextual_cards_version_cached_, - &contextual_cards_version_); + return GetIntParamValueOrDefault( + contextual_search::kContextualCardsVersionParamName, 0, + &is_contextual_cards_version_cached_, &contextual_cards_version_); } bool ContextualSearchFieldTrial::GetBooleanParam(const std::string& name, @@ -121,10 +120,15 @@ param_string = GetParam(name); // If we still didn't get a param, try getting a Feature param. if (param_string.empty()) { - // For now, we just support the Contextual Search Definitions feature. + // First check the Contextual Search Definitions feature. param_string = base::GetFieldTrialParamValueByFeature( chrome::android::kContextualSearchDefinitions, name); } + if (param_string.empty()) { + // Now check for the Contextual Search Simplified Server feature. + param_string = base::GetFieldTrialParamValueByFeature( + chrome::android::kContextualSearchSimplifiedServer, name); + } int param_int; if (!param_string.empty() && base::StringToInt(param_string, ¶m_int)) @@ -147,6 +151,6 @@ } std::string ContextualSearchFieldTrial::GetParam(const std::string& name) { - return variations::GetVariationParamValue(kContextualSearchFieldTrialName, - name); + return variations::GetVariationParamValue( + contextual_search::kContextualSearchFieldTrialName, name); }
diff --git a/chrome/browser/badging/badge_service_impl.h b/chrome/browser/badging/badge_service_impl.h index e5483f71..05abe4f 100644 --- a/chrome/browser/badging/badge_service_impl.h +++ b/chrome/browser/badging/badge_service_impl.h
@@ -7,7 +7,7 @@ #include "base/optional.h" #include "content/public/browser/frame_service_base.h" -#include "third_party/blink/public/platform/modules/badging/badging.mojom.h" +#include "third_party/blink/public/mojom/badging/badging.mojom.h" namespace content { class RenderFrameHost;
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd index 05d6fa88..f0c2346f 100644 --- a/chrome/browser/browser_resources.grd +++ b/chrome/browser/browser_resources.grd
@@ -97,9 +97,9 @@ <include name="IDR_ABOUT_FLASH_JS" file="resources\about_flash.js" type="BINDATA" /> </if> <if expr="not disable_nacl"> - <include name="IDR_ABOUT_NACL_HTML" file="resources\about_nacl.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" /> - <include name="IDR_ABOUT_NACL_CSS" file="resources\about_nacl.css" flattenhtml="true" type="chrome_html" /> - <include name="IDR_ABOUT_NACL_JS" file="resources\about_nacl.js" type="BINDATA" /> + <include name="IDR_ABOUT_NACL_HTML" file="resources\about_nacl\about_nacl.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" /> + <include name="IDR_ABOUT_NACL_CSS" file="resources\about_nacl\about_nacl.css" flattenhtml="true" type="chrome_html" /> + <include name="IDR_ABOUT_NACL_JS" file="resources\about_nacl\about_nacl.js" type="BINDATA" /> </if> <if expr="not is_android"> <include name="IDR_ABOUT_SYS_HTML" file="resources\about_sys\about_sys.html" compress="gzip" flattenhtml="true" type="BINDATA" /> @@ -145,9 +145,9 @@ <include name="IDR_CLOUDPRINT_MANIFEST" file="resources\cloud_print_app\manifest.json" type="BINDATA" /> </if> <include name="IDR_DEVTOOLS_DISCOVERY_PAGE_HTML" file="devtools\frontend\devtools_discovery_page.html" type="BINDATA"/> - <include name="IDR_DOMAIN_RELIABILITY_INTERNALS_HTML" file="resources\domain_reliability_internals.html" compress="gzip" type="BINDATA" /> - <include name="IDR_DOMAIN_RELIABILITY_INTERNALS_CSS" file="resources\domain_reliability_internals.css" compress="gzip" type="BINDATA" /> - <include name="IDR_DOMAIN_RELIABILITY_INTERNALS_JS" file="resources\domain_reliability_internals.js" compress="gzip" type="BINDATA" /> + <include name="IDR_DOMAIN_RELIABILITY_INTERNALS_HTML" file="resources\domain_reliability_internals\domain_reliability_internals.html" compress="gzip" type="BINDATA" /> + <include name="IDR_DOMAIN_RELIABILITY_INTERNALS_CSS" file="resources\domain_reliability_internals\domain_reliability_internals.css" compress="gzip" type="BINDATA" /> + <include name="IDR_DOMAIN_RELIABILITY_INTERNALS_JS" file="resources\domain_reliability_internals\domain_reliability_internals.js" compress="gzip" type="BINDATA" /> <if expr="safe_browsing_mode != 0"> <include name="IDR_DOWNLOAD_FILE_TYPES_PB" file="${root_gen_dir}\chrome\browser\resources\safe_browsing\download_file_types.pb" use_base_dir="false" type="BINDATA" /> </if> @@ -214,9 +214,9 @@ <include name="IDR_SNIPPETS_INTERNALS_JS" file="resources\snippets_internals\snippets_internals.js" compress="gzip" type="BINDATA" /> <include name="IDR_SNIPPETS_INTERNALS_MOJO_JS" file="${root_gen_dir}\chrome\browser\ui\webui\snippets_internals\snippets_internals.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" /> </if> - <include name="IDR_SUPERVISED_USER_INTERNALS_HTML" file="resources\supervised_user_internals.html" allowexternalscript="true" compress="gzip" type="BINDATA" /> - <include name="IDR_SUPERVISED_USER_INTERNALS_CSS" file="resources\supervised_user_internals.css" compress="gzip" type="BINDATA" /> - <include name="IDR_SUPERVISED_USER_INTERNALS_JS" file="resources\supervised_user_internals.js" compress="gzip" type="BINDATA" /> + <include name="IDR_SUPERVISED_USER_INTERNALS_HTML" file="resources\supervised_user_internals\supervised_user_internals.html" allowexternalscript="true" compress="gzip" type="BINDATA" /> + <include name="IDR_SUPERVISED_USER_INTERNALS_CSS" file="resources\supervised_user_internals\supervised_user_internals.css" compress="gzip" type="BINDATA" /> + <include name="IDR_SUPERVISED_USER_INTERNALS_JS" file="resources\supervised_user_internals\supervised_user_internals.js" compress="gzip" type="BINDATA" /> <if expr="enable_hangout_services_extension"> <!-- Hangout Services extension, included in Google Chrome builds only. --> <include name="IDR_HANGOUT_SERVICES_MANIFEST" file="resources\hangout_services\manifest.json" type="BINDATA" /> @@ -436,8 +436,8 @@ <include name="IDR_ORIGIN_MOJO_JS" file="${root_gen_dir}\url\mojom\origin.mojom-lite.js" use_base_dir="false" type="BINDATA" compress="gzip"/> <include name="IDR_COMPONENTS_HTML" file="resources\components.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" /> <include name="IDR_COMPONENTS_JS" file="resources\components.js" type="BINDATA" compress="gzip" /> - <include name="IDR_MEMORY_INTERNALS_HTML" file="resources\memory_internals.html" flattenhtml="true" type="BINDATA" compress="gzip" /> - <include name="IDR_MEMORY_INTERNALS_JS" file="resources\memory_internals.js" type="BINDATA" compress="gzip" /> + <include name="IDR_MEMORY_INTERNALS_HTML" file="resources\memory_internals\memory_internals.html" flattenhtml="true" type="BINDATA" compress="gzip" /> + <include name="IDR_MEMORY_INTERNALS_JS" file="resources\memory_internals\memory_internals.js" type="BINDATA" compress="gzip" /> <if expr="enable_plugins"> <include name="IDR_PDF_MANIFEST" file="resources\pdf\manifest.json" type="BINDATA" /> </if> @@ -589,9 +589,9 @@ <include name="IDR_MD_USER_MANAGER_TUTORIAL_JS" file="resources\md_user_manager\user_manager_tutorial.js" type="BINDATA" /> </if> <if expr="not is_android"> - <include name="IDR_IDENTITY_INTERNALS_HTML" file="resources\identity_internals.html" type="BINDATA" /> - <include name="IDR_IDENTITY_INTERNALS_CSS" file="resources\identity_internals.css" type="BINDATA" /> - <include name="IDR_IDENTITY_INTERNALS_JS" file="resources\identity_internals.js" type="BINDATA" /> + <include name="IDR_IDENTITY_INTERNALS_HTML" file="resources\identity_internals\identity_internals.html" type="BINDATA" /> + <include name="IDR_IDENTITY_INTERNALS_CSS" file="resources\identity_internals\identity_internals.css" type="BINDATA" /> + <include name="IDR_IDENTITY_INTERNALS_JS" file="resources\identity_internals\identity_internals.js" type="BINDATA" /> </if> <include name="IDR_DEVICE_LOG_UI_HTML" file="resources\device_log_ui\device_log_ui.html" type="BINDATA" compress="gzip" /> <include name="IDR_DEVICE_LOG_UI_JS" file="resources\device_log_ui\device_log_ui.js" type="BINDATA" compress="gzip" />
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc index 78db9e7..41d942a 100644 --- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc +++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -1523,6 +1523,116 @@ } /////////////////////////////////////////////////////////////////////////////// +// AutotestPrivateGetShelfAutoHideBehaviorFunction +/////////////////////////////////////////////////////////////////////////////// + +AutotestPrivateGetShelfAutoHideBehaviorFunction:: + AutotestPrivateGetShelfAutoHideBehaviorFunction() = default; + +AutotestPrivateGetShelfAutoHideBehaviorFunction:: + ~AutotestPrivateGetShelfAutoHideBehaviorFunction() = default; + +ExtensionFunction::ResponseAction +AutotestPrivateGetShelfAutoHideBehaviorFunction::Run() { + DVLOG(1) << "AutotestPrivateGetShelfAutoHideBehaviorFunction"; + + std::unique_ptr<api::autotest_private::GetShelfAutoHideBehavior::Params> + params(api::autotest_private::GetShelfAutoHideBehavior::Params::Create( + *args_)); + EXTENSION_FUNCTION_VALIDATE(params); + + service_manager::Connector* connector = + content::ServiceManagerConnection::GetForProcess()->GetConnector(); + connector->BindInterface(ash::mojom::kServiceName, &shelf_controller_); + + int64_t display_id; + if (!base::StringToInt64(params->display_id, &display_id)) { + return RespondNow( + Error("Invalid display_id. Expected string with numbers only. got %s", + params->display_id)); + } + + shelf_controller_->GetAutoHideBehaviorForTesting( + display_id, + base::BindOnce(&AutotestPrivateGetShelfAutoHideBehaviorFunction:: + OnGetShelfAutoHideBehaviorCompleted, + this)); + return RespondLater(); +} + +void AutotestPrivateGetShelfAutoHideBehaviorFunction:: + OnGetShelfAutoHideBehaviorCompleted(ash::ShelfAutoHideBehavior behavior) { + std::string str_behavior; + switch (behavior) { + case ash::ShelfAutoHideBehavior::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS: + str_behavior = "always"; + break; + case ash::ShelfAutoHideBehavior::SHELF_AUTO_HIDE_BEHAVIOR_NEVER: + str_behavior = "never"; + break; + case ash::ShelfAutoHideBehavior::SHELF_AUTO_HIDE_ALWAYS_HIDDEN: + str_behavior = "hidden"; + break; + } + Respond(OneArgument(std::make_unique<base::Value>(str_behavior))); +} + +/////////////////////////////////////////////////////////////////////////////// +// AutotestPrivateSetShelfAutoHideBehaviorFunction +/////////////////////////////////////////////////////////////////////////////// + +AutotestPrivateSetShelfAutoHideBehaviorFunction:: + AutotestPrivateSetShelfAutoHideBehaviorFunction() = default; + +AutotestPrivateSetShelfAutoHideBehaviorFunction:: + ~AutotestPrivateSetShelfAutoHideBehaviorFunction() = default; + +ExtensionFunction::ResponseAction +AutotestPrivateSetShelfAutoHideBehaviorFunction::Run() { + DVLOG(1) << "AutotestPrivateSetShelfAutoHideBehaviorFunction"; + + std::unique_ptr<api::autotest_private::SetShelfAutoHideBehavior::Params> + params(api::autotest_private::SetShelfAutoHideBehavior::Params::Create( + *args_)); + EXTENSION_FUNCTION_VALIDATE(params); + + service_manager::Connector* connector = + content::ServiceManagerConnection::GetForProcess()->GetConnector(); + connector->BindInterface(ash::mojom::kServiceName, &shelf_controller_); + + ash::ShelfAutoHideBehavior behavior; + if (params->behavior == "always") { + behavior = ash::ShelfAutoHideBehavior::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS; + } else if (params->behavior == "never") { + behavior = ash::ShelfAutoHideBehavior::SHELF_AUTO_HIDE_BEHAVIOR_NEVER; + } else if (params->behavior == "hidden") { + behavior = ash::ShelfAutoHideBehavior::SHELF_AUTO_HIDE_ALWAYS_HIDDEN; + } else { + return RespondNow(Error( + "Invalid argument: '%s'. Expected: 'always', 'never' or 'hidden'.", + params->behavior)); + } + int64_t display_id; + if (!base::StringToInt64(params->display_id, &display_id)) { + return RespondNow( + Error("Invalid display_id. Expected string with numbers only. got %s", + params->display_id)); + } + + shelf_controller_->SetAutoHideBehaviorForTesting( + display_id, behavior, + base::BindOnce(&AutotestPrivateSetShelfAutoHideBehaviorFunction:: + OnSetShelfAutoHideBehaviorCompleted, + this)); + return RespondLater(); +} + +void AutotestPrivateSetShelfAutoHideBehaviorFunction:: + OnSetShelfAutoHideBehaviorCompleted() { + Respond(NoArguments()); +} + +/////////////////////////////////////////////////////////////////////////////// // AutotestPrivateAPI ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h index dfb0592..8b9ee2a 100644 --- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h +++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
@@ -12,6 +12,7 @@ #include "ash/public/cpp/assistant/assistant_state_proxy.h" #include "ash/public/cpp/assistant/default_voice_interaction_observer.h" #include "ash/public/interfaces/ash_message_center_controller.mojom.h" +#include "ash/public/interfaces/shelf.mojom.h" #include "base/compiler_specific.h" #include "base/timer/timer.h" #include "chrome/browser/chromeos/printing/cups_printers_manager.h" @@ -609,6 +610,38 @@ ResponseAction Run() override; }; +// Returns the shelf auto hide behavior. +class AutotestPrivateGetShelfAutoHideBehaviorFunction + : public UIThreadExtensionFunction { + public: + AutotestPrivateGetShelfAutoHideBehaviorFunction(); + DECLARE_EXTENSION_FUNCTION("autotestPrivate.getShelfAutoHideBehavior", + AUTOTESTPRIVATE_GETSHELFAUTOHIDEBEHAVIOR) + + private: + void OnGetShelfAutoHideBehaviorCompleted(ash::ShelfAutoHideBehavior behavior); + ~AutotestPrivateGetShelfAutoHideBehaviorFunction() override; + ResponseAction Run() override; + + ash::mojom::ShelfControllerPtr shelf_controller_; +}; + +// Sets shelf autohide behavior. +class AutotestPrivateSetShelfAutoHideBehaviorFunction + : public UIThreadExtensionFunction { + public: + AutotestPrivateSetShelfAutoHideBehaviorFunction(); + DECLARE_EXTENSION_FUNCTION("autotestPrivate.setShelfAutoHideBehavior", + AUTOTESTPRIVATE_SETSHELFAUTOHIDEBEHAVIOR) + + private: + void OnSetShelfAutoHideBehaviorCompleted(); + ~AutotestPrivateSetShelfAutoHideBehaviorFunction() override; + ResponseAction Run() override; + + ash::mojom::ShelfControllerPtr shelf_controller_; +}; + template <> KeyedService* BrowserContextKeyedAPIFactory<AutotestPrivateAPI>::BuildServiceInstanceFor(
diff --git a/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc b/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc index d0c95e7..a6d1089 100644 --- a/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc +++ b/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
@@ -100,7 +100,7 @@ const std::string& email) { return IdentityManagerFactory::GetInstance() ->GetForProfile(profile) - ->LegacyPickAccountIdForAccount(gaia_id, email); + ->PickAccountIdForAccount(gaia_id, email); } const char* BoolToString(bool value) {
diff --git a/chrome/browser/chromeos/login/webview_login_browsertest.cc b/chrome/browser/chromeos/login/webview_login_browsertest.cc index 96af35b..006854e 100644 --- a/chrome/browser/chromeos/login/webview_login_browsertest.cc +++ b/chrome/browser/chromeos/login/webview_login_browsertest.cc
@@ -643,10 +643,8 @@ // Test that client certificate autoselect selects the right certificate even // with multiple filters for the same pattern. -// -// Disabled due to flaky timeouts: https://crbug.com/830337. IN_PROC_BROWSER_TEST_F(WebviewClientCertsLoginTest, - DISABLED_SigninFrameCertMultipleFiltersAutoSelected) { + SigninFrameCertMultipleFiltersAutoSelected) { ASSERT_NO_FATAL_FAILURE(SetUpClientCertInSystemSlot()); net::SpawnedTestServer::SSLOptions ssl_options; ssl_options.request_client_certificate = true;
diff --git a/chrome/browser/extensions/service_worker_messaging_apitest.cc b/chrome/browser/extensions/service_worker_messaging_apitest.cc index de3e4086..dc1cd85 100644 --- a/chrome/browser/extensions/service_worker_messaging_apitest.cc +++ b/chrome/browser/extensions/service_worker_messaging_apitest.cc
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "chrome/browser/extensions/api/messaging/native_messaging_test_util.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/common/chrome_switches.h" #include "chrome/test/base/ui_test_utils.h" @@ -106,6 +107,24 @@ EXPECT_TRUE(reply_listener.WaitUntilSatisfied()); } +// Tests chrome.runtime.sendNativeMessage from SW extension to a native +// messaging host. +IN_PROC_BROWSER_TEST_P(ServiceWorkerMessagingTest, NativeMessagingBasic) { + extensions::ScopedTestNativeMessagingHost test_host; + ASSERT_NO_FATAL_FAILURE(test_host.RegisterTestHost(false)); + ASSERT_TRUE(RunExtensionTest("service_worker/messaging/send_native_message")) + << message_; +} + +// Tests chrome.runtime.connectNative from SW extension to a native messaging +// host. +IN_PROC_BROWSER_TEST_P(ServiceWorkerMessagingTest, ConnectNative) { + extensions::ScopedTestNativeMessagingHost test_host; + ASSERT_NO_FATAL_FAILURE(test_host.RegisterTestHost(false)); + ASSERT_TRUE(RunExtensionTest("service_worker/messaging/connect_native")) + << message_; +} + // Tests chrome.tabs.sendMessage from SW extension to content script. IN_PROC_BROWSER_TEST_P(ServiceWorkerMessagingTest, WorkerToTab) { ASSERT_TRUE(StartEmbeddedTestServer());
diff --git a/chrome/browser/extensions/unpacked_installer.cc b/chrome/browser/extensions/unpacked_installer.cc index 8f807997..5f432ef 100644 --- a/chrome/browser/extensions/unpacked_installer.cc +++ b/chrome/browser/extensions/unpacked_installer.cc
@@ -24,7 +24,6 @@ #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "extensions/browser/api/declarative_net_request/ruleset_source.h" -#include "extensions/browser/api/declarative_net_request/utils.h" #include "extensions/browser/extension_file_task_runner.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/install_flag.h" @@ -273,9 +272,10 @@ // TODO(crbug.com/761107): Change this so that we don't need to parse JSON // in the browser process. + auto ruleset_source = + declarative_net_request::RulesetSource::Create(*extension()); declarative_net_request::IndexAndPersistRulesResult result = - declarative_net_request::IndexAndPersistRulesUnsafe( - declarative_net_request::RulesetSource::Create(*extension())); + ruleset_source.IndexAndPersistRulesUnsafe(); if (!result.success) { *error = std::move(result.error); return false;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index a505296c..09ac0c7 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -422,6 +422,11 @@ "expiry_milestone": 76 }, { + "name": "contextual-search-simplified-server", + "owners": [ "//chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/OWNERS" ], + "expiry_milestone": 78 + }, + { "name": "contextual-search-unity-integration", "owners": [ "//chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/OWNERS" ], "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index aeeee72..3f20b40 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -2274,6 +2274,13 @@ const char kContextualSearchRankerQueryDescription[] = "Enables prediction of tap gestures using Assist-Ranker machine learning."; +const char kContextualSearchSimplifiedServerName[] = + "Contextual Search simplified server logic"; +const char kContextualSearchSimplifiedServerDescription[] = + "Enables simpler server-side logic for determining what data to return and " + "show in the Contextual Search UI. Option to allow all cards CoCa " + "returns."; + const char kContextualSearchSecondTapName[] = "Contextual Search second tap triggering"; const char kContextualSearchSecondTapDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 836d067..5839a86 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -1346,6 +1346,9 @@ extern const char kContextualSearchSecondTapName[]; extern const char kContextualSearchSecondTapDescription[]; +extern const char kContextualSearchSimplifiedServerName[]; +extern const char kContextualSearchSimplifiedServerDescription[]; + extern const char kContextualSearchUnityIntegrationName[]; extern const char kContextualSearchUnityIntegrationDescription[];
diff --git a/chrome/browser/notifications/scheduler/BUILD.gn b/chrome/browser/notifications/scheduler/BUILD.gn index 5939a70..b476b84 100644 --- a/chrome/browser/notifications/scheduler/BUILD.gn +++ b/chrome/browser/notifications/scheduler/BUILD.gn
@@ -28,6 +28,7 @@ "notification_params.cc", "notification_params.h", "notification_schedule_service.h", + "notification_scheduler_types.h", "schedule_params.cc", "schedule_params.h", ] @@ -50,12 +51,21 @@ "icon_entry.h", "icon_store.cc", "icon_store.h", + "impression_entry.cc", + "impression_entry.h", + "notification_entry.cc", + "notification_entry.h", "notification_schedule_service_impl.cc", "notification_schedule_service_impl.h", "proto_conversion.cc", "proto_conversion.h", "proto_db_collection_store.cc", "proto_db_collection_store.h", + "scheduler_model.h", + "scheduler_model_impl.cc", + "scheduler_model_impl.h", + "type_state.cc", + "type_state.h", ] # This target should not depend on anything in //chrome/* except the proto library.
diff --git a/chrome/browser/notifications/scheduler/impression_entry.cc b/chrome/browser/notifications/scheduler/impression_entry.cc new file mode 100644 index 0000000..950458a --- /dev/null +++ b/chrome/browser/notifications/scheduler/impression_entry.cc
@@ -0,0 +1,12 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/notifications/scheduler/impression_entry.h" + +namespace notifications { + +ImpressionEntry::ImpressionEntry() = default; +ImpressionEntry::~ImpressionEntry() = default; + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/impression_entry.h b/chrome/browser/notifications/scheduler/impression_entry.h new file mode 100644 index 0000000..79e47ea9 --- /dev/null +++ b/chrome/browser/notifications/scheduler/impression_entry.h
@@ -0,0 +1,31 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_IMPRESSION_ENTRY_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_IMPRESSION_ENTRY_H_ + +#include "base/time/time.h" +#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h" + +namespace notifications { + +// Contains data about the impression of a certain notification from the user's +// perspective. +struct ImpressionEntry { + ImpressionEntry(); + ~ImpressionEntry(); + + // Creation timestamp. + base::Time create_time; + + // The user action that generates the impression result. + UserFeedback user_feedback; + + // The impression result. + ImpressionResult impression; +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_IMPRESSION_ENTRY_H_
diff --git a/chrome/browser/notifications/scheduler/notification_entry.cc b/chrome/browser/notifications/scheduler/notification_entry.cc new file mode 100644 index 0000000..e5405b73 --- /dev/null +++ b/chrome/browser/notifications/scheduler/notification_entry.cc
@@ -0,0 +1,15 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/notifications/scheduler/notification_entry.h" + +#include <utility> + +namespace notifications { + +NotificationEntry::NotificationEntry(const std::string& guid) : guid(guid) {} + +NotificationEntry::~NotificationEntry() = default; + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/notification_entry.h b/chrome/browser/notifications/scheduler/notification_entry.h new file mode 100644 index 0000000..335d29c1 --- /dev/null +++ b/chrome/browser/notifications/scheduler/notification_entry.h
@@ -0,0 +1,31 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_ENTRY_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_ENTRY_H_ + +#include <string> + +#include "chrome/browser/notifications/scheduler/notification_data.h" +#include "chrome/browser/notifications/scheduler/schedule_params.h" + +namespace notifications { + +// Represents the in-memory counterpart of scheduled notification database +// record. +struct NotificationEntry { + explicit NotificationEntry(const std::string& guid); + ~NotificationEntry(); + + // The unique id of the notification database entry. + std::string guid; + + NotificationData notification_data; + + ScheduleParams schedule_params; +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_ENTRY_H_
diff --git a/chrome/browser/notifications/scheduler/notification_params.cc b/chrome/browser/notifications/scheduler/notification_params.cc index 63d1b45..bf42f6b 100644 --- a/chrome/browser/notifications/scheduler/notification_params.cc +++ b/chrome/browser/notifications/scheduler/notification_params.cc
@@ -10,7 +10,7 @@ namespace notifications { -NotificationParams::NotificationParams(Type type, +NotificationParams::NotificationParams(SchedulerClientType type, NotificationData notification, ScheduleParams schedule_params) : type(type),
diff --git a/chrome/browser/notifications/scheduler/notification_params.h b/chrome/browser/notifications/scheduler/notification_params.h index 8ad881f..5b684f9c 100644 --- a/chrome/browser/notifications/scheduler/notification_params.h +++ b/chrome/browser/notifications/scheduler/notification_params.h
@@ -8,23 +8,20 @@ #include <memory> #include "chrome/browser/notifications/scheduler/notification_data.h" +#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h" #include "chrome/browser/notifications/scheduler/schedule_params.h" namespace notifications { // Struct used to schedule a notification. struct NotificationParams { - enum class Type { - PLACE_HOLDER, - }; - - NotificationParams(Type type, + NotificationParams(SchedulerClientType type, NotificationData notification, ScheduleParams schedule_params); ~NotificationParams(); // The type of notification using the scheduling system. - Type type; + SchedulerClientType type; // Data used to show the notification, such as text or title on the // notification.
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler_types.h b/chrome/browser/notifications/scheduler/notification_scheduler_types.h new file mode 100644 index 0000000..1b3567c --- /dev/null +++ b/chrome/browser/notifications/scheduler/notification_scheduler_types.h
@@ -0,0 +1,47 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_TYPES_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_TYPES_H_ + +namespace notifications { + +// The type of a list of clients using the notification scheduler system. +enum class SchedulerClientType { + PLACE_HOLDER, +}; + +// The type of user feedback from a displayed notification. +enum class UserFeedback { + // Unknown feedback from the user. + kUnknown = 0, + // The user taps the helpful button, potentially a strong indicator of user's + // positive preference on the notification. + kHelpful = 1, + // The user taps the unhelpful button, potentially a strong indicator of + // user's negative preference on the notification. + kNotHelpful = 2, + // The user clicks the body of the notification. + kBodyClick = 3, + // The user has no interaction with the notification for a while. + kIgnore = 4, + kMaxValue = kIgnore +}; + +// The user impression result of a particular notification. +enum class ImpressionResult { + // Unknown user impression. + kUnknown = 0, + // Positive user impression that the user may like the notification. + kPositive = 1, + // Positive user impression that the user may dislike the notification. + kNegative = 2, + // The feedback is netural to the user. + kNetural = 3, + kMaxValue = kNetural +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_TYPES_H_
diff --git a/chrome/browser/notifications/scheduler/scheduler_model.h b/chrome/browser/notifications/scheduler/scheduler_model.h new file mode 100644 index 0000000..bd8aa99 --- /dev/null +++ b/chrome/browser/notifications/scheduler/scheduler_model.h
@@ -0,0 +1,64 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULER_MODEL_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULER_MODEL_H_ + +#include <string> + +#include "base/callback.h" +#include "base/macros.h" +#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h" + +namespace notifications { + +struct TypeState; +struct NotificationEntry; + +// A model class which contains in-memory representations of scheduled +// notifications and impression data for a certain type of notification and +// provides functionalites to update these data. +class SchedulerModel { + public: + using InitCallback = base::OnceCallback<void(bool)>; + + // Initializes the model and load data into memory. + virtual void Initialize(InitCallback init_callback) = 0; + + // Get the type state of a certain scheduler client. Returns nullptr when the + // type state doesn't exist. + virtual TypeState* GetTypeState(SchedulerClientType type) = 0; + + // Updates a type state. Uses GetTypeState() to modify data and call this to + // commit the change to database. + virtual void UpdateTypeState(SchedulerClientType type) = 0; + + // Deletes a type state. + virtual void DeleteTypeState(SchedulerClientType type) = 0; + + // Adds a scheduled notification. + virtual void AddNotification(NotificationEntry entry) = 0; + + // Gets a scheduled notification. Returns nullptr when the notification entry + // doesn't exist. + virtual NotificationEntry* GetNotification(const std::string& guid) = 0; + + // Updates a notification. Uses GetNotification() to modify data and call this + // to commit the change to database. + virtual void UpdateNotification(const std::string& guid) = 0; + + // Deletes a scheduled notification. + virtual void DeleteNotification(const std::string& guid) = 0; + + protected: + SchedulerModel() = default; + virtual ~SchedulerModel() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(SchedulerModel); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULER_MODEL_H_
diff --git a/chrome/browser/notifications/scheduler/scheduler_model_impl.cc b/chrome/browser/notifications/scheduler/scheduler_model_impl.cc new file mode 100644 index 0000000..96dc869 --- /dev/null +++ b/chrome/browser/notifications/scheduler/scheduler_model_impl.cc
@@ -0,0 +1,52 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/notifications/scheduler/scheduler_model_impl.h" + +#include "base/logging.h" +#include "base/macros.h" +#include "chrome/browser/notifications/scheduler/notification_entry.h" + +namespace notifications { + +SchedulerModelImpl::SchedulerModelImpl() = default; + +SchedulerModelImpl::~SchedulerModelImpl() = default; + +void SchedulerModelImpl::Initialize(InitCallback init_callback) { + NOTIMPLEMENTED(); +} + +TypeState* SchedulerModelImpl::GetTypeState(SchedulerClientType type) { + NOTIMPLEMENTED(); + return nullptr; +} + +void SchedulerModelImpl::UpdateTypeState(SchedulerClientType type) { + NOTIMPLEMENTED(); +} + +void SchedulerModelImpl::DeleteTypeState(SchedulerClientType type) { + NOTIMPLEMENTED(); +} + +void SchedulerModelImpl::AddNotification(NotificationEntry entry) { + NOTIMPLEMENTED(); +} + +NotificationEntry* SchedulerModelImpl::GetNotification( + const std::string& guid) { + NOTIMPLEMENTED(); + return nullptr; +} + +void SchedulerModelImpl::UpdateNotification(const std::string& guid) { + NOTIMPLEMENTED(); +} + +void SchedulerModelImpl::DeleteNotification(const std::string& guid) { + NOTIMPLEMENTED(); +} + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/scheduler_model_impl.h b/chrome/browser/notifications/scheduler/scheduler_model_impl.h new file mode 100644 index 0000000..0990c9b --- /dev/null +++ b/chrome/browser/notifications/scheduler/scheduler_model_impl.h
@@ -0,0 +1,36 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULER_MODEL_IMPL_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULER_MODEL_IMPL_H_ + +#include "base/macros.h" +#include "chrome/browser/notifications/scheduler/scheduler_model.h" + +namespace notifications { + +struct NotificationEntry; + +class SchedulerModelImpl : public SchedulerModel { + public: + SchedulerModelImpl(); + ~SchedulerModelImpl() override; + + private: + // SchedulerModel implementation. + void Initialize(InitCallback init_callback) override; + TypeState* GetTypeState(SchedulerClientType type) override; + void UpdateTypeState(SchedulerClientType type) override; + void DeleteTypeState(SchedulerClientType type) override; + void AddNotification(NotificationEntry entry) override; + NotificationEntry* GetNotification(const std::string& guid) override; + void UpdateNotification(const std::string& guid) override; + void DeleteNotification(const std::string& guid) override; + + DISALLOW_COPY_AND_ASSIGN(SchedulerModelImpl); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULER_MODEL_IMPL_H_
diff --git a/chrome/browser/notifications/scheduler/type_state.cc b/chrome/browser/notifications/scheduler/type_state.cc new file mode 100644 index 0000000..f9c8ce1 --- /dev/null +++ b/chrome/browser/notifications/scheduler/type_state.cc
@@ -0,0 +1,13 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/notifications/scheduler/type_state.h" + +namespace notifications { + +TypeState::TypeState(SchedulerClientType type) : type(type) {} + +TypeState::~TypeState() = default; + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/type_state.h b/chrome/browser/notifications/scheduler/type_state.h new file mode 100644 index 0000000..b0bb0aa --- /dev/null +++ b/chrome/browser/notifications/scheduler/type_state.h
@@ -0,0 +1,28 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_TYPE_STATE_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_TYPE_STATE_H_ + +#include <vector> + +#include "chrome/browser/notifications/scheduler/impression_entry.h" +#include "chrome/browser/notifications/scheduler/notification_entry.h" + +namespace notifications { + +// Stores all data about a particular type of notification. +struct TypeState { + explicit TypeState(SchedulerClientType type); + ~TypeState(); + + SchedulerClientType type; + + // A list of user impression history. + std::vector<ImpressionEntry> impressions; +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_TYPE_STATE_H_
diff --git a/chrome/browser/resources/about_nacl.css b/chrome/browser/resources/about_nacl/about_nacl.css similarity index 88% rename from chrome/browser/resources/about_nacl.css rename to chrome/browser/resources/about_nacl/about_nacl.css index 9d16ecd..7740798d 100644 --- a/chrome/browser/resources/about_nacl.css +++ b/chrome/browser/resources/about_nacl/about_nacl.css
@@ -8,5 +8,5 @@ } .value { - margin-left: 10px; + margin-inline-start: 10px; }
diff --git a/chrome/browser/resources/about_nacl.html b/chrome/browser/resources/about_nacl/about_nacl.html similarity index 94% rename from chrome/browser/resources/about_nacl.html rename to chrome/browser/resources/about_nacl/about_nacl.html index 53b9da1..d492adb 100644 --- a/chrome/browser/resources/about_nacl.html +++ b/chrome/browser/resources/about_nacl/about_nacl.html
@@ -12,11 +12,10 @@ <h1>About NaCl</h1> </div> <div id="naclInfoTemplate"> - <span jsselect="naclInfo"> + <div jsselect="naclInfo"> <span jscontent="key" class="key"></span> <span jscontent="value" class="value"></span> - <br> - </span> + </div> </div> </div> <script src="chrome://resources/js/promise_resolver.js"></script>
diff --git a/chrome/browser/resources/about_nacl.js b/chrome/browser/resources/about_nacl/about_nacl.js similarity index 99% rename from chrome/browser/resources/about_nacl.js rename to chrome/browser/resources/about_nacl/about_nacl.js index c0e0bc8..3ff098ec3 100644 --- a/chrome/browser/resources/about_nacl.js +++ b/chrome/browser/resources/about_nacl/about_nacl.js
@@ -30,5 +30,4 @@ // Get data and have it displayed upon loading. document.addEventListener('DOMContentLoaded', requestNaClInfo); - })();
diff --git a/chrome/browser/resources/domain_reliability_internals.css b/chrome/browser/resources/domain_reliability_internals/domain_reliability_internals.css similarity index 100% rename from chrome/browser/resources/domain_reliability_internals.css rename to chrome/browser/resources/domain_reliability_internals/domain_reliability_internals.css
diff --git a/chrome/browser/resources/domain_reliability_internals.html b/chrome/browser/resources/domain_reliability_internals/domain_reliability_internals.html similarity index 99% rename from chrome/browser/resources/domain_reliability_internals.html rename to chrome/browser/resources/domain_reliability_internals/domain_reliability_internals.html index 95d6d18..7bb2a1b8 100644 --- a/chrome/browser/resources/domain_reliability_internals.html +++ b/chrome/browser/resources/domain_reliability_internals/domain_reliability_internals.html
@@ -3,7 +3,7 @@ <head> <meta charset="utf-8"> <link rel="stylesheet" href="chrome://resources/css/text_defaults.css"> - <link rel="stylesheet" href="domain_reliability_internals.css" /> + <link rel="stylesheet" href="domain_reliability_internals.css"> <script src="chrome://resources/js/cr.js"></script> <script src="chrome://resources/js/util.js"></script> <script src="chrome://resources/js/jstemplate_compiled.js"></script>
diff --git a/chrome/browser/resources/domain_reliability_internals.js b/chrome/browser/resources/domain_reliability_internals/domain_reliability_internals.js similarity index 100% rename from chrome/browser/resources/domain_reliability_internals.js rename to chrome/browser/resources/domain_reliability_internals/domain_reliability_internals.js
diff --git a/chrome/browser/resources/identity_internals.css b/chrome/browser/resources/identity_internals/identity_internals.css similarity index 100% rename from chrome/browser/resources/identity_internals.css rename to chrome/browser/resources/identity_internals/identity_internals.css
diff --git a/chrome/browser/resources/identity_internals.html b/chrome/browser/resources/identity_internals/identity_internals.html similarity index 100% rename from chrome/browser/resources/identity_internals.html rename to chrome/browser/resources/identity_internals/identity_internals.html
diff --git a/chrome/browser/resources/identity_internals.js b/chrome/browser/resources/identity_internals/identity_internals.js similarity index 100% rename from chrome/browser/resources/identity_internals.js rename to chrome/browser/resources/identity_internals/identity_internals.js
diff --git a/chrome/browser/resources/md_user_manager/user_manager.html b/chrome/browser/resources/md_user_manager/user_manager.html index 375a5fe..43c8ecd4 100644 --- a/chrome/browser/resources/md_user_manager/user_manager.html +++ b/chrome/browser/resources/md_user_manager/user_manager.html
@@ -2,7 +2,8 @@ <html build="$i18n{buildType}" dir="$i18n{textdirection}" lang="$i18n{language}" - screen="$i18n{screenType}"> + screen="$i18n{screenType}" + $i18n{dark}> <head> <meta charset="utf-8"> <meta name="google" value="notranslate"> @@ -407,5 +408,6 @@ <include src="../../../../ui/login/account_picker/user_pod_template.html"> </user-manager-pages> <script src="user_manager.js"></script> + <link rel="import" href="chrome://resources/html/dark_mode.html"> </body> </html>
diff --git a/chrome/browser/resources/memory_internals.html b/chrome/browser/resources/memory_internals/memory_internals.html similarity index 96% rename from chrome/browser/resources/memory_internals.html rename to chrome/browser/resources/memory_internals/memory_internals.html index 020e7c1..2c3a17b5 100644 --- a/chrome/browser/resources/memory_internals.html +++ b/chrome/browser/resources/memory_internals/memory_internals.html
@@ -8,7 +8,7 @@ th { border-bottom: 1px solid black; padding: 5px; - text-align: left; + text-align: start; } td { padding: 5px;
diff --git a/chrome/browser/resources/memory_internals.js b/chrome/browser/resources/memory_internals/memory_internals.js similarity index 100% rename from chrome/browser/resources/memory_internals.js rename to chrome/browser/resources/memory_internals/memory_internals.js
diff --git a/chrome/browser/resources/pdf/pdf_viewer.js b/chrome/browser/resources/pdf/pdf_viewer.js index 8051b83d..30a01f5 100644 --- a/chrome/browser/resources/pdf/pdf_viewer.js +++ b/chrome/browser/resources/pdf/pdf_viewer.js
@@ -1234,6 +1234,9 @@ * conditions. */ updateAnnotationAvailable_() { + if (!this.toolbar_) { + return; + } let annotationAvailable = true; if (this.viewport_.getClockwiseRotations() != 0) { annotationAvailable = false;
diff --git a/chrome/browser/resources/print_preview/print_preview_new.html b/chrome/browser/resources/print_preview/print_preview_new.html index 2917106..e85a24c1 100644 --- a/chrome/browser/resources/print_preview/print_preview_new.html +++ b/chrome/browser/resources/print_preview/print_preview_new.html
@@ -1,5 +1,5 @@ <!doctype html> -<html dir="$i18n{textdirection}" lang="$i18n{language}"> +<html dir="$i18n{textdirection}" lang="$i18n{language}" $i18n{dark}> <head> <meta charset="utf-8"> <style> @@ -22,5 +22,6 @@ <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css"> <link rel="stylesheet" href="chrome://resources/css/md_colors.css"> <link rel="import" href="new/app.html"> + <link rel="import" href="chrome://resources/html/dark_mode.html"> </body> </html>
diff --git a/chrome/browser/resources/supervised_user_internals.css b/chrome/browser/resources/supervised_user_internals/supervised_user_internals.css similarity index 96% rename from chrome/browser/resources/supervised_user_internals.css rename to chrome/browser/resources/supervised_user_internals/supervised_user_internals.css index 97d3dba0..9fcb0f02 100644 --- a/chrome/browser/resources/supervised_user_internals.css +++ b/chrome/browser/resources/supervised_user_internals/supervised_user_internals.css
@@ -23,8 +23,8 @@ #info .section { display: inline-block; - margin-left: auto; - margin-right: auto; + margin-inline-end: auto; + margin-inline-start: auto; } #info .section.hidden {
diff --git a/chrome/browser/resources/supervised_user_internals.html b/chrome/browser/resources/supervised_user_internals/supervised_user_internals.html similarity index 100% rename from chrome/browser/resources/supervised_user_internals.html rename to chrome/browser/resources/supervised_user_internals/supervised_user_internals.html
diff --git a/chrome/browser/resources/supervised_user_internals.js b/chrome/browser/resources/supervised_user_internals/supervised_user_internals.js similarity index 100% rename from chrome/browser/resources/supervised_user_internals.js rename to chrome/browser/resources/supervised_user_internals/supervised_user_internals.js
diff --git a/chrome/browser/resources/usb_internals/devices_page.js b/chrome/browser/resources/usb_internals/devices_page.js index 134977c..4ce09539 100644 --- a/chrome/browser/resources/usb_internals/devices_page.js +++ b/chrome/browser/resources/usb_internals/devices_page.js
@@ -25,18 +25,23 @@ td[0].textContent = device.busNumber; td[1].textContent = device.portNumber; - td[2].textContent = toHex(device.vendorId); - td[3].textContent = toHex(device.productId); + td[2].textContent = toHex_(device.vendorId); + td[3].textContent = toHex_(device.productId); if (device.manufacturerName) { - td[4].textContent = decodeString16(device.manufacturerName.data); + td[4].textContent = decodeString16_(device.manufacturerName.data); } if (device.productName) { - td[5].textContent = decodeString16(device.productName.data); + td[5].textContent = decodeString16_(device.productName.data); } if (device.serialNumber) { - td[6].textContent = decodeString16(device.serialNumber.data); + td[6].textContent = decodeString16_(device.serialNumber.data); } + const inspectButton = clone.querySelector('button'); + inspectButton.addEventListener('click', () => { + switchToTab_(device); + }); + tableBody.appendChild(clone); } } @@ -45,8 +50,9 @@ * Parses utf16 coded string. * @param {!mojoBase.mojom.String16} arr * @return {string} + * @private */ - function decodeString16(arr) { + function decodeString16_(arr) { return arr.map(ch => String.fromCodePoint(ch)).join(''); } @@ -54,12 +60,254 @@ * Parses the decimal number to hex format. * @param {number} num * @return {string} + * @private */ - function toHex(num) { + function toHex_(num) { return '0x' + num.toString(16).padStart(4, '0').slice(-4).toUpperCase(); } + /** + * Switches to the device's tab, creating one if necessary. + * @param {!device.mojom.UsbDeviceInfo} device + * @private + */ + function switchToTab_(device) { + const tabId = device.guid; + + if (null == $(tabId)) { + addTab_(device); + } + $(tabId).selected = true; + } + + /** + * Adds a tab to display a tree view showing device's detail information. + * @param {!device.mojom.UsbDeviceInfo} device + * @private + */ + function addTab_(device) { + const tabs = document.querySelector('tabs'); + + const tabTemplate = document.querySelector('#tab-template'); + const tabClone = document.importNode(tabTemplate.content, true); + + const tab = tabClone.querySelector('tab'); + if (device.productName) { + tab.textContent = decodeString16_(device.productName.data); + } else { + tab.textContent = toHex_(device.vendorId).slice(2) + ':' + + toHex_(device.productId).slice(2); + } + tab.id = device.guid; + + tabs.appendChild(tabClone); + cr.ui.decorate('tab', cr.ui.Tab); + + const tabPanels = document.querySelector('tabpanels'); + + const tabPanelTemplate = document.querySelector('#tabpanel-template'); + const tabPanelClone = document.importNode(tabPanelTemplate.content, true); + + /** + * Root of the WebContents tree of current device. + * @type {cr.ui.Tree|null} + */ + const treeViewRoot = tabPanelClone.querySelector('#tree-view'); + cr.ui.decorate(treeViewRoot, cr.ui.Tree); + treeViewRoot.detail = {payload: {}, children: {}}; + // Clear the tree first before populating it with the new content. + treeViewRoot.innerText = ''; + renderDeviceTree_(device, treeViewRoot); + + tabPanels.appendChild(tabPanelClone); + cr.ui.decorate('tabpanel', cr.ui.TabPanel); + } + + /** + * Renders a customized TreeItem with the given content and class name. + * @param {string} itemLabel + * @return {!cr.ui.TreeItem} + * @private + */ + function customTreeItem_(itemLabel) { + const item = new cr.ui.TreeItem({ + label: itemLabel, + icon: '', + }); + return item; + } + + + /** + * Renders a tree to display the device's detail information. + * @param {!device.mojom.UsbDeviceInfo} device + * @param {!cr.ui.Tree} root + * @private + */ + function renderDeviceTree_(device, root) { + root.add(customTreeItem_( + 'USB Version: ' + device.usbVersionMajor + '.' + + device.usbVersionMinor + '.' + device.usbVersionSubminor)); + + root.add(customTreeItem_('Class Code: ' + device.classCode)); + + root.add(customTreeItem_('Subclass Code: ' + device.subclassCode)); + + root.add(customTreeItem_('Protocol Code: ' + device.protocolCode)); + + root.add(customTreeItem_('Port Number: ' + device.portNumber)); + + root.add(customTreeItem_('Vendor Id: ' + toHex_(device.vendorId))); + + root.add(customTreeItem_('Product Id: ' + toHex_(device.productId))); + + root.add(customTreeItem_( + 'Device Version: ' + device.deviceVersionMajor + ',' + + device.deviceVersionMinor + ',' + device.deviceVersionSubminor)); + + root.add(customTreeItem_( + 'Manufacturer Name: ' + decodeString16_(device.manufacturerName.data))); + + root.add(customTreeItem_( + 'Product Name: ' + decodeString16_(device.productName.data))); + + root.add(customTreeItem_( + 'Serial Number: ' + decodeString16_(device.serialNumber.data))); + + root.add(customTreeItem_( + 'WebUSB Landing Page: ' + device.webusbLandingPage.url)); + + root.add( + customTreeItem_('Active Configuration: ' + device.activeConfiguration)); + + const configurationsArray = device.configurations; + renderConfigurationTreeItem_(configurationsArray, root); + } + + /** + * Renders a tree item to display the device's configurations information. + * @param {!Array<!device.mojom.UsbConfigurationInfo>} configurationsArray + * @param {!cr.ui.TreeItem} root + * @private + */ + function renderConfigurationTreeItem_(configurationsArray, root) { + for (const configuration of configurationsArray) { + const configurationItem = + customTreeItem_('Configuration ' + configuration.configurationValue); + + if (configuration.configurationName) { + configurationItem.add(customTreeItem_( + 'Configuration Name: ' + + decodeString16_(configuration.configurationName.data))); + } + + const interfacesArray = configuration.interfaces; + renderInterfacesTreeItem_(interfacesArray, configurationItem); + + root.add(configurationItem); + } + } + + /** + * Renders a tree item to display the device's interfaces information. + * @param {!Array<!device.mojom.UsbInterfaceInfo>} interfacesArray + * @param {!cr.ui.TreeItem} root + * @private + */ + function renderInterfacesTreeItem_(interfacesArray, root) { + for (const currentInterface of interfacesArray) { + const interfaceItem = + customTreeItem_('Interface ' + currentInterface.interfaceNumber); + + const alternatesArray = currentInterface.alternates; + renderAlternatesTreeItem_(alternatesArray, interfaceItem); + + root.add(interfaceItem); + } + } + + /** + * Renders a tree item to display the device's alternate interfaces + * information. + * @param {!Array<!device.mojom.UsbAlternateInterfaceInfo>} alternatesArray + * @param {!cr.ui.TreeItem} root + * @private + */ + function renderAlternatesTreeItem_(alternatesArray, root) { + for (const alternate of alternatesArray) { + const alternateItem = + customTreeItem_('Alternate ' + alternate.alternateSetting); + + alternateItem.add(customTreeItem_('Class Code: ' + alternate.classCode)); + + alternateItem.add( + customTreeItem_('Subclass Code: ' + alternate.subclassCode)); + + alternateItem.add( + customTreeItem_('Protocol Code: ' + alternate.protocolCode)); + + if (alternate.interfaceName) { + alternateItem.add(customTreeItem_( + 'Interface Name: ' + + decodeString16_(alternate.interfaceName.data))); + } + + const endpointsArray = alternate.endpoints; + renderEndpointsTreeItem_(endpointsArray, alternateItem); + + root.add(alternateItem); + } + } + + /** + * Renders a tree item to display the device's endpoints information. + * @param {!Array<!device.mojom.UsbEndpointInfo>} endpointsArray + * @param {!cr.ui.TreeItem} root + * @private + */ + function renderEndpointsTreeItem_(endpointsArray, root) { + for (const endpoint of endpointsArray) { + let itemLabel = 'Endpoint '; + + itemLabel += endpoint.endpointNumber; + + switch (endpoint.direction) { + case device.mojom.UsbTransferDirection.INBOUND: + itemLabel += ' (INBOUND)'; + break; + case device.mojom.UsbTransferDirection.OUTBOUND: + itemLabel += ' (OUTBOUND)'; + break; + } + + const endpointItem = customTreeItem_(itemLabel); + + let usbTransferType = ''; + switch (endpoint.type) { + case device.mojom.UsbTransferType.CONTROL: + usbTransferType = 'CONTROL'; + break; + case device.mojom.UsbTransferType.ISOCHRONOUS: + usbTransferType = 'ISOCHRONOUS'; + break; + case device.mojom.UsbTransferType.BULK: + usbTransferType = 'BULK'; + break; + case device.mojom.UsbTransferType.INTERRUPT: + usbTransferType = 'INTERRUPT'; + break; + } + + endpointItem.add( + customTreeItem_('USB Transfer Type: ' + usbTransferType)); + + endpointItem.add(customTreeItem_('Packet Size: ' + endpoint.packetSize)); + + root.add(endpointItem); + } + } + return { setDevices: setDevices, }; -}); +}); \ No newline at end of file
diff --git a/chrome/browser/resources/usb_internals/usb_internals.html b/chrome/browser/resources/usb_internals/usb_internals.html index af7ff97c..d1c93db30 100644 --- a/chrome/browser/resources/usb_internals/usb_internals.html +++ b/chrome/browser/resources/usb_internals/usb_internals.html
@@ -6,6 +6,7 @@ <title>USB Internals</title> <link rel="stylesheet" href="chrome://resources/css/text_defaults.css"> <link rel="stylesheet" href="chrome://resources/css/tabs.css"> + <link rel="stylesheet" href="chrome://resources/css/tree.css"> <link rel="stylesheet" href="usb_internals.css"> <script src="chrome://resources/js/assert.js"></script> @@ -14,6 +15,7 @@ <script src="chrome://resources/js/cr/ui.js"></script> <script src="chrome://resources/js/cr/ui/focus_outline_manager.js"></script> <script src="chrome://resources/js/cr/ui/tabs.js"></script> + <script src="chrome://resources/js/cr/ui/tree.js"></script> <script src="chrome://resources/js/mojo_bindings_lite.js"></script> <script src="chrome://resources/js/util.js"></script> <script src="chrome://resources/js/big_buffer.mojom-lite.js"></script> @@ -127,6 +129,17 @@ </tabpanels> </tabbox> + + <template id="tab-template"> + <tab></tab> + </template> + + <template id="tabpanel-template"> + <tabpanel> + <tree id="tree-view"></tree> + </tabpanel> + </template> + <script src="usb_internals.js"></script> </body>
diff --git a/chrome/browser/signin/dice_browsertest.cc b/chrome/browser/signin/dice_browsertest.cc index c19ab1b..c9dfc8d 100644 --- a/chrome/browser/signin/dice_browsertest.cc +++ b/chrome/browser/signin/dice_browsertest.cc
@@ -370,14 +370,14 @@ // Returns the account ID associated with |main_email_| and its associated // gaia ID. std::string GetMainAccountID() { - return GetIdentityManager()->LegacyPickAccountIdForAccount( + return GetIdentityManager()->PickAccountIdForAccount( identity::GetTestGaiaIdForEmail(main_email_), main_email_); } // Returns the account ID associated with kSecondaryEmail and its associated // gaia ID. std::string GetSecondaryAccountID() { - return GetIdentityManager()->LegacyPickAccountIdForAccount( + return GetIdentityManager()->PickAccountIdForAccount( identity::GetTestGaiaIdForEmail(kSecondaryEmail), kSecondaryEmail); }
diff --git a/chrome/browser/signin/dice_response_handler.cc b/chrome/browser/signin/dice_response_handler.cc index 08aef0c4..ea2ebd4b 100644 --- a/chrome/browser/signin/dice_response_handler.cc +++ b/chrome/browser/signin/dice_response_handler.cc
@@ -322,7 +322,7 @@ } } std::string account_id = - identity_manager_->LegacyPickAccountIdForAccount(gaia_id, email); + identity_manager_->PickAccountIdForAccount(gaia_id, email); delegate->EnableSync(account_id); } @@ -334,9 +334,8 @@ bool primary_account_signed_out = false; auto* accounts_mutator = identity_manager_->GetAccountsMutator(); for (const auto& account_info : account_infos) { - std::string signed_out_account = - identity_manager_->LegacyPickAccountIdForAccount(account_info.gaia_id, - account_info.email); + std::string signed_out_account = identity_manager_->PickAccountIdForAccount( + account_info.gaia_id, account_info.email); if (signed_out_account == primary_account) { primary_account_signed_out = true; RecordDiceResponseHeader(kSignoutPrimary); @@ -364,8 +363,8 @@ // If a token fetch is in flight for the same account, cancel it. for (auto it = token_fetchers_.begin(); it != token_fetchers_.end(); ++it) { std::string token_fetcher_account_id = - identity_manager_->LegacyPickAccountIdForAccount(it->get()->gaia_id(), - it->get()->email()); + identity_manager_->PickAccountIdForAccount(it->get()->gaia_id(), + it->get()->email()); if (token_fetcher_account_id == signed_out_account) { token_fetchers_.erase(it); break; @@ -418,7 +417,7 @@ const std::string& email = token_fetcher->email(); const std::string& gaia_id = token_fetcher->gaia_id(); std::string account_id = - identity_manager_->LegacyPickAccountIdForAccount(gaia_id, email); + identity_manager_->PickAccountIdForAccount(gaia_id, email); about_signin_internals_->OnRefreshTokenReceived( base::StringPrintf("Failure (%s)", account_id.c_str())); token_fetcher->delegate()->HandleTokenExchangeFailure(email, error);
diff --git a/chrome/browser/signin/dice_response_handler_unittest.cc b/chrome/browser/signin/dice_response_handler_unittest.cc index ac45123..9c035942 100644 --- a/chrome/browser/signin/dice_response_handler_unittest.cc +++ b/chrome/browser/signin/dice_response_handler_unittest.cc
@@ -213,7 +213,7 @@ InitializeDiceResponseHandler(signin::AccountConsistencyMethod::kDice); DiceResponseParams dice_params = MakeDiceParams(DiceAction::SIGNIN); const auto& account_info = dice_params.signin_info->account_info; - std::string account_id = identity_manager()->LegacyPickAccountIdForAccount( + std::string account_id = identity_manager()->PickAccountIdForAccount( account_info.gaia_id, account_info.email); EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id)); dice_response_handler_->ProcessDiceHeader( @@ -247,7 +247,7 @@ InitializeDiceResponseHandler(signin::AccountConsistencyMethod::kDice); DiceResponseParams dice_params = MakeDiceParams(DiceAction::SIGNIN); const auto& account_info = dice_params.signin_info->account_info; - std::string account_id = identity_manager()->LegacyPickAccountIdForAccount( + std::string account_id = identity_manager()->PickAccountIdForAccount( account_info.gaia_id, account_info.email); EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id)); dice_response_handler_->ProcessDiceHeader( @@ -275,7 +275,7 @@ InitializeDiceResponseHandler(signin::AccountConsistencyMethod::kDice); DiceResponseParams dice_params = MakeDiceParams(DiceAction::SIGNIN); const auto& account_info = dice_params.signin_info->account_info; - std::string account_id = identity_manager()->LegacyPickAccountIdForAccount( + std::string account_id = identity_manager()->PickAccountIdForAccount( account_info.gaia_id, account_info.email); ASSERT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id)); dice_response_handler_->ProcessDiceHeader( @@ -311,9 +311,9 @@ dice_params_2.signin_info->account_info.email = "other_email"; dice_params_2.signin_info->account_info.gaia_id = "other_gaia_id"; const auto& account_info_2 = dice_params_2.signin_info->account_info; - std::string account_id_1 = identity_manager()->LegacyPickAccountIdForAccount( + std::string account_id_1 = identity_manager()->PickAccountIdForAccount( account_info_1.gaia_id, account_info_1.email); - std::string account_id_2 = identity_manager()->LegacyPickAccountIdForAccount( + std::string account_id_2 = identity_manager()->PickAccountIdForAccount( account_info_2.gaia_id, account_info_2.email); ASSERT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id_1)); ASSERT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id_2)); @@ -364,7 +364,7 @@ InitializeDiceResponseHandler(signin::AccountConsistencyMethod::kDice); DiceResponseParams dice_params = MakeDiceParams(DiceAction::SIGNIN); const auto& account_info = dice_params.signin_info->account_info; - std::string account_id = identity_manager()->LegacyPickAccountIdForAccount( + std::string account_id = identity_manager()->PickAccountIdForAccount( account_info.gaia_id, account_info.email); ASSERT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id)); dice_response_handler_->ProcessDiceHeader( @@ -396,7 +396,7 @@ InitializeDiceResponseHandler(signin::AccountConsistencyMethod::kDice); DiceResponseParams dice_params = MakeDiceParams(DiceAction::SIGNIN); const auto& account_info = dice_params.signin_info->account_info; - std::string account_id = identity_manager()->LegacyPickAccountIdForAccount( + std::string account_id = identity_manager()->PickAccountIdForAccount( account_info.gaia_id, account_info.email); ASSERT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id)); dice_response_handler_->ProcessDiceHeader( @@ -426,7 +426,7 @@ InitializeDiceResponseHandler(signin::AccountConsistencyMethod::kDice); DiceResponseParams dice_params = MakeDiceParams(DiceAction::SIGNIN); const auto& account_info = dice_params.signin_info->account_info; - std::string account_id = identity_manager()->LegacyPickAccountIdForAccount( + std::string account_id = identity_manager()->PickAccountIdForAccount( account_info.gaia_id, account_info.email); ASSERT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id)); dice_response_handler_->ProcessDiceHeader( @@ -639,9 +639,9 @@ signin_params_2.signin_info->account_info.gaia_id = "other_gaia_id"; const auto& signin_account_info_1 = signin_params_1.signin_info->account_info; const auto& signin_account_info_2 = signin_params_2.signin_info->account_info; - std::string account_id_1 = identity_manager()->LegacyPickAccountIdForAccount( + std::string account_id_1 = identity_manager()->PickAccountIdForAccount( signin_account_info_1.gaia_id, signin_account_info_1.email); - std::string account_id_2 = identity_manager()->LegacyPickAccountIdForAccount( + std::string account_id_2 = identity_manager()->PickAccountIdForAccount( signin_account_info_2.gaia_id, signin_account_info_2.email); dice_response_handler_->ProcessDiceHeader( signin_params_1, std::make_unique<TestProcessDiceHeaderDelegate>(this));
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 17171ba..b52232c 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -3496,11 +3496,7 @@ deps += [ "//apps/ui/views" ] } if (use_aura) { - sources += [ - "views/chrome_javascript_native_dialog_factory_views.cc", - "views/extensions/extension_popup_aura.cc", - "views/extensions/extension_popup_aura.h", - ] + sources += [ "views/chrome_javascript_native_dialog_factory_views.cc" ] deps += [ "//ui/wm/public" ] } }
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc index 535cdfb..f66e8dd 100644 --- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc +++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -338,6 +338,18 @@ ash::mojom::ShelfItemDelegatePtr) override { set_delegate_count_++; } + void GetAutoHideBehaviorForTesting( + int64_t display_id, + GetAutoHideBehaviorForTestingCallback callback) override { + std::move(callback).Run(auto_hide_behavior_); + } + void SetAutoHideBehaviorForTesting( + int64_t display_id, + ash::ShelfAutoHideBehavior behavior, + SetAutoHideBehaviorForTestingCallback callback) override { + auto_hide_behavior_ = behavior; + std::move(callback).Run(); + } // Helper that waits for idle and extracts the non-default bitmap from the // last updated item in shelf controller. @@ -376,6 +388,8 @@ base::OnceClosure updated_callback_; size_t set_delegate_count_ = 0; ash::ShelfItem last_item_; + ash::ShelfAutoHideBehavior auto_hide_behavior_ = + ash::ShelfAutoHideBehavior::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS; ash::mojom::ShelfObserverAssociatedPtr observer_; mojo::Binding<ash::mojom::ShelfController> binding_;
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client.h b/chrome/browser/ui/ash/wallpaper_controller_client.h index 041fae2a..5d515b0 100644 --- a/chrome/browser/ui/ash/wallpaper_controller_client.h +++ b/chrome/browser/ui/ash/wallpaper_controller_client.h
@@ -135,8 +135,6 @@ // Observes if user names should be shown on the login screen, which // determines whether a user wallpaper or a default wallpaper should be shown. - // TODO(wzang|784495): Views-based login should observe this and send - // different requests accordingly. std::unique_ptr<chromeos::CrosSettings::ObserverSubscription> show_user_names_on_signin_subscription_;
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc index 10c680a..1b7d23b8 100644 --- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc +++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
@@ -260,7 +260,7 @@ /////////////////////////////////////////////////////////////////////////////// // views::ContextMenuController implementation: -void ChromeNativeAppWindowViewsAuraAsh::ShowContextMenuForView( +void ChromeNativeAppWindowViewsAuraAsh::ShowContextMenuForViewImpl( views::View* source, const gfx::Point& p, ui::MenuSourceType source_type) { @@ -353,10 +353,16 @@ const std::vector<extensions::DraggableRegion>& regions) { ChromeNativeAppWindowViewsAura::UpdateDraggableRegions(regions); + if (!features::IsUsingWindowService() || !widget()) + return; + SkRegion* draggable_region = GetDraggableRegion(); + auto* window_tree_host = + aura::WindowTreeHostMus::ForWindow(widget()->GetNativeWindow()); + DCHECK(window_tree_host); + // Set the NativeAppWindow's draggable region on the mus window. - if (draggable_region && !draggable_region->isEmpty() && widget() && - features::IsUsingWindowService()) { + if (draggable_region && !draggable_region->isEmpty()) { // Supply client area insets that encompass all draggable regions. gfx::Insets insets(draggable_region->getBounds().bottom(), 0, 0, 0); @@ -372,11 +378,14 @@ for (SkRegion::Iterator i(inverted_region); !i.done(); i.next()) additional_client_regions.push_back(gfx::SkIRectToRect(i.rect())); - aura::WindowTreeHostMus* window_tree_host = - static_cast<aura::WindowTreeHostMus*>( - widget()->GetNativeWindow()->GetHost()); window_tree_host->SetClientArea(insets, std::move(additional_client_regions)); + draggable_regions_sent_ = true; + } else if (draggable_regions_sent_) { + // Once client area is sent and now it's empty, it needs to resend the empty + // insets. + window_tree_host->SetClientArea(gfx::Insets(), std::vector<gfx::Rect>()); + draggable_regions_sent_ = false; } }
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.h b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.h index ee9d1eab..d304dc8 100644 --- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.h +++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.h
@@ -71,9 +71,9 @@ bool IsAlwaysOnTop() const override; // views::ContextMenuController: - void ShowContextMenuForView(views::View* source, - const gfx::Point& p, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& p, + ui::MenuSourceType source_type) override; // WidgetDelegate: views::NonClientFrameView* CreateNonClientFrameView( @@ -187,6 +187,7 @@ std::unique_ptr<ExclusiveAccessBubbleViews> exclusive_access_bubble_; bool tablet_mode_enabled_ = false; + bool draggable_regions_sent_ = false; // Only used in mash. ash::mojom::AshWindowManagerAssociatedPtr ash_window_manager_;
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc index 14afc7f..894c67f5 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -1434,9 +1434,10 @@ RecordBookmarkLaunch(node, GetBookmarkLaunchLocation()); } -void BookmarkBarView::ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) { +void BookmarkBarView::ShowContextMenuForViewImpl( + views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) { if (!model_->loaded()) { // Don't do anything if the model isn't loaded. return;
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h index 0b4d1377..61edc49 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h +++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h
@@ -237,9 +237,9 @@ void ButtonPressed(views::Button* sender, const ui::Event& event) override; // views::ContextMenuController: - void ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; private: class ButtonSeparatorView;
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc index ee6ef08..8aa89e5 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
@@ -25,6 +25,7 @@ #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/views/bookmarks/bookmark_bar_view_observer.h" #include "chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h" #include "chrome/browser/ui/views/chrome_constrained_window_views_client.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" @@ -57,6 +58,7 @@ #include "ui/views/controls/menu/menu_item_view.h" #include "ui/views/controls/menu/submenu_view.h" #include "ui/views/layout/layout_provider.h" +#include "ui/views/widget/drop_helper.h" #include "ui/views/widget/widget.h" #if defined(OS_WIN) @@ -228,13 +230,6 @@ DISALLOW_COPY_AND_ASSIGN(TestingPageNavigator); }; -// TODO(erg): Fix bookmark DND tests on linux_aura. crbug.com/163931 -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) -#define MAYBE(x) DISABLED_##x -#else -#define MAYBE(x) x -#endif - } // namespace // Base class for event generating bookmark view tests. These test are intended @@ -422,12 +417,15 @@ std::unique_ptr<ScopedTestingLocalState> local_state_; }; -class BookmarkBarViewDragTestBase : public BookmarkBarViewEventTestBase { +class BookmarkBarViewDragTestBase : public BookmarkBarViewEventTestBase, + public BookmarkBarViewObserver, + public views::WidgetObserver { public: BookmarkBarViewDragTestBase() = default; ~BookmarkBarViewDragTestBase() override = default; - void OnWidgetDragWillStart() { + // views::WidgetObserver: + void OnWidgetDragWillStart(views::Widget* widget) override { const gfx::Point target = GetDragTargetInScreen(); GetDragTaskRunner()->PostTask( FROM_HERE, @@ -435,7 +433,7 @@ target.x(), target.y())); } - virtual void OnWidgetDragComplete() { + void OnWidgetDragComplete(views::Widget* widget) override { // All drag tests drag node f1a, so at the end of the test, if the node was // dropped where it was expected, the dropped node should have f1a's URL. EXPECT_EQ(f1a_url_, GetDroppedNode()->url()); @@ -446,6 +444,8 @@ protected: // BookmarkBarViewEventTestBase: void DoTestOnMessageLoop() override { + bookmark_bar_observer_.Add(bb_view_.get()); + // Record the URL for node f1a. f1a_url_ = model_->bookmark_bar_node()->GetChild(0)->GetChild(0)->url(); @@ -457,13 +457,22 @@ CreateEventTask(this, &BookmarkBarViewDragTestBase::OnMenuOpened)); } - void OnMenuOpened() { + void TearDown() override { + bookmark_bar_observer_.RemoveAll(); + widget_observer_.RemoveAll(); + BookmarkBarViewEventTestBase::TearDown(); + } + + virtual void OnMenuOpened() { // Menu should be showing. views::MenuItemView* menu = bb_view_->GetMenu(); ASSERT_NE(nullptr, menu); views::SubmenuView* submenu = menu->GetSubmenu(); ASSERT_TRUE(submenu->IsShowing()); + // The menu is showing, so it has a widget we can observe now. + widget_observer_.Add(submenu->GetWidget()); + // Move mouse to center of node f1a and press button. views::View* f1a = submenu->GetMenuItemAt(0); ASSERT_NE(nullptr, f1a); @@ -472,14 +481,13 @@ CreateEventTask(this, &BookmarkBarViewDragTestBase::StartDrag)); } - virtual void AfterDragStarted() = 0; - virtual void OnDragEntered() { - // Drop the element. - ASSERT_TRUE(ui_controls::SendMouseEventsNotifyWhenDone( - ui_controls::LEFT, ui_controls::UP, - CreateEventTask(this, - &BookmarkBarViewDragTestBase::OnWidgetDragComplete))); + // Drop the element, which should result in calling OnWidgetDragComplete(). + GetDragTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce(base::IgnoreResult(&ui_controls::SendMouseEvents), + ui_controls::LEFT, ui_controls::UP, + ui_controls::kNoAccelerator)); } // Called after the drag ends; returns the node the test thinks should be the @@ -490,35 +498,30 @@ // Returns the point the node should be dragged to, in screen coordinates. virtual gfx::Point GetDragTargetInScreen() const = 0; + void SetStopDraggingView(const views::View* view) { + views::DropHelper::SetDragEnteredCallbackForTesting( + view, base::BindRepeating(&BookmarkBarViewDragTestBase::OnDragEntered, + base::Unretained(this))); + } + + ScopedObserver<views::Widget, views::WidgetObserver>* widget_observer() { + return &widget_observer_; + } + private: void StartDrag() { const views::View* drag_view = bb_view_->GetMenu()->GetSubmenu()->GetMenuItemAt(0); const gfx::Point current_position = ui_test_utils::GetCenterInScreenCoordinates(drag_view); -#if defined(USE_AURA) - // TODO: fix this. Aura requires an additional mouse event to trigger drag - // and drop checking state. - EXPECT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone( - current_position.x() + 10, current_position.y(), - CreateEventTask(this, &BookmarkBarViewDragTestBase::StartDrag2))); -#else - EXPECT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone( - current_position.x() + 10, current_position.y(), - CreateEventTask(this, &BookmarkBarViewDragTestBase::AfterDragStarted))); - - OnWidgetDragWillStart(); -#endif - } - - void StartDrag2() { - const gfx::Point target = GetDragTargetInScreen(); - EXPECT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone( - target.x(), target.y(), - CreateEventTask(this, &BookmarkBarViewDragTestBase::AfterDragStarted))); + EXPECT_TRUE(ui_controls::SendMouseMove(current_position.x() + 10, + current_position.y())); } GURL f1a_url_; + ScopedObserver<BookmarkBarView, BookmarkBarViewObserver> + bookmark_bar_observer_{this}; + ScopedObserver<views::Widget, views::WidgetObserver> widget_observer_{this}; }; #if !defined(OS_MACOSX) @@ -795,7 +798,12 @@ class BookmarkBarViewTest5 : public BookmarkBarViewDragTestBase { protected: // BookmarkBarViewDragTestBase: - void AfterDragStarted() override { OnDragEntered(); } + void OnMenuOpened() override { + BookmarkBarViewDragTestBase::OnMenuOpened(); + + // Cause the second menu item to trigger a mouse up when dragged over. + SetStopDraggingView(bb_view_->GetMenu()->GetSubmenu()->GetMenuItemAt(1)); + } const BookmarkNode* GetDroppedNode() const override { return model_->bookmark_bar_node()->GetChild(0)->GetChild(1); @@ -809,8 +817,8 @@ return target; } }; -// Flaky: https://crbug.com/758210 -VIEW_TEST(BookmarkBarViewTest5, DISABLED_DND) + +VIEW_TEST(BookmarkBarViewTest5, DND) // Tests holding mouse down on overflow button, dragging such that menu pops up // then selecting an item. @@ -852,7 +860,8 @@ // Tests drag and drop to different menu. class BookmarkBarViewTest7 : public BookmarkBarViewDragTestBase { public: - void OnDropMenuShown() { + // BookmarkBarViewDragTestBase: + void OnDropMenuShown() override { views::MenuItemView* drop_menu = bb_view_->GetDropMenu(); ASSERT_NE(nullptr, drop_menu); views::SubmenuView* drop_submenu = drop_menu->GetSubmenu(); @@ -862,29 +871,29 @@ EXPECT_EQ(views::Button::STATE_PRESSED, bb_view_->other_bookmarks_button()->state()); + // Cause the target view to trigger a mouse up when dragged over. const views::View* target_view = drop_submenu->GetMenuItemAt(0); + SetStopDraggingView(target_view); // Drag to the top of the target view. gfx::Point target(target_view->width() / 2, 0); views::View::ConvertPointToScreen(target_view, &target); - ASSERT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone( - target.x(), target.y(), - CreateEventTask(this, &BookmarkBarViewTest7::OnDragEntered))); + GetDragTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce(base::IgnoreResult(&ui_controls::SendMouseMove), + target.x(), target.y())); } - // BookmarkBarViewDragTestBase: - void OnWidgetDragComplete() override { + void OnWidgetDragComplete(views::Widget* widget) override { // The button should be in normal state now. EXPECT_EQ(views::Button::STATE_NORMAL, bb_view_->other_bookmarks_button()->state()); - BookmarkBarViewDragTestBase::OnWidgetDragComplete(); + BookmarkBarViewDragTestBase::OnWidgetDragComplete(widget); } protected: // BookmarkBarViewDragTestBase: - void AfterDragStarted() override { OnDropMenuShown(); } - const BookmarkNode* GetDroppedNode() const override { return model_->other_node()->GetChild(0); } @@ -895,46 +904,42 @@ } }; -#if !defined(OS_WIN) -// This test passes locally (on aero and non-aero) but fails on the trybots and -// buildbot. -// http://crbug.com/154081 -VIEW_TEST(BookmarkBarViewTest7, MAYBE(DNDToDifferentMenu)) -#endif +VIEW_TEST(BookmarkBarViewTest7, DNDToDifferentMenu) // Drags from one menu to next so that original menu closes, then back to // original menu. class BookmarkBarViewTest8 : public BookmarkBarViewDragTestBase { public: - void OnDropMenuShown() { + // BookmarkBarViewDragTestBase: + void OnDropMenuShown() override { views::MenuItemView* drop_menu = bb_view_->GetDropMenu(); ASSERT_NE(nullptr, drop_menu); views::SubmenuView* drop_submenu = drop_menu->GetSubmenu(); ASSERT_TRUE(drop_submenu->IsShowing()); const views::View* target_view; - base::OnceClosure task; const auto* controller = static_cast<const BookmarkMenuController*>(drop_menu->GetDelegate()); if (controller->node() == model_->other_node()) { // Now drag back over first menu. target_view = GetBookmarkButton(0); - task = CreateEventTask(this, &BookmarkBarViewTest8::OnDropMenuShown); } else { // Drag to folder F11. target_view = drop_submenu->GetMenuItemAt(1); - task = CreateEventTask(this, &BookmarkBarViewTest8::OnDragEntered); + + // Cause folder F11 to trigger a mouse up when dragged over. + SetStopDraggingView(target_view); } const gfx::Point target = ui_test_utils::GetCenterInScreenCoordinates(target_view); - ASSERT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(target.x(), target.y(), - std::move(task))); + GetDragTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce(base::IgnoreResult(&ui_controls::SendMouseMove), + target.x(), target.y())); } protected: // BookmarkBarViewDragTestBase: - void AfterDragStarted() override { OnDropMenuShown(); } - const BookmarkNode* GetDroppedNode() const override { return model_->bookmark_bar_node()->GetChild(0)->GetChild(0)->GetChild(1); } @@ -945,12 +950,7 @@ } }; -#if !defined(OS_WIN) -// This test passes locally (on aero and non-aero) but fails on the trybots and -// buildbot. -// http://crbug.com/154081 -VIEW_TEST(BookmarkBarViewTest8, MAYBE(DNDBackToOriginatingMenu)) -#endif +VIEW_TEST(BookmarkBarViewTest8, DNDBackToOriginatingMenu) // Moves the mouse over the scroll button and makes sure we get scrolling. class BookmarkBarViewTest9 : public BookmarkBarViewEventTestBase { @@ -1938,27 +1938,37 @@ class BookmarkBarViewTest22 : public BookmarkBarViewDragTestBase { public: // BookmarkBarViewDragTestBase: - void OnWidgetDestroyed() { + void OnWidgetDragWillStart(views::Widget* widget) override { + // Watch for main window destruction instead of menu dragging. + widget_observer()->RemoveAll(); + widget_observer()->Add(window_); + + BookmarkBarViewDragTestBase::OnWidgetDragWillStart(widget); + } + + void OnWidgetDragComplete(views::Widget* widget) override {} + + void OnWidgetDestroyed(views::Widget* widget) override { + widget_observer()->RemoveAll(); Done(); } protected: // BookmarkBarViewDragTestBase: - void AfterDragStarted() override { OnDragEntered(); } + void OnMenuOpened() override { + BookmarkBarViewDragTestBase::OnMenuOpened(); + + // Cause the second menu item to close the window when dragged over. + SetStopDraggingView(bb_view_->GetMenu()->GetSubmenu()->GetMenuItemAt(1)); + } void OnDragEntered() override { -#if defined(OS_CHROMEOS) - ASSERT_TRUE(ui_controls::SendMouseEventsNotifyWhenDone( - ui_controls::LEFT, ui_controls::UP, - CreateEventTask(this, &BookmarkBarViewTest22::OnWidgetDestroyed))); -#endif + // Stop the drag, so any nested message loop will terminate; closing the + // window alone may not exit this message loop. + BookmarkBarViewDragTestBase::OnDragEntered(); window_->Close(); window_ = nullptr; - -#if !defined(OS_CHROMEOS) - OnWidgetDestroyed(); -#endif } const BookmarkNode* GetDroppedNode() const override { @@ -1972,9 +1982,7 @@ } }; -// This test times out on Windows. TODO(pkotwicz): Find out why. -// It also flakes on CrOS and Linux : http://crbug/754188. -VIEW_TEST(BookmarkBarViewTest22, DISABLED_CloseSourceBrowserDuringDrag) +VIEW_TEST(BookmarkBarViewTest22, CloseSourceBrowserDuringDrag) // Tests opening a context menu for a bookmark node from the keyboard. class BookmarkBarViewTest23 : public BookmarkBarViewEventTestBase {
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc index 57cccf34..92c88d92 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
@@ -230,7 +230,7 @@ title_tf_->RequestFocus(); } -void BookmarkEditorView::ShowContextMenuForView( +void BookmarkEditorView::ShowContextMenuForViewImpl( views::View* source, const gfx::Point& point, ui::MenuSourceType source_type) {
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h index 0a271ea..54e099c 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h +++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h
@@ -118,9 +118,9 @@ void Show(gfx::NativeWindow parent); // views::ContextMenuController: - void ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; private: friend class BookmarkEditorViewTest;
diff --git a/chrome/browser/ui/views/download/download_item_view.cc b/chrome/browser/ui/views/download/download_item_view.cc index bb3fc05..8a5daca9 100644 --- a/chrome/browser/ui/views/download/download_item_view.cc +++ b/chrome/browser/ui/views/download/download_item_view.cc
@@ -520,9 +520,10 @@ views::View::OnGestureEvent(event); } -void DownloadItemView::ShowContextMenuForView(View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) { +void DownloadItemView::ShowContextMenuForViewImpl( + View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) { ShowContextMenuImpl(gfx::Rect(point, gfx::Size()), source_type); }
diff --git a/chrome/browser/ui/views/download/download_item_view.h b/chrome/browser/ui/views/download/download_item_view.h index daa83d3d..90842a87 100644 --- a/chrome/browser/ui/views/download/download_item_view.h +++ b/chrome/browser/ui/views/download/download_item_view.h
@@ -113,9 +113,9 @@ void OnGestureEvent(ui::GestureEvent* event) override; // views::ContextMenuController. - void ShowContextMenuForView(View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; // views::ButtonListener: void ButtonPressed(views::Button* sender, const ui::Event& event) override;
diff --git a/chrome/browser/ui/views/extensions/extension_popup.cc b/chrome/browser/ui/views/extensions/extension_popup.cc index 8bc3467..414d003 100644 --- a/chrome/browser/ui/views/extensions/extension_popup.cc +++ b/chrome/browser/ui/views/extensions/extension_popup.cc
@@ -19,6 +19,11 @@ #include "ui/views/layout/fill_layout.h" #include "ui/views/widget/widget.h" +#if defined(USE_AURA) +#include "chrome/browser/ui/browser_dialogs.h" +#include "ui/wm/core/window_animations.h" +#endif + // The minimum/maximum dimensions of the popup. // The minimum is just a little larger than the size of the button itself. // The maximum is an arbitrary number that should be smaller than most screens. @@ -27,7 +32,6 @@ const int ExtensionPopup::kMaxWidth = 800; const int ExtensionPopup::kMaxHeight = 600; -#if !defined(USE_AURA) // static void ExtensionPopup::ShowPopup( std::unique_ptr<extensions::ExtensionViewHost> host, @@ -37,8 +41,16 @@ auto* popup = new ExtensionPopup(host.release(), anchor_view, arrow, show_action); views::BubbleDialogDelegateView::CreateBubble(popup); -} + +#if defined(USE_AURA) + gfx::NativeView native_view = popup->GetWidget()->GetNativeView(); + wm::SetWindowVisibilityAnimationType( + native_view, wm::WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL); + wm::SetWindowVisibilityAnimationVerticalPosition(native_view, -3.0f); + + chrome::RecordDialogCreation(chrome::DialogIdentifier::EXTENSION_POPUP_AURA); #endif +} ExtensionPopup::ExtensionPopup(extensions::ExtensionViewHost* host, views::View* anchor_view, @@ -165,11 +177,6 @@ GetWidget()->Close(); } -void ExtensionPopup::CloseUnlessUnderInspection() { - if (show_action_ == SHOW) - GetWidget()->Close(); -} - void ExtensionPopup::UpdateShowAction(ShowAction show_action) { show_action_ = show_action; set_close_on_deactivate(show_action == SHOW);
diff --git a/chrome/browser/ui/views/extensions/extension_popup.h b/chrome/browser/ui/views/extensions/extension_popup.h index 13bf2f4..9853ff46 100644 --- a/chrome/browser/ui/views/extensions/extension_popup.h +++ b/chrome/browser/ui/views/extensions/extension_popup.h
@@ -92,8 +92,6 @@ views::BubbleBorder::Arrow arrow, ShowAction show_action); - void CloseUnlessUnderInspection(); - private: // Changes internal state to follow the supplied |show_action|. void UpdateShowAction(ShowAction show_action);
diff --git a/chrome/browser/ui/views/extensions/extension_popup_aura.cc b/chrome/browser/ui/views/extensions/extension_popup_aura.cc deleted file mode 100644 index 80e9558..0000000 --- a/chrome/browser/ui/views/extensions/extension_popup_aura.cc +++ /dev/null
@@ -1,68 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/ui/views/extensions/extension_popup_aura.h" - -#include "chrome/browser/extensions/extension_view_host.h" -#include "chrome/browser/ui/browser_dialogs.h" -#include "ui/aura/window.h" -#include "ui/views/widget/widget.h" -#include "ui/wm/core/window_animations.h" -#include "ui/wm/core/window_util.h" -#include "ui/wm/public/activation_client.h" - -// static -void ExtensionPopup::ShowPopup( - std::unique_ptr<extensions::ExtensionViewHost> host, - views::View* anchor_view, - views::BubbleBorder::Arrow arrow, - ShowAction show_action) { - auto* popup = - new ExtensionPopupAura(host.release(), anchor_view, arrow, show_action); - views::Widget* widget = views::BubbleDialogDelegateView::CreateBubble(popup); - gfx::NativeView native_view = widget->GetNativeView(); - - wm::SetWindowVisibilityAnimationType( - native_view, wm::WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL); - wm::SetWindowVisibilityAnimationVerticalPosition(native_view, -3.0f); - - wm::GetActivationClient(native_view->GetRootWindow())->AddObserver(popup); -} - -ExtensionPopupAura::ExtensionPopupAura(extensions::ExtensionViewHost* host, - views::View* anchor_view, - views::BubbleBorder::Arrow arrow, - ShowAction show_action) - : ExtensionPopup(host, anchor_view, arrow, show_action) { - chrome::RecordDialogCreation(chrome::DialogIdentifier::EXTENSION_POPUP_AURA); -} - -ExtensionPopupAura::~ExtensionPopupAura() { -} - -void ExtensionPopupAura::OnWidgetDestroying(views::Widget* widget) { - ExtensionPopup::OnWidgetDestroying(widget); - - if (widget == GetWidget()) { - auto* activation_client = - wm::GetActivationClient(widget->GetNativeWindow()->GetRootWindow()); - // If the popup was being inspected with devtools and the browser window - // was closed, then the root window and activation client are already - // destroyed. - if (activation_client) - activation_client->RemoveObserver(this); - } -} - -void ExtensionPopupAura::OnWindowActivated( - wm::ActivationChangeObserver::ActivationReason reason, - aura::Window* gained_active, - aura::Window* lost_active) { - // Close on anchor window activation (ie. user clicked the browser window). - // DesktopNativeWidgetAura does not trigger the expected browser widget - // [de]activation events when activating widgets in its own root window. - // This additional check handles those cases. See: http://crbug.com/320889 - if (gained_active == anchor_widget()->GetNativeWindow()) - CloseUnlessUnderInspection(); -}
diff --git a/chrome/browser/ui/views/extensions/extension_popup_aura.h b/chrome/browser/ui/views/extensions/extension_popup_aura.h deleted file mode 100644 index bf4e628..0000000 --- a/chrome/browser/ui/views/extensions/extension_popup_aura.h +++ /dev/null
@@ -1,33 +0,0 @@ -// Copyright 2015 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_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSION_POPUP_AURA_H_ -#define CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSION_POPUP_AURA_H_ - -#include "base/macros.h" -#include "chrome/browser/ui/views/extensions/extension_popup.h" -#include "ui/wm/public/activation_change_observer.h" - -class ExtensionPopupAura : public ExtensionPopup, - public wm::ActivationChangeObserver { - public: - ExtensionPopupAura(extensions::ExtensionViewHost* host, - views::View* anchor_view, - views::BubbleBorder::Arrow arrow, - ShowAction show_action); - ~ExtensionPopupAura() override; - - // views::WidgetObserver overrides. - void OnWidgetDestroying(views::Widget* widget) override; - - // wm::ActivationChangeObserver overrides. - void OnWindowActivated(wm::ActivationChangeObserver::ActivationReason reason, - aura::Window* gained_active, - aura::Window* lost_active) override; - - private: - DISALLOW_COPY_AND_ASSIGN(ExtensionPopupAura); -}; - -#endif // CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSION_POPUP_AURA_H_
diff --git a/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc b/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc index fb152c4..93abbea 100644 --- a/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc +++ b/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc
@@ -292,7 +292,7 @@ } } -void MediaGalleriesDialogViews::ShowContextMenuForView( +void MediaGalleriesDialogViews::ShowContextMenuForViewImpl( views::View* source, const gfx::Point& point, ui::MenuSourceType source_type) {
diff --git a/chrome/browser/ui/views/extensions/media_galleries_dialog_views.h b/chrome/browser/ui/views/extensions/media_galleries_dialog_views.h index 6cf4eaa..2de366d 100644 --- a/chrome/browser/ui/views/extensions/media_galleries_dialog_views.h +++ b/chrome/browser/ui/views/extensions/media_galleries_dialog_views.h
@@ -56,9 +56,9 @@ void ButtonPressed(views::Button* sender, const ui::Event& event) override; // views::ContextMenuController implementation: - void ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; private: FRIEND_TEST_ALL_PREFIXES(MediaGalleriesDialogTest, InitializeCheckboxes);
diff --git a/chrome/browser/ui/views/frame/browser_frame.cc b/chrome/browser/ui/views/frame/browser_frame.cc index 80819733..bba0a98 100644 --- a/chrome/browser/ui/views/frame/browser_frame.cc +++ b/chrome/browser/ui/views/frame/browser_frame.cc
@@ -231,9 +231,9 @@ browser_view_->NativeThemeUpdated(observed_theme); } -void BrowserFrame::ShowContextMenuForView(views::View* source, - const gfx::Point& p, - ui::MenuSourceType source_type) { +void BrowserFrame::ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& p, + ui::MenuSourceType source_type) { if (chrome::IsRunningInForcedAppMode()) return;
diff --git a/chrome/browser/ui/views/frame/browser_frame.h b/chrome/browser/ui/views/frame/browser_frame.h index 0f36993..f56be87c 100644 --- a/chrome/browser/ui/views/frame/browser_frame.h +++ b/chrome/browser/ui/views/frame/browser_frame.h
@@ -114,9 +114,9 @@ void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override; // views::ContextMenuController: - void ShowContextMenuForView(views::View* source, - const gfx::Point& p, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& p, + ui::MenuSourceType source_type) override; // Returns the menu model. BrowserFrame owns the returned model. // Note that in multi user mode this will upon each call create a new model.
diff --git a/chrome/browser/ui/views/frame/browser_frame_mash.cc b/chrome/browser/ui/views/frame/browser_frame_mash.cc index 92eb1c3..0536dcb 100644 --- a/chrome/browser/ui/views/frame/browser_frame_mash.cc +++ b/chrome/browser/ui/views/frame/browser_frame_mash.cc
@@ -88,9 +88,6 @@ std::unique_ptr<views::DesktopWindowTreeHostMus> desktop_window_tree_host = std::make_unique<views::DesktopWindowTreeHostMus>( std::move(window_tree_host_init_params), browser_frame_, this); - // BrowserNonClientFrameViewAsh::OnBoundsChanged() takes care of updating - // the insets. - desktop_window_tree_host->set_auto_update_client_area(false); SetDesktopWindowTreeHost(std::move(desktop_window_tree_host)); return params; }
diff --git a/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc b/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc index 7d747da21..086487cc 100644 --- a/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc +++ b/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc
@@ -14,6 +14,7 @@ #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/controls/menu/submenu_view.h" #include "ui/views/view.h" +#include "ui/views/widget/drop_helper.h" namespace { @@ -134,7 +135,8 @@ } // namespace -class MenuViewDragAndDropTest : public MenuTestBase { +class MenuViewDragAndDropTest : public MenuTestBase, + public views::WidgetObserver { public: MenuViewDragAndDropTest() = default; ~MenuViewDragAndDropTest() override = default; @@ -143,9 +145,16 @@ // MenuTestBase: void BuildMenu(views::MenuItemView* menu) override; void DoTestWithMenuOpen() override; + void TearDown() override; virtual void OnDragEntered(); + void SetStopDraggingView(const views::View* view) { + views::DropHelper::SetDragEnteredCallbackForTesting( + view, base::BindRepeating(&MenuViewDragAndDropTest::OnDragEntered, + base::Unretained(this))); + } + TestTargetView* target_view() { return target_view_; } bool asked_to_close() const { return asked_to_close_; } bool performed_in_menu_drop() const { return performed_in_menu_drop_; } @@ -180,6 +189,8 @@ // in separate child views). bool performed_in_menu_drop_ = false; + ScopedObserver<views::Widget, views::WidgetObserver> widget_observer_{this}; + DISALLOW_COPY_AND_ASSIGN(MenuViewDragAndDropTest); }; @@ -208,13 +219,21 @@ const views::View* child_view = first_view->child_at(0); EXPECT_EQ(child_view, target_view_); + // The menu is showing, so it has a widget we can observe now. + widget_observer_.Add(submenu->GetWidget()); + // We do this here (instead of in BuildMenu()) so that the menu is already // built and the bounds are correct. target_view_->Init(); } +void MenuViewDragAndDropTest::TearDown() { + widget_observer_.RemoveAll(); + MenuTestBase::TearDown(); +} + void MenuViewDragAndDropTest::OnDragEntered() { - // Drop the element. + // Drop the element, which should result in calling OnWidgetDragComplete(). GetDragTaskRunner()->PostTask( FROM_HERE, base::BindOnce(base::IgnoreResult(&ui_controls::SendMouseEvents), @@ -278,8 +297,9 @@ MenuViewDragAndDropTestTestInMenuDrag() = default; ~MenuViewDragAndDropTestTestInMenuDrag() override = default; - void OnWidgetDragWillStart(); - void OnWidgetDragComplete(); + // views::WidgetObserver: + void OnWidgetDragWillStart(views::Widget* widget) override; + void OnWidgetDragComplete(views::Widget* widget) override; protected: // MenuViewDragAndDropTest: @@ -289,8 +309,10 @@ void StartDrag(); }; -void MenuViewDragAndDropTestTestInMenuDrag::OnWidgetDragWillStart() { - // Enqueue an event to drag the second menu element to the third element. +void MenuViewDragAndDropTestTestInMenuDrag::OnWidgetDragWillStart( + views::Widget* widget) { + // Enqueue an event to drag the second menu element to the third element, + // which should result in calling OnDragEntered(). const views::View* drop_target_view = menu()->GetSubmenu()->GetMenuItemAt(2); const gfx::Point target = ui_test_utils::GetCenterInScreenCoordinates(drop_target_view); @@ -299,7 +321,8 @@ target.x(), target.y())); } -void MenuViewDragAndDropTestTestInMenuDrag::OnWidgetDragComplete() { +void MenuViewDragAndDropTestTestInMenuDrag::OnWidgetDragComplete( + views::Widget* widget) { // We should have performed an in-menu drop, and the nested view should not // have had a drag and drop. Since the drag happened in menu code, the // delegate should not have been asked whether or not to close, and the menu @@ -315,7 +338,9 @@ void MenuViewDragAndDropTestTestInMenuDrag::DoTestWithMenuOpen() { MenuViewDragAndDropTest::DoTestWithMenuOpen(); + // Cause the third menu item to trigger a mouse up when dragged over. views::SubmenuView* submenu = menu()->GetSubmenu(); + SetStopDraggingView(submenu->GetMenuItemAt(2)); // We're going to drag the second menu element. views::MenuItemView* drag_view = submenu->GetMenuItemAt(1); @@ -326,33 +351,34 @@ } void MenuViewDragAndDropTestTestInMenuDrag::StartDrag() { - // Begin dragging the second menu element. + // Begin dragging the second menu element, which should result in calling + // OnWidgetDragWillStart(). const views::View* drag_view = menu()->GetSubmenu()->GetMenuItemAt(1); const gfx::Point current_position = ui_test_utils::GetCenterInScreenCoordinates(drag_view); - EXPECT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone( - current_position.x() + 10, current_position.y(), - CreateEventTask(this, - &MenuViewDragAndDropTestTestInMenuDrag::OnDragEntered))); - - OnWidgetDragWillStart(); + EXPECT_TRUE(ui_controls::SendMouseMove(current_position.x() + 10, + current_position.y())); } // Test that an in-menu (i.e., entirely implemented in the menu code) closes the // menu automatically once the drag is complete, and does not ask the delegate // to stay open. -// Disabled for being flaky. Tracked in: -// TODO(erg): Fix DND tests on linux_aura. http://crbug.com/163931. -// TODO(tapted): De-flake and run on Mac. http://crbug.com/449058. -VIEW_TEST(MenuViewDragAndDropTestTestInMenuDrag, DISABLED_TestInMenuDrag) +// TODO(pkasting): https://crbug.com/939621 Fails on Mac. +#if defined(OS_MACOSX) +#define MAYBE_TestInMenuDrag DISABLED_TestInMenuDrag +#else +#define MAYBE_TestInMenuDrag TestInMenuDrag +#endif +VIEW_TEST(MenuViewDragAndDropTestTestInMenuDrag, MAYBE_TestInMenuDrag) class MenuViewDragAndDropTestNestedDrag : public MenuViewDragAndDropTest { public: MenuViewDragAndDropTestNestedDrag() = default; ~MenuViewDragAndDropTestNestedDrag() override = default; - void OnWidgetDragWillStart(); - void OnWidgetDragComplete(); + // views::WidgetObserver: + void OnWidgetDragWillStart(views::Widget* widget) override; + void OnWidgetDragComplete(views::Widget* widget) override; protected: // MenuViewDragAndDropTest: @@ -363,8 +389,10 @@ void StartDrag(); }; -void MenuViewDragAndDropTestNestedDrag::OnWidgetDragWillStart() { - // Enqueue an event to drag the target's first child to its second. +void MenuViewDragAndDropTestNestedDrag::OnWidgetDragWillStart( + views::Widget* widget) { + // Enqueue an event to drag the target's first child to its second, which + // should result in calling OnDragEntered(). const views::View* drop_target_view = target_view()->child_at(1); const gfx::Point target = ui_test_utils::GetCenterInScreenCoordinates(drop_target_view); @@ -373,7 +401,8 @@ target.x(), target.y())); } -void MenuViewDragAndDropTestNestedDrag::OnWidgetDragComplete() { +void MenuViewDragAndDropTestNestedDrag::OnWidgetDragComplete( + views::Widget* widget) { // The target view should have finished its drag, and should have dropped the // view. The main menu should not have done any drag, and the delegate should // have been asked if it wanted to close. Since the delegate did not want to @@ -394,8 +423,9 @@ void MenuViewDragAndDropTestNestedDrag::DoTestWithMenuOpen() { MenuViewDragAndDropTest::DoTestWithMenuOpen(); - // The target view should now have two children. + // Cause the target's second child to trigger a mouse up when dragged over. ASSERT_EQ(2, target_view()->child_count()); + SetStopDraggingView(target_view()->child_at(1)); // We're going to drag the target's first child. views::View* drag_view = target_view()->child_at(0); @@ -413,27 +443,27 @@ } void MenuViewDragAndDropTestNestedDrag::StartDrag() { - // Begin dragging the target's first child. + // Begin dragging the target's first child, which should result in calling + // OnWidgetDragWillStart(). const views::View* drag_view = target_view()->child_at(0); const gfx::Point current_position = ui_test_utils::GetCenterInScreenCoordinates(drag_view); - EXPECT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone( - current_position.x() + 10, current_position.y(), - CreateEventTask(this, - &MenuViewDragAndDropTestNestedDrag::OnDragEntered))); - - OnWidgetDragWillStart(); + EXPECT_TRUE(ui_controls::SendMouseMove(current_position.x() + 10, + current_position.y())); } // Test that a nested drag (i.e. one via a child view, and not entirely // implemented in menu code) will consult the delegate before closing the view // after the drag. -// Disabled for being flaky. Tracked in: -// TODO(erg): Fix DND tests on linux_aura. http://crbug.com/163931. -// TODO(tapted): De-flake and run on Mac. http://crbug.com/449058. -// TODO(crbug.com/829922): Flaky on Windows. +// TODO(pkasting): https://crbug.com/939621 Fails on Mac. +#if defined(OS_MACOSX) +#define MAYBE_MenuViewDragAndDropNestedDrag \ + DISABLED_MenuViewDragAndDropNestedDrag +#else +#define MAYBE_MenuViewDragAndDropNestedDrag MenuViewDragAndDropNestedDrag +#endif VIEW_TEST(MenuViewDragAndDropTestNestedDrag, - DISABLED_MenuViewDragAndDropNestedDrag) + MAYBE_MenuViewDragAndDropNestedDrag) class MenuViewDragAndDropForDropStayOpen : public MenuViewDragAndDropTest { public:
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc index ae7e64a..b61d44a 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
@@ -421,9 +421,10 @@ SchedulePaint(); } -void OmniboxResultView::ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) { +void OmniboxResultView::ShowContextMenuForViewImpl( + views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) { // Deferred unhover of the result until the context menu is closed. // If the mouse is still over the result when the context menu is closed, the // View will receive an OnMouseMoved call anyways, which sets hover to true.
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.h b/chrome/browser/ui/views/omnibox/omnibox_result_view.h index 88750f4..a7fbe597 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_result_view.h +++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.h
@@ -97,9 +97,9 @@ void OnNativeThemeChanged(const ui::NativeTheme* theme) override; // views::ContextMenuController: - void ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; // ui::SimpleMenuModel::Delegate overrides: bool IsCommandIdEnabled(int command_id) const override;
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc index dc518b8e..3a3d939d8 100644 --- a/chrome/browser/ui/views/tabs/tab.cc +++ b/chrome/browser/ui/views/tabs/tab.cc
@@ -197,9 +197,9 @@ TouchUMA::RecordGestureAction(TouchUMA::kGestureTabCloseTap); } -void Tab::ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) { +void Tab::ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) { if (!closing_) controller_->ShowContextMenuForTab(this, point, source_type); }
diff --git a/chrome/browser/ui/views/tabs/tab.h b/chrome/browser/ui/views/tabs/tab.h index 019c1aec..0320a31 100644 --- a/chrome/browser/ui/views/tabs/tab.h +++ b/chrome/browser/ui/views/tabs/tab.h
@@ -69,9 +69,9 @@ void ButtonPressed(views::Button* sender, const ui::Event& event) override; // views::ContextMenuController: - void ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; // views::MaskedTargeterDelegate: bool GetHitTestMask(SkPath* mask) const override;
diff --git a/chrome/browser/ui/views/task_manager_view.cc b/chrome/browser/ui/views/task_manager_view.cc index d9a9d82..878f092 100644 --- a/chrome/browser/ui/views/task_manager_view.cc +++ b/chrome/browser/ui/views/task_manager_view.cc
@@ -272,9 +272,10 @@ ActivateSelectedTab(); } -void TaskManagerView::ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) { +void TaskManagerView::ShowContextMenuForViewImpl( + views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) { menu_model_.reset(new ui::SimpleMenuModel(this)); for (const auto& table_column : columns_) {
diff --git a/chrome/browser/ui/views/task_manager_view.h b/chrome/browser/ui/views/task_manager_view.h index 2213794..822e31b 100644 --- a/chrome/browser/ui/views/task_manager_view.h +++ b/chrome/browser/ui/views/task_manager_view.h
@@ -79,9 +79,9 @@ void OnKeyDown(ui::KeyboardCode keycode) override; // views::ContextMenuController: - void ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; // ui::SimpleMenuModel::Delegate: bool IsCommandIdChecked(int id) const override;
diff --git a/chrome/browser/ui/views/test/view_event_test_base.cc b/chrome/browser/ui/views/test/view_event_test_base.cc index 0cc625e..45f6ce54 100644 --- a/chrome/browser/ui/views/test/view_event_test_base.cc +++ b/chrome/browser/ui/views/test/view_event_test_base.cc
@@ -64,6 +64,7 @@ } void ViewEventTestBase::Done() { + drag_event_thread_.reset(); run_loop_.Quit(); } @@ -169,16 +170,25 @@ scoped_refptr<base::SingleThreadTaskRunner> ViewEventTestBase::GetDragTaskRunner() { +#if defined(OS_WIN) + // Drag events must be posted from a background thread, since starting a drag + // triggers a nested message loop that filters messages other than mouse + // events, so further tasks on the main message loop will be blocked. if (!drag_event_thread_) { drag_event_thread_ = std::make_unique<base::Thread>("drag-event-thread"); drag_event_thread_->Start(); } return drag_event_thread_->task_runner(); +#else + // Drag events must be posted from the current thread, since UI events on many + // platforms cannot be posted from background threads. The nested drag + // message loop on non-Windows does not filter out non-input events, so these + // tasks will run. + return base::ThreadTaskRunnerHandle::Get(); +#endif } void ViewEventTestBase::RunTestMethod(base::OnceClosure task) { - drag_event_thread_.reset(); - std::move(task).Run(); if (HasFatalFailure()) Done();
diff --git a/chrome/browser/ui/views/test/view_event_test_base.h b/chrome/browser/ui/views/test/view_event_test_base.h index d18bed61..a3fc4c84 100644 --- a/chrome/browser/ui/views/test/view_event_test_base.h +++ b/chrome/browser/ui/views/test/view_event_test_base.h
@@ -122,8 +122,8 @@ views::Widget* window_; private: - // Callback from CreateEventTask. Stops the background thread, runs the - // supplied task and if there are failures invokes Done. + // Callback from CreateEventTask. Runs the supplied task and if there are + // failures invokes Done. void RunTestMethod(base::OnceClosure task); // The content of the Window.
diff --git a/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc b/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc index 6d0b592..22ac7e15 100644 --- a/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc +++ b/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
@@ -409,9 +409,9 @@ GetToolbarInkDropInsets(this, *GetProperty(views::kInternalPaddingKey)); const float corner_radius = (height() - ink_drop_insets.top() - ink_drop_insets.bottom()) / 2.0f; - return std::make_unique<PulsingInkDropMask>( - ink_drop_container(), ink_drop_container()->size(), ink_drop_insets, - corner_radius, kFeaturePromoPulseInsetDip); + return std::make_unique<PulsingInkDropMask>(ink_drop_container(), size(), + ink_drop_insets, corner_radius, + kFeaturePromoPulseInsetDip); } #endif
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view.cc b/chrome/browser/ui/views/toolbar/toolbar_action_view.cc index b651ac1..f679c65 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_action_view.cc +++ b/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
@@ -137,7 +137,7 @@ bool ToolbarActionView::OnKeyPressed(const ui::KeyEvent& event) { if (event.key_code() == ui::VKEY_DOWN) { - ShowContextMenuForView(this, gfx::Point(), ui::MENU_SOURCE_KEYBOARD); + ShowContextMenuForViewImpl(this, gfx::Point(), ui::MENU_SOURCE_KEYBOARD); return true; } return MenuButton::OnKeyPressed(event); @@ -308,9 +308,10 @@ pressed_lock_.reset(); // Unpress the menu button if it was pressed. } -void ToolbarActionView::ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) { +void ToolbarActionView::ShowContextMenuForViewImpl( + views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) { if (CloseActiveMenuIfNeeded()) return;
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view.h b/chrome/browser/ui/views/toolbar/toolbar_action_view.h index 334c66c..0e06771e 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_action_view.h +++ b/chrome/browser/ui/views/toolbar/toolbar_action_view.h
@@ -116,9 +116,9 @@ void OnPopupClosed() override; // views::ContextMenuController: - void ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; // Shows the context menu (if one exists) for the toolbar action. void DoShowContextMenu(ui::MenuSourceType source_type);
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view_unittest.cc b/chrome/browser/ui/views/toolbar/toolbar_action_view_unittest.cc index bb679b7..e896019 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_action_view_unittest.cc +++ b/chrome/browser/ui/views/toolbar/toolbar_action_view_unittest.cc
@@ -77,9 +77,9 @@ view_->set_context_menu_controller(nullptr); } - void ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override { + void ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override { opened_menu_ = true; }
diff --git a/chrome/browser/ui/views/toolbar/toolbar_button.cc b/chrome/browser/ui/views/toolbar/toolbar_button.cc index dcff72c..9e5dac52 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_button.cc +++ b/chrome/browser/ui/views/toolbar/toolbar_button.cc
@@ -257,9 +257,9 @@ return GetToolbarInkDropBaseColor(this); } -void ToolbarButton::ShowContextMenuForView(View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) { +void ToolbarButton::ShowContextMenuForViewImpl(View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) { if (!enabled()) return;
diff --git a/chrome/browser/ui/views/toolbar/toolbar_button.h b/chrome/browser/ui/views/toolbar/toolbar_button.h index c91d68d..b54a6a0 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_button.h +++ b/chrome/browser/ui/views/toolbar/toolbar_button.h
@@ -84,9 +84,9 @@ SkColor GetInkDropBaseColor() const override; // views::ContextMenuController: - void ShowContextMenuForView(View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; ui::MenuModel* menu_model_for_test() { return model_.get(); }
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc b/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc index a3b8179..dbf8987 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc +++ b/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc
@@ -10,9 +10,7 @@ #include "base/location.h" #include "base/macros.h" #include "base/run_loop.h" -#include "base/single_thread_task_runner.h" #include "base/strings/utf_string_conversions.h" -#include "base/threading/thread_restrictions.h" #include "build/build_config.h" #include "chrome/app/chrome_command_ids.h" #include "chrome/browser/bookmarks/bookmark_model_factory.h" @@ -39,10 +37,18 @@ #include "ui/views/view.h" #include "ui/views/widget/widget.h" +#if defined(OS_WIN) +#include "base/threading/thread.h" +#include "base/threading/thread_restrictions.h" +#else +#include "base/threading/thread_task_runner_handle.h" +#endif + using bookmarks::BookmarkModel; class ToolbarViewInteractiveUITest : public AppMenuButtonObserver, - public extensions::ExtensionBrowserTest { + public extensions::ExtensionBrowserTest, + public views::WidgetObserver { public: ToolbarViewInteractiveUITest() = default; ~ToolbarViewInteractiveUITest() override = default; @@ -50,8 +56,9 @@ // AppMenuButtonObserver: void AppMenuShown() override; - void OnWidgetDragWillStart(); - void OnWidgetDragComplete(); + // views::WidgetObserver: + void OnWidgetDragWillStart(views::Widget* widget) override; + void OnWidgetDragComplete(views::Widget* widget) override; // Starts a drag to the app menu button. void StartDrag(); @@ -88,14 +95,17 @@ void ToolbarViewInteractiveUITest::AppMenuShown() { menu_shown_ = true; - // Release the mouse button. - ui_controls::SendMouseEventsNotifyWhenDone( - ui_controls::LEFT, ui_controls::UP, - base::BindOnce(&ToolbarViewInteractiveUITest::OnWidgetDragComplete, - base::Unretained(this))); + // Release the mouse button, which should result in calling + // OnWidgetDragComplete(). + task_runner_->PostTask( + FROM_HERE, + base::BindOnce(base::IgnoreResult(&ui_controls::SendMouseEvents), + ui_controls::LEFT, ui_controls::UP, + ui_controls::kNoAccelerator)); } -void ToolbarViewInteractiveUITest::OnWidgetDragWillStart() { +void ToolbarViewInteractiveUITest::OnWidgetDragWillStart( + views::Widget* widget) { // Enqueue an event to move the mouse to the app menu button, which should // result in calling AppMenuShown(). const gfx::Point target = @@ -105,20 +115,19 @@ target.x(), target.y())); } -void ToolbarViewInteractiveUITest::OnWidgetDragComplete() { +void ToolbarViewInteractiveUITest::OnWidgetDragComplete(views::Widget* widget) { // Return control to the testcase. std::move(quit_closure_).Run(); } void ToolbarViewInteractiveUITest::StartDrag() { - // Move the mouse outside the toolbar action. + // Move the mouse outside the toolbar action, which should result in calling + // OnWidgetDragWillStart(). const views::View* toolbar_action = GetBrowserActions()->GetToolbarActionViewAt(0); gfx::Point target(toolbar_action->width() + 1, toolbar_action->height() / 2); views::View::ConvertPointToScreen(toolbar_action, &target); EXPECT_TRUE(ui_controls::SendMouseMove(target.x(), target.y())); - - OnWidgetDragWillStart(); } void ToolbarViewInteractiveUITest::SetUpOnMainThread() { @@ -129,16 +138,8 @@ ToolbarActionsBar::disable_animations_for_testing_ = true; } -#if defined(OS_LINUX) && defined(USE_AURA) -// TODO(pkasting): https://crbug.com/923188 Flaky -#define MAYBE_TestAppMenuOpensOnDrag DISABLED_TestAppMenuOpensOnDrag -#elif defined(OS_MACOSX) -// TODO(pkasting): https://crbug.com/910435 Test hangs in the run loop on Mac, I -// don't know why. -#define MAYBE_TestAppMenuOpensOnDrag DISABLED_TestAppMenuOpensOnDrag -#elif defined(USE_OZONE) -// TODO(pkasting): https://crbug.com/910423 Can't post mouse events from -// background threads on Ozone, which is required to avoid hanging. +// TODO(pkasting): https://crbug.com/939621 Fails on Mac. +#if defined(OS_MACOSX) #define MAYBE_TestAppMenuOpensOnDrag DISABLED_TestAppMenuOpensOnDrag #else #define MAYBE_TestAppMenuOpensOnDrag TestAppMenuOpensOnDrag @@ -156,12 +157,16 @@ // Set up observers that will drive the test along. AppMenuButton* const app_menu_button = GetAppMenuButton(); EXPECT_FALSE(app_menu_button->IsMenuShowing()); + ScopedObserver<views::Widget, views::WidgetObserver> widget_observer(this); + widget_observer.Add( + BrowserView::GetBrowserViewForBrowser(browser())->GetWidget()); ScopedObserver<AppMenuButton, AppMenuButtonObserver> button_observer(this); button_observer.Add(app_menu_button); // Set up the task runner to use for posting drag actions. // TODO(devlin): This is basically ViewEventTestBase::GetDragTaskRunner(). In // a perfect world, this would be factored better. +#if defined(OS_WIN) // Drag events must be posted from a background thread, since starting a drag // triggers a nested message loop that filters messages other than mouse // events, so further tasks on the main message loop will be blocked. @@ -169,6 +174,13 @@ base::Thread drag_event_thread("drag-event-thread"); drag_event_thread.Start(); set_task_runner(drag_event_thread.task_runner()); +#else + // Drag events must be posted from the current thread, since UI events on many + // platforms cannot be posted from background threads. The nested drag + // message loop on non-Windows does not filter out non-input events, so these + // tasks will run. + set_task_runner(base::ThreadTaskRunnerHandle::Get()); +#endif // Click on the toolbar action. BrowserActionsContainer* const browser_actions = GetBrowserActions();
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc index 5248587f..424212a 100644 --- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc +++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -30,6 +30,7 @@ #include "chrome/browser/printing/print_preview_data_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/chrome_pages.h" +#include "chrome/browser/ui/webui/dark_mode_handler.h" #include "chrome/browser/ui/webui/localized_string.h" #include "chrome/browser/ui/webui/metrics_handler.h" #include "chrome/browser/ui/webui/print_preview/print_preview_handler.h" @@ -471,6 +472,7 @@ // Set up the chrome://print/ data source. Profile* profile = Profile::FromWebUI(web_ui); content::WebUIDataSource* source = CreatePrintPreviewUISource(profile); + DarkModeHandler::Initialize(web_ui, source); content::WebUIDataSource::Add(profile, source); // Set up the chrome://theme/ source.
diff --git a/chrome/browser/ui/webui/signin/md_user_manager_ui.cc b/chrome/browser/ui/webui/signin/md_user_manager_ui.cc index c012745..7c65f834 100644 --- a/chrome/browser/ui/webui/signin/md_user_manager_ui.cc +++ b/chrome/browser/ui/webui/signin/md_user_manager_ui.cc
@@ -13,6 +13,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_shortcut_manager.h" #include "chrome/browser/signin/signin_util.h" +#include "chrome/browser/ui/webui/dark_mode_handler.h" #include "chrome/browser/ui/webui/signin/signin_create_profile_handler.h" #include "chrome/browser/ui/webui/signin/signin_utils.h" #include "chrome/browser/ui/webui/signin/user_manager_screen_handler.h" @@ -44,6 +45,7 @@ // Set up the chrome://md-user-manager/ source. auto* md_user_source = CreateUIDataSource(localized_strings); + DarkModeHandler::Initialize(web_ui, md_user_source); content::WebUIDataSource::Add(profile, md_user_source); // Set up the chrome://theme/ source
diff --git a/chrome/common/extensions/api/autotest_private.idl b/chrome/common/extensions/api/autotest_private.idl index ee0e2924..ed28d97 100644 --- a/chrome/common/extensions/api/autotest_private.idl +++ b/chrome/common/extensions/api/autotest_private.idl
@@ -164,6 +164,8 @@ callback SetTabletModeEnabledCallback = void(boolean enabled); + callback GetShelfAutoHideBehaviorCallback = void (DOMString behavior); + callback VoidCallback = void (); interface Functions { @@ -334,7 +336,7 @@ static void getPrimaryDisplayScaleFactor( GetPrimaryDisplayScaleFactorCallback callback); - // Returns the tablet mode enabled status. + // Get the tablet mode enabled status. // |callback| is invoked with the tablet mode enablement status. static void isTabletModeEnabled(IsTabletModeEnabledCallback callback); @@ -345,5 +347,19 @@ // |callback|: Called when the operation has completed. static void setTabletModeEnabled(boolean enabled, SetTabletModeEnabledCallback callback); + + // Get the shelf auto hide behavior. + // |displayId|: display that contains the shelf. + // |callback| is invoked with the shelf auto hide behavior. Possible + // behavior values are: "always", "never" or "hidden". + static void getShelfAutoHideBehavior(DOMString displayId, + GetShelfAutoHideBehaviorCallback callback); + + // Set the shelf auto hide behavior. + // |displayId|: display that contains the shelf. + // |behavior|: an enum of "always", "never" or "hidden". + // |callback|: Called when the operation has completed. + static void setShelfAutoHideBehavior(DOMString displayId, + DOMString behavior, VoidCallback callback); }; };
diff --git a/chrome/renderer/plugins/chrome_plugin_placeholder.cc b/chrome/renderer/plugins/chrome_plugin_placeholder.cc index eac14f9a..83de4a9d 100644 --- a/chrome/renderer/plugins/chrome_plugin_placeholder.cc +++ b/chrome/renderer/plugins/chrome_plugin_placeholder.cc
@@ -94,6 +94,7 @@ IDR_BLOCKED_PLUGIN_HTML)); base::DictionaryValue values; + values.SetString("name", ""); values.SetString("message", l10n_util::GetStringUTF8(IDS_PLUGIN_NOT_SUPPORTED)); @@ -138,6 +139,12 @@ roundf(power_saver_info.custom_poster_size.height() / zoom_factor); values.SetString("visibleWidth", base::NumberToString(width) + "px"); values.SetString("visibleHeight", base::NumberToString(height) + "px"); + } else { + // Need to populate these to please $i18n{...} replacement mechanism. + // 'undefined' is used on purpose as an invalid value for width and + // height, which is ignored by CSS. + values.SetString("visibleWidth", "undefined"); + values.SetString("visibleHeight", "undefined"); } }
diff --git a/chrome/renderer/plugins/non_loadable_plugin_placeholder.cc b/chrome/renderer/plugins/non_loadable_plugin_placeholder.cc index ab364af..796b206 100644 --- a/chrome/renderer/plugins/non_loadable_plugin_placeholder.cc +++ b/chrome/renderer/plugins/non_loadable_plugin_placeholder.cc
@@ -27,6 +27,7 @@ IDR_BLOCKED_PLUGIN_HTML)); base::DictionaryValue values; + values.SetString("name", ""); values.SetString("message", l10n_util::GetStringUTF8(IDS_PLUGIN_NOT_SUPPORTED)); @@ -41,6 +42,7 @@ content::RenderFrame* render_frame, const base::FilePath& file_path) { base::DictionaryValue values; + values.SetString("name", ""); values.SetString("message", l10n_util::GetStringUTF8(IDS_PLUGIN_INITIALIZATION_ERROR));
diff --git a/chrome/renderer/resources/plugins/blocked_plugin.html b/chrome/renderer/resources/plugins/blocked_plugin.html index bbbee95d..c4554552 100644 --- a/chrome/renderer/resources/plugins/blocked_plugin.html +++ b/chrome/renderer/resources/plugins/blocked_plugin.html
@@ -25,7 +25,7 @@ <link rel="stylesheet" href="plugin_placeholders.css"></link> </head> <body id="t" onload="notifyDidFinishLoading();"> - <div i18n-values="title:name" id="outer"> + <div title="$i18n{name}" id="outer"> <img class="icon" src="../../../../ui/webui/resources/images/extension.svg"> <h1 id="message">$i18n{message}</h1>
diff --git a/chrome/renderer/resources/plugins/disabled_plugin.html b/chrome/renderer/resources/plugins/disabled_plugin.html index fd40773d..98b28021 100644 --- a/chrome/renderer/resources/plugins/disabled_plugin.html +++ b/chrome/renderer/resources/plugins/disabled_plugin.html
@@ -6,7 +6,7 @@ <link rel="stylesheet" href="plugin_placeholders.css"></link> </head> <body id="t" onLoad="insertLink()"> - <div i18n-values="title:name" id="outer"> + <div title="$i18n{name}" id="outer"> <img class="icon" src="../../../../ui/webui/resources/images/extension.svg"> <h1 id="message">$i18n{message}</h1>
diff --git a/chrome/renderer/resources/plugins/plugin_poster.html b/chrome/renderer/resources/plugins/plugin_poster.html index f7adefb0..6758fa72 100644 --- a/chrome/renderer/resources/plugins/plugin_poster.html +++ b/chrome/renderer/resources/plugins/plugin_poster.html
@@ -67,15 +67,15 @@ z-index: 2; } </style> -<base i18n-values="href:baseurl"> +<base href="$i18n{baseurl}"> </head> <body> - <div i18n-values="title:name" id="outer"> + <div title="$i18n{name}" id="outer"> <img id="poster" i18n-values="srcset:poster"> <div id="shielding"></div> <div id="inner-container" - i18n-values=".style.width:visibleWidth;.style.height:visibleHeight"> + style="width:$i18n{visibleWidth};height:$i18n{visibleHeight}"> <img id="plugin-icon" src="plugin_power_saver_play.png" /> </div> </div>
diff --git a/chrome/renderer/resources/plugins/prefer_html_plugin.html b/chrome/renderer/resources/plugins/prefer_html_plugin.html index f4479f1..6a3f4a69 100644 --- a/chrome/renderer/resources/plugins/prefer_html_plugin.html +++ b/chrome/renderer/resources/plugins/prefer_html_plugin.html
@@ -23,7 +23,7 @@ </style> </head> <body id="t" onload="notifyDidFinishLoading();"> - <div i18n-values="title:name" id="outer"> + <div title="$i18n{name}" id="outer"> <img class="icon" src="../../../../ui/webui/resources/images/extension.svg"> <h1 id="message">$i18n{message}</h1>
diff --git a/chrome/test/data/extensions/api_test/autotest_private/manifest.json b/chrome/test/data/extensions/api_test/autotest_private/manifest.json index 33725d4f..bba3017c 100644 --- a/chrome/test/data/extensions/api_test/autotest_private/manifest.json +++ b/chrome/test/data/extensions/api_test/autotest_private/manifest.json
@@ -8,5 +8,5 @@ "persistent": false, "scripts": ["test.js"] }, - "permissions": ["autotestPrivate"] + "permissions": ["autotestPrivate", "system.display"] }
diff --git a/chrome/test/data/extensions/api_test/autotest_private/test.js b/chrome/test/data/extensions/api_test/autotest_private/test.js index 2be6f9e..e64e54b2 100644 --- a/chrome/test/data/extensions/api_test/autotest_private/test.js +++ b/chrome/test/data/extensions/api_test/autotest_private/test.js
@@ -242,8 +242,8 @@ chrome.test.callbackFail( 'Assistant is not available for the current user')); }, - // This test verifies that getArcState returns provisined False in case ARC - // is not provisoned by default. + // This test verifies that getArcState returns provisioned False in case ARC + // is not provisioned by default. function arcNotProvisioned() {chrome.autotestPrivate.getArcState( function(state) { chrome.test.assertFalse(state.provisioned); @@ -314,11 +314,40 @@ chrome.test.succeed(); }); }, + // This test verifies that changing the shelf behavior works as expected. + function setShelfAutoHideBehavior() { + // Using shelf from primary display. + var displayId = "-1"; + chrome.system.display.getInfo(function(info) { + var l = info.length; + for (var i = 0; i < l; i++) { + if (info[i].isPrimary === true) { + displayId = info[i].id; + break; + } + } + chrome.test.assertTrue(displayId != "-1"); + var behaviors = ["always", "never", "hidden"]; + var l = behaviors.length; + for (var i = 0; i < l; i++) { + var behavior = behaviors[i]; + chrome.autotestPrivate.setShelfAutoHideBehavior(displayId, behavior, + function() { + chrome.autotestPrivate.getShelfAutoHideBehavior(displayId, + function(newBehavior) { + chrome.test.assertTrue(behavior === newBehavior); + chrome.test.assertNoLastError(); + chrome.test.succeed(); + }); + }); + } + }); + }, ]; var arcEnabledTests = [ - // This test verifies that getArcState returns provisined True in case ARC - // provisiong is done. + // This test verifies that getArcState returns provisioned True in case ARC + // provisioning is done. function arcProvisioned() {chrome.autotestPrivate.getArcState( function(state) { chrome.test.assertTrue(state.provisioned);
diff --git a/chrome/test/data/extensions/api_test/service_worker/messaging/connect_native/manifest.json b/chrome/test/data/extensions/api_test/service_worker/messaging/connect_native/manifest.json new file mode 100644 index 0000000..12afcbd --- /dev/null +++ b/chrome/test/data/extensions/api_test/service_worker/messaging/connect_native/manifest.json
@@ -0,0 +1,10 @@ +{ + // Extension ID: knldjmfmopnpolahpmmgbagdohdnhkik + "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcBHwzDvyBQ6bDppkIs9MP4ksKqCMyXQ/A52JivHZKh4YO/9vJsT3oaYhSpDCE9RPocOEQvwsHsFReW2nUEc6OLLyoCFFxIb7KkLGsmfakkut/fFdNJYh0xOTbSN8YvLWcqph09XAY2Y/f0AL7vfO1cuCqtkMt8hFrBGWxDdf9CQIDAQAB", + "version": "1.0.0.0", + "manifest_version": 2, + "name": "Service worker extension: native messaging Port connection test", + "description": "Test the basic functionality of passing native messages.", + "background": {"service_worker_script": "service_worker_background.js"}, + "permissions": ["nativeMessaging"] +}
diff --git a/chrome/test/data/extensions/api_test/service_worker/messaging/connect_native/service_worker_background.js b/chrome/test/data/extensions/api_test/service_worker/messaging/connect_native/service_worker_background.js new file mode 100644 index 0000000..5ae3aa4 --- /dev/null +++ b/chrome/test/data/extensions/api_test/service_worker/messaging/connect_native/service_worker_background.js
@@ -0,0 +1,47 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +const appName = 'com.google.chrome.test.echo'; +const kExtensionURL = 'chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/'; + +// NOTE: These tests are based on (copied from =P) +// chrome/test/data/extensions/api_test/native_messaging/test.js. +// TODO(lazyboy): We should run tests with and without Service Worker based +// background from the same place! This will require some tweaking. +chrome.test.runTests([ + function connect() { + var messagesToSend = [{text: 'foo'}, + {text: 'bar', funCount: 9001}, + {}]; + var currentMessage = 0; + + port = chrome.runtime.connectNative(appName); + port.postMessage(messagesToSend[currentMessage]); + + port.onMessage.addListener(function(message) { + chrome.test.assertEq(currentMessage + 1, message.id); + chrome.test.assertEq(messagesToSend[currentMessage], message.echo); + // NOTE: the original test [1] was using window.location.origin that + // does not work in Service Workers, use kExtensionURL instead. + chrome.test.assertEq(kExtensionURL, message.caller_url); + currentMessage++; + + if (currentMessage == messagesToSend.length) + chrome.test.succeed(); + else + port.postMessage(messagesToSend[currentMessage]); + }); + }, + + // Verify that the case when host stops itself is handled properly. + function stopHost() { + port = chrome.runtime.connectNative(appName); + + port.onDisconnect.addListener( + chrome.test.callback(function() {}, 'Native host has exited.')); + + // Send first message that should stop the host. + port.postMessage({ 'stopHostTest': true }); + }, +]);
diff --git a/chrome/test/data/extensions/api_test/service_worker/messaging/send_native_message/manifest.json b/chrome/test/data/extensions/api_test/service_worker/messaging/send_native_message/manifest.json new file mode 100644 index 0000000..79ae241 --- /dev/null +++ b/chrome/test/data/extensions/api_test/service_worker/messaging/send_native_message/manifest.json
@@ -0,0 +1,10 @@ +{ + // Extension ID: knldjmfmopnpolahpmmgbagdohdnhkik + "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcBHwzDvyBQ6bDppkIs9MP4ksKqCMyXQ/A52JivHZKh4YO/9vJsT3oaYhSpDCE9RPocOEQvwsHsFReW2nUEc6OLLyoCFFxIb7KkLGsmfakkut/fFdNJYh0xOTbSN8YvLWcqph09XAY2Y/f0AL7vfO1cuCqtkMt8hFrBGWxDdf9CQIDAQAB", + "version": "1.0.0.0", + "manifest_version": 2, + "name": "Service worker extension: native messaging test", + "description": "Test the basic functionality of passing native messages.", + "background": {"service_worker_script": "service_worker_background.js"}, + "permissions": ["nativeMessaging"] +}
diff --git a/chrome/test/data/extensions/api_test/service_worker/messaging/send_native_message/service_worker_background.js b/chrome/test/data/extensions/api_test/service_worker/messaging/send_native_message/service_worker_background.js new file mode 100644 index 0000000..87f4edb --- /dev/null +++ b/chrome/test/data/extensions/api_test/service_worker/messaging/send_native_message/service_worker_background.js
@@ -0,0 +1,68 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +const appName = 'com.google.chrome.test.echo'; +const kExtensionURL = 'chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/'; + +// NOTE: These tests are based on (copied from =P) +// chrome/test/data/extensions/api_test/native_messaging/test.js [1]. +// TODO(lazyboy): We should run tests with and without Service Worker based +// background from the same place! This will require some tweaking. +chrome.test.runTests([ + function invalidHostName() { + console.log('invalid host -name test'); + var message = {'text': 'Hello!'}; + chrome.runtime.sendNativeMessage( + 'not.installed.app', message, + chrome.test.callbackFail( + 'Specified native messaging host not found.', + function(response) { + chrome.test.assertEq(undefined, response); + })); + }, + + function nonexistentHost() { + var message = {text: 'Hello!'}; + chrome.runtime.sendNativeMessage( + 'com.google.chrome.test.host_binary_missing', message, + chrome.test.callbackFail( + 'Specified native messaging host not found.', + function(response) { + chrome.test.assertEq(undefined, response); + })); + }, + + function sendMessageWithCallback() { + var message = {text: 'Hi there!', number: 3}; + chrome.runtime.sendNativeMessage( + appName, message, + chrome.test.callbackPass(function(response) { + chrome.test.assertEq(1, response.id); + chrome.test.assertEq(message, response.echo); + // NOTE: the original test [1] was using window.location.origin that + // does not work in Service Workers, use kExtensionURL instead. + chrome.test.assertEq(kExtensionURL, response.caller_url); + })); + }, + + // The goal of this test is just not to crash. + function sendMessageWithoutCallback() { + var message = {text: 'Hi there!', number: 3}; + chrome.extension.sendNativeMessage(appName, message); + chrome.test.succeed(); // Mission Complete + }, + + function bigMessage() { + // Create a special message for which the test host must try sending a + // message that is bigger than the limit. + var message = {bigMessageTest: true}; + chrome.runtime.sendNativeMessage( + appName, message, + chrome.test.callbackFail( + 'Error when communicating with the native messaging host.', + function(response) { + chrome.test.assertEq(undefined, response); + })); + }, +]);
diff --git a/chromeos/services/assistant/platform/audio_output_provider_impl_unittest.cc b/chromeos/services/assistant/platform/audio_output_provider_impl_unittest.cc index e8bab3d..74e2c10 100644 --- a/chromeos/services/assistant/platform/audio_output_provider_impl_unittest.cc +++ b/chromeos/services/assistant/platform/audio_output_provider_impl_unittest.cc
@@ -104,7 +104,7 @@ delegate.Reset(); auto owner = std::make_unique<AudioDeviceOwner>( base::SequencedTaskRunnerHandle::Get(), - base::SequencedTaskRunnerHandle::Get()); + base::SequencedTaskRunnerHandle::Get(), "test device"); // Upon start, it will start to fill the buffer. owner->StartOnMainThread(&delegate, nullptr, format); delegate.Wait();
diff --git a/chromeos/services/assistant/platform/power_manager_provider_impl_unittest.cc b/chromeos/services/assistant/platform/power_manager_provider_impl_unittest.cc index e9e2598..0852c2e 100644 --- a/chromeos/services/assistant/platform/power_manager_provider_impl_unittest.cc +++ b/chromeos/services/assistant/platform/power_manager_provider_impl_unittest.cc
@@ -7,7 +7,6 @@ #include "base/logging.h" #include "base/run_loop.h" #include "base/test/scoped_task_environment.h" -#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/fake_power_manager_client.h" #include "services/device/public/cpp/test/test_wake_lock_provider.h" #include "services/device/public/mojom/constants.mojom.h" @@ -31,16 +30,21 @@ : scoped_task_environment_( base::test::ScopedTaskEnvironment::MainThreadType::IO), wake_lock_provider_( - connector_factory_.RegisterInstance(device::mojom::kServiceName)) { - fake_power_manager_client_ = new chromeos::FakePowerManagerClient; - chromeos::DBusThreadManager::GetSetterForTesting()->SetPowerManagerClient( - base::WrapUnique(fake_power_manager_client_)); + connector_factory_.RegisterInstance(device::mojom::kServiceName)) {} + ~PowerManagerProviderImplTest() override = default; + void SetUp() override { + chromeos::PowerManagerClient::Initialize(); power_manager_provider_impl_ = std::make_unique<PowerManagerProviderImpl>( connector_factory_.GetDefaultConnector(), scoped_task_environment_.GetMainThreadTaskRunner()); } + void TearDown() override { + power_manager_provider_impl_.reset(); + chromeos::PowerManagerClient::Shutdown(); + } + protected: PowerManagerProviderImpl* GetPowerManagerProviderImpl() { return power_manager_provider_impl_.get(); @@ -108,9 +112,6 @@ device::TestWakeLockProvider wake_lock_provider_; - // Owned by chromeos::DBusThreadManager. - chromeos::FakePowerManagerClient* fake_power_manager_client_; - std::unique_ptr<PowerManagerProviderImpl> power_manager_provider_impl_; DISALLOW_COPY_AND_ASSIGN(PowerManagerProviderImplTest);
diff --git a/components/contextual_search/core/BUILD.gn b/components/contextual_search/core/BUILD.gn index ca18b98..5571750a 100644 --- a/components/contextual_search/core/BUILD.gn +++ b/components/contextual_search/core/BUILD.gn
@@ -8,6 +8,8 @@ "browser/contextual_search_preference.h", "browser/ctr_aggregator.cc", "browser/ctr_aggregator.h", + "browser/public.cc", + "browser/public.h", "browser/weekly_activity_storage.cc", "browser/weekly_activity_storage.h", ]
diff --git a/components/contextual_search/core/browser/public.cc b/components/contextual_search/core/browser/public.cc new file mode 100644 index 0000000..31831d5 --- /dev/null +++ b/components/contextual_search/core/browser/public.cc
@@ -0,0 +1,23 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/contextual_search/core/browser/public.h" + +namespace contextual_search { + +const char kContextualSearchFieldTrialName[] = "ContextualSearch"; +const char kContextualCardsVersionParamName[] = "contextual_cards_version"; + +// The version of the Contextual Cards API that we want to invoke. +const int kContextualCardsEntityIntegration = 1; +const int kContextualCardsQuickActionsIntegration = 2; +const int kContextualCardsUrlActionsIntegration = 3; +const int kContextualCardsDefinitionsIntegration = 4; +const int kContextualCardsDiagnosticIntegration = 9; + +const int kContextualCardsSimplifiedServerMixin = 100; +const char kContextualCardsSimplifiedServerMixinChar[] = "100"; +const char kContextualCardsSimplifiedServerWithDiagnosticChar[] = "109"; + +} // namespace contextual_search
diff --git a/components/contextual_search/core/browser/public.h b/components/contextual_search/core/browser/public.h new file mode 100644 index 0000000..1aabfc6 --- /dev/null +++ b/components/contextual_search/core/browser/public.h
@@ -0,0 +1,40 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Provides public definitions. + +#ifndef COMPONENTS_CONTEXTUAL_SEARCH_CORE_BROWSER_PUBLIC_H_ +#define COMPONENTS_CONTEXTUAL_SEARCH_CORE_BROWSER_PUBLIC_H_ + +namespace contextual_search { + +// The name of our default field trial. +extern const char kContextualSearchFieldTrialName[]; + +// The name of the variations parameter we use for the Coca Integration param. +extern const char kContextualCardsVersionParamName[]; + +// The version of the Contextual Cards API that we want to invoke. + +// Support of entities with thumnail and caption. +extern const int kContextualCardsEntityIntegration; +// Support of quick actions in the Bar, e.g. dial a phone number. +extern const int kContextualCardsQuickActionsIntegration; +// Support of non-linkified web URLs for quick navigation. +extern const int kContextualCardsUrlActionsIntegration; +// Support of dictionary definitions in the bar. +extern const int kContextualCardsDefinitionsIntegration; +// Support of unlimited cards with diagnostics enabled, for development. +extern const int kContextualCardsDiagnosticIntegration; + +// Can be mixed in with one of the above. +extern const int kContextualCardsSimplifiedServerMixin; +extern const char kContextualCardsSimplifiedServerMixinChar[]; + +// String form of kContextualCardsSimplifiedServerMixin + +// kContextualCardsDiagnosticIntegration. +extern const char kContextualCardsSimplifiedServerWithDiagnosticChar[]; +} // namespace contextual_search + +#endif // COMPONENTS_CONTEXTUAL_SEARCH_CORE_BROWSER_PUBLIC_H_
diff --git a/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadCollectionBridge.java b/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadCollectionBridge.java index 618e5a6..4fd83d8 100644 --- a/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadCollectionBridge.java +++ b/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadCollectionBridge.java
@@ -148,6 +148,13 @@ } /** + * @return whether download collection is supported. + */ + protected boolean isDownloadCollectionSupported() { + return false; + } + + /** * Creates an intermediate URI for download to be written into. On completion, call * nativeOnCreateIntermediateUriResult() with |callbackId|. * @param fileName Name of the file. @@ -258,4 +265,11 @@ private static DisplayNameInfo[] getDisplayNamesForDownloads() { return getDownloadCollectionBridge().getDisplayNames(); } + + /** + * @return whether download collection is supported. + */ + public static boolean supportsDownloadCollection() { + return getDownloadCollectionBridge().isDownloadCollectionSupported(); + } }
diff --git a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java index ed1b680c..1194511 100644 --- a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java +++ b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java
@@ -83,6 +83,11 @@ public static final String DATA_SAVER_DETAIL_OPENED = "data_saver_overview_opened"; /** + * The data saver milestone promo was used (tapped). + */ + public static final String DATA_SAVER_MILESTONE_PROMO_OPENED = "data_saver_milestone_promo"; + + /** * The previews verbose status view was opened. */ public static final String PREVIEWS_VERBOSE_STATUS_OPENED = "previews_verbose_status_opened";
diff --git a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java index afa581b..b6cc8e0 100644 --- a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java +++ b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java
@@ -42,6 +42,7 @@ String CONTEXTUAL_SUGGESTIONS_FEATURE = "IPH_ContextualSuggestions"; String DATA_SAVER_PREVIEW_FEATURE = "IPH_DataSaverPreview"; String DATA_SAVER_DETAIL_FEATURE = "IPH_DataSaverDetail"; + String DATA_SAVER_MILESTONE_PROMO_FEATURE = "IPH_DataSaverMilestonePromo"; String NTP_BUTTON_FEATURE = "IPH_NewTabPageButton"; String PREVIEWS_OMNIBOX_UI_FEATURE = "IPH_PreviewsOmniboxUI"; String HOMEPAGE_TILE_FEATURE = "IPH_HomepageTile";
diff --git a/components/feature_engagement/public/feature_constants.cc b/components/feature_engagement/public/feature_constants.cc index b8e016e..6748da2 100644 --- a/components/feature_engagement/public/feature_constants.cc +++ b/components/feature_engagement/public/feature_constants.cc
@@ -17,6 +17,8 @@ #if defined(OS_ANDROID) const base::Feature kIPHDataSaverDetailFeature{ "IPH_DataSaverDetail", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kIPHDataSaverMilestonePromoFeature{ + "IPH_DataSaverMilestonePromo", base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kIPHDataSaverPreviewFeature{ "IPH_DataSaverPreview", base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kIPHDownloadHomeFeature{"IPH_DownloadHome",
diff --git a/components/feature_engagement/public/feature_constants.h b/components/feature_engagement/public/feature_constants.h index dd9253e1..865e504 100644 --- a/components/feature_engagement/public/feature_constants.h +++ b/components/feature_engagement/public/feature_constants.h
@@ -22,6 +22,7 @@ // org.chromium.components.feature_engagement.FeatureConstants. #if defined(OS_ANDROID) extern const base::Feature kIPHDataSaverDetailFeature; +extern const base::Feature kIPHDataSaverMilestonePromoFeature; extern const base::Feature kIPHDataSaverPreviewFeature; extern const base::Feature kIPHDownloadHomeFeature; extern const base::Feature kIPHDownloadPageFeature;
diff --git a/components/feature_engagement/public/feature_list.cc b/components/feature_engagement/public/feature_list.cc index 8048163c7..5dbeefcc 100644 --- a/components/feature_engagement/public/feature_list.cc +++ b/components/feature_engagement/public/feature_list.cc
@@ -18,6 +18,7 @@ &kIPHDummyFeature, // Ensures non-empty array for all platforms. #if defined(OS_ANDROID) &kIPHDataSaverDetailFeature, + &kIPHDataSaverMilestonePromoFeature, &kIPHDataSaverPreviewFeature, &kIPHDownloadHomeFeature, &kIPHDownloadPageFeature,
diff --git a/components/feature_engagement/public/feature_list.h b/components/feature_engagement/public/feature_list.h index 0f9e73b..ae85b1a 100644 --- a/components/feature_engagement/public/feature_list.h +++ b/components/feature_engagement/public/feature_list.h
@@ -48,6 +48,8 @@ DEFINE_VARIATION_PARAM(kIPHDummyFeature, "IPH_Dummy"); #if defined(OS_ANDROID) DEFINE_VARIATION_PARAM(kIPHDataSaverDetailFeature, "IPH_DataSaverDetail"); +DEFINE_VARIATION_PARAM(kIPHDataSaverMilestonePromoFeature, + "IPH_DataSaverMilestonePromo"); DEFINE_VARIATION_PARAM(kIPHDataSaverPreviewFeature, "IPH_DataSaverPreview"); DEFINE_VARIATION_PARAM(kIPHDownloadHomeFeature, "IPH_DownloadHome"); DEFINE_VARIATION_PARAM(kIPHDownloadPageFeature, "IPH_DownloadPage"); @@ -109,6 +111,7 @@ kIPHDemoModeChoiceVariations[] = { #if defined(OS_ANDROID) VARIATION_ENTRY(kIPHDataSaverDetailFeature), + VARIATION_ENTRY(kIPHDataSaverMilestonePromoFeature), VARIATION_ENTRY(kIPHDataSaverPreviewFeature), VARIATION_ENTRY(kIPHDownloadHomeFeature), VARIATION_ENTRY(kIPHDownloadPageFeature),
diff --git a/components/omnibox/browser/DEPS b/components/omnibox/browser/DEPS index d501293..70c8326 100644 --- a/components/omnibox/browser/DEPS +++ b/components/omnibox/browser/DEPS
@@ -4,7 +4,6 @@ "+components/data_use_measurement/core", "+components/favicon_base", "+components/favicon/core", - "+copmonents/grit", "+components/history/core/browser", "+components/history/core/test", "+components/keyed_service/content",
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc index 78f7b74..ee13c76 100644 --- a/components/omnibox/browser/omnibox_edit_model.cc +++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -212,6 +212,14 @@ keyword_ = state->keyword; is_keyword_hint_ = state->is_keyword_hint; keyword_mode_entry_method_ = state->keyword_mode_entry_method; + } else if (!state->user_text.empty()) { + // If the |user_input_in_progress| is false but we have |user_text|, + // restore the |user_text| to the model and the view. It's likely unelided + // text that the user has not made any modifications to. + InternalSetUserText(state->user_text); + + // We let the View manage restoring the cursor position afterwards. + view_->SetWindowTextAndCaretPos(state->user_text, 0, false, false); } } @@ -299,6 +307,10 @@ location_bar_model->GetDisplaySearchTerms(nullptr)) return false; + // Set the user text to the unelided URL, but don't change + // |user_input_in_progress_|. This is to save the unelided URL on tab switch. + InternalSetUserText(url_for_editing_); + view_->SetWindowTextAndCaretPos(url_for_editing_, 0, false, false); // Select all in reverse to ensure the beginning of the URL is shown.
diff --git a/components/omnibox/browser/omnibox_edit_model.h b/components/omnibox/browser/omnibox_edit_model.h index dadd810..45e7aa0d 100644 --- a/components/omnibox/browser/omnibox_edit_model.h +++ b/components/omnibox/browser/omnibox_edit_model.h
@@ -164,7 +164,7 @@ // Returns the permanent display text for the current page and Omnibox state. base::string16 GetPermanentDisplayText() const; - // Sets the user_text_ to |text|. + // Sets the user_text_ to |text|. Also enters user-input-in-progress mode. void SetUserText(const base::string16& text); // If the omnibox is currently displaying elided text, this method will @@ -411,7 +411,8 @@ // Virtual for testing. virtual bool PopupIsOpen() const; - // Called whenever user_text_ should change. + // An internal method to set the user text. Notably, this differs from + // SetUserText because it does not change the user-input-in-progress state. void InternalSetUserText(const base::string16& text); // Conversion between user text and display text. User text is the text the @@ -514,7 +515,10 @@ bool user_input_in_progress_; // The text that the user has entered. This does not include inline - // autocomplete text that has not yet been accepted. + // autocomplete text that has not yet been accepted. |user_text_| can + // contain a string without |user_input_in_progress_| being true. + // For instance, this is the case when the user has unelided a URL without + // modifying its contents. base::string16 user_text_; // We keep track of when the user last focused on the omnibox.
diff --git a/components/search_engines/template_url_service.cc b/components/search_engines/template_url_service.cc index e0ef9dc..386b649 100644 --- a/components/search_engines/template_url_service.cc +++ b/components/search_engines/template_url_service.cc
@@ -1915,6 +1915,12 @@ if (default_search_provider_) { TemplateURLData update_data(*data); update_data.sync_guid = default_search_provider_->sync_guid(); + + // Now that we are auto-updating the favicon_url as the user browses, + // respect the favicon_url entry in the database, instead of falling back + // to the one in the prepopulated list. + update_data.favicon_url = default_search_provider_->favicon_url(); + if (!default_search_provider_->safe_for_autoreplace()) { update_data.safe_for_autoreplace = false; update_data.SetKeyword(default_search_provider_->keyword());
diff --git a/components/signin/core/browser/account_reconcilor_unittest.cc b/components/signin/core/browser/account_reconcilor_unittest.cc index ea32943d..7975fd0 100644 --- a/components/signin/core/browser/account_reconcilor_unittest.cc +++ b/components/signin/core/browser/account_reconcilor_unittest.cc
@@ -357,7 +357,7 @@ std::string AccountReconcilorTest::PickAccountIdForAccount( const std::string& gaia_id, const std::string& username) { - return identity_test_env()->identity_manager()->LegacyPickAccountIdForAccount( + return identity_test_env()->identity_manager()->PickAccountIdForAccount( gaia_id, username); }
diff --git a/components/tracing/common/native_stack_sampler_android.cc b/components/tracing/common/native_stack_sampler_android.cc index e28ae1f..8c02e350 100644 --- a/components/tracing/common/native_stack_sampler_android.cc +++ b/components/tracing/common/native_stack_sampler_android.cc
@@ -17,8 +17,7 @@ NativeStackSamplerAndroid::~NativeStackSamplerAndroid() = default; -std::vector<base::StackSamplingProfiler::Frame> -NativeStackSamplerAndroid::RecordStackFrames( +void NativeStackSamplerAndroid::RecordStackFrames( StackBuffer* stack_buffer, base::StackSamplingProfiler::ProfileBuilder* profile_builder) { if (!unwinder_.is_initialized()) { @@ -36,7 +35,7 @@ // TODO(ssid): Add support for obtaining modules here. frames.emplace_back(reinterpret_cast<uintptr_t>(pcs[i]), nullptr); } - return frames; + profile_builder->OnSampleCompleted(std::move(frames)); } } // namespace tracing
diff --git a/components/tracing/common/native_stack_sampler_android.h b/components/tracing/common/native_stack_sampler_android.h index e80ae2e5..a386c4a 100644 --- a/components/tracing/common/native_stack_sampler_android.h +++ b/components/tracing/common/native_stack_sampler_android.h
@@ -22,7 +22,7 @@ ~NativeStackSamplerAndroid() override; // StackSamplingProfiler::NativeStackSampler: - std::vector<base::StackSamplingProfiler::Frame> RecordStackFrames( + void RecordStackFrames( StackBuffer* stack_buffer, base::StackSamplingProfiler::ProfileBuilder* profile_builder) override;
diff --git a/components/viz/common/constants.cc b/components/viz/common/constants.cc index b1fdd43..ebb6bf3 100644 --- a/components/viz/common/constants.cc +++ b/components/viz/common/constants.cc
@@ -6,6 +6,15 @@ namespace viz { +// We expect the begin frames that viz client read in can give a good estimate +// of the arrival time of the next begin frame. But in case the next begin frame +// is not arriving as expected, we start the polling process by spinning on the +// begin frame slot for |kClientSpinForBeginFrameIntervalUs| and sleep for +// |kClientSleepForBeginFrameIntervalMs| repeatedly until we see the next begin +// frame +const int64_t kClientSleepForBeginFrameIntervalMs = 1; +const int64_t kClientSpinForBeginFrameIntervalUs = 100; + const uint32_t kDefaultActivationDeadlineInFrames = 4u; } // namespace viz
diff --git a/components/viz/common/constants.h b/components/viz/common/constants.h index ab09b8e..613a5eb 100644 --- a/components/viz/common/constants.h +++ b/components/viz/common/constants.h
@@ -12,6 +12,10 @@ namespace viz { // Keep list in alphabetical order. +VIZ_COMMON_EXPORT extern const int64_t kClientSleepForBeginFrameIntervalMs; + +VIZ_COMMON_EXPORT extern const int64_t kClientSpinForBeginFrameIntervalUs; + VIZ_COMMON_EXPORT extern const uint32_t kDefaultActivationDeadlineInFrames; } // namespace viz
diff --git a/components/viz/common/display/renderer_settings.h b/components/viz/common/display/renderer_settings.h index ce4fb4c..2f1e2e2d 100644 --- a/components/viz/common/display/renderer_settings.h +++ b/components/viz/common/display/renderer_settings.h
@@ -37,6 +37,7 @@ bool auto_resize_output_surface = true; bool requires_alpha_channel = false; bool record_sk_picture = false; + bool enable_shared_mem_begin_frame = false; int slow_down_compositing_scale_factor = 1;
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc index 52fe758..c9c9749 100644 --- a/components/viz/common/features.cc +++ b/components/viz/common/features.cc
@@ -42,6 +42,10 @@ const base::Feature kEnableVizHitTestSurfaceLayer{ "VizHitTestSurfaceLayer", base::FEATURE_DISABLED_BY_DEFAULT}; +// Enables shared-memory distributed BeginFrames. Assumes VizDisplayCompositor. +const base::Feature kEnableSharedMemoryBeginFrame{ + "EnableSharedMemoryBeginFrame", base::FEATURE_DISABLED_BY_DEFAULT}; + // Use the SkiaRenderer. const base::Feature kUseSkiaRenderer{"UseSkiaRenderer", base::FEATURE_DISABLED_BY_DEFAULT}; @@ -93,6 +97,11 @@ base::FeatureList::IsEnabled(kEnableVizHitTestSurfaceLayer); } +bool IsSharedMemoryBeginFrameEnabled() { + return base::FeatureList::IsEnabled(kVizDisplayCompositor) && + base::FeatureList::IsEnabled(kEnableSharedMemoryBeginFrame); +} + bool IsUsingSkiaRenderer() { return base::FeatureList::IsEnabled(kUseSkiaRenderer); }
diff --git a/components/viz/common/features.h b/components/viz/common/features.h index 1ddae70c..6e38a246 100644 --- a/components/viz/common/features.h +++ b/components/viz/common/features.h
@@ -14,6 +14,7 @@ VIZ_COMMON_EXPORT extern const base::Feature kEnableSurfaceSynchronization; VIZ_COMMON_EXPORT extern const base::Feature kEnableVizHitTestDrawQuad; VIZ_COMMON_EXPORT extern const base::Feature kEnableVizHitTestSurfaceLayer; +VIZ_COMMON_EXPORT extern const base::Feature kEnableSharedMemoryBeginFrame; VIZ_COMMON_EXPORT extern const base::Feature kUseSkiaRenderer; VIZ_COMMON_EXPORT extern const base::Feature kUseSkiaRendererNonDDL; VIZ_COMMON_EXPORT extern const base::Feature kRecordSkPicture; @@ -25,6 +26,7 @@ VIZ_COMMON_EXPORT bool IsVizHitTestingDrawQuadEnabled(); VIZ_COMMON_EXPORT bool IsVizHitTestingEnabled(); VIZ_COMMON_EXPORT bool IsVizHitTestingSurfaceLayerEnabled(); +VIZ_COMMON_EXPORT bool IsSharedMemoryBeginFrameEnabled(); VIZ_COMMON_EXPORT bool IsUsingSkiaRenderer(); VIZ_COMMON_EXPORT bool IsUsingSkiaRendererNonDDL(); VIZ_COMMON_EXPORT bool IsRecordingSkPicture();
diff --git a/components/viz/host/renderer_settings_creation.cc b/components/viz/host/renderer_settings_creation.cc index ee1f83f..f9b9a4d3 100644 --- a/components/viz/host/renderer_settings_creation.cc +++ b/components/viz/host/renderer_settings_creation.cc
@@ -69,6 +69,8 @@ switches::kDisableMacOverlays); #endif renderer_settings.record_sk_picture = features::IsRecordingSkPicture(); + renderer_settings.enable_shared_mem_begin_frame = + features::IsSharedMemoryBeginFrameEnabled(); if (command_line->HasSwitch(switches::kSlowDownCompositingScaleFactor)) { const int kMinSlowDownScaleFactor = 1;
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index b98613d0..2194b54 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -16,6 +16,7 @@ import("//printing/buildflags/buildflags.gni") import("//third_party/blink/public/public_features.gni") import("//tools/ipc_fuzzer/ipc_fuzzer.gni") +import("//ui/base/mpris/buildflags/buildflags.gni") jumbo_source_set("browser") { # Only the public target should depend on this. All other targets (even @@ -200,6 +201,7 @@ "//ui/base:buildflags", "//ui/base/clipboard", "//ui/base/ime", + "//ui/base/mpris/buildflags", "//ui/display", "//ui/display/types", "//ui/events", @@ -2601,6 +2603,10 @@ ] defines += [ "USE_VIZ_DEVTOOLS" ] } + + if (use_mpris) { + deps += [ "//ui/base/mpris" ] + } } buildflag_header("accessibility_buildflags") {
diff --git a/content/browser/DEPS b/content/browser/DEPS index 942ac9c2..d0baf6f 100644 --- a/content/browser/DEPS +++ b/content/browser/DEPS
@@ -129,7 +129,6 @@ # Allow mojo generated files in WebKit. These files use STL types and # don't use WTF types. - "+third_party/blink/public/platform/modules/background_sync/background_sync.mojom.h", "+third_party/blink/public/platform/modules/bluetooth/web_bluetooth.mojom.h", "+third_party/blink/public/platform/modules/broadcastchannel/broadcast_channel.mojom-test-utils.h", "+third_party/blink/public/platform/modules/broadcastchannel/broadcast_channel.mojom.h",
diff --git a/content/browser/android/selection/selection_popup_controller.cc b/content/browser/android/selection/selection_popup_controller.cc index f8a7db7..f226423 100644 --- a/content/browser/android/selection/selection_popup_controller.cc +++ b/content/browser/android/selection/selection_popup_controller.cc
@@ -218,4 +218,13 @@ Java_SelectionPopupControllerImpl_hidePopupsAndPreserveSelection(env, obj); } +void SelectionPopupController::RestoreSelectionPopupsIfNecessary() { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> obj = java_obj_.get(env); + if (obj.is_null()) + return; + + Java_SelectionPopupControllerImpl_restoreSelectionPopupsIfNecessary(env, obj); +} + } // namespace content
diff --git a/content/browser/android/selection/selection_popup_controller.h b/content/browser/android/selection/selection_popup_controller.h index d8f29db..151a407 100644 --- a/content/browser/android/selection/selection_popup_controller.h +++ b/content/browser/android/selection/selection_popup_controller.h
@@ -47,6 +47,7 @@ int start_adjust, int end_adjust); void HidePopupsAndPreserveSelection(); + void RestoreSelectionPopupsIfNecessary(); std::unique_ptr<ui::TouchHandleDrawable> CreateTouchHandleDrawable(); void MoveRangeSelectionExtent(const gfx::PointF& extent);
diff --git a/content/browser/appcache/appcache_internals_ui.cc b/content/browser/appcache/appcache_internals_ui.cc index d63844fe..38f10de7 100644 --- a/content/browser/appcache/appcache_internals_ui.cc +++ b/content/browser/appcache/appcache_internals_ui.cc
@@ -439,6 +439,7 @@ incognito_path_prefix = "Incognito "; web_ui()->CallJavascriptFunctionUnsafe( kFunctionOnAllAppCacheInfoReady, + base::Value(partition_path.AsUTF8Unsafe()), base::Value(incognito_path_prefix + partition_path.AsUTF8Unsafe()), *GetListValueFromAppCacheInfoCollection(collection.get())); }
diff --git a/content/browser/background_sync/background_sync_context_impl.h b/content/browser/background_sync/background_sync_context_impl.h index ceb0874..0d18258 100644 --- a/content/browser/background_sync/background_sync_context_impl.h +++ b/content/browser/background_sync/background_sync_context_impl.h
@@ -12,7 +12,7 @@ #include "base/memory/ref_counted_delete_on_sequence.h" #include "content/common/content_export.h" #include "content/public/browser/background_sync_context.h" -#include "third_party/blink/public/platform/modules/background_sync/background_sync.mojom.h" +#include "third_party/blink/public/mojom/background_sync/background_sync.mojom.h" namespace content {
diff --git a/content/browser/background_sync/background_sync_manager.h b/content/browser/background_sync/background_sync_manager.h index 8e99231a..ae03f3c 100644 --- a/content/browser/background_sync/background_sync_manager.h +++ b/content/browser/background_sync/background_sync_manager.h
@@ -29,7 +29,7 @@ #include "content/public/browser/background_sync_parameters.h" #include "content/public/browser/browser_thread.h" #include "third_party/blink/public/common/service_worker/service_worker_status_code.h" -#include "third_party/blink/public/platform/modules/background_sync/background_sync.mojom.h" +#include "third_party/blink/public/mojom/background_sync/background_sync.mojom.h" #include "third_party/blink/public/platform/modules/permissions/permission_status.mojom.h" #include "url/gurl.h" #include "url/origin.h"
diff --git a/content/browser/background_sync/background_sync_registration.h b/content/browser/background_sync/background_sync_registration.h index 883160a..5fc4d0e 100644 --- a/content/browser/background_sync/background_sync_registration.h +++ b/content/browser/background_sync/background_sync_registration.h
@@ -15,7 +15,7 @@ #include "base/time/time.h" #include "content/browser/background_sync/background_sync.pb.h" #include "content/common/content_export.h" -#include "third_party/blink/public/platform/modules/background_sync/background_sync.mojom.h" +#include "third_party/blink/public/mojom/background_sync/background_sync.mojom.h" namespace content {
diff --git a/content/browser/background_sync/background_sync_service_impl.h b/content/browser/background_sync/background_sync_service_impl.h index 9de8a7f..d044a62158 100644 --- a/content/browser/background_sync/background_sync_service_impl.h +++ b/content/browser/background_sync/background_sync_service_impl.h
@@ -15,7 +15,7 @@ #include "base/memory/ref_counted.h" #include "content/browser/background_sync/background_sync_manager.h" #include "mojo/public/cpp/bindings/binding.h" -#include "third_party/blink/public/platform/modules/background_sync/background_sync.mojom.h" +#include "third_party/blink/public/mojom/background_sync/background_sync.mojom.h" namespace content {
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h index 3642892..1771e0a 100644 --- a/content/browser/bad_message.h +++ b/content/browser/bad_message.h
@@ -128,7 +128,7 @@ OBSOLETE_WC_CONTENT_WITH_CERT_ERRORS_BAD_SECURITY_INFO = 101, RFMF_RENDERER_FAKED_ITS_OWN_DEATH = 102, DWNLD_INVALID_SAVABLE_RESOURCE_LINKS_RESPONSE = 103, - DWNLD_INVALID_SERIALIZE_AS_MHTML_RESPONSE = 104, + OBSOLETE_DWNLD_INVALID_SERIALIZE_AS_MHTML_RESPONSE = 104, BDH_DEVICE_NOT_ALLOWED_FOR_ORIGIN = 105, OBSOLETE_ACI_WRONG_STORAGE_PARTITION = 106, OBSOLETE_RDHI_WRONG_STORAGE_PARTITION = 107,
diff --git a/content/browser/download/mhtml_generation_browsertest.cc b/content/browser/download/mhtml_generation_browsertest.cc index c0b1e4cf..c62e936 100644 --- a/content/browser/download/mhtml_generation_browsertest.cc +++ b/content/browser/download/mhtml_generation_browsertest.cc
@@ -18,7 +18,7 @@ #include "base/threading/thread_restrictions.h" #include "components/download/public/common/download_task_runner.h" #include "content/browser/renderer_host/render_process_host_impl.h" -#include "content/common/frame_messages.h" +#include "content/common/mhtml_file_writer.mojom.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/mhtml_extra_parts.h" @@ -36,6 +36,7 @@ #include "net/test/embedded_test_server/embedded_test_server.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" #include "third_party/blink/public/mojom/frame/find_in_page.mojom.h" using testing::ContainsRegex; @@ -97,9 +98,111 @@ } // namespace +// This Mock injects our overwritten interface, running the callback +// SerializeAsMHTMLResponse and immediately disconnecting the message pipe. +class MockMhtmlFileWriter : public mojom::MhtmlFileWriter { + public: + explicit MockMhtmlFileWriter() : binding_(this) {} + + ~MockMhtmlFileWriter() override {} + + void BindRequest(mojo::ScopedInterfaceEndpointHandle handle) { + binding_.Bind(mojom::MhtmlFileWriterAssociatedRequest(std::move(handle))); + } + + void WriteDataToDestinationFile(base::File& destination_file) { + const char kTestData[] = + "Sample Text to write on generated MHTML " + "file to verify it has been written to."; + base::ScopedAllowBlockingForTesting allow_blocking; + destination_file.WriteAtCurrentPos(kTestData, strlen(kTestData)); + destination_file.Close(); + } + + void SerializeAsMHTML(mojom::SerializeAsMHTMLParamsPtr params, + SerializeAsMHTMLCallback callback) override { + // Upon using the overridden mock interface implementation, this will be + // handled by the product code as illustrated below. (1), (2), (3) depict + // points in time when product code runs on UI thread and download sequence. + // For the repro, the message pipe disconnection needs to happen between (1) + // and (3). + // + // Test instance UI thread download sequence + // --------- --------- ----------- + // | | | + // WE ARE HERE | | + // | | | + // | | | + // +--------------->+ | + // | | | + // | | | + // | | | + // | | | + // | | | + // | | | + // (1) | MHTMLGenerationManager::Job | + // | ::SerializeAsMHTMLResponse | + // | +-------------------->+ + // | | | + // | | | + // | | | + // (2) | | MHTMLGenerationManager::Job + // | | ::CloseFileOnFileThread + // | | | + // | | | + // | test needs to | + // | disconnect message pipe | + // | HERE - between (1) and (3) | + // | | | + // | | | + // | +<--------------------+ + // | | | + // (3) | MHTMLGenerationManager | + // | Job::OnFinished | + // | | | + // + // We hope that the error handler is invoked between (1) and (3) by doing + // the following: + // - From here, run the callback response to the UI thread. This queues + // the response message onto the bound message pipe. + // - After running the callback response, immediately unbind the message + // pipe in order to queue a message onto the bound message pipe to notify + // the Browser the connection was closed and invoke the error handler. + // - Upon resuming operation, the FIFO ordering property of associated + // interfaces guarantees the execution of (1) before the error handler. + // (1) posts (2) to the download sequence and terminates. The client end + // then accepts the error notification and invokes the connection error + // handler, guaranteeing its execution before (3). + + // Write a valid MHTML file to destination_file, since we are not + // actively running a serialization pipeline in the mock implementation. + WriteDataToDestinationFile(params->destination_file); + + std::vector<std::string> dummy_digests; + base::TimeDelta dummy_time_delta = base::TimeDelta::Max(); + std::move(callback).Run(mojom::MhtmlSaveStatus::SUCCESS, dummy_digests, + dummy_time_delta); + + // Close the message pipe connection to invoke the connection error + // callback. The connection error handler from here will finalize + // the Job and attempt to call MHTMLGenerationManager::Job::CloseFile + // a second time. If this situation is handled correctly, the + // browser file should be invalidated and idempotent. + binding_.Unbind(); + } + + private: + mojo::AssociatedBinding<mojom::MhtmlFileWriter> binding_; + + DISALLOW_COPY_AND_ASSIGN(MockMhtmlFileWriter); +}; + class MHTMLGenerationTest : public ContentBrowserTest { public: - MHTMLGenerationTest() : has_mhtml_callback_run_(false), file_size_(0) {} + MHTMLGenerationTest() + : has_mhtml_callback_run_(false), + file_size_(0), + well_formedness_check_(true) {} protected: void SetUpOnMainThread() override { @@ -131,8 +234,9 @@ ASSERT_TRUE(has_mhtml_callback_run()) << "Unexpected error generating MHTML file"; - // Skip well formedness check if there was an generation error. - if (file_size() == -1) + // Skip well formedness check if explicitly disabled or there was a + // generation error. + if (!well_formedness_check_ || file_size() == -1) return; // Loads the generated file to check if it is well formed. @@ -218,6 +322,13 @@ } } + // In the case that we are using a pre-generated .mhtml file, we do + // not have any control over the final mhtml_boundary_marker write + // operation. This results in the post-generation verification tests + // reporting a malformed multipart archive, unintentionally failing the + // test. + void DisableWellformednessCheck() { well_formedness_check_ = false; } + bool has_mhtml_callback_run() const { return has_mhtml_callback_run_; } int64_t file_size() const { return file_size_; } base::HistogramTester* histogram_tester() { return histogram_tester_.get(); } @@ -233,6 +344,7 @@ bool has_mhtml_callback_run_; int64_t file_size_; + bool well_formedness_check_; std::unique_ptr<base::HistogramTester> histogram_tester_; }; @@ -264,120 +376,28 @@ static_cast<int>(MhtmlSaveStatus::SUCCESS), 1); } -class GenerateMHTMLAndExitRendererMessageFilter : public BrowserMessageFilter { - public: - GenerateMHTMLAndExitRendererMessageFilter( - RenderProcessHostImpl* render_process_host) - : BrowserMessageFilter(FrameMsgStart), - render_process_host_(render_process_host) {} - - protected: - ~GenerateMHTMLAndExitRendererMessageFilter() override {} - - private: - bool OnMessageReceived(const IPC::Message& message) override { - if (message.type() == FrameHostMsg_SerializeAsMHTMLResponse::ID) { - // After |return false| below, this IPC message will be handled by the - // product code as illustrated below. (1), (2), (3) depict points in time - // when product code runs on UI thread and download sequence. (X), (Y), - // (Z) depict when we want test-injected tasks to run - for the repro, (Z) - // has to happen between (1) and (3). (Y?) and (Z?) depict when test - // tasks can theoretically happen and ruin the repro. - // - // IO thread UI thread download sequence - // --------- --------- ----------- - // | | | - // WE ARE HERE | | - // | | | - // after |return false| | | - // +--------------->+ | - // | | | - // | (X) | - // | | | - // | | (Y?) - // | (Z?) | - // | | | - // (1) | MHTMLGenerationManager | - // | ::OnSerializeAsMHTMLResponse | - // | +-------------------->+ - // | | | - // | | (Y) - // | | | - // (2) | | MHTMLGenerationManager::Job - // | | ::CloseFileOnFileThread - // | | | - // | (Z) | - // | test needs to inject | - // | fast renderer shutdown | - // | HERE - between (1) and (3) | - // | | | - // | | | - // | +<--------------------+ - // | | | - // (3) | MHTMLGenerationManager | - // | ::OnFileClosed | - // | | | - // - // We hope that (Z) happens between (1) and (3) by doing the following: - // - From here post TaskX to UI thread. (X) is guaranteed to happen - // before timepoint (1) (because posting of (1) happens after - // |return false| / before we post TaskX below). - // - From (X) post TaskY to download sequence. Because this posting is - // done before (1), we can guarantee that (Y) will happen before (2). - // - From (Y) post TaskZ to UI thread. Because this posting is done - // before (2), we can guarantee that (Z) will happen before (3). - // - We cannot really guarantee that (Y) and (Z) happen *after* (1) - i.e. - // execution at (Y?) and (Z?) instead is possible. In practice, - // bouncing off of UI and download sequence does mean (Z) happens - // after (1). - base::PostTaskWithTraits( - FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&GenerateMHTMLAndExitRendererMessageFilter::TaskX, - base::Unretained(this))); - } - - return false; - } - - void TaskX() { - download::GetDownloadTaskRunner()->PostTask( - FROM_HERE, - base::BindOnce(&GenerateMHTMLAndExitRendererMessageFilter::TaskY, - base::Unretained(this))); - } - - void TaskY() { - base::PostTaskWithTraits( - FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&GenerateMHTMLAndExitRendererMessageFilter::TaskZ, - base::Unretained(this))); - } - - void TaskZ() { - render_process_host_->FastShutdownIfPossible(); - } - - RenderProcessHostImpl* render_process_host_; - - DISALLOW_COPY_AND_ASSIGN(GenerateMHTMLAndExitRendererMessageFilter); -}; - // Regression test for the crash/race from https://crbug.com/612098. -IN_PROC_BROWSER_TEST_F(MHTMLGenerationTest, GenerateMHTMLAndExitRenderer) { +IN_PROC_BROWSER_TEST_F(MHTMLGenerationTest, GenerateMHTMLAndCloseConnection) { + MockMhtmlFileWriter mock_writer; + NavigateToURL(shell(), embedded_test_server()->GetURL("/simple_page.html")); - - RenderProcessHostImpl* render_process_host = - static_cast<RenderProcessHostImpl*>( - shell()->web_contents()->GetMainFrame()->GetProcess()); - scoped_refptr<BrowserMessageFilter> filter = - new GenerateMHTMLAndExitRendererMessageFilter(render_process_host); - render_process_host->AddFilter(filter.get()); - base::FilePath path(temp_dir_.GetPath()); path = path.Append(FILE_PATH_LITERAL("test.mht")); + + blink::AssociatedInterfaceProvider* remote_interfaces = + shell()->web_contents()->GetMainFrame()->GetRemoteAssociatedInterfaces(); + remote_interfaces->OverrideBinderForTesting( + mojom::MhtmlFileWriter::Name_, + base::BindRepeating(&MockMhtmlFileWriter::BindRequest, + base::Unretained(&mock_writer))); + + DisableWellformednessCheck(); GenerateMHTMLForCurrentPage(MHTMLGenerationParams(path)); - EXPECT_GT(ReadFileSizeFromDisk(path), 100); // Verify the actual file size. + // Verify the file has some contents written to it. + EXPECT_GT(ReadFileSizeFromDisk(path), 100); + // Verify the reported file size matches the file written to disk. + EXPECT_EQ(ReadFileSizeFromDisk(path), file_size()); } // TODO(crbug.com/672313): Flaky on Windows.
diff --git a/content/browser/download/mhtml_generation_manager.cc b/content/browser/download/mhtml_generation_manager.cc index 61d87ed..d87df05 100644 --- a/content/browser/download/mhtml_generation_manager.cc +++ b/content/browser/download/mhtml_generation_manager.cc
@@ -4,7 +4,6 @@ #include "content/browser/download/mhtml_generation_manager.h" -#include <map> #include <utility> #include "base/bind.h" @@ -26,88 +25,61 @@ #include "content/browser/download/mhtml_extra_parts_impl.h" #include "content/browser/frame_host/frame_tree_node.h" #include "content/browser/frame_host/render_frame_host_impl.h" -#include "content/common/frame_messages.h" +#include "content/common/mhtml_file_writer.mojom.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/mhtml_extra_parts.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" -#include "content/public/browser/render_process_host_observer.h" #include "content/public/browser/web_contents.h" #include "content/public/common/mhtml_generation_params.h" +#include "mojo/core/embedder/embedder.h" #include "net/base/mime_util.h" +#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" namespace { const char kContentLocation[] = "Content-Location: "; const char kContentType[] = "Content-Type: "; int kInvalidFileSize = -1; +struct CloseFileResult { + CloseFileResult(content::MhtmlSaveStatus status, int64_t size) + : save_status(status), file_size(size) {} + content::MhtmlSaveStatus save_status; + int64_t file_size; +}; } // namespace namespace content { // The class and all of its members live on the UI thread. Only static methods // are executed on other threads. -class MHTMLGenerationManager::Job : public RenderProcessHostObserver { +// Job instances are created in MHTMLGenerationManager::Job::StartNewJob(), +// proceeding with the MHTML saving process unmanaged. Every instance is +// self-owned and responsible for deleting itself upon invoking OnFinished. +// With self-ownership lifetime concerns, we make the following precautions: +// - SerializeAsMHTMLResponse() always proceeds with finalizing upon detecting +// Job completion/cancellation. +// - Jobs are prematurely finalized and deleted upon detecting a connection +// error with the message pipe during serialization. +// - Any pending callbacks after deletion are invalidated using weak pointers. +class MHTMLGenerationManager::Job { public: - Job(int job_id, - WebContents* web_contents, - const MHTMLGenerationParams& params, - GenerateMHTMLCallback callback); - ~Job() override; - - int id() const { return job_id_; } - void set_browser_file(base::File file) { browser_file_ = std::move(file); } - base::TimeTicks creation_time() const { return creation_time_; } - - GenerateMHTMLCallback callback() { return std::move(callback_); } - - // Indicates whether we expect a message from the |sender| at this time. - // We expect only one message per frame - therefore calling this method - // will always clear |frame_tree_node_id_of_busy_frame_|. - bool IsMessageFromFrameExpected(RenderFrameHostImpl* sender); - - // Handler for FrameHostMsg_SerializeAsMHTMLResponse (a notification from the - // renderer that the MHTML generation for previous frame has finished). - // Returns MhtmlSaveStatus::SUCCESS or a specific error status. - MhtmlSaveStatus OnSerializeAsMHTMLResponse( - const std::set<std::string>& digests_of_uris_of_serialized_resources); - - // Sends IPC to the renderer, asking for MHTML generation of the next frame. - // Returns MhtmlSaveStatus::SUCCESS or a specific error status. - MhtmlSaveStatus SendToNextRenderFrame(); - - // Indicates if more calls to SendToNextRenderFrame are needed. - bool IsDone() const { - bool waiting_for_response_from_renderer = - frame_tree_node_id_of_busy_frame_ != - FrameTreeNode::kFrameTreeNodeInvalidId; - bool no_more_requests_to_send = pending_frame_tree_node_ids_.empty(); - return !waiting_for_response_from_renderer && no_more_requests_to_send; - } - - // Write the MHTML footer and close the file on the file thread and respond - // back on the UI thread with the updated status and file size (which will be - // negative in case of errors). - void CloseFile( - base::OnceCallback<void(const std::tuple<MhtmlSaveStatus, int64_t>&)> - callback, - MhtmlSaveStatus save_status); - - // RenderProcessHostObserver: - void RenderProcessExited(RenderProcessHost* host, - const ChildProcessTerminationInfo& info) override; - void RenderProcessHostDestroyed(RenderProcessHost* host) override; - - void MarkAsFinished(); - - void ReportRendererMainThreadTime(base::TimeDelta renderer_main_thread_time); + // Creates and registers a new job. + static void StartNewJob(WebContents* web_contents, + const MHTMLGenerationParams& params, + GenerateMHTMLCallback callback); private: + Job(WebContents* web_contents, + const MHTMLGenerationParams& params, + GenerateMHTMLCallback callback); + ~Job(); + // Writes the MHTML footer to the file and closes it. // // Note: The same |boundary| marker must be used for all "boundaries" -- in // the header, parts and footer -- that belong to the same MHTML document (see // also rfc1341, section 7.2.1, "boundary" description). - static std::tuple<MhtmlSaveStatus, int64_t> FinalizeAndCloseFileOnFileThread( + static CloseFileResult FinalizeAndCloseFileOnFileThread( MhtmlSaveStatus save_status, const std::string& boundary, base::File file, @@ -125,14 +97,62 @@ // Writes the footer into the MHTML file. Returns false for faiulre. static bool WriteFooter(const std::string& boundary, base::File& file); + // Called on the UI thread when the file that should hold the MHTML data has + // been created. + void OnFileAvailable(base::File browser_file); + + // Called on the UI thread after the file got finalized and we have its size, + // or an error occurred while creating a new file. + void OnFinished(const CloseFileResult& close_file_result); + + // Called when the message pipe to the renderer is disconnected. + void OnConnectionError(); + + // Handler for the Mojo interface callback (a notification from the + // renderer that the MHTML generation for previous frame has finished). + void SerializeAsMHTMLResponse( + mojom::MhtmlSaveStatus mojo_save_status, + const std::vector<std::string>& digests_of_uris_of_serialized_resources, + base::TimeDelta renderer_main_thread_time); + + // Records newly serialized resource digests into + // |digests_of_already_serialized_uris_|, and continues sending serialization + // requests to the next frame if there are more frames to be serialized. + // Returns MhtmlSaveStatus::SUCCESS or a specific error status. + MhtmlSaveStatus RecordDigestsAndContinue( + const std::vector<std::string>& digests_of_uris_of_serialized_resources); + + // Packs up the current status of the MHTML file saving into a Mojo + // struct to send to the renderer process. + mojom::SerializeAsMHTMLParamsPtr CreateMojoParams(); + + // Sends Mojo interface call to the renderer, asking for MHTML + // generation of the next frame. Returns MhtmlSaveStatus::SUCCESS or a + // specific error status. + MhtmlSaveStatus SendToNextRenderFrame(); + + // Indicates if more calls to SendToNextRenderFrame are needed. + // This check is necessary to prevent a race condition between the + // Renderer and Browser where the Job is deleted before the response + // is received. + bool IsDone() const; + + // Called on the UI thread when a job has been finished. + void Finalize(MhtmlSaveStatus save_status); + + // Write the MHTML footer and close the file on the file thread and respond + // back on the UI thread with the updated status and file size (which will be + // negative in case of errors). + void CloseFile(MhtmlSaveStatus save_status); + + void MarkAsFinished(); + + void ReportRendererMainThreadTime(base::TimeDelta renderer_main_thread_time); + // Close the MHTML file if it looks good, setting the size param. Returns // false for failure. static bool CloseFileIfValid(base::File& file, int64_t* file_size); - // Id used to map renderer responses to jobs. - // See also MHTMLGenerationManager::id_to_job_ map. - const int job_id_; - // Time tracking for performance metrics reporting. const base::TimeTicks creation_time_; base::TimeTicks wait_on_renderer_start_time_; @@ -147,9 +167,9 @@ // The IDs of frames that still need to be processed. base::queue<int> pending_frame_tree_node_ids_; - // Identifies a frame to which we've sent FrameMsg_SerializeAsMHTML but for - // which we didn't yet process FrameHostMsg_SerializeAsMHTMLResponse via - // OnSerializeAsMHTMLResponse. + // Identifies a frame to which we've sent through + // MhtmlFileWriter::SerializeAsMHTML but for which we didn't yet process + // the response via SerializeAsMHTMLResponse. int frame_tree_node_id_of_busy_frame_; // The handle to the file the MHTML is saved to for the browser process. @@ -166,34 +186,39 @@ GenerateMHTMLCallback callback_; // Whether the job is finished (set to true only for the short duration of - // time between MHTMLGenerationManager::JobFinished is called and the job is - // destroyed by MHTMLGenerationManager::OnFileClosed). + // time between MHTMLGenerationManager::Job::Finalize is called and the job is + // destroyed by MHTMLGenerationManager::Job::OnFinished). bool is_finished_; // Any extra data parts that should be emitted into the output MHTML. std::vector<MHTMLExtraDataPart> extra_data_parts_; - // RAII helper for registering this Job as a RenderProcessHost observer. - ScopedObserver<RenderProcessHost, MHTMLGenerationManager::Job> - observed_renderer_process_host_; + // MHTML File Writer pointer to keep the variable alive. + mojom::MhtmlFileWriterAssociatedPtr writer_; + + base::WeakPtrFactory<Job> weak_factory_; DISALLOW_COPY_AND_ASSIGN(Job); }; -MHTMLGenerationManager::Job::Job(int job_id, - WebContents* web_contents, +MHTMLGenerationManager::Job::Job(WebContents* web_contents, const MHTMLGenerationParams& params, GenerateMHTMLCallback callback) - : job_id_(job_id), - creation_time_(base::TimeTicks::Now()), + : creation_time_(base::TimeTicks::Now()), params_(params), frame_tree_node_id_of_busy_frame_(FrameTreeNode::kFrameTreeNodeInvalidId), mhtml_boundary_marker_(net::GenerateMimeMultipartBoundary()), salt_(base::GenerateGUID()), callback_(std::move(callback)), is_finished_(false), - observed_renderer_process_host_(this) { + weak_factory_(this) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + + TRACE_EVENT_NESTABLE_ASYNC_BEGIN2( + "page-serialization", "SavingMhtmlJob", this, "url", + web_contents->GetLastCommittedURL().possibly_invalid_spec(), "file", + params.file_path.AsUTF8Unsafe()); + web_contents->ForEachFrame(base::BindRepeating( &MHTMLGenerationManager::Job::AddFrame, base::Unretained(this))); // Safe because ForEachFrame() is synchronous. @@ -208,23 +233,42 @@ MHTMLExtraParts::FromWebContents(web_contents)); if (extra_parts) extra_data_parts_ = extra_parts->parts(); + + base::PostTaskAndReplyWithResult( + download::GetDownloadTaskRunner().get(), FROM_HERE, + base::BindOnce(&CreateFile, params.file_path), + base::BindOnce(&Job::OnFileAvailable, weak_factory_.GetWeakPtr())); } MHTMLGenerationManager::Job::~Job() { DCHECK_CURRENTLY_ON(BrowserThread::UI); } +mojom::SerializeAsMHTMLParamsPtr +MHTMLGenerationManager::Job::CreateMojoParams() { + mojom::SerializeAsMHTMLParamsPtr mojo_params = + mojom::SerializeAsMHTMLParams::New(); + mojo_params->mhtml_boundary_marker = mhtml_boundary_marker_; + mojo_params->mhtml_binary_encoding = params_.use_binary_encoding; + mojo_params->mhtml_popup_overlay_removal = params_.remove_popup_overlay; + mojo_params->mhtml_problem_detection = params_.use_page_problem_detectors; + + // File::Duplicate() creates a reference to this file for use in the Renderer. + mojo_params->destination_file = browser_file_.Duplicate(); + + // Tell the renderer to skip (= deduplicate) already covered MHTML parts. + mojo_params->salt = salt_; + mojo_params->digests_of_uris_to_skip.assign( + digests_of_already_serialized_uris_.begin(), + digests_of_already_serialized_uris_.end()); + + return mojo_params; +} + MhtmlSaveStatus MHTMLGenerationManager::Job::SendToNextRenderFrame() { DCHECK(browser_file_.IsValid()); DCHECK(!pending_frame_tree_node_ids_.empty()); - FrameMsg_SerializeAsMHTML_Params ipc_params; - ipc_params.job_id = job_id_; - ipc_params.mhtml_boundary_marker = mhtml_boundary_marker_; - ipc_params.mhtml_binary_encoding = params_.use_binary_encoding; - ipc_params.mhtml_popup_overlay_removal = params_.remove_popup_overlay; - ipc_params.mhtml_problem_detection = params_.use_page_problem_detectors; - int frame_tree_node_id = pending_frame_tree_node_ids_.front(); pending_frame_tree_node_ids_.pop(); @@ -233,22 +277,22 @@ return MhtmlSaveStatus::FRAME_NO_LONGER_EXISTS; RenderFrameHost* rfh = ftn->current_frame_host(); - // Get notified if the target of the IPC message dies between responding. - observed_renderer_process_host_.RemoveAll(); - observed_renderer_process_host_.Add(rfh->GetProcess()); + // Bind Mojo interface to the RenderFrame + rfh->GetRemoteAssociatedInterfaces()->GetInterface(&writer_); + auto callback = base::BindOnce(&Job::SerializeAsMHTMLResponse, + weak_factory_.GetWeakPtr()); - // Tell the renderer to skip (= deduplicate) already covered MHTML parts. - ipc_params.salt = salt_; - ipc_params.digests_of_uris_to_skip = digests_of_already_serialized_uris_; + // Safe, as |writer_| is owned by this Job instance. + auto error_callback = + base::BindOnce(&Job::OnConnectionError, base::Unretained(this)); + writer_.set_connection_error_handler(std::move(error_callback)); - ipc_params.destination_file = IPC::GetPlatformFileForTransit( - browser_file_.GetPlatformFile(), false); // |close_source_handle|. - - // Send the IPC asking the renderer to serialize the frame. + // Send a Mojo request asking to serialize the frame. DCHECK_EQ(FrameTreeNode::kFrameTreeNodeInvalidId, frame_tree_node_id_of_busy_frame_); frame_tree_node_id_of_busy_frame_ = frame_tree_node_id; - rfh->Send(new FrameMsg_SerializeAsMHTML(rfh->GetRoutingID(), ipc_params)); + writer_->SerializeAsMHTML(CreateMojoParams(), std::move(callback)); + TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("page-serialization", "WaitingOnRenderer", this, "frame tree node id", frame_tree_node_id); @@ -257,23 +301,57 @@ return MhtmlSaveStatus::SUCCESS; } -void MHTMLGenerationManager::Job::RenderProcessExited( - RenderProcessHost* host, - const ChildProcessTerminationInfo& info) { +void MHTMLGenerationManager::Job::OnConnectionError() { DCHECK_CURRENTLY_ON(BrowserThread::UI); - MHTMLGenerationManager::GetInstance()->RenderProcessExited(this); + DLOG(ERROR) << "Message pipe to renderer closed while expecting response"; + Finalize(MhtmlSaveStatus::RENDER_PROCESS_EXITED); +} + +void MHTMLGenerationManager::Job::OnFileAvailable(base::File browser_file) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + if (!browser_file.IsValid()) { + DLOG(ERROR) << "Failed to create file"; + Finalize(MhtmlSaveStatus::FILE_CREATION_ERROR); + return; + } + + browser_file_ = std::move(browser_file); + + MhtmlSaveStatus save_status = SendToNextRenderFrame(); + if (save_status != MhtmlSaveStatus::SUCCESS) + Finalize(save_status); +} + +void MHTMLGenerationManager::Job::OnFinished( + const CloseFileResult& close_file_result) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + MhtmlSaveStatus save_status = close_file_result.save_status; + int64_t file_size = close_file_result.file_size; + + TRACE_EVENT_NESTABLE_ASYNC_END2( + "page-serialization", "SavingMhtmlJob", this, "job save status", + GetMhtmlSaveStatusLabel(save_status), "file size", file_size); + UMA_HISTOGRAM_TIMES("PageSerialization.MhtmlGeneration.FullPageSavingTime", + base::TimeTicks::Now() - creation_time_); + UMA_HISTOGRAM_ENUMERATION("PageSerialization.MhtmlGeneration.FinalSaveStatus", + static_cast<int>(save_status), + static_cast<int>(MhtmlSaveStatus::LAST)); + std::move(callback_).Run(save_status == MhtmlSaveStatus::SUCCESS ? file_size + : -1); + delete this; // This is the last time the Job is referenced. } void MHTMLGenerationManager::Job::MarkAsFinished() { - DCHECK(!is_finished_); - if (is_finished_) + // MarkAsFinished() may be called twice only in the case which + // writer_.reset() does not correctly stop OnConnectionError + // notifications for the case described in https://crbug.com/612098. + if (is_finished_) { + NOTREACHED(); return; - + } is_finished_ = true; - - // Stopping RenderProcessExited notifications is needed to avoid calling - // JobFinished twice. See also https://crbug.com/612098. - observed_renderer_process_host_.RemoveAll(); + writer_.reset(); TRACE_EVENT_NESTABLE_ASYNC_INSTANT0("page-serialization", "JobFinished", this); @@ -321,16 +399,7 @@ pending_frame_tree_node_ids_.push(frame_tree_node_id); } -void MHTMLGenerationManager::Job::RenderProcessHostDestroyed( - RenderProcessHost* host) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - observed_renderer_process_host_.Remove(host); -} - -void MHTMLGenerationManager::Job::CloseFile( - base::OnceCallback<void(const std::tuple<MhtmlSaveStatus, int64_t>&)> - callback, - MhtmlSaveStatus save_status) { +void MHTMLGenerationManager::Job::CloseFile(MhtmlSaveStatus save_status) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!mhtml_boundary_marker_.empty()); @@ -338,7 +407,7 @@ // Only update the status if that won't hide an earlier error. if (save_status == MhtmlSaveStatus::SUCCESS) save_status = MhtmlSaveStatus::FILE_WRITTING_ERROR; - std::move(callback).Run(std::make_tuple(save_status, -1)); + OnFinished(CloseFileResult(save_status, -1)); return; } @@ -351,24 +420,44 @@ (save_status == MhtmlSaveStatus::SUCCESS ? mhtml_boundary_marker_ : std::string()), std::move(browser_file_), std::move(extra_data_parts_)), - std::move(callback)); + base::BindOnce(&Job::OnFinished, weak_factory_.GetWeakPtr())); } -bool MHTMLGenerationManager::Job::IsMessageFromFrameExpected( - RenderFrameHostImpl* sender) { - int sender_id = sender->frame_tree_node()->frame_tree_node_id(); - if (sender_id != frame_tree_node_id_of_busy_frame_) - return false; +void MHTMLGenerationManager::Job::SerializeAsMHTMLResponse( + mojom::MhtmlSaveStatus mojo_save_status, + const std::vector<std::string>& digests_of_uris_of_serialized_resources, + base::TimeDelta renderer_main_thread_time) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); - // We only expect one message per frame - let's make sure subsequent messages - // from the same |sender| will be rejected. + TRACE_EVENT_NESTABLE_ASYNC_END0("page-serialization", "WaitingOnRenderer", + this); + ReportRendererMainThreadTime(renderer_main_thread_time); + frame_tree_node_id_of_busy_frame_ = FrameTreeNode::kFrameTreeNodeInvalidId; - return true; + // TODO(crbug.com/915966): Remove this statement once enums are dedupped. + MhtmlSaveStatus save_status = static_cast<MhtmlSaveStatus>(mojo_save_status); + + // If the renderer succeeded, update the status. + if (save_status == MhtmlSaveStatus::SUCCESS) { + save_status = + RecordDigestsAndContinue(digests_of_uris_of_serialized_resources); + } + + // If there was a failure (either from the renderer or from the job) then + // terminate the job and return. + if (save_status != MhtmlSaveStatus::SUCCESS) { + Finalize(save_status); + return; + } + + // Otherwise report completion if the job is done. + if (IsDone()) + Finalize(MhtmlSaveStatus::SUCCESS); } -MhtmlSaveStatus MHTMLGenerationManager::Job::OnSerializeAsMHTMLResponse( - const std::set<std::string>& digests_of_uris_of_serialized_resources) { +MhtmlSaveStatus MHTMLGenerationManager::Job::RecordDigestsAndContinue( + const std::vector<std::string>& digests_of_uris_of_serialized_resources) { DCHECK(!wait_on_renderer_start_time_.is_null()); base::TimeDelta renderer_wait_time = base::TimeTicks::Now() - wait_on_renderer_start_time_; @@ -382,7 +471,10 @@ // Renderer should be deduping resources with the same uris. DCHECK_EQ(0u, base::STLSetIntersection<std::set<std::string>>( digests_of_already_serialized_uris_, - digests_of_uris_of_serialized_resources).size()); + std::set<std::string>( + digests_of_uris_of_serialized_resources.begin(), + digests_of_uris_of_serialized_resources.end())) + .size()); digests_of_already_serialized_uris_.insert( digests_of_uris_of_serialized_resources.begin(), digests_of_uris_of_serialized_resources.end()); @@ -394,9 +486,33 @@ return SendToNextRenderFrame(); } +bool MHTMLGenerationManager::Job::IsDone() const { + bool waiting_for_response_from_renderer = + frame_tree_node_id_of_busy_frame_ != + FrameTreeNode::kFrameTreeNodeInvalidId; + bool no_more_requests_to_send = pending_frame_tree_node_ids_.empty(); + return !waiting_for_response_from_renderer && no_more_requests_to_send; +} + +void MHTMLGenerationManager::Job::Finalize(MhtmlSaveStatus save_status) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + MarkAsFinished(); + CloseFile(save_status); +} + // static -std::tuple<MhtmlSaveStatus, int64_t> -MHTMLGenerationManager::Job::FinalizeAndCloseFileOnFileThread( +void MHTMLGenerationManager::Job::StartNewJob( + WebContents* web_contents, + const MHTMLGenerationParams& params, + GenerateMHTMLCallback callback) { + // Creates a new Job. + // The constructor starts the serialization process and it will delete + // itself upon finishing. + new Job(web_contents, params, std::move(callback)); +} + +// static +CloseFileResult MHTMLGenerationManager::Job::FinalizeAndCloseFileOnFileThread( MhtmlSaveStatus save_status, const std::string& boundary, base::File file, @@ -429,7 +545,7 @@ save_status = MhtmlSaveStatus::FILE_CLOSING_ERROR; } - return std::make_tuple(save_status, file_size); + return CloseFileResult(save_status, file_size); } // static @@ -493,66 +609,16 @@ return base::Singleton<MHTMLGenerationManager>::get(); } -MHTMLGenerationManager::MHTMLGenerationManager() : next_job_id_(0) {} +MHTMLGenerationManager::MHTMLGenerationManager() {} -MHTMLGenerationManager::~MHTMLGenerationManager() { -} +MHTMLGenerationManager::~MHTMLGenerationManager() {} void MHTMLGenerationManager::SaveMHTML(WebContents* web_contents, const MHTMLGenerationParams& params, GenerateMHTMLCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - Job* job = NewJob(web_contents, params, std::move(callback)); - TRACE_EVENT_NESTABLE_ASYNC_BEGIN2( - "page-serialization", "SavingMhtmlJob", job, "url", - web_contents->GetLastCommittedURL().possibly_invalid_spec(), - "file", params.file_path.AsUTF8Unsafe()); - - base::PostTaskAndReplyWithResult( - download::GetDownloadTaskRunner().get(), FROM_HERE, - base::Bind(&MHTMLGenerationManager::CreateFile, params.file_path), - base::Bind(&MHTMLGenerationManager::OnFileAvailable, - base::Unretained(this), // Safe b/c |this| is a singleton. - job->id())); -} - -void MHTMLGenerationManager::OnSerializeAsMHTMLResponse( - RenderFrameHostImpl* sender, - int job_id, - MhtmlSaveStatus save_status, - const std::set<std::string>& digests_of_uris_of_serialized_resources, - base::TimeDelta renderer_main_thread_time) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - Job* job = FindJob(job_id); - if (!job || !job->IsMessageFromFrameExpected(sender)) { - NOTREACHED(); - ReceivedBadMessage(sender->GetProcess(), - bad_message::DWNLD_INVALID_SERIALIZE_AS_MHTML_RESPONSE); - return; - } - - TRACE_EVENT_NESTABLE_ASYNC_END0("page-serialization", "WaitingOnRenderer", - job); - job->ReportRendererMainThreadTime(renderer_main_thread_time); - - // If the renderer succeeded notify the Job and update the status. - if (save_status == MhtmlSaveStatus::SUCCESS) { - save_status = job->OnSerializeAsMHTMLResponse( - digests_of_uris_of_serialized_resources); - } - - // If there was a failure (either from the renderer or from the job) then - // terminate the job and return. - if (save_status != MhtmlSaveStatus::SUCCESS) { - JobFinished(job, save_status); - return; - } - - // Otherwise report completion if the job is done. - if (job->IsDone()) - JobFinished(job, MhtmlSaveStatus::SUCCESS); + Job::StartNewJob(web_contents, params, std::move(callback)); } // static @@ -569,93 +635,10 @@ base::File browser_file(file_path, file_flags); if (!browser_file.IsValid()) { - LOG(ERROR) << "Failed to create file to save MHTML at: " - << file_path.value(); + DLOG(ERROR) << "Failed to create file to save MHTML at: " + << file_path.value(); } return browser_file; } -void MHTMLGenerationManager::OnFileAvailable(int job_id, - base::File browser_file) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - Job* job = FindJob(job_id); - DCHECK(job); - - if (!browser_file.IsValid()) { - LOG(ERROR) << "Failed to create file"; - JobFinished(job, MhtmlSaveStatus::FILE_CREATION_ERROR); - return; - } - - job->set_browser_file(std::move(browser_file)); - - MhtmlSaveStatus save_status = job->SendToNextRenderFrame(); - if (save_status != MhtmlSaveStatus::SUCCESS) { - JobFinished(job, save_status); - } -} - -void MHTMLGenerationManager::JobFinished(Job* job, - MhtmlSaveStatus save_status) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(job); - job->MarkAsFinished(); - job->CloseFile( - base::BindOnce(&MHTMLGenerationManager::OnFileClosed, - base::Unretained(this), // Safe b/c |this| is a singleton. - job->id()), - save_status); -} - -void MHTMLGenerationManager::OnFileClosed( - int job_id, - const std::tuple<MhtmlSaveStatus, int64_t>& save_status_size) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - MhtmlSaveStatus save_status = std::get<0>(save_status_size); - int64_t file_size = std::get<1>(save_status_size); - - Job* job = FindJob(job_id); - DCHECK(job); - TRACE_EVENT_NESTABLE_ASYNC_END2( - "page-serialization", "SavingMhtmlJob", job, "job save status", - GetMhtmlSaveStatusLabel(save_status), "file size", file_size); - UMA_HISTOGRAM_TIMES("PageSerialization.MhtmlGeneration.FullPageSavingTime", - base::TimeTicks::Now() - job->creation_time()); - UMA_HISTOGRAM_ENUMERATION("PageSerialization.MhtmlGeneration.FinalSaveStatus", - static_cast<int>(save_status), - static_cast<int>(MhtmlSaveStatus::LAST)); - std::move(job->callback()) - .Run(save_status == MhtmlSaveStatus::SUCCESS ? file_size : -1); - id_to_job_.erase(job_id); -} - -MHTMLGenerationManager::Job* MHTMLGenerationManager::NewJob( - WebContents* web_contents, - const MHTMLGenerationParams& params, - GenerateMHTMLCallback callback) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - Job* job = new Job(++next_job_id_, web_contents, params, std::move(callback)); - id_to_job_[job->id()] = base::WrapUnique(job); - return job; -} - -MHTMLGenerationManager::Job* MHTMLGenerationManager::FindJob(int job_id) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - auto iter = id_to_job_.find(job_id); - if (iter == id_to_job_.end()) { - NOTREACHED(); - return nullptr; - } - return iter->second.get(); -} - -void MHTMLGenerationManager::RenderProcessExited(Job* job) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(job); - JobFinished(job, MhtmlSaveStatus::RENDER_PROCESS_EXITED); -} - } // namespace content
diff --git a/content/browser/download/mhtml_generation_manager.h b/content/browser/download/mhtml_generation_manager.h index 57a1c7e..72c259bf 100644 --- a/content/browser/download/mhtml_generation_manager.h +++ b/content/browser/download/mhtml_generation_manager.h
@@ -6,7 +6,6 @@ #include <stdint.h> -#include <map> #include <memory> #include <set> #include <string> @@ -17,7 +16,6 @@ #include "base/process/process.h" #include "content/common/download/mhtml_save_status.h" #include "content/public/common/mhtml_generation_params.h" -#include "ipc/ipc_platform_file.h" namespace base { class FilePath; @@ -25,15 +23,14 @@ namespace content { -class RenderFrameHostImpl; class WebContents; // The class and all of its members live on the UI thread. Only static methods // are executed on other threads. // // MHTMLGenerationManager is a singleton. Each call to SaveMHTML method creates -// a new instance of MHTMLGenerationManager::Job that tracks generation of a -// single MHTML file. +// a new instance of MHTMLGenerationManager::Job that continues with the MHTML +// serialization process on its own, eventually deleting itself. class MHTMLGenerationManager { public: static MHTMLGenerationManager* GetInstance(); @@ -49,14 +46,8 @@ const MHTMLGenerationParams& params, GenerateMHTMLCallback callback); - // Handler for FrameHostMsg_SerializeAsMHTMLResponse (a notification from the - // renderer that the MHTML generation finished for a single frame). - void OnSerializeAsMHTMLResponse( - RenderFrameHostImpl* sender, - int job_id, - MhtmlSaveStatus save_status, - const std::set<std::string>& digests_of_uris_of_serialized_resources, - base::TimeDelta renderer_main_thread_time); + // Called on the file thread to create a new file for MHTML serialization. + static base::File CreateFile(const base::FilePath& file_path); private: friend struct base::DefaultSingletonTraits<MHTMLGenerationManager>; @@ -65,36 +56,6 @@ MHTMLGenerationManager(); virtual ~MHTMLGenerationManager(); - // Called on the file thread to create |file|. - static base::File CreateFile(const base::FilePath& file_path); - - // Called on the UI thread when the file that should hold the MHTML data has - // been created. - void OnFileAvailable(int job_id, base::File browser_file); - - // Called on the UI thread when a job has been finished. - void JobFinished(Job* job, MhtmlSaveStatus save_status); - - // Called on the UI thread after the file got finalized and we have its size. - void OnFileClosed( - int job_id, - const std::tuple<MhtmlSaveStatus, int64_t>& save_status_size); - - // Creates and registers a new job. - Job* NewJob(WebContents* web_contents, - const MHTMLGenerationParams& params, - GenerateMHTMLCallback callback); - - // Finds job by id. Returns nullptr if no job with a given id was found. - Job* FindJob(int job_id); - - // Called when the render process connected to a job exits. - void RenderProcessExited(Job* job); - - std::map<int, std::unique_ptr<Job>> id_to_job_; - - int next_job_id_; - DISALLOW_COPY_AND_ASSIGN(MHTMLGenerationManager); };
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index b12276d..e3582271 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -1426,8 +1426,6 @@ IPC_MESSAGE_HANDLER(FrameHostMsg_DidStopLoading, OnDidStopLoading) IPC_MESSAGE_HANDLER(FrameHostMsg_DidChangeLoadProgress, OnDidChangeLoadProgress) - IPC_MESSAGE_HANDLER(FrameHostMsg_SerializeAsMHTMLResponse, - OnSerializeAsMHTMLResponse) IPC_MESSAGE_HANDLER(FrameHostMsg_SelectionChanged, OnSelectionChanged) IPC_MESSAGE_HANDLER(FrameHostMsg_FocusedNodeChanged, OnFocusedNodeChanged) IPC_MESSAGE_HANDLER(FrameHostMsg_UpdateUserActivationState, @@ -3452,16 +3450,6 @@ frame_tree_node_->DidChangeLoadProgress(load_progress); } -void RenderFrameHostImpl::OnSerializeAsMHTMLResponse( - int job_id, - MhtmlSaveStatus save_status, - const std::set<std::string>& digests_of_uris_of_serialized_resources, - base::TimeDelta renderer_main_thread_time) { - MHTMLGenerationManager::GetInstance()->OnSerializeAsMHTMLResponse( - this, job_id, save_status, digests_of_uris_of_serialized_resources, - renderer_main_thread_time); -} - void RenderFrameHostImpl::OnSelectionChanged(const base::string16& text, uint32_t offset, const gfx::Range& range) {
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h index 8a1b64b9..44faa84 100644 --- a/content/browser/frame_host/render_frame_host_impl.h +++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -40,7 +40,6 @@ #include "content/common/buildflags.h" #include "content/common/content_export.h" #include "content/common/content_security_policy/csp_context.h" -#include "content/common/download/mhtml_save_status.h" #include "content/common/frame.mojom.h" #include "content/common/frame_message_enums.h" #include "content/common/frame_replication_state.h" @@ -1094,11 +1093,6 @@ blink::WebSuddenTerminationDisablerType disabler_type); void OnDidStopLoading(); void OnDidChangeLoadProgress(double load_progress); - void OnSerializeAsMHTMLResponse( - int job_id, - MhtmlSaveStatus save_status, - const std::set<std::string>& digests_of_uris_of_serialized_resources, - base::TimeDelta renderer_main_thread_time); void OnSelectionChanged(const base::string16& text, uint32_t offset, const gfx::Range& range);
diff --git a/content/browser/media/media_keys_listener_manager_impl.cc b/content/browser/media/media_keys_listener_manager_impl.cc index 48b8e68..386c890 100644 --- a/content/browser/media/media_keys_listener_manager_impl.cc +++ b/content/browser/media/media_keys_listener_manager_impl.cc
@@ -10,6 +10,11 @@ #include "content/browser/browser_main_loop.h" #include "content/browser/media/hardware_key_media_controller.h" #include "ui/base/accelerators/accelerator.h" +#include "ui/base/mpris/buildflags/buildflags.h" + +#if BUILDFLAG(USE_MPRIS) +#include "ui/base/mpris/mpris_service.h" // nogncheck +#endif namespace content { @@ -30,7 +35,8 @@ service_manager::Connector* connector) : hardware_key_media_controller_( std::make_unique<HardwareKeyMediaController>(connector)), - media_key_handling_enabled_(true) { + media_key_handling_enabled_(true), + auxiliary_services_started_(false) { DCHECK(!MediaKeysListenerManager::GetInstance()); } @@ -126,10 +132,23 @@ delegate.OnMediaKeysAccelerator(accelerator); } +void MediaKeysListenerManagerImpl::EnsureAuxiliaryServices() { + if (auxiliary_services_started_) + return; + +#if BUILDFLAG(USE_MPRIS) + mpris::MprisService::GetInstance()->StartService(); +#endif + + auxiliary_services_started_ = true; +} + void MediaKeysListenerManagerImpl::EnsureMediaKeysListener() { if (media_keys_listener_) return; + EnsureAuxiliaryServices(); + media_keys_listener_ = ui::MediaKeysListener::Create( this, ui::MediaKeysListener::Scope::kGlobal); DCHECK(media_keys_listener_); @@ -182,4 +201,4 @@ return media_key_handling_enabled_ && !AnyDelegatesListening(); } -} // namespace content \ No newline at end of file +} // namespace content
diff --git a/content/browser/media/media_keys_listener_manager_impl.h b/content/browser/media/media_keys_listener_manager_impl.h index d3312b7..6a67f75 100644 --- a/content/browser/media/media_keys_listener_manager_impl.h +++ b/content/browser/media/media_keys_listener_manager_impl.h
@@ -72,6 +72,9 @@ DISALLOW_COPY_AND_ASSIGN(ListeningData); }; + // Creates/Starts any OS-specific services needed for listening to media keys. + void EnsureAuxiliaryServices(); + void EnsureMediaKeysListener(); ListeningData* GetOrCreateListeningData(ui::KeyboardCode key_code); @@ -97,9 +100,12 @@ // |DisableInternalMediaKeyHandling()|. bool media_key_handling_enabled_; + // True if auxiliary services have already been started. + bool auxiliary_services_started_; + DISALLOW_COPY_AND_ASSIGN(MediaKeysListenerManagerImpl); }; } // namespace content -#endif // CONTENT_BROWSER_MEDIA_MEDIA_KEYS_LISTENER_MANAGER_IMPL_H_ \ No newline at end of file +#endif // CONTENT_BROWSER_MEDIA_MEDIA_KEYS_LISTENER_MANAGER_IMPL_H_
diff --git a/content/browser/resources/appcache/appcache_internals.html b/content/browser/resources/appcache/appcache_internals.html index c2e529c..01bdd32 100644 --- a/content/browser/resources/appcache/appcache_internals.html +++ b/content/browser/resources/appcache/appcache_internals.html
@@ -17,13 +17,8 @@ <div id="appcache-list-template" jsvalues=".partitionPath:$this.partition_path"> <div class="appcache-summary"> - <span jsdisplay="$this.partition_path"> - <span>Instances in: </span> - <span jscontent="$this.partition_path"></span> - </span> - <span jsdisplay="!$this.partition_path"> - <span>Instances: Incognito </span> - </span> + <span>Instances in: </span> + <span jscontent="$this.display_name"></span> <span jscontent="'(' + $this.appcache_vector.length + ')'"> </span> </div>
diff --git a/content/browser/resources/appcache/appcache_internals.js b/content/browser/resources/appcache/appcache_internals.js index 8e4a947..6191754 100644 --- a/content/browser/resources/appcache/appcache_internals.js +++ b/content/browser/resources/appcache/appcache_internals.js
@@ -68,13 +68,16 @@ } - function onAllAppCacheInfoReady(partition_path, data) { + function onAllAppCacheInfoReady(partition_path, display_name, data) { var template = jstGetTemplate('appcache-list-template'); var container = $('appcache-list'); container.appendChild(template); jstProcess( - new JsEvalContext( - {appcache_vector: data, partition_path: partition_path}), + new JsEvalContext({ + appcache_vector: data, + partition_path: partition_path, + display_name: display_name + }), template); var removeLinks = container.querySelectorAll('a.remove-manifest'); for (var i = 0; i < removeLinks.length; ++i) {
diff --git a/content/browser/web_contents/web_contents_view_android.cc b/content/browser/web_contents/web_contents_view_android.cc index cf02399..3d07268 100644 --- a/content/browser/web_contents/web_contents_view_android.cc +++ b/content/browser/web_contents/web_contents_view_android.cc
@@ -386,8 +386,13 @@ return; } - if (selection_popup_controller_) + if (selection_popup_controller_) { selection_popup_controller_->HidePopupsAndPreserveSelection(); + // Hide the handles temporarily. + auto* rwhva = GetRenderWidgetHostViewAndroid(); + if (rwhva) + rwhva->SetTextHandlesTemporarilyHidden(true); + } } void WebContentsViewAndroid::UpdateDragCursor(blink::WebDragOperation op) { @@ -483,6 +488,14 @@ void WebContentsViewAndroid::OnSystemDragEnded() { web_contents_->GetRenderViewHost()->GetWidget()->DragSourceSystemDragEnded(); + + // Restore the selection popups and the text handles if necessary. + if (selection_popup_controller_) { + selection_popup_controller_->RestoreSelectionPopupsIfNecessary(); + auto* rwhva = GetRenderWidgetHostViewAndroid(); + if (rwhva) + rwhva->SetTextHandlesTemporarilyHidden(false); + } } void WebContentsViewAndroid::OnDragEnded() {
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn index a1ac31a..482dd3e 100644 --- a/content/common/BUILD.gn +++ b/content/common/BUILD.gn
@@ -484,6 +484,7 @@ "media/peer_connection_tracker.mojom", "media/renderer_audio_input_stream_factory.mojom", "media/renderer_audio_output_stream_factory.mojom", + "mhtml_file_writer.mojom", "native_types.mojom", "navigation_client.mojom", "navigation_params.mojom",
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h index f1b940d..831e7c4 100644 --- a/content/common/frame_messages.h +++ b/content/common/frame_messages.h
@@ -26,7 +26,6 @@ #include "content/common/content_param_traits.h" #include "content/common/content_security_policy/csp_context.h" #include "content/common/content_security_policy_header.h" -#include "content/common/download/mhtml_save_status.h" #include "content/common/frame_message_enums.h" #include "content/common/frame_message_structs.h" #include "content/common/frame_owner_properties.h" @@ -613,40 +612,6 @@ IPC_STRUCT_TRAITS_MEMBER(routing_id) IPC_STRUCT_TRAITS_END() -IPC_STRUCT_BEGIN(FrameMsg_SerializeAsMHTML_Params) - // Job id - used to match responses to requests. - IPC_STRUCT_MEMBER(int, job_id) - - // Destination file handle. - IPC_STRUCT_MEMBER(IPC::PlatformFileForTransit, destination_file) - - // MHTML boundary marker / MIME multipart boundary maker. The same - // |mhtml_boundary_marker| should be used for serialization of each frame. - IPC_STRUCT_MEMBER(std::string, mhtml_boundary_marker) - - // Whether to use binary encoding while serializing. Binary encoding is not - // supported outside of Chrome, so this should not be used if the MHTML is - // intended for sharing. - IPC_STRUCT_MEMBER(bool, mhtml_binary_encoding) - - // Whether to remove popup overlay while serializing. - IPC_STRUCT_MEMBER(bool, mhtml_popup_overlay_removal) - - // Whether to detect problems while serializing. - IPC_STRUCT_MEMBER(bool, mhtml_problem_detection) - - // |digests_of_uris_to_skip| contains digests of uris of MHTML parts that - // should be skipped. This helps deduplicate mhtml parts across frames. - // SECURITY NOTE: Sha256 digests (rather than uris) are used to prevent - // disclosing uris to other renderer processes; the digests should be - // generated using SHA256HashString function from crypto/sha2.h and hashing - // |salt + url.spec()|. - IPC_STRUCT_MEMBER(std::set<std::string>, digests_of_uris_to_skip) - - // Salt used for |digests_of_uris_to_skip|. - IPC_STRUCT_MEMBER(std::string, salt) -IPC_STRUCT_END() - // This message is used to send hittesting data from the renderer in order // to perform hittesting on the browser process. IPC_STRUCT_BEGIN(FrameHostMsg_HittestData_Params) @@ -1013,13 +978,6 @@ FrameMsg_GetSerializedHtmlWithLocalLinks_UrlMap, FrameMsg_GetSerializedHtmlWithLocalLinks_FrameRoutingIdMap) -// Serialize target frame and its resources into MHTML and write it into the -// provided destination file handle. Note that when serializing multiple -// frames, one needs to serialize the *main* frame first (the main frame -// needs to go first according to RFC2557 + the main frame will trigger -// generation of the MHTML header). -IPC_MESSAGE_ROUTED1(FrameMsg_SerializeAsMHTML, FrameMsg_SerializeAsMHTML_Params) - IPC_MESSAGE_ROUTED1(FrameMsg_SetFrameOwnerProperties, content::FrameOwnerProperties /* frame_owner_properties */) @@ -1614,14 +1572,6 @@ std::string /* data buffer */, bool /* end of data? */) -// Response to FrameMsg_SerializeAsMHTML. -IPC_MESSAGE_ROUTED4( - FrameHostMsg_SerializeAsMHTMLResponse, - int /* job_id (used to match responses to requests) */, - content::MhtmlSaveStatus /* final success/failure status */, - std::set<std::string> /* digests of uris of serialized resources */, - base::TimeDelta /* how much time of the main render thread was used */) - // Sent when the renderer updates hint for importance of a tab. IPC_MESSAGE_ROUTED1(FrameHostMsg_UpdatePageImportanceSignals, content::PageImportanceSignals)
diff --git a/content/common/mhtml_file_writer.mojom b/content/common/mhtml_file_writer.mojom new file mode 100644 index 0000000..a245732 --- /dev/null +++ b/content/common/mhtml_file_writer.mojom
@@ -0,0 +1,67 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module content.mojom; + +import "mojo/public/mojom/base/file.mojom"; +import "mojo/public/mojom/base/time.mojom"; + +// TODO(crbug/915966): This is currently mapped from mhtml_save_status.h. +// We should consolidate these sources later to prevent duplicate enums. +enum MhtmlSaveStatus { + SUCCESS = 0, + FILE_CLOSING_ERROR, + FILE_CREATION_ERROR, + FILE_WRITTING_ERROR, + FRAME_NO_LONGER_EXISTS, + FRAME_SERIALIZATION_FORBIDDEN, + RENDER_PROCESS_EXITED, +}; + +struct SerializeAsMHTMLParams { + // Destination file handle. + mojo_base.mojom.File destination_file; + + // MHTML boundary marker / MIME multipart boundary maker. The same + // |mhtml_boundary_marker| should be used for serialization of each frame. + string mhtml_boundary_marker; + + // Whether to use binary encoding while serializing. Binary encoding is not + // supported outside of Chrome, so this should not be used if the MHTML is + // intended for sharing. + bool mhtml_binary_encoding; + + // Whether to remove popup overlay while serializing. + bool mhtml_popup_overlay_removal; + + // Whether to detect problems while serializing. + bool mhtml_problem_detection; + + // To avoid duplicating MHTML parts across frames, |digests_of_uris_to_skip| + // contains digests of parts that have already been serialized and should + // be skipped. + // SECURITY NOTE: Sha256 digests (rather than uris) are used to prevent + // disclosing uris to other renderer processes; the digests should be + // generated using SHA256HashString function from crypto/sha2.h and hashing + // |salt + url.spec()|. + // This array MUST be sorted and contain no duplicates. + array<string> digests_of_uris_to_skip; + + // Salt used for |digests_of_uris_to_skip|. + string salt; +}; + +// Serialize target frame and its resources into MHTML and write it into the +// provided destination file handle. Note that when serializing multiple +// frames, one needs to serialize the *main* frame first (the main frame +// needs to go first according to RFC2557 + the main frame will trigger +// generation of the MHTML header). +interface MhtmlFileWriter { + // |renderer_main_thread_time| is the amount of time spent in the main + // thread serializing the frame. + SerializeAsMHTML(SerializeAsMHTMLParams params) + => (MhtmlSaveStatus status, + array<string> digests_of_uris_to_skip, + mojo_base.mojom.TimeDelta renderer_main_thread_time); +}; \ No newline at end of file
diff --git a/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java b/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java index 7978411..213be7c 100644 --- a/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java +++ b/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
@@ -1219,6 +1219,7 @@ nativeSetTextHandlesTemporarilyHidden(mNativeSelectionPopupController, hide); } + @CalledByNative public void restoreSelectionPopupsIfNecessary() { if (hasSelection() && !isActionModeValid()) { showActionModeOrClearOnFailure();
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ContentTextSelectionTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ContentTextSelectionTest.java index 2626d50f..d0e4f956 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/ContentTextSelectionTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/ContentTextSelectionTest.java
@@ -9,6 +9,7 @@ import android.content.Context; import android.content.Intent; import android.os.Build; +import android.os.SystemClock; import android.support.test.filters.MediumTest; import android.support.test.filters.SmallTest; import android.text.TextUtils; @@ -193,6 +194,33 @@ @Test @SmallTest + @Feature({"TextSelection"}) + @DisableIf. + Build(sdk_is_less_than = Build.VERSION_CODES.N, message = "Drag and drop not enabled pre-N") + public void testSelectionPreservedAfterDragAndDrop() throws Throwable { + DOMUtils.longPressNode(mWebContents, "plain_text_1"); + waitForSelectActionBarVisible(true); + Assert.assertTrue(mSelectionPopupController.hasSelection()); + + // Long press the selected text without release for the following drag. + long downTime = SystemClock.uptimeMillis(); + DOMUtils.longPressNodeWithoutUp(mWebContents, "plain_text_1", downTime); + waitForSelectActionBarVisible(true); + Assert.assertTrue(mSelectionPopupController.hasSelection()); + + // Drag to the specified position by a DOM node id. + int stepCount = 10; + DOMUtils.dragNodeTo(mWebContents, "plain_text_1", "plain_text_2", stepCount, downTime); + waitForSelectActionBarVisible(false); + Assert.assertTrue(mSelectionPopupController.hasSelection()); + + DOMUtils.dragNodeEnd(mWebContents, "plain_text_2", downTime); + waitForSelectActionBarVisible(true); + Assert.assertTrue(mSelectionPopupController.hasSelection()); + } + + @Test + @SmallTest @Feature({"TextInput"}) public void testPastePopupNotShownOnLongPressingNonEmptyInput() throws Throwable { copyStringToClipboard("SampleTextToCopy");
diff --git a/content/public/app/content_renderer_manifest.cc b/content/public/app/content_renderer_manifest.cc index 22d3f7d..dccb95d 100644 --- a/content/public/app/content_renderer_manifest.cc +++ b/content/public/app/content_renderer_manifest.cc
@@ -34,6 +34,7 @@ "content.mojom.ChildHistogramFetcher", "content.mojom.ChildHistogramFetcherFactory", "content.mojom.FrameFactory", + "content.mojom.MhtmlFileWriter", "content.mojom.RenderWidgetWindowTreeClientFactory", "content.mojom.ResourceUsageReporter", "IPC.mojom.ChannelBootstrap",
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/DOMUtils.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/DOMUtils.java index 23d9a7b59..b8152e6 100644 --- a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/DOMUtils.java +++ b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/DOMUtils.java
@@ -6,6 +6,7 @@ import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout; +import android.app.Activity; import android.graphics.Rect; import android.util.JsonReader; import android.view.View; @@ -17,6 +18,7 @@ import org.chromium.content.browser.RenderCoordinatesImpl; import org.chromium.content.browser.webcontents.WebContentsImpl; import org.chromium.content_public.browser.WebContents; +import org.chromium.ui.base.WindowAndroid; import java.io.IOException; import java.io.StringReader; @@ -178,6 +180,10 @@ return ((WebContentsImpl) webContents).getViewAndroidDelegate().getContainerView(); } + private static Activity getActivity(final WebContents webContents) { + return WindowAndroid.activityFromContext(((WebContentsImpl) webContents).getContext()); + } + /** * Returns the rect boundaries for a node by its id. * @param webContents The WebContents in which the node lives. @@ -288,6 +294,86 @@ } /** + * Starts (synchronously) a drag motion on the specified coordinates of a DOM node by its id, + * scrolling it into view first. Normally followed by dragNodeTo() and dragNodeEnd(). + * + * @param webContents The WebContents in which the node lives. + * @param nodeId The id of the node. + * @param downTime When the drag was started, in millis since the epoch. + */ + public static void dragNodeStart(final WebContents webContents, String nodeId, long downTime) + throws InterruptedException, TimeoutException { + scrollNodeIntoView(webContents, nodeId); + String jsCode = "document.getElementById('" + nodeId + "')"; + int[] fromTarget = getClickTargetForNodeByJs(webContents, jsCode); + TouchCommon.dragStart(getActivity(webContents), fromTarget[0], fromTarget[1], downTime); + } + + /** + * Drags / moves (synchronously) to the specified coordinates of a DOM node by its id. Normally + * preceded by dragNodeStart() and followed by dragNodeEnd() + * + * @param webContents The WebContents in which the node lives. + * @param fromNodeId The id of the node's coordinates of the initial touch. + * @param toNodeId The id of the node's coordinates of the drag destination. + * @param stepCount How many move steps to include in the drag. + * @param downTime When the drag was started, in millis since the epoch. + */ + public static void dragNodeTo(final WebContents webContents, String fromNodeId, String toNodeId, + int stepCount, long downTime) throws InterruptedException, TimeoutException { + int[] fromTarget = getClickTargetForNodeByJs( + webContents, "document.getElementById('" + fromNodeId + "')"); + int[] toTarget = getClickTargetForNodeByJs( + webContents, "document.getElementById('" + toNodeId + "')"); + TouchCommon.dragTo(getActivity(webContents), fromTarget[0], fromTarget[1], toTarget[0], + toTarget[1], stepCount, downTime); + } + + /** + * Finishes (synchronously) a drag / move at the specified coordinate of a DOM node by its id, + * scrolling it into view first. Normally preceded by dragNodeStart() and dragNodeTo(). + * + * @param webContents The WebContents in which the node lives. + * @param nodeId The id of the node. + * @param downTime When the drag was started, in millis since the epoch. + */ + public static void dragNodeEnd(final WebContents webContents, String nodeId, long downTime) + throws InterruptedException, TimeoutException { + scrollNodeIntoView(webContents, nodeId); + String jsCode = "document.getElementById('" + nodeId + "')"; + int[] endTarget = getClickTargetForNodeByJs(webContents, jsCode); + TouchCommon.dragEnd(getActivity(webContents), endTarget[0], endTarget[1], downTime); + } + + /** + * Long-press a DOM node by its id, scrolling it into view first and without release. + * @param webContents The WebContents in which the node lives. + * @param nodeId The id of the node. + * @param downTime When the Long-press was started, in millis since the epoch. + */ + public static void longPressNodeWithoutUp(final WebContents webContents, String nodeId, + long downTime) throws InterruptedException, TimeoutException { + scrollNodeIntoView(webContents, nodeId); + String jsCode = "document.getElementById('" + nodeId + "')"; + longPressNodeWithoutUpByJs(webContents, jsCode, downTime); + } + + /** + * Long-press a DOM node by its id, without release. + * <p>Note that content view should be located in the current position for a foreseeable + * amount of time because this involves sleep to simulate touch to long press transition. + * @param webContents The WebContents in which the node lives. + * @param jsCode js code that returns an element. + * @param downTime When the Long-press was started, in millis since the epoch. + */ + public static void longPressNodeWithoutUpByJs(final WebContents webContents, String jsCode, + long downTime) throws InterruptedException, TimeoutException { + int[] clickTarget = getClickTargetForNodeByJs(webContents, jsCode); + TouchCommon.longPressViewWithoutUp( + getContainerView(webContents), clickTarget[0], clickTarget[1], downTime); + } + + /** * Long-press a DOM node by its id, scrolling it into view first. * @param webContents The WebContents in which the node lives. * @param nodeId The id of the node.
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/TouchCommon.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/TouchCommon.java index 5b30f17..2b5d329 100644 --- a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/TouchCommon.java +++ b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/TouchCommon.java
@@ -171,8 +171,23 @@ longPressView(v, v.getWidth() / 2, v.getHeight() / 2); } - private static void longPressInternal(View view, float windowX, float windowY) { - long downTime = SystemClock.uptimeMillis(); + /** + * Sends (synchronously) a long press to the View at the specified coordinates, without release. + * + * @param v The view to receive the long press. + * @param x X coordinate, relative to v. + * @param y Y coordinate, relative to v. + * @param downTime When the drag was started, in millis since the epoch. + */ + public static void longPressViewWithoutUp(View v, int x, int y, long downTime) { + int windowXY[] = viewToWindowCoordinates(v, x, y); + int windowX = windowXY[0]; + int windowY = windowXY[1]; + longPressWithoutUpInternal(v.getRootView(), windowX, windowY, downTime); + } + + private static void longPressWithoutUpInternal( + View view, float windowX, float windowY, long downTime) { long eventTime = SystemClock.uptimeMillis(); MotionEvent event = MotionEvent.obtain( @@ -183,9 +198,15 @@ // Long press is flaky with just longPressTimeout. Doubling the time to be safe. SystemClock.sleep(longPressTimeout * 2); + } - eventTime = SystemClock.uptimeMillis(); - event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, windowX, windowY, 0); + private static void longPressInternal(View view, float windowX, float windowY) { + long downTime = SystemClock.uptimeMillis(); + longPressWithoutUpInternal(view, windowX, windowY, downTime); + + long eventTime = SystemClock.uptimeMillis(); + MotionEvent event = + MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, windowX, windowY, 0); dispatchTouchEvent(view, event); }
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index b82743e..ea7e926b 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -630,7 +630,7 @@ // Implementation of WebFrameSerializer::MHTMLPartsGenerationDelegate that // 1. Bases shouldSkipResource and getContentID responses on contents of -// FrameMsg_SerializeAsMHTML_Params. +// SerializeAsMHTMLParams. // 2. Stores digests of urls of serialized resources (i.e. urls reported via // shouldSkipResource) into |serialized_resources_uri_digests| passed // to the constructor. @@ -638,11 +638,18 @@ : public WebFrameSerializer::MHTMLPartsGenerationDelegate { public: MHTMLPartsGenerationDelegate( - const FrameMsg_SerializeAsMHTML_Params& params, - std::set<std::string>* serialized_resources_uri_digests) + const mojom::SerializeAsMHTMLParams& params, + std::unordered_set<std::string>* serialized_resources_uri_digests) : params_(params), serialized_resources_uri_digests_(serialized_resources_uri_digests) { DCHECK(serialized_resources_uri_digests_); + // Digests must be sorted for binary search. + DCHECK(std::is_sorted(params_.digests_of_uris_to_skip.begin(), + params_.digests_of_uris_to_skip.end())); + // URLs are not duplicated. + DCHECK(std::adjacent_find(params_.digests_of_uris_to_skip.begin(), + params_.digests_of_uris_to_skip.end()) == + params_.digests_of_uris_to_skip.end()); } bool ShouldSkipResource(const WebURL& url) override { @@ -650,7 +657,8 @@ crypto::SHA256HashString(params_.salt + GURL(url).spec()); // Skip if the |url| already covered by serialization of an *earlier* frame. - if (base::ContainsKey(params_.digests_of_uris_to_skip, digest)) + if (std::binary_search(params_.digests_of_uris_to_skip.begin(), + params_.digests_of_uris_to_skip.end(), digest)) return true; // Let's record |url| as being serialized for the *current* frame. @@ -672,8 +680,8 @@ } private: - const FrameMsg_SerializeAsMHTML_Params& params_; - std::set<std::string>* serialized_resources_uri_digests_; + const mojom::SerializeAsMHTMLParams& params_; + std::unordered_set<std::string>* serialized_resources_uri_digests_; DISALLOW_COPY_AND_ASSIGN(MHTMLPartsGenerationDelegate); }; @@ -1709,6 +1717,7 @@ frame_bindings_control_binding_(this), frame_navigation_control_binding_(this), fullscreen_binding_(this), + mhtml_file_writer_binding_(this), navigation_client_impl_(nullptr), has_accessed_initial_document_(false), media_factory_(this, @@ -2155,7 +2164,6 @@ OnGetSavableResourceLinks) IPC_MESSAGE_HANDLER(FrameMsg_GetSerializedHtmlWithLocalLinks, OnGetSerializedHtmlWithLocalLinks) - IPC_MESSAGE_HANDLER(FrameMsg_SerializeAsMHTML, OnSerializeAsMHTML) IPC_MESSAGE_HANDLER(FrameMsg_EnableViewSourceMode, OnEnableViewSourceMode) IPC_MESSAGE_HANDLER(FrameMsg_SuppressFurtherDialogs, OnSuppressFurtherDialogs) @@ -6508,21 +6516,24 @@ &delegate); } -void RenderFrameImpl::OnSerializeAsMHTML( - const FrameMsg_SerializeAsMHTML_Params& params) { - TRACE_EVENT0("page-serialization", "RenderFrameImpl::OnSerializeAsMHTML"); +// mojom::MhtmlFileWriter implementation +// ---------------------------------------- + +void RenderFrameImpl::SerializeAsMHTML(mojom::SerializeAsMHTMLParamsPtr params, + SerializeAsMHTMLCallback callback) { + TRACE_EVENT0("page-serialization", "RenderFrameImpl::SerializeAsMHTML"); base::TimeTicks start_time = base::TimeTicks::Now(); - // Unpack IPC payload. - base::File file = IPC::PlatformFileForTransitToFile(params.destination_file); + + // Unpack payload. const WebString mhtml_boundary = - WebString::FromUTF8(params.mhtml_boundary_marker); + WebString::FromUTF8(params->mhtml_boundary_marker); DCHECK(!mhtml_boundary.IsEmpty()); // Holds WebThreadSafeData instances for some or all of header, contents and // footer. std::vector<WebThreadSafeData> mhtml_contents; - std::set<std::string> serialized_resources_uri_digests; - MHTMLPartsGenerationDelegate delegate(params, + std::unordered_set<std::string> serialized_resources_uri_digests; + MHTMLPartsGenerationDelegate delegate(*params, &serialized_resources_uri_digests); MhtmlSaveStatus save_status = MhtmlSaveStatus::SUCCESS; @@ -6531,7 +6542,7 @@ // Generate MHTML header if needed. if (IsMainFrame()) { TRACE_EVENT0("page-serialization", - "RenderFrameImpl::OnSerializeAsMHTML header"); + "RenderFrameImpl::SerializeAsMHTML header"); // The returned data can be empty if the main frame should be skipped. If // the main frame is skipped, then the whole archive is bad. mhtml_contents.emplace_back(WebFrameSerializer::GenerateMHTMLHeader( @@ -6544,7 +6555,7 @@ // results in an omitted resource in the final file. if (save_status == MhtmlSaveStatus::SUCCESS) { TRACE_EVENT0("page-serialization", - "RenderFrameImpl::OnSerializeAsMHTML parts serialization"); + "RenderFrameImpl::SerializeAsMHTML parts serialization"); // The returned data can be empty if the frame should be skipped, but this // is OK. mhtml_contents.emplace_back(WebFrameSerializer::GenerateMHTMLParts( @@ -6566,34 +6577,41 @@ if (save_status == MhtmlSaveStatus::SUCCESS && has_some_data) { base::PostTaskWithTraitsAndReplyWithResult( FROM_HERE, {base::MayBlock()}, - base::Bind(&WriteMHTMLToDisk, base::Passed(&mhtml_contents), - base::Passed(&file)), - base::Bind(&RenderFrameImpl::OnWriteMHTMLToDiskComplete, - weak_factory_.GetWeakPtr(), params.job_id, - base::Passed(&serialized_resources_uri_digests), - main_thread_use_time)); + base::BindOnce(&WriteMHTMLToDisk, std::move(mhtml_contents), + std::move(params->destination_file)), + base::BindOnce(&RenderFrameImpl::OnWriteMHTMLToDiskComplete, + weak_factory_.GetWeakPtr(), std::move(callback), + std::move(serialized_resources_uri_digests), + main_thread_use_time)); } else { - file.Close(); - OnWriteMHTMLToDiskComplete(params.job_id, serialized_resources_uri_digests, + params->destination_file.Close(); + OnWriteMHTMLToDiskComplete(std::move(callback), + std::move(serialized_resources_uri_digests), main_thread_use_time, save_status); } } void RenderFrameImpl::OnWriteMHTMLToDiskComplete( - int job_id, - std::set<std::string> serialized_resources_uri_digests, + SerializeAsMHTMLCallback callback, + std::unordered_set<std::string> serialized_resources_uri_digests, base::TimeDelta main_thread_use_time, MhtmlSaveStatus save_status) { TRACE_EVENT1("page-serialization", "RenderFrameImpl::OnWriteMHTMLToDiskComplete", "frame save status", GetMhtmlSaveStatusLabel(save_status)); DCHECK(RenderThread::Get()) << "Must run in the main renderer thread"; - // Notify the browser process about completion. + + // Convert the set into a vector for transport. + std::vector<std::string> digests_of_new_parts( + std::make_move_iterator(serialized_resources_uri_digests.begin()), + std::make_move_iterator(serialized_resources_uri_digests.end())); + + // Notify the browser process about completion using the callback. // Note: we assume this method is fast enough to not need to be accounted for // in PageSerialization.MhtmlGeneration.RendererMainThreadTime.SingleFrame. - Send(new FrameHostMsg_SerializeAsMHTMLResponse( - routing_id_, job_id, save_status, serialized_resources_uri_digests, - main_thread_use_time)); + std::move(callback).Run( + static_cast<content::mojom::MhtmlSaveStatus>(save_status), + std::move(digests_of_new_parts), main_thread_use_time); } #ifndef STATIC_ASSERT_ENUM @@ -7247,6 +7265,9 @@ registry_.AddInterface( base::Bind(&RenderFrameImpl::BindWidget, weak_factory_.GetWeakPtr())); + GetAssociatedInterfaceRegistry()->AddInterface(base::BindRepeating( + &RenderFrameImpl::BindMhtmlFileWriter, base::Unretained(this))); + if (!frame_->Parent()) { // Only main frame have ImageDownloader service. registry_.AddInterface(base::Bind(&ImageDownloaderImpl::CreateMojoService, @@ -7270,6 +7291,12 @@ GetTaskRunner(blink::TaskType::kInternalIPC)); } +void RenderFrameImpl::BindMhtmlFileWriter( + mojom::MhtmlFileWriterAssociatedRequest request) { + mhtml_file_writer_binding_.Bind( + std::move(request), GetTaskRunner(blink::TaskType::kInternalDefault)); +} + void RenderFrameImpl::CheckIfAudioSinkExistsAndIsAuthorized( const blink::WebString& sink_id, std::unique_ptr<blink::WebSetSinkIdCallbacks> callbacks) {
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h index 634e9d0..8f17a70 100644 --- a/content/renderer/render_frame_impl.h +++ b/content/renderer/render_frame_impl.h
@@ -12,6 +12,7 @@ #include <memory> #include <set> #include <string> +#include <unordered_set> #include <utility> #include <vector> @@ -35,6 +36,7 @@ #include "content/common/frame_message_enums.h" #include "content/common/host_zoom.mojom.h" #include "content/common/media/renderer_audio_input_stream_factory.mojom.h" +#include "content/common/mhtml_file_writer.mojom.h" #include "content/common/possibly_associated_interface_ptr.h" #include "content/common/renderer.mojom.h" #include "content/common/unique_name_helper.h" @@ -110,7 +112,6 @@ struct FrameMsg_MixedContentFound_Params; struct FrameMsg_PostMessage_Params; -struct FrameMsg_SerializeAsMHTML_Params; struct FrameMsg_TextTrackSettings_Params; namespace blink { @@ -190,6 +191,7 @@ mojom::FullscreenVideoElementHandler, mojom::HostZoom, mojom::FrameBindingsControl, + mojom::MhtmlFileWriter, public blink::WebLocalFrameClient, public blink::WebFrameSerializerClient, service_manager::mojom::InterfaceProvider { @@ -638,6 +640,10 @@ // mojom::HostZoom implementation: void SetHostZoomLevel(const GURL& url, double zoom_level) override; + // mojom::MhtmlFileWriter implementation: + void SerializeAsMHTML(const mojom::SerializeAsMHTMLParamsPtr params, + SerializeAsMHTMLCallback callback) override; + // blink::WebLocalFrameClient implementation: blink::WebPlugin* CreatePlugin(const blink::WebPluginParams& params) override; blink::WebMediaPlayer* CreateMediaPlayer( @@ -857,6 +863,9 @@ void BindFullscreen( mojom::FullscreenVideoElementHandlerAssociatedRequest request); + // Binds to the MHTML file generation service in the browser. + void BindMhtmlFileWriter(mojom::MhtmlFileWriterAssociatedRequest request); + // Binds to the autoplay configuration service in the browser. void BindAutoplayConfiguration( blink::mojom::AutoplayConfigurationClientAssociatedRequest request); @@ -1119,7 +1128,6 @@ void OnGetSerializedHtmlWithLocalLinks( const std::map<GURL, base::FilePath>& url_to_local_path, const std::map<int, base::FilePath>& frame_routing_id_to_local_path); - void OnSerializeAsMHTML(const FrameMsg_SerializeAsMHTML_Params& params); void OnEnableViewSourceMode(); void OnSuppressFurtherDialogs(); void OnClearFocusedElement(); @@ -1142,11 +1150,11 @@ #endif #endif - // Callback scheduled from OnSerializeAsMHTML for when writing serialized + // Callback scheduled from SerializeAsMHTML for when writing serialized // MHTML to file has been completed in the file thread. void OnWriteMHTMLToDiskComplete( - int job_id, - std::set<std::string> serialized_resources_uri_digests, + SerializeAsMHTMLCallback callback, + std::unordered_set<std::string> serialized_resources_uri_digests, base::TimeDelta main_thread_use_time, MhtmlSaveStatus save_status); @@ -1634,6 +1642,7 @@ frame_navigation_control_binding_; mojo::AssociatedBinding<mojom::FullscreenVideoElementHandler> fullscreen_binding_; + mojo::AssociatedBinding<mojom::MhtmlFileWriter> mhtml_file_writer_binding_; // Only used when PerNavigationMojoInterface is enabled. std::unique_ptr<NavigationClient> navigation_client_impl_;
diff --git a/docs/infra/cq_builders.md b/docs/infra/cq_builders.md index 8c7833f..9ccf90a 100644 --- a/docs/infra/cq_builders.md +++ b/docs/infra/cq_builders.md
@@ -112,6 +112,7 @@ * [`//services/viz/.+`](https://cs.chromium.org/chromium/src/services/viz/) * [`//testing/trigger_scripts/.+`](https://cs.chromium.org/chromium/src/testing/trigger_scripts/) * [`//third_party/blink/renderer/modules/webgl/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgl/) + * [`//third_party/blink/renderer/platform/graphics/gpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/gpu/) * [`//ui/gl/.+`](https://cs.chromium.org/chromium/src/ui/gl/) * [chromeos-kevin-compile-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/chromeos-kevin-compile-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/chromeos-kevin-compile-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+chromeos-kevin-compile-rel)) @@ -191,6 +192,7 @@ * [`//media/gpu/.+`](https://cs.chromium.org/chromium/src/media/gpu/) * [`//testing/trigger_scripts/.+`](https://cs.chromium.org/chromium/src/testing/trigger_scripts/) * [`//third_party/blink/renderer/modules/webgl/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgl/) + * [`//third_party/blink/renderer/platform/graphics/gpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/gpu/) * [`//ui/gl/.+`](https://cs.chromium.org/chromium/src/ui/gl/) * [linux_vr](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_vr) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/linux_vr)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_vr)) @@ -210,6 +212,7 @@ * [`//services/shape_detection/.+`](https://cs.chromium.org/chromium/src/services/shape_detection/) * [`//testing/trigger_scripts/.+`](https://cs.chromium.org/chromium/src/testing/trigger_scripts/) * [`//third_party/blink/renderer/modules/webgl/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgl/) + * [`//third_party/blink/renderer/platform/graphics/gpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/gpu/) * [`//ui/gl/.+`](https://cs.chromium.org/chromium/src/ui/gl/) * [win_optional_gpu_tests_rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/win_optional_gpu_tests_rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/win_optional_gpu_tests_rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+win_optional_gpu_tests_rel))
diff --git a/extensions/browser/api/declarative_net_request/rules_monitor_service.cc b/extensions/browser/api/declarative_net_request/rules_monitor_service.cc index eae1c27..21485f8 100644 --- a/extensions/browser/api/declarative_net_request/rules_monitor_service.cc +++ b/extensions/browser/api/declarative_net_request/rules_monitor_service.cc
@@ -20,7 +20,6 @@ #include "extensions/browser/api/declarative_net_request/ruleset_manager.h" #include "extensions/browser/api/declarative_net_request/ruleset_matcher.h" #include "extensions/browser/api/declarative_net_request/ruleset_source.h" -#include "extensions/browser/api/declarative_net_request/utils.h" #include "extensions/browser/extension_file_task_runner.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_prefs_factory.h" @@ -111,7 +110,7 @@ DCHECK(GetExtensionFileTaskRunner()->RunsTasksInCurrentSequence()); load_ruleset_result_ = RulesetMatcher::CreateVerifiedMatcher( - source_.indexed_path, *expected_checksum_, &matcher_); + source_.indexed_path(), *expected_checksum_, &matcher_); UMA_HISTOGRAM_ENUMERATION( "Extensions.DeclarativeNetRequest.LoadRulesetResult", @@ -217,12 +216,13 @@ // Attempt to reindex the extension ruleset. // Using a weak pointer here is safe since |ruleset_reindexed_callback| will // be called on this sequence itself. - IndexAndPersistRulesCallback ruleset_reindexed_callback = base::BindOnce( - &FileSequenceState::OnRulesetReindexed, weak_factory_.GetWeakPtr(), - std::move(load_data), std::move(ui_callback)); - IndexAndPersistRules(connector_.get(), base::nullopt /* decoder_batch_id */, - std::move(source_copy), - std::move(ruleset_reindexed_callback)); + RulesetSource::IndexAndPersistRulesCallback ruleset_reindexed_callback = + base::BindOnce(&FileSequenceState::OnRulesetReindexed, + weak_factory_.GetWeakPtr(), std::move(load_data), + std::move(ui_callback)); + source_copy.IndexAndPersistRules(connector_.get(), + base::nullopt /* decoder_batch_id */, + std::move(ruleset_reindexed_callback)); } private:
diff --git a/extensions/browser/api/declarative_net_request/ruleset_matcher_unittest.cc b/extensions/browser/api/declarative_net_request/ruleset_matcher_unittest.cc index 13b38f17..0a0c958a 100644 --- a/extensions/browser/api/declarative_net_request/ruleset_matcher_unittest.cc +++ b/extensions/browser/api/declarative_net_request/ruleset_matcher_unittest.cc
@@ -42,10 +42,10 @@ ListBuilder builder; for (const auto& rule : rules) builder.Append(rule.ToValue()); - JSONFileValueSerializer(source.json_path).Serialize(*builder.Build()); + JSONFileValueSerializer(source.json_path()).Serialize(*builder.Build()); // Index ruleset. - IndexAndPersistRulesResult result = IndexAndPersistRulesUnsafe(source); + IndexAndPersistRulesResult result = source.IndexAndPersistRulesUnsafe(); ASSERT_TRUE(result.success); ASSERT_TRUE(result.error.empty()); @@ -54,7 +54,7 @@ // Create verified matcher. RulesetMatcher::LoadRulesetResult load_result = - RulesetMatcher::CreateVerifiedMatcher(source.indexed_path, + RulesetMatcher::CreateVerifiedMatcher(source.indexed_path(), result.ruleset_checksum, matcher); ASSERT_EQ(RulesetMatcher::kLoadSuccess, load_result); } @@ -126,9 +126,9 @@ // occurs. std::string data = "invalid data"; ASSERT_EQ(static_cast<int>(data.size()), - base::WriteFile(source.indexed_path, data.c_str(), data.size())); + base::WriteFile(source.indexed_path(), data.c_str(), data.size())); EXPECT_EQ(RulesetMatcher::kLoadErrorVersionMismatch, - RulesetMatcher::CreateVerifiedMatcher(source.indexed_path, + RulesetMatcher::CreateVerifiedMatcher(source.indexed_path(), expected_checksum, &matcher)); // Now, persist invalid data to the ruleset file, while maintaining the @@ -136,9 +136,9 @@ // mismatch. data = GetVersionHeaderForTesting() + "invalid data"; ASSERT_EQ(static_cast<int>(data.size()), - base::WriteFile(source.indexed_path, data.c_str(), data.size())); + base::WriteFile(source.indexed_path(), data.c_str(), data.size())); EXPECT_EQ(RulesetMatcher::kLoadErrorChecksumMismatch, - RulesetMatcher::CreateVerifiedMatcher(source.indexed_path, + RulesetMatcher::CreateVerifiedMatcher(source.indexed_path(), expected_checksum, &matcher)); }
diff --git a/extensions/browser/api/declarative_net_request/ruleset_source.cc b/extensions/browser/api/declarative_net_request/ruleset_source.cc index 323d674..04cb0f8 100644 --- a/extensions/browser/api/declarative_net_request/ruleset_source.cc +++ b/extensions/browser/api/declarative_net_request/ruleset_source.cc
@@ -4,16 +4,217 @@ #include "extensions/browser/api/declarative_net_request/ruleset_source.h" +#include <memory> +#include <set> #include <utility> +#include "base/bind.h" +#include "base/callback.h" +#include "base/callback_helpers.h" +#include "base/files/file_util.h" +#include "base/json/json_file_value_serializer.h" +#include "base/metrics/histogram_macros.h" +#include "base/strings/strcat.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/timer/elapsed_timer.h" +#include "base/values.h" +#include "extensions/browser/api/declarative_net_request/constants.h" +#include "extensions/browser/api/declarative_net_request/flat_ruleset_indexer.h" +#include "extensions/browser/api/declarative_net_request/indexed_rule.h" +#include "extensions/browser/api/declarative_net_request/parse_info.h" +#include "extensions/browser/api/declarative_net_request/utils.h" +#include "extensions/common/api/declarative_net_request.h" #include "extensions/common/api/declarative_net_request/dnr_manifest_data.h" +#include "extensions/common/api/declarative_net_request/utils.h" +#include "extensions/common/error_utils.h" #include "extensions/common/extension.h" #include "extensions/common/extension_resource.h" #include "extensions/common/file_util.h" +#include "extensions/common/install_warning.h" +#include "extensions/common/manifest_constants.h" +#include "services/data_decoder/public/cpp/safe_json_parser.h" +#include "services/service_manager/public/cpp/identity.h" namespace extensions { namespace declarative_net_request { +namespace { + +namespace dnr_api = extensions::api::declarative_net_request; + +// Helper to retrieve the filename for the given |file_path|. +std::string GetFilename(const base::FilePath& file_path) { + return file_path.BaseName().AsUTF8Unsafe(); +} + +std::string GetJSONParseError(const std::string& json_ruleset_filename, + const std::string& json_parse_error) { + return base::StrCat({json_ruleset_filename, ": ", json_parse_error}); +} + +InstallWarning CreateInstallWarning(const std::string& message) { + return InstallWarning(message, manifest_keys::kDeclarativeNetRequestKey, + manifest_keys::kDeclarativeRuleResourcesKey); +} + +// Helper function to index |rules| and persist them to |indexed_path|. +ParseInfo IndexAndPersistRulesImpl(const base::Value& rules, + const base::FilePath& indexed_path, + std::vector<InstallWarning>* warnings, + int* ruleset_checksum) { + DCHECK(warnings); + DCHECK(ruleset_checksum); + + if (!rules.is_list()) + return ParseInfo(ParseResult::ERROR_LIST_NOT_PASSED); + + FlatRulesetIndexer indexer; + + const size_t kRuleCountLimit = dnr_api::MAX_NUMBER_OF_RULES; + bool rule_count_exceeded = false; + + // Limit the maximum number of rule unparsed warnings to 5. + const size_t kMaxUnparsedRulesWarnings = 5; + bool unparsed_warnings_limit_exeeded = false; + size_t unparsed_warning_count = 0; + + base::ElapsedTimer timer; + { + std::set<int> id_set; // Ensure all ids are distinct. + + const auto& rules_list = rules.GetList(); + for (size_t i = 0; i < rules_list.size(); i++) { + dnr_api::Rule parsed_rule; + base::string16 parse_error; + + // Ignore rules which can't be successfully parsed and show an install + // warning for them. A hard error is not thrown to maintain backwards + // compatibility. + if (!dnr_api::Rule::Populate(rules_list[i], &parsed_rule, &parse_error) || + !parse_error.empty()) { + if (unparsed_warning_count < kMaxUnparsedRulesWarnings) { + ++unparsed_warning_count; + std::string rule_location; + + // If possible use the rule ID in the install warning. + if (auto* id_val = rules_list[i].FindKeyOfType( + kIDKey, base::Value::Type::INTEGER)) { + rule_location = base::StringPrintf("id %d", id_val->GetInt()); + } else { + // Use one-based indices. + rule_location = base::StringPrintf("index %zu", i + 1); + } + + warnings->push_back( + CreateInstallWarning(ErrorUtils::FormatErrorMessage( + kRuleNotParsedWarning, rule_location, + base::UTF16ToUTF8(parse_error)))); + } else { + unparsed_warnings_limit_exeeded = true; + } + continue; + } + + int rule_id = parsed_rule.id; + bool inserted = id_set.insert(rule_id).second; + if (!inserted) + return ParseInfo(ParseResult::ERROR_DUPLICATE_IDS, rule_id); + + IndexedRule indexed_rule; + ParseResult parse_result = + IndexedRule::CreateIndexedRule(std::move(parsed_rule), &indexed_rule); + if (parse_result != ParseResult::SUCCESS) + return ParseInfo(parse_result, rule_id); + + if (indexer.indexed_rules_count() >= kRuleCountLimit) { + rule_count_exceeded = true; + break; + } + + indexer.AddUrlRule(indexed_rule); + } + } + indexer.Finish(); + UMA_HISTOGRAM_TIMES(kIndexRulesTimeHistogram, timer.Elapsed()); + + if (!PersistIndexedRuleset(indexed_path, indexer.GetData(), ruleset_checksum)) + return ParseInfo(ParseResult::ERROR_PERSISTING_RULESET); + + if (rule_count_exceeded) + warnings->push_back(CreateInstallWarning(kRuleCountExceeded)); + + if (unparsed_warnings_limit_exeeded) { + DCHECK_EQ(kMaxUnparsedRulesWarnings, unparsed_warning_count); + warnings->push_back(CreateInstallWarning(ErrorUtils::FormatErrorMessage( + kTooManyParseFailuresWarning, + std::to_string(kMaxUnparsedRulesWarnings)))); + } + + UMA_HISTOGRAM_TIMES(kIndexAndPersistRulesTimeHistogram, timer.Elapsed()); + UMA_HISTOGRAM_COUNTS_100000(kManifestRulesCountHistogram, + indexer.indexed_rules_count()); + + return ParseInfo(ParseResult::SUCCESS); +} + +void OnSafeJSONParserSuccess( + const RulesetSource& source, + RulesetSource::IndexAndPersistRulesCallback callback, + std::unique_ptr<base::Value> root) { + DCHECK(root); + + std::vector<InstallWarning> warnings; + int ruleset_checksum; + const ParseInfo info = IndexAndPersistRulesImpl(*root, source.indexed_path(), + &warnings, &ruleset_checksum); + if (info.result() == ParseResult::SUCCESS) { + std::move(callback).Run(IndexAndPersistRulesResult::CreateSuccessResult( + ruleset_checksum, std::move(warnings))); + return; + } + + std::string error = info.GetErrorDescription(GetFilename(source.json_path())); + std::move(callback).Run( + IndexAndPersistRulesResult::CreateErrorResult(std::move(error))); +} + +void OnSafeJSONParserError(RulesetSource::IndexAndPersistRulesCallback callback, + const std::string& json_ruleset_filename, + const std::string& json_parse_error) { + std::move(callback).Run(IndexAndPersistRulesResult::CreateErrorResult( + GetJSONParseError(json_ruleset_filename, json_parse_error))); +} + +} // namespace + +// static +IndexAndPersistRulesResult IndexAndPersistRulesResult::CreateSuccessResult( + int ruleset_checksum, + std::vector<InstallWarning> warnings) { + IndexAndPersistRulesResult result; + result.success = true; + result.ruleset_checksum = ruleset_checksum; + result.warnings = std::move(warnings); + return result; +} + +// static +IndexAndPersistRulesResult IndexAndPersistRulesResult::CreateErrorResult( + std::string error) { + IndexAndPersistRulesResult result; + result.success = false; + result.error = std::move(error); + return result; +} + +IndexAndPersistRulesResult::~IndexAndPersistRulesResult() = default; +IndexAndPersistRulesResult::IndexAndPersistRulesResult( + IndexAndPersistRulesResult&&) = default; +IndexAndPersistRulesResult& IndexAndPersistRulesResult::operator=( + IndexAndPersistRulesResult&&) = default; +IndexAndPersistRulesResult::IndexAndPersistRulesResult() = default; + // static RulesetSource RulesetSource::Create(const Extension& extension) { return RulesetSource( @@ -23,14 +224,72 @@ RulesetSource::RulesetSource(base::FilePath json_path, base::FilePath indexed_path) - : json_path(std::move(json_path)), indexed_path(std::move(indexed_path)) {} + : json_path_(std::move(json_path)), + indexed_path_(std::move(indexed_path)) {} RulesetSource::~RulesetSource() = default; RulesetSource::RulesetSource(RulesetSource&&) = default; RulesetSource& RulesetSource::operator=(RulesetSource&&) = default; RulesetSource RulesetSource::Clone() const { - return RulesetSource(json_path, indexed_path); + return RulesetSource(json_path_, indexed_path_); +} + +IndexAndPersistRulesResult RulesetSource::IndexAndPersistRulesUnsafe() const { + DCHECK(IsAPIAvailable()); + + JSONFileValueDeserializer deserializer(json_path_); + std::string error; + std::unique_ptr<base::Value> root = deserializer.Deserialize( + nullptr /*error_code*/, &error /*error_message*/); + if (!root) { + return IndexAndPersistRulesResult::CreateErrorResult( + GetJSONParseError(GetFilename(json_path_), error)); + } + + std::vector<InstallWarning> warnings; + int ruleset_checksum; + const ParseInfo info = IndexAndPersistRulesImpl(*root, indexed_path_, + &warnings, &ruleset_checksum); + if (info.result() == ParseResult::SUCCESS) { + return IndexAndPersistRulesResult::CreateSuccessResult(ruleset_checksum, + std::move(warnings)); + } + + error = info.GetErrorDescription(GetFilename(json_path_)); + return IndexAndPersistRulesResult::CreateErrorResult(std::move(error)); +} + +void RulesetSource::IndexAndPersistRules( + service_manager::Connector* connector, + const base::Optional<base::Token>& decoder_batch_id, + IndexAndPersistRulesCallback callback) const { + DCHECK(IsAPIAvailable()); + + std::string json_contents; + if (!base::ReadFileToString(json_path_, &json_contents)) { + std::move(callback).Run(IndexAndPersistRulesResult::CreateErrorResult( + manifest_errors::kDeclarativeNetRequestJSONRulesFileReadError)); + return; + } + + // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating + // the callee interface. + auto repeating_callback = + base::AdaptCallbackForRepeating(std::move(callback)); + auto error_callback = base::BindRepeating( + &OnSafeJSONParserError, repeating_callback, GetFilename(json_path_)); + auto success_callback = base::BindRepeating(&OnSafeJSONParserSuccess, Clone(), + repeating_callback); + + if (decoder_batch_id) { + data_decoder::SafeJsonParser::ParseBatch(connector, json_contents, + success_callback, error_callback, + *decoder_batch_id); + } else { + data_decoder::SafeJsonParser::Parse(connector, json_contents, + success_callback, error_callback); + } } } // namespace declarative_net_request
diff --git a/extensions/browser/api/declarative_net_request/ruleset_source.h b/extensions/browser/api/declarative_net_request/ruleset_source.h index f99fc53..aad63c11 100644 --- a/extensions/browser/api/declarative_net_request/ruleset_source.h +++ b/extensions/browser/api/declarative_net_request/ruleset_source.h
@@ -5,21 +5,62 @@ #ifndef EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_RULESET_SOURCE_H_ #define EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_RULESET_SOURCE_H_ +#include <string> +#include <vector> + +#include "base/callback_forward.h" #include "base/files/file_path.h" +namespace base { +class Token; +} // namespace base + +namespace service_manager { +class Connector; +} // namespace service_manager + namespace extensions { class Extension; +struct InstallWarning; namespace declarative_net_request { +struct IndexAndPersistRulesResult { + public: + static IndexAndPersistRulesResult CreateSuccessResult( + int ruleset_checksum, + std::vector<InstallWarning> warnings); + static IndexAndPersistRulesResult CreateErrorResult(std::string error); + + ~IndexAndPersistRulesResult(); + IndexAndPersistRulesResult(IndexAndPersistRulesResult&&); + IndexAndPersistRulesResult& operator=(IndexAndPersistRulesResult&&); + + // Whether IndexAndPersistRules succeeded. + bool success; + + // Checksum of the persisted indexed ruleset file. Valid if |success| if true. + int ruleset_checksum; + + // Valid if |success| is true. + std::vector<InstallWarning> warnings; + + // Valid if |success| is false. + std::string error; + + private: + IndexAndPersistRulesResult(); + DISALLOW_COPY_AND_ASSIGN(IndexAndPersistRulesResult); +}; + // Holds paths for an extension ruleset. -struct RulesetSource { +class RulesetSource { + public: // This must only be called for extensions which specified a declarative // ruleset. static RulesetSource Create(const Extension& extension); RulesetSource(base::FilePath json_path, base::FilePath indexed_path); - ~RulesetSource(); RulesetSource(RulesetSource&&); RulesetSource& operator=(RulesetSource&&); @@ -27,12 +68,33 @@ RulesetSource Clone() const; // Path to the JSON rules. - base::FilePath json_path; + const base::FilePath& json_path() const { return json_path_; } // Path to the indexed flatbuffer rules. - base::FilePath indexed_path; + const base::FilePath& indexed_path() const { return indexed_path_; } + + // Indexes and persists the JSON ruleset. This is potentially unsafe since the + // JSON rules file is parsed in-process. Note: This must be called on a + // sequence where file IO is allowed. + IndexAndPersistRulesResult IndexAndPersistRulesUnsafe() const; + + using IndexAndPersistRulesCallback = + base::OnceCallback<void(IndexAndPersistRulesResult)>; + // Same as IndexAndPersistRulesUnsafe but parses the JSON rules file out-of- + // process. |connector| should be a connector to the ServiceManager usable on + // the current sequence. Optionally clients can pass a valid + // |decoder_batch_id| to be used when accessing the data decoder service, + // which is used internally to parse JSON. + // + // NOTE: This must be called on a sequence where file IO is allowed. + void IndexAndPersistRules(service_manager::Connector* connector, + const base::Optional<base::Token>& decoder_batch_id, + IndexAndPersistRulesCallback callback) const; private: + base::FilePath json_path_; + base::FilePath indexed_path_; + DISALLOW_COPY_AND_ASSIGN(RulesetSource); };
diff --git a/extensions/browser/api/declarative_net_request/utils.cc b/extensions/browser/api/declarative_net_request/utils.cc index 9564d0bc..0833146 100644 --- a/extensions/browser/api/declarative_net_request/utils.cc +++ b/extensions/browser/api/declarative_net_request/utils.cc
@@ -8,43 +8,20 @@ #include <set> #include <utility> -#include "base/bind.h" -#include "base/callback.h" -#include "base/callback_helpers.h" #include "base/files/file.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/hash.h" -#include "base/json/json_file_value_serializer.h" -#include "base/metrics/histogram_macros.h" -#include "base/strings/strcat.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "base/timer/elapsed_timer.h" -#include "base/values.h" #include "components/url_pattern_index/url_pattern_index.h" -#include "extensions/browser/api/declarative_net_request/constants.h" #include "extensions/browser/api/declarative_net_request/flat/extension_ruleset_generated.h" -#include "extensions/browser/api/declarative_net_request/flat_ruleset_indexer.h" -#include "extensions/browser/api/declarative_net_request/indexed_rule.h" -#include "extensions/browser/api/declarative_net_request/parse_info.h" -#include "extensions/browser/api/declarative_net_request/ruleset_source.h" -#include "extensions/common/api/declarative_net_request.h" -#include "extensions/common/api/declarative_net_request/utils.h" -#include "extensions/common/error_utils.h" -#include "extensions/common/install_warning.h" -#include "extensions/common/manifest_constants.h" -#include "services/data_decoder/public/cpp/safe_json_parser.h" -#include "services/service_manager/public/cpp/identity.h" #include "third_party/flatbuffers/src/include/flatbuffers/flatbuffers.h" namespace extensions { namespace declarative_net_request { namespace { -namespace dnr_api = extensions::api::declarative_net_request; - // The ruleset format version of the flatbuffer schema. Increment this whenever // making an incompatible change to the schema at extension_ruleset.fbs or // url_pattern_index.fbs. Whenever an extension with an indexed ruleset format @@ -88,276 +65,8 @@ return static_cast<int>(hash & 0x7fffffff); } -// Helper function to persist the indexed ruleset |data| at the given |path|. -// The ruleset is composed of a version header corresponding to the current -// ruleset format version, followed by the actual ruleset data. Note: The -// checksum only corresponds to this ruleset data and does not include the -// version header. -bool PersistRuleset(const base::FilePath& path, - base::span<const uint8_t> data, - int* ruleset_checksum) { - DCHECK(ruleset_checksum); - - // Create the directory corresponding to |path| if it does not exist. - if (!base::CreateDirectory(path.DirName())) - return false; - - base::File ruleset_file( - path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); - if (!ruleset_file.IsValid()) - return false; - - // Write the version header. - std::string version_header = GetVersionHeader(); - int version_header_size = static_cast<int>(version_header.size()); - if (ruleset_file.WriteAtCurrentPos( - version_header.data(), version_header_size) != version_header_size) { - return false; - } - - // Write the flatbuffer ruleset. - if (!base::IsValueInRangeForNumericType<int>(data.size())) - return false; - int data_size = static_cast<int>(data.size()); - if (ruleset_file.WriteAtCurrentPos(reinterpret_cast<const char*>(data.data()), - data_size) != data_size) { - return false; - } - - *ruleset_checksum = GetChecksum(data); - return true; -} - -// Helper to retrieve the filename for the given |file_path|. -std::string GetFilename(const base::FilePath& file_path) { - return file_path.BaseName().AsUTF8Unsafe(); -} - -InstallWarning CreateInstallWarning(const std::string& message) { - return InstallWarning(message, manifest_keys::kDeclarativeNetRequestKey, - manifest_keys::kDeclarativeRuleResourcesKey); -} - -// Helper function to index |rules| and persist them to |indexed_path|. -ParseInfo IndexAndPersistRulesImpl(const base::Value& rules, - const base::FilePath& indexed_path, - std::vector<InstallWarning>* warnings, - int* ruleset_checksum) { - DCHECK(warnings); - DCHECK(ruleset_checksum); - - if (!rules.is_list()) - return ParseInfo(ParseResult::ERROR_LIST_NOT_PASSED); - - FlatRulesetIndexer indexer; - - const size_t kRuleCountLimit = dnr_api::MAX_NUMBER_OF_RULES; - bool rule_count_exceeded = false; - - // Limit the maximum number of rule unparsed warnings to 5. - const size_t kMaxUnparsedRulesWarnings = 5; - bool unparsed_warnings_limit_exeeded = false; - size_t unparsed_warning_count = 0; - - base::ElapsedTimer timer; - { - std::set<int> id_set; // Ensure all ids are distinct. - - const auto& rules_list = rules.GetList(); - for (size_t i = 0; i < rules_list.size(); i++) { - dnr_api::Rule parsed_rule; - base::string16 parse_error; - - // Ignore rules which can't be successfully parsed and show an install - // warning for them. A hard error is not thrown to maintain backwards - // compatibility. - if (!dnr_api::Rule::Populate(rules_list[i], &parsed_rule, &parse_error) || - !parse_error.empty()) { - if (unparsed_warning_count < kMaxUnparsedRulesWarnings) { - ++unparsed_warning_count; - std::string rule_location; - - // If possible use the rule ID in the install warning. - if (auto* id_val = rules_list[i].FindKeyOfType( - kIDKey, base::Value::Type::INTEGER)) { - rule_location = base::StringPrintf("id %d", id_val->GetInt()); - } else { - // Use one-based indices. - rule_location = base::StringPrintf("index %zu", i + 1); - } - - warnings->push_back( - CreateInstallWarning(ErrorUtils::FormatErrorMessage( - kRuleNotParsedWarning, rule_location, - base::UTF16ToUTF8(parse_error)))); - } else { - unparsed_warnings_limit_exeeded = true; - } - continue; - } - - int rule_id = parsed_rule.id; - bool inserted = id_set.insert(rule_id).second; - if (!inserted) - return ParseInfo(ParseResult::ERROR_DUPLICATE_IDS, rule_id); - - IndexedRule indexed_rule; - ParseResult parse_result = - IndexedRule::CreateIndexedRule(std::move(parsed_rule), &indexed_rule); - if (parse_result != ParseResult::SUCCESS) - return ParseInfo(parse_result, rule_id); - - if (indexer.indexed_rules_count() >= kRuleCountLimit) { - rule_count_exceeded = true; - break; - } - - indexer.AddUrlRule(indexed_rule); - } - } - indexer.Finish(); - UMA_HISTOGRAM_TIMES(kIndexRulesTimeHistogram, timer.Elapsed()); - - if (!PersistRuleset(indexed_path, indexer.GetData(), ruleset_checksum)) - return ParseInfo(ParseResult::ERROR_PERSISTING_RULESET); - - if (rule_count_exceeded) - warnings->push_back(CreateInstallWarning(kRuleCountExceeded)); - - if (unparsed_warnings_limit_exeeded) { - DCHECK_EQ(kMaxUnparsedRulesWarnings, unparsed_warning_count); - warnings->push_back(CreateInstallWarning(ErrorUtils::FormatErrorMessage( - kTooManyParseFailuresWarning, - std::to_string(kMaxUnparsedRulesWarnings)))); - } - - UMA_HISTOGRAM_TIMES(kIndexAndPersistRulesTimeHistogram, timer.Elapsed()); - UMA_HISTOGRAM_COUNTS_100000(kManifestRulesCountHistogram, - indexer.indexed_rules_count()); - - return ParseInfo(ParseResult::SUCCESS); -} - -void OnSafeJSONParserSuccess(const RulesetSource& source, - IndexAndPersistRulesCallback callback, - std::unique_ptr<base::Value> root) { - DCHECK(root); - - std::vector<InstallWarning> warnings; - int ruleset_checksum; - const ParseInfo info = IndexAndPersistRulesImpl(*root, source.indexed_path, - &warnings, &ruleset_checksum); - if (info.result() == ParseResult::SUCCESS) { - std::move(callback).Run(IndexAndPersistRulesResult::CreateSuccessResult( - ruleset_checksum, std::move(warnings))); - return; - } - - std::string error = info.GetErrorDescription(GetFilename(source.json_path)); - std::move(callback).Run( - IndexAndPersistRulesResult::CreateErrorResult(std::move(error))); -} - -std::string GetJSONParseError(const std::string& json_ruleset_filename, - const std::string& json_parse_error) { - return base::StrCat({json_ruleset_filename, ": ", json_parse_error}); -} - -void OnSafeJSONParserError(IndexAndPersistRulesCallback callback, - const std::string& json_ruleset_filename, - const std::string& json_parse_error) { - std::move(callback).Run(IndexAndPersistRulesResult::CreateErrorResult( - GetJSONParseError(json_ruleset_filename, json_parse_error))); -} - } // namespace -// static -IndexAndPersistRulesResult IndexAndPersistRulesResult::CreateSuccessResult( - int ruleset_checksum, - std::vector<InstallWarning> warnings) { - IndexAndPersistRulesResult result; - result.success = true; - result.ruleset_checksum = ruleset_checksum; - result.warnings = std::move(warnings); - return result; -} - -// static -IndexAndPersistRulesResult IndexAndPersistRulesResult::CreateErrorResult( - std::string error) { - IndexAndPersistRulesResult result; - result.success = false; - result.error = std::move(error); - return result; -} - -IndexAndPersistRulesResult::~IndexAndPersistRulesResult() = default; -IndexAndPersistRulesResult::IndexAndPersistRulesResult( - IndexAndPersistRulesResult&&) = default; -IndexAndPersistRulesResult& IndexAndPersistRulesResult::operator=( - IndexAndPersistRulesResult&&) = default; -IndexAndPersistRulesResult::IndexAndPersistRulesResult() = default; - -IndexAndPersistRulesResult IndexAndPersistRulesUnsafe( - const RulesetSource& source) { - DCHECK(IsAPIAvailable()); - - JSONFileValueDeserializer deserializer(source.json_path); - std::string error; - std::unique_ptr<base::Value> root = deserializer.Deserialize( - nullptr /*error_code*/, &error /*error_message*/); - if (!root) { - return IndexAndPersistRulesResult::CreateErrorResult( - GetJSONParseError(GetFilename(source.json_path), error)); - } - - std::vector<InstallWarning> warnings; - int ruleset_checksum; - const ParseInfo info = IndexAndPersistRulesImpl(*root, source.indexed_path, - &warnings, &ruleset_checksum); - if (info.result() == ParseResult::SUCCESS) { - return IndexAndPersistRulesResult::CreateSuccessResult(ruleset_checksum, - std::move(warnings)); - } - - error = info.GetErrorDescription(GetFilename(source.json_path)); - return IndexAndPersistRulesResult::CreateErrorResult(std::move(error)); -} - -void IndexAndPersistRules(service_manager::Connector* connector, - const base::Optional<base::Token>& decoder_batch_id, - RulesetSource source, - IndexAndPersistRulesCallback callback) { - DCHECK(IsAPIAvailable()); - - std::string json_contents; - if (!base::ReadFileToString(source.json_path, &json_contents)) { - std::move(callback).Run(IndexAndPersistRulesResult::CreateErrorResult( - manifest_errors::kDeclarativeNetRequestJSONRulesFileReadError)); - return; - } - - // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating - // the callee interface. - auto repeating_callback = - base::AdaptCallbackForRepeating(std::move(callback)); - auto error_callback = - base::BindRepeating(&OnSafeJSONParserError, repeating_callback, - GetFilename(source.json_path)); - auto success_callback = base::BindRepeating( - &OnSafeJSONParserSuccess, std::move(source), repeating_callback); - - if (decoder_batch_id) { - data_decoder::SafeJsonParser::ParseBatch(connector, json_contents, - success_callback, error_callback, - *decoder_batch_id); - } else { - data_decoder::SafeJsonParser::Parse(connector, json_contents, - success_callback, error_callback); - } -} - bool IsValidRulesetData(base::span<const uint8_t> data, int expected_checksum) { flatbuffers::Verifier verifier(data.data(), data.size()); return expected_checksum == GetChecksum(data) && @@ -391,5 +100,40 @@ return true; } +bool PersistIndexedRuleset(const base::FilePath& path, + base::span<const uint8_t> data, + int* ruleset_checksum) { + DCHECK(ruleset_checksum); + + // Create the directory corresponding to |path| if it does not exist. + if (!base::CreateDirectory(path.DirName())) + return false; + + base::File ruleset_file( + path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); + if (!ruleset_file.IsValid()) + return false; + + // Write the version header. + std::string version_header = GetVersionHeader(); + int version_header_size = static_cast<int>(version_header.size()); + if (ruleset_file.WriteAtCurrentPos( + version_header.data(), version_header_size) != version_header_size) { + return false; + } + + // Write the flatbuffer ruleset. + if (!base::IsValueInRangeForNumericType<int>(data.size())) + return false; + int data_size = static_cast<int>(data.size()); + if (ruleset_file.WriteAtCurrentPos(reinterpret_cast<const char*>(data.data()), + data_size) != data_size) { + return false; + } + + *ruleset_checksum = GetChecksum(data); + return true; +} + } // namespace declarative_net_request } // namespace extensions
diff --git a/extensions/browser/api/declarative_net_request/utils.h b/extensions/browser/api/declarative_net_request/utils.h index c61513a..33848b8 100644 --- a/extensions/browser/api/declarative_net_request/utils.h +++ b/extensions/browser/api/declarative_net_request/utils.h
@@ -17,67 +17,11 @@ namespace base { class FilePath; -class ListValue; -class Token; } // namespace base -namespace service_manager { -class Connector; -} // namespace service_manager - namespace extensions { -struct InstallWarning; namespace declarative_net_request { -struct RulesetSource; - -struct IndexAndPersistRulesResult { - public: - static IndexAndPersistRulesResult CreateSuccessResult( - int ruleset_checksum, - std::vector<InstallWarning> warnings); - static IndexAndPersistRulesResult CreateErrorResult(std::string error); - - ~IndexAndPersistRulesResult(); - IndexAndPersistRulesResult(IndexAndPersistRulesResult&&); - IndexAndPersistRulesResult& operator=(IndexAndPersistRulesResult&&); - - // Whether IndexAndPersistRules succeeded. - bool success; - - // Checksum of the persisted indexed ruleset file. Valid if |success| if true. - int ruleset_checksum; - - // Valid if |success| is true. - std::vector<InstallWarning> warnings; - - // Valid if |success| is false. - std::string error; - - private: - IndexAndPersistRulesResult(); - DISALLOW_COPY_AND_ASSIGN(IndexAndPersistRulesResult); -}; - -// Indexes and persists the JSON ruleset for |source|. This is potentially -// unsafe since the JSON rules file is parsed in-process. Note: This must be -// called on a sequence where file IO is allowed. -IndexAndPersistRulesResult IndexAndPersistRulesUnsafe( - const RulesetSource& source); - -using IndexAndPersistRulesCallback = - base::OnceCallback<void(IndexAndPersistRulesResult)>; -// Same as IndexAndPersistRulesUnsafe but parses the JSON rules file out-of- -// process. |connector| should be a connector to the ServiceManager usable on -// the current sequence. Optionally clients can pass a valid |decoder_batch_id| -// to be used when accessing the data decoder service, which is used internally -// to parse JSON. -// -// NOTE: This must be called on a sequence where file IO is allowed. -void IndexAndPersistRules(service_manager::Connector* connector, - const base::Optional<base::Token>& decoder_batch_id, - RulesetSource source, - IndexAndPersistRulesCallback callback); // Returns true if |data| represents a valid data buffer containing indexed // ruleset data with |expected_checksum|. @@ -97,6 +41,15 @@ // mismatch. bool StripVersionHeaderAndParseVersion(std::string* ruleset_data); +// Helper function to persist the indexed ruleset |data| at the given |path|. +// The ruleset is composed of a version header corresponding to the current +// ruleset format version, followed by the actual ruleset data. Note: The +// checksum only corresponds to this ruleset data and does not include the +// version header. +bool PersistIndexedRuleset(const base::FilePath& path, + base::span<const uint8_t> data, + int* ruleset_checksum); + } // namespace declarative_net_request } // namespace extensions
diff --git a/extensions/browser/api/messaging/message_service.cc b/extensions/browser/api/messaging/message_service.cc index f5a8579..2793ff0 100644 --- a/extensions/browser/api/messaging/message_service.cc +++ b/extensions/browser/api/messaging/message_service.cc
@@ -78,6 +78,34 @@ return LazyContextId(browser_context, extension->id(), extension->url()); } +const Extension* GetExtensionForNativeAppChannel( + const ChannelEndpoint& source) { + DCHECK(!source.is_for_native_host()); + + if (source.is_for_service_worker()) { + const ExtensionId& extension_id = + source.port_context().worker->extension_id; + return ExtensionRegistry::Get(source.browser_context()) + ->enabled_extensions() + .GetByID(extension_id); + } + + DCHECK(source.is_for_render_frame()); + content::RenderFrameHost* source_rfh = source.GetRenderFrameHost(); + if (!source_rfh) + return nullptr; + content::WebContents* web_contents = + content::WebContents::FromRenderFrameHost(source_rfh); + if (!web_contents) + return nullptr; + ExtensionWebContentsObserver* extension_web_contents_observer = + ExtensionWebContentsObserver::GetForWebContents(web_contents); + if (!extension_web_contents_observer) + return nullptr; + return extension_web_contents_observer->GetExtensionFromFrame(source_rfh, + true); +} + } // namespace struct MessageService::MessageChannel { @@ -189,13 +217,10 @@ source_endpoint.type == MessagingEndpoint::Type::kNativeApp); content::RenderFrameHost* source_render_frame_host = source.is_for_render_frame() ? source.GetRenderFrameHost() : nullptr; - BrowserContext* context = context_; if (!source.IsValid()) return; - if (!source.is_for_native_host()) { - context = source.browser_context(); - DCHECK(ExtensionsBrowserClient::Get()->IsSameContext(context, context_)); - } + BrowserContext* context = source.browser_context(); + DCHECK(ExtensionsBrowserClient::Get()->IsSameContext(context, context_)); if (!opener_port) { DCHECK(source_endpoint.type == MessagingEndpoint::Type::kTab || @@ -338,34 +363,23 @@ } void MessageService::OpenChannelToNativeApp( - int source_process_id, - int source_routing_id, + const ChannelEndpoint& source, const PortId& source_port_id, const std::string& native_app_name) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(source_port_id.is_opener); - content::RenderFrameHost* source = - content::RenderFrameHost::FromID(source_process_id, source_routing_id); - if (!source) + if (!source.IsValid()) return; + const Extension* extension = GetExtensionForNativeAppChannel(source); - content::WebContents* web_contents = - content::WebContents::FromRenderFrameHost(source); - if (!web_contents) - return; - ExtensionWebContentsObserver* extension_web_contents_observer = - ExtensionWebContentsObserver::GetForWebContents(web_contents); - if (!extension_web_contents_observer) - return; - const Extension* extension = - extension_web_contents_observer->GetExtensionFromFrame(source, true); if (!extension) return; - auto opener_port = std::make_unique<ExtensionMessagePort>( - weak_factory_.GetWeakPtr(), source_port_id, extension->id(), source, - false /* include_child_frames */); + std::unique_ptr<ExtensionMessagePort> opener_port = + ExtensionMessagePort::CreateForEndpoint( + weak_factory_.GetWeakPtr(), source_port_id, extension->id(), source, + false /* include_child_frames */); if (!opener_port->IsValidPort()) return; @@ -378,7 +392,7 @@ } // Verify that the host is not blocked by policies. - BrowserContext* source_context = source->GetProcess()->GetBrowserContext(); + BrowserContext* source_context = source.browser_context(); DCHECK( ExtensionsBrowserClient::Get()->IsSameContext(source_context, context_)); MessagingDelegate::PolicyPermission policy_permission = @@ -391,15 +405,19 @@ std::unique_ptr<MessageChannel> channel = std::make_unique<MessageChannel>(); channel->opener = std::move(opener_port); - channel->opener->OpenPort(source_process_id, - PortContext::ForFrame(source_routing_id)); + channel->opener->OpenPort(source.render_process_id(), source.port_context()); + content::RenderFrameHost* source_rfh = + source.is_for_render_frame() ? source.GetRenderFrameHost() : nullptr; std::string error = kReceivingEndDoesntExistError; const PortId receiver_port_id = source_port_id.GetOppositePortId(); + // NOTE: We're creating |receiver| with nullptr |source_rfh|, which seems to + // work for native messaging tests. This might need further checking in case + // any issues arise from it. std::unique_ptr<MessagePort> receiver( messaging_delegate_->CreateReceiverForNativeApp( - weak_factory_.GetWeakPtr(), source, extension->id(), receiver_port_id, - native_app_name, + weak_factory_.GetWeakPtr(), source_rfh, extension->id(), + receiver_port_id, native_app_name, policy_permission == MessagingDelegate::PolicyPermission::ALLOW_ALL, &error));
diff --git a/extensions/browser/api/messaging/message_service.h b/extensions/browser/api/messaging/message_service.h index 280b9b7..df64d94 100644 --- a/extensions/browser/api/messaging/message_service.h +++ b/extensions/browser/api/messaging/message_service.h
@@ -100,8 +100,7 @@ const std::string& extension_id, const std::string& channel_name); - void OpenChannelToNativeApp(int source_process_id, - int source_routing_id, + void OpenChannelToNativeApp(const ChannelEndpoint& source, const PortId& source_port_id, const std::string& native_app_name);
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h index 21c3096..d4432f8 100644 --- a/extensions/browser/extension_function_histogram_value.h +++ b/extensions/browser/extension_function_histogram_value.h
@@ -1384,6 +1384,8 @@ AUTOTESTPRIVATE_GETARCSTATE = 1321, AUTOTESTPRIVATE_ISTABLETMODEENABLED = 1322, AUTOTESTPRIVATE_SETTABLETMODEENABLED = 1323, + AUTOTESTPRIVATE_GETSHELFAUTOHIDEBEHAVIOR = 1324, + AUTOTESTPRIVATE_SETSHELFAUTOHIDEBEHAVIOR = 1325, // Last entry: Add new entries above, then run: // python tools/metrics/histograms/update_extension_histograms.py ENUM_BOUNDARY
diff --git a/extensions/browser/extension_message_filter.cc b/extensions/browser/extension_message_filter.cc index cfa39d92..7492592 100644 --- a/extensions/browser/extension_message_filter.cc +++ b/extensions/browser/extension_message_filter.cc
@@ -413,15 +413,13 @@ const std::string& native_app_name, const PortId& port_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - // TODO(crbug.com/925918): Support messages from Service Worker. - DCHECK(source_context.is_for_render_frame()); if (!browser_context_) return; + ChannelEndpoint source_endpoint(browser_context_, render_process_id_, + source_context); MessageService::Get(browser_context_) - ->OpenChannelToNativeApp(render_process_id_, - source_context.frame->routing_id, port_id, - native_app_name); + ->OpenChannelToNativeApp(source_endpoint, port_id, native_app_name); } void ExtensionMessageFilter::OnOpenChannelToTab(
diff --git a/extensions/browser/sandboxed_unpacker.cc b/extensions/browser/sandboxed_unpacker.cc index ddc8a34..54f6ec7 100644 --- a/extensions/browser/sandboxed_unpacker.cc +++ b/extensions/browser/sandboxed_unpacker.cc
@@ -30,7 +30,6 @@ #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "extensions/browser/api/declarative_net_request/ruleset_source.h" -#include "extensions/browser/api/declarative_net_request/utils.h" #include "extensions/browser/extension_file_task_runner.h" #include "extensions/browser/install/crx_install_error.h" #include "extensions/browser/install/sandboxed_unpacker_failure_reason.h" @@ -688,9 +687,10 @@ return; } - declarative_net_request::IndexAndPersistRules( + auto ruleset_source = + declarative_net_request::RulesetSource::Create(*extension_); + ruleset_source.IndexAndPersistRules( connector_.get(), *data_decoder_service_filter_.instance_id(), - declarative_net_request::RulesetSource::Create(*extension_), base::BindOnce(&SandboxedUnpacker::OnJSONRulesetIndexed, this, std::move(manifest))); }
diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json index a0f87c9..8aba3a0 100644 --- a/extensions/common/api/_api_features.json +++ b/extensions/common/api/_api_features.json
@@ -422,7 +422,7 @@ }, "runtime.connectNative": { "dependencies": ["permission:nativeMessaging"], - "contexts": ["blessed_extension"] + "contexts": ["blessed_extension", "extension_service_worker"] }, "runtime.getURL": { "contexts": [ @@ -483,7 +483,7 @@ }, "runtime.sendNativeMessage": { "dependencies": ["permission:nativeMessaging"], - "contexts": ["blessed_extension"] + "contexts": ["blessed_extension", "extension_service_worker"] }, "serial": { "dependencies": ["permission:serial"],
diff --git a/extensions/renderer/ipc_message_sender.cc b/extensions/renderer/ipc_message_sender.cc index ebbba4e..b3216455 100644 --- a/extensions/renderer/ipc_message_sender.cc +++ b/extensions/renderer/ipc_message_sender.cc
@@ -372,7 +372,9 @@ break; } case MessageTarget::NATIVE_APP: - NOTIMPLEMENTED() << "https://crbug.com/925918."; + dispatcher_->Send(new ExtensionHostMsg_OpenChannelToNativeApp( + PortContextForCurrentWorker(), *target.native_application_name, + port_id)); break; } }
diff --git a/extensions/renderer/messaging_bindings.cc b/extensions/renderer/messaging_bindings.cc index 9978f14f..2f8d03e 100644 --- a/extensions/renderer/messaging_bindings.cc +++ b/extensions/renderer/messaging_bindings.cc
@@ -245,11 +245,10 @@ // This should be checked by our function routing code. CHECK(context()->GetAvailability("runtime.connectNative").is_available()); - // TODO(crbug.com/925918): Support native messaging for Service Workers. - DCHECK(!worker_thread_util::IsWorkerThread()); - content::RenderFrame* render_frame = context()->GetRenderFrame(); - if (!render_frame) + bool is_for_service_worker = false; + if (!render_frame && + !(is_for_service_worker = worker_thread_util::IsWorkerThread())) return; std::string native_app_name = @@ -262,9 +261,17 @@ { SCOPED_UMA_HISTOGRAM_TIMER( "Extensions.Messaging.SetPortIdTime.NativeApp"); - render_frame->Send(new ExtensionHostMsg_OpenChannelToNativeApp( - PortContext::ForFrame(render_frame->GetRoutingID()), native_app_name, - port_id)); + if (is_for_service_worker) { + WorkerThreadDispatcher::GetBindingsSystem() + ->GetIPCMessageSender() + ->SendOpenMessageChannel(context(), port_id, + MessageTarget::ForNativeApp(native_app_name), + "", false /* include_tls_channel_id */); + } else { + render_frame->Send(new ExtensionHostMsg_OpenChannelToNativeApp( + PortContext::ForFrame(render_frame->GetRoutingID()), native_app_name, + port_id)); + } } args.GetReturnValue().Set(static_cast<int32_t>(js_id));
diff --git a/extensions/renderer/resources/runtime_custom_bindings.js b/extensions/renderer/resources/runtime_custom_bindings.js index cafd658..fe0c023 100644 --- a/extensions/renderer/resources/runtime_custom_bindings.js +++ b/extensions/renderer/resources/runtime_custom_bindings.js
@@ -91,7 +91,7 @@ // // Privileged APIs. // - if (contextType != 'BLESSED_EXTENSION') + if (contextType != 'BLESSED_EXTENSION' && contextType != 'SERVICE_WORKER') return; apiFunctions.setHandleRequest('connectNative', @@ -102,6 +102,11 @@ throw new Error('Error connecting to native app: ' + nativeAppName); }); + // Following APIs require DOM access. Hence, these are unavailable in + // extension Service Workers. + if (contextType == 'SERVICE_WORKER') + return; + apiFunctions.setCustomCallback('getBackgroundPage', function(name, request, callback, response) { if (callback) {
diff --git a/infra/config/commit-queue.cfg b/infra/config/commit-queue.cfg index e17e658b..477d575 100644 --- a/infra/config/commit-queue.cfg +++ b/infra/config/commit-queue.cfg
@@ -178,6 +178,7 @@ location_regexp: ".+/[+]/services/viz/.+" location_regexp: ".+/[+]/testing/trigger_scripts/.+" location_regexp: ".+/[+]/third_party/blink/renderer/modules/webgl/.+" + location_regexp: ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+" location_regexp: ".+/[+]/ui/gl/.+" } builders { @@ -248,6 +249,7 @@ location_regexp: ".+/[+]/media/gpu/.+" location_regexp: ".+/[+]/testing/trigger_scripts/.+" location_regexp: ".+/[+]/third_party/blink/renderer/modules/webgl/.+" + location_regexp: ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+" location_regexp: ".+/[+]/ui/gl/.+" } builders { @@ -265,6 +267,7 @@ location_regexp: ".+/[+]/services/shape_detection/.+" location_regexp: ".+/[+]/testing/trigger_scripts/.+" location_regexp: ".+/[+]/third_party/blink/renderer/modules/webgl/.+" + location_regexp: ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+" location_regexp: ".+/[+]/ui/gl/.+" } builders {
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg index 1daa26e..890d52f5 100644 --- a/infra/config/cr-buildbucket.cfg +++ b/infra/config/cr-buildbucket.cfg
@@ -1029,6 +1029,7 @@ mixins: "code-coverage" mixins: "fyi-ci" mixins: "linux" + dimensions: "cores:32" } builders {
diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg index ffdc316f..c9e29b4 100644 --- a/infra/config/luci-milo.cfg +++ b/infra/config/luci-milo.cfg
@@ -623,24 +623,24 @@ short_name: "kvn" } builders { - name: "buildbucket/luci.chrome.ci/Google Chrome Win" + name: "buildbucket/luci.chrome.ci/linux-chromeos-google-rel" category: "chromium.chrome" - short_name: "win" + short_name: "cro" } builders { - name: "buildbucket/luci.chrome.ci/Google Chrome Linux x64" + name: "buildbucket/luci.chrome.ci/linux-google-rel" category: "chromium.chrome" short_name: "lnx" } builders { - name: "buildbucket/luci.chrome.ci/Google Chrome Mac" + name: "buildbucket/luci.chrome.ci/mac-google-rel" category: "chromium.chrome" short_name: "mac" } builders { - name: "buildbucket/luci.chrome.ci/Google Chrome ChromeOS" + name: "buildbucket/luci.chrome.ci/win-google-rel" category: "chromium.chrome" - short_name: "cro" + short_name: "win" } builders { name: "buildbot/chromium.memory/win-asan"
diff --git a/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm b/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm index 9150d9627..073d03c8 100644 --- a/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm +++ b/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm
@@ -298,7 +298,7 @@ browserState->GetPrefs()->GetString(prefs::kGoogleServicesLastAccountId); std::string currentSignedInAccountId = IdentityManagerFactory::GetForBrowserState(browserState) - ->LegacyPickAccountIdForAccount( + ->PickAccountIdForAccount( base::SysNSStringToUTF8([identity gaiaID]), base::SysNSStringToUTF8([identity userEmail])); if (!lastSignedInAccountId.empty()) {
diff --git a/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.mm b/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.mm index c8b6466..1589fc80 100644 --- a/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.mm +++ b/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.mm
@@ -277,7 +277,7 @@ : [self acceptSigninButtonStringId]; std::string account_id = IdentityManagerFactory::GetForBrowserState(_browserState) - ->LegacyPickAccountIdForAccount( + ->PickAccountIdForAccount( base::SysNSStringToUTF8([_selectedIdentity gaiaID]), base::SysNSStringToUTF8([_selectedIdentity userEmail]));
diff --git a/ios/chrome/browser/ui/authentication/chrome_signin_view_controller_unittest.mm b/ios/chrome/browser/ui/authentication/chrome_signin_view_controller_unittest.mm index b8a9010d..f6a5b381 100644 --- a/ios/chrome/browser/ui/authentication/chrome_signin_view_controller_unittest.mm +++ b/ios/chrome/browser/ui/authentication/chrome_signin_view_controller_unittest.mm
@@ -406,7 +406,7 @@ fake_consent_auditor_->recorded_statuses().at(0)); EXPECT_EQ(consent_auditor::Feature::CHROME_SYNC, fake_consent_auditor_->recorded_features().at(0)); - EXPECT_EQ(identity_manager_->LegacyPickAccountIdForAccount( + EXPECT_EQ(identity_manager_->PickAccountIdForAccount( base::SysNSStringToUTF8([identity_ gaiaID]), base::SysNSStringToUTF8([identity_ userEmail])), fake_consent_auditor_->account_id()); @@ -434,7 +434,7 @@ fake_consent_auditor_->recorded_statuses().at(0)); EXPECT_EQ(consent_auditor::Feature::CHROME_SYNC, fake_consent_auditor_->recorded_features().at(0)); - EXPECT_EQ(identity_manager_->LegacyPickAccountIdForAccount( + EXPECT_EQ(identity_manager_->PickAccountIdForAccount( base::SysNSStringToUTF8([identity_ gaiaID]), base::SysNSStringToUTF8([identity_ userEmail])), fake_consent_auditor_->account_id());
diff --git a/ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.mm b/ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.mm index 3a8ae83..67ee499 100644 --- a/ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.mm +++ b/ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.mm
@@ -39,6 +39,9 @@ // Icon constants. const CGFloat kIconWidth = 25.0; + +// PanGesture constants. +const CGFloat kChangeInPositionForTransition = 100.0; } // namespace @interface InfobarBannerViewController () @@ -190,20 +193,27 @@ if (gesture.state == UIGestureRecognizerStateBegan) { self.originalCenter = self.view.center; - } else if (gesture.state == UIGestureRecognizerStateChanged) { self.view.center = CGPointMake(self.view.center.x, self.view.center.y + translation.y); + // If the translation in the positive Y axis is larger than + // kChangeInPositionForTransition then present the InfobarModal. + if (self.view.center.y - self.originalCenter.y > + kChangeInPositionForTransition) { + [self.delegate presentInfobarModalFromBanner]; + return; + } } if (gesture.state == UIGestureRecognizerStateEnded || gesture.state == UIGestureRecognizerStateCancelled) { - if (self.view.center.y > self.originalCenter.y) { - self.view.center = self.originalCenter; - [self.delegate presentInfobarModalFromBanner]; - } else { + // If there's more than a 1px translation in the negative Y axis when the + // gesture ended dismiss the banner. + if (self.view.center.y - self.originalCenter.y < 0) { [self.delegate dismissInfobarBanner:self]; + return; } + self.view.center = self.originalCenter; } [gesture setTranslation:CGPointZero inView:self.view];
diff --git a/ios/chrome/browser/ui/infobars/coordinators/infobar_confirm_coordinator.mm b/ios/chrome/browser/ui/infobars/coordinators/infobar_confirm_coordinator.mm index 7a66c2f..b95fc72 100644 --- a/ios/chrome/browser/ui/infobars/coordinators/infobar_confirm_coordinator.mm +++ b/ios/chrome/browser/ui/infobars/coordinators/infobar_confirm_coordinator.mm
@@ -129,18 +129,24 @@ #pragma mark - InfobarModalDelegate - (void)dismissInfobarModal:(UIButton*)sender { - [self.modalViewController.presentingViewController - dismissViewControllerAnimated:YES - completion:^{ - // If the Modal was presented by the - // BannerViewController, dismiss it too. - if (self.modalTransitionDriver.transitionMode == - InfobarModalTransitionBanner) { + if (self.modalTransitionDriver.transitionMode == + InfobarModalTransitionBanner) { + [self.bannerViewController + dismissViewControllerAnimated:YES + completion:^{ + // Since the Modal was presented by the + // BannerViewController, dismiss that too. [self dismissInfobarBanner: self.bannerViewController]; - } - self.modalTransitionDriver = nil; - }]; + self.modalTransitionDriver = nil; + }]; + } else { + [self.modalViewController.presentingViewController + dismissViewControllerAnimated:YES + completion:^{ + self.modalTransitionDriver = nil; + }]; + } } #pragma mark - Private
diff --git a/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm b/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm index ccd67f9..b344e338 100644 --- a/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm +++ b/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm
@@ -132,18 +132,24 @@ #pragma mark - InfobarModalDelegate - (void)dismissInfobarModal:(UIButton*)sender { - [self.modalViewController.presentingViewController - dismissViewControllerAnimated:YES - completion:^{ - // If the Modal was presented by the - // BannerViewController, dismiss it too. - if (self.modalTransitionDriver.transitionMode == - InfobarModalTransitionBanner) { + if (self.modalTransitionDriver.transitionMode == + InfobarModalTransitionBanner) { + [self.bannerViewController + dismissViewControllerAnimated:YES + completion:^{ + // Since the Modal was presented by the + // BannerViewController, dismiss that too. [self dismissInfobarBanner: self.bannerViewController]; - } - self.modalTransitionDriver = nil; - }]; + self.modalTransitionDriver = nil; + }]; + } else { + [self.modalViewController.presentingViewController + dismissViewControllerAnimated:YES + completion:^{ + self.modalTransitionDriver = nil; + }]; + } } #pragma mark - Private
diff --git a/ios/chrome/browser/ui/infobars/presentation/infobar_modal_transition_driver.mm b/ios/chrome/browser/ui/infobars/presentation/infobar_modal_transition_driver.mm index 7c84acf..421f2e6 100644 --- a/ios/chrome/browser/ui/infobars/presentation/infobar_modal_transition_driver.mm +++ b/ios/chrome/browser/ui/infobars/presentation/infobar_modal_transition_driver.mm
@@ -54,16 +54,9 @@ - (id<UIViewControllerAnimatedTransitioning>) animationControllerForDismissedController:(UIViewController*)dismissed { - switch (self.transitionMode) { - case InfobarModalTransitionBase: - return nil; - - case InfobarModalTransitionBanner: - InfobarExpandBannerAnimator* animator = - [[InfobarExpandBannerAnimator alloc] init]; - animator.presenting = NO; - return animator; - } + // When dismissing the modal ViewController the default UIKit dismiss + // animation is used. + return nil; } @end
diff --git a/ios/third_party/material_components_ios/README.chromium b/ios/third_party/material_components_ios/README.chromium index 6dbd3b6..c2ad6874 100644 --- a/ios/third_party/material_components_ios/README.chromium +++ b/ios/third_party/material_components_ios/README.chromium
@@ -1,7 +1,7 @@ Name: Material Components for iOS URL: https://github.com/material-components/material-components-ios Version: 0 -Revision: c2575332bb4bee4a0e8330765cb8e8980bb554ec +Revision: a64c49634b47b6f0608197a5020ce43ce4951b22 License: Apache 2.0 License File: LICENSE Security Critical: yes
diff --git a/ios/third_party/motion_transitioning_objc/README.chromium b/ios/third_party/motion_transitioning_objc/README.chromium index 7b31b4758..d198e20 100644 --- a/ios/third_party/motion_transitioning_objc/README.chromium +++ b/ios/third_party/motion_transitioning_objc/README.chromium
@@ -1,7 +1,7 @@ Name: Motion Transitioning for Objective-C URL: https://github.com/material-motion/motion-transitioning-objc Version: 0 -Revision: 78ac32badf9ca9c1ad497a6131ce2bd539094812 +Revision: 8f360fc6f016af373276f858796a5e9f73498af9 License: Apache 2.0 License File: LICENSE Security Critical: yes
diff --git a/ios/web/find_in_page/find_in_page_manager_impl.h b/ios/web/find_in_page/find_in_page_manager_impl.h index 57423ac..00c562f 100644 --- a/ios/web/find_in_page/find_in_page_manager_impl.h +++ b/ios/web/find_in_page/find_in_page_manager_impl.h
@@ -13,6 +13,8 @@ #import "ios/web/public/find_in_page/find_in_page_manager.h" #include "ios/web/public/web_state/web_state_observer.h" +@class NSString; + namespace web { class WebState; @@ -43,6 +45,9 @@ // find. This ensures that an old find doesn't decrement // |pending_frame_calls_count| after it has been reset by the new find. int unique_id = 0; + // Query string of find request. NSString type to ensure query passed to + // delegate methods is the same type as what is passed into Find(). + NSString* query; // Counter to keep track of pending frame JavaScript calls. int pending_frame_call_count = 0; // Holds number of matches found for each frame keyed by frame_id. @@ -51,13 +56,16 @@ std::list<std::string> frame_order; }; - // Determines whether find is finished. If not, calls pumpSearch to continue. - // If it is, calls UpdateFrameMatchesCount(). If find returned null, then does - // nothing more. - void ProcessFindInPageResult(const std::string& query, - const std::string& frame_id, + // Executes find logic for |FindInPageSearch| option. + void StartSearch(NSString* query); + // Determines whether find is finished. If not, calls pumpSearch to + // continue. If it is, calls UpdateFrameMatchesCount(). If find returned + // null, then does nothing more. + void ProcessFindInPageResult(const std::string& frame_id, const int request_id, const base::Value* result); + // Calls delegate DidCountMatches() method if |delegate_| is set. + void NotifyDelegateDidCountMatches(); // WebStateObserver overrides void WebFrameDidBecomeAvailable(WebState* web_state,
diff --git a/ios/web/find_in_page/find_in_page_manager_impl.mm b/ios/web/find_in_page/find_in_page_manager_impl.mm index 80c3c0b..d70766c 100644 --- a/ios/web/find_in_page/find_in_page_manager_impl.mm +++ b/ios/web/find_in_page/find_in_page_manager_impl.mm
@@ -5,12 +5,14 @@ #import "ios/web/find_in_page/find_in_page_manager_impl.h" #import "base/strings/sys_string_conversions.h" +#include "base/task/post_task.h" #include "base/values.h" #import "ios/web/find_in_page/find_in_page_constants.h" #import "ios/web/public/find_in_page/find_in_page_manager_delegate.h" #import "ios/web/public/web_state/web_frame.h" #include "ios/web/public/web_state/web_frame_util.h" #import "ios/web/public/web_state/web_frames_manager.h" +#include "ios/web/public/web_task_traits.h" #import "ios/web/web_state/web_state_impl.h" #if !defined(__has_feature) || !__has_feature(objc_arc) @@ -99,23 +101,8 @@ switch (options) { case FindInPageOptions::FindInPageSearch: { DCHECK(query); + StartSearch(query); - std::set<WebFrame*> all_frames = GetAllWebFrames(web_state_); - last_find_request_.pending_frame_call_count = all_frames.size(); - - std::vector<base::Value> params; - params.push_back(base::Value(base::SysNSStringToUTF8(query))); - params.push_back(base::Value(kFindInPageFindTimeout)); - int unique_id = ++last_find_request_.unique_id; - for (WebFrame* frame : all_frames) { - frame->CallJavaScriptFunction( - kFindInPageSearch, params, - base::BindOnce(&FindInPageManagerImpl::ProcessFindInPageResult, - weak_factory_.GetWeakPtr(), - base::SysNSStringToUTF8(query), frame->GetFrameId(), - unique_id), - base::TimeDelta::FromSeconds(kJavaScriptFunctionCallTimeout)); - } break; } case FindInPageOptions::FindInPageNext: @@ -124,10 +111,49 @@ } } +void FindInPageManagerImpl::StartSearch(NSString* query) { + std::set<WebFrame*> all_frames = GetAllWebFrames(web_state_); + last_find_request_.pending_frame_call_count = all_frames.size(); + last_find_request_.query = query; + int unique_id = ++last_find_request_.unique_id; + if (all_frames.size() == 0) { + // No frames to search in. + // Call asyncronously to match behavior if find was successful in frames. + base::PostTaskWithTraits( + FROM_HERE, {WebThread::UI}, + base::BindOnce(&FindInPageManagerImpl::NotifyDelegateDidCountMatches, + weak_factory_.GetWeakPtr())); + return; + } + + std::vector<base::Value> params; + params.push_back(base::Value(base::SysNSStringToUTF8(query))); + params.push_back(base::Value(kFindInPageFindTimeout)); + for (WebFrame* frame : all_frames) { + bool result = frame->CallJavaScriptFunction( + kFindInPageSearch, params, + base::BindOnce(&FindInPageManagerImpl::ProcessFindInPageResult, + weak_factory_.GetWeakPtr(), frame->GetFrameId(), + unique_id), + base::TimeDelta::FromSeconds(kJavaScriptFunctionCallTimeout)); + if (!result) { + // Calling JavaScript function failed or the frame does not support + // messaging. + if (--last_find_request_.pending_frame_call_count == 0) { + // Call asyncronously to match behavior if find was done in frames. + base::PostTaskWithTraits( + FROM_HERE, {WebThread::UI}, + base::BindOnce( + &FindInPageManagerImpl::NotifyDelegateDidCountMatches, + weak_factory_.GetWeakPtr())); + } + } + } +} + void FindInPageManagerImpl::StopFinding() {} -void FindInPageManagerImpl::ProcessFindInPageResult(const std::string& query, - const std::string& frame_id, +void FindInPageManagerImpl::ProcessFindInPageResult(const std::string& frame_id, const int unique_id, const base::Value* result) { if (unique_id != last_find_request_.unique_id) { @@ -139,7 +165,6 @@ return; } - last_find_request_.pending_frame_call_count--; WebFrame* frame = GetWebFrameWithId(web_state_, frame_id); if (!result || !frame) { // The frame no longer exists or the function call timed out. In both cases, @@ -161,17 +186,23 @@ frame->CallJavaScriptFunction( kFindInPagePump, params, base::BindOnce(&FindInPageManagerImpl::ProcessFindInPageResult, - weak_factory_.GetWeakPtr(), query, frame_id, - unique_id), + weak_factory_.GetWeakPtr(), frame_id, unique_id), base::TimeDelta::FromSeconds(kJavaScriptFunctionCallTimeout)); return; } last_find_request_.frame_match_count[frame_id] = match_count; } - if (last_find_request_.pending_frame_call_count == 0) { - int total_match_count = last_find_request_.GetTotalMatchCount(); - delegate_->DidCountMatches(web_state_, total_match_count, query); + if (--last_find_request_.pending_frame_call_count == 0) { + NotifyDelegateDidCountMatches(); + } +} + +void FindInPageManagerImpl::NotifyDelegateDidCountMatches() { + if (delegate_) { + delegate_->DidCountMatches(web_state_, + last_find_request_.GetTotalMatchCount(), + last_find_request_.query); } }
diff --git a/ios/web/find_in_page/find_in_page_manger_impl_unittest.mm b/ios/web/find_in_page/find_in_page_manger_impl_unittest.mm index c625b2a4..2aa26351c 100644 --- a/ios/web/find_in_page/find_in_page_manger_impl_unittest.mm +++ b/ios/web/find_in_page/find_in_page_manger_impl_unittest.mm
@@ -116,7 +116,7 @@ EXPECT_EQ(1, fake_delegate_.state()->match_count); } -// Tests that Find in Page returns a total match count matching the latest find +// Tests that Find In Page returns a total match count matching the latest find // if two finds are called. TEST_F(FindInPageManagerImplTest, ReturnLatestFind) { auto frame_with_one_match = CreateWebFrameWithJsResultForFind( @@ -160,7 +160,7 @@ EXPECT_FALSE(fake_delegate_.state()); } -// Tests that Find in Page updates total match count when a frame with matches +// Tests that Find In Page updates total match count when a frame with matches // becomes unavailable during find. TEST_F(FindInPageManagerImplTest, FrameUnavailableAfterDelegateCallback) { auto frame_with_one_match = CreateWebFrameWithJsResultForFind( @@ -186,4 +186,77 @@ EXPECT_EQ(1, fake_delegate_.state()->match_count); } +// Tests that Find In Page returns with the right match count for a frame with +// one match and another that requires pumping to return its two matches. +TEST_F(FindInPageManagerImplTest, FrameRespondsWithPending) { + std::unique_ptr<FakeWebFrame> frame_with_two_matches = + CreateWebFrameWithJsResultForFind(std::make_unique<base::Value>(-1.0), + kTwoMatchesFrameId); + frame_with_two_matches->AddJsResultForFunctionCall( + std::make_unique<base::Value>(2.0), kFindInPagePump); + FakeWebFrame* frame_with_two_matches_ptr = frame_with_two_matches.get(); + test_web_state_->AddWebFrame(std::move(frame_with_two_matches)); + auto frame_with_one_match = CreateWebFrameWithJsResultForFind( + std::make_unique<base::Value>(1.0), kOneMatchFrameId); + FakeWebFrame* frame_with_one_match_ptr = frame_with_one_match.get(); + test_web_state_->AddWebFrame(std::move(frame_with_one_match)); + + GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch); + + EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);", + frame_with_one_match_ptr->last_javascript_call()); + EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);", + frame_with_two_matches_ptr->last_javascript_call()); + ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool { + base::RunLoop().RunUntilIdle(); + return fake_delegate_.state(); + })); + EXPECT_EQ("__gCrWeb.findInPage.pumpSearch(100.0);", + frame_with_two_matches_ptr->last_javascript_call()); + EXPECT_EQ(3, fake_delegate_.state()->match_count); +} + +// Tests that Find In Page doesn't fail when delegate is not set. +TEST_F(FindInPageManagerImplTest, DelegateNotSet) { + GetFindInPageManager()->SetDelegate(nullptr); + auto frame_with_one_match = CreateWebFrameWithJsResultForFind( + std::make_unique<base::Value>(1.0), kOneMatchFrameId); + FakeWebFrame* frame_with_one_match_ptr = frame_with_one_match.get(); + test_web_state_->AddWebFrame(std::move(frame_with_one_match)); + + GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch); + + EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);", + frame_with_one_match_ptr->last_javascript_call()); + base::RunLoop().RunUntilIdle(); +} + +// Tests that Find In Page returns no matches if can't call JavaScript function. +TEST_F(FindInPageManagerImplTest, FrameCannotCallJavaScriptFunction) { + auto frame_cannot_call_func = CreateWebFrameWithJsResultForFind( + std::make_unique<base::Value>(1.0), kOneMatchFrameId); + frame_cannot_call_func->set_can_call_function(false); + test_web_state_->AddWebFrame(std::move(frame_cannot_call_func)); + + GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch); + + ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool { + base::RunLoop().RunUntilIdle(); + return fake_delegate_.state(); + })); + EXPECT_EQ(0, fake_delegate_.state()->match_count); +} + +// Tests that Find In Page responds with a total match count of zero when there +// are no known webpage frames. +TEST_F(FindInPageManagerImplTest, NoFrames) { + GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch); + + ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool { + base::RunLoop().RunUntilIdle(); + return fake_delegate_.state(); + })); + EXPECT_EQ(0, fake_delegate_.state()->match_count); +} + } // namespace web
diff --git a/ios/web/public/find_in_page/find_in_page_manager_delegate.h b/ios/web/public/find_in_page/find_in_page_manager_delegate.h index edab868..3732d3a7 100644 --- a/ios/web/public/find_in_page/find_in_page_manager_delegate.h +++ b/ios/web/public/find_in_page/find_in_page_manager_delegate.h
@@ -9,6 +9,8 @@ #include "base/macros.h" +@class NSString; + namespace web { class WebState; @@ -24,7 +26,7 @@ // that it is processing |match_count| for the correct find. virtual void DidCountMatches(WebState* web_state, int match_count, - const std::string& query) = 0; + NSString* query) = 0; // Called when a match number |index| is highlighted. This is triggered by // calling FindInPageManager::Find() with any FindInPageOptions to indicate
diff --git a/ios/web/public/test/fakes/fake_find_in_page_manager_delegate.h b/ios/web/public/test/fakes/fake_find_in_page_manager_delegate.h index ef3f132..e010467 100644 --- a/ios/web/public/test/fakes/fake_find_in_page_manager_delegate.h +++ b/ios/web/public/test/fakes/fake_find_in_page_manager_delegate.h
@@ -23,7 +23,7 @@ // FindInPageManagerDelegate override void DidCountMatches(WebState* web_state, int match_count, - const std::string& query) override; + NSString* query) override; void DidHighlightMatch(WebState* web_state, int index) override; // Holds the last response values passed to DidCountMatches. @@ -32,7 +32,7 @@ ~State(); WebState* web_state = nullptr; int match_count = -1; - std::string query; + NSString* query; }; // Returns the current State.
diff --git a/ios/web/public/test/fakes/fake_find_in_page_manager_delegate.mm b/ios/web/public/test/fakes/fake_find_in_page_manager_delegate.mm index 9daa1679..2202569 100644 --- a/ios/web/public/test/fakes/fake_find_in_page_manager_delegate.mm +++ b/ios/web/public/test/fakes/fake_find_in_page_manager_delegate.mm
@@ -20,7 +20,7 @@ void FakeFindInPageManagerDelegate::DidCountMatches(WebState* web_state, int match_count, - const std::string& query) { + NSString* query) { delegate_state_ = std::make_unique<State>(); delegate_state_->web_state = web_state; delegate_state_->match_count = match_count;
diff --git a/media/base/reentrancy_checker.h b/media/base/reentrancy_checker.h index 4ac134b..574a70b 100644 --- a/media/base/reentrancy_checker.h +++ b/media/base/reentrancy_checker.h
@@ -40,7 +40,7 @@ #define REENTRANCY_CHECKER(name) ::base::Lock name #define NON_REENTRANT_SCOPE(name) ::media::NonReentrantScope name##scope(name) #else // DCHECK_IS_ON() -#define REENTRANCY_CHECKER(name) +#define REENTRANCY_CHECKER(name) static_assert(true, "") #define NON_REENTRANT_SCOPE(name) #endif // DCHECK_IS_ON()
diff --git a/media/capabilities/learning_helper.cc b/media/capabilities/learning_helper.cc index 9b35fae..6109c6b 100644 --- a/media/capabilities/learning_helper.cc +++ b/media/capabilities/learning_helper.cc
@@ -34,7 +34,7 @@ // it's likely that the session will live on the main thread, and handle // delegation of LearningTaskControllers to other threads. However, for now, // do it here. - learning_session_ = base::SequenceBound<LearningSessionImpl>( + learning_session_ = std::make_unique<LearningSessionImpl>( base::CreateSequencedTaskRunnerWithTraits( {base::TaskPriority::BEST_EFFORT, base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})); @@ -58,16 +58,16 @@ // Enable hacky reporting of accuracy. dropped_frame_task.uma_hacky_confusion_matrix = "Media.Learning.MediaCapabilities.DroppedFrameRatioTask.BaseTree"; - learning_session_.Post(FROM_HERE, &LearningSessionImpl::RegisterTask, - dropped_frame_task, SequenceBoundFeatureProvider()); + learning_session_->RegisterTask(dropped_frame_task, + SequenceBoundFeatureProvider()); // Modify the task to use ExtraTrees. dropped_frame_task.name = kDroppedFrameRatioBaseTreeTaskName; dropped_frame_task.model = LearningTask::Model::kExtraTrees; dropped_frame_task.uma_hacky_confusion_matrix = "Media.Learning.MediaCapabilities.DroppedFrameRatioTask.BaseTable"; - learning_session_.Post(FROM_HERE, &LearningSessionImpl::RegisterTask, - dropped_frame_task, SequenceBoundFeatureProvider()); + learning_session_->RegisterTask(dropped_frame_task, + SequenceBoundFeatureProvider()); // Add common features, if we have a factory. if (feature_factory) { @@ -78,9 +78,8 @@ FeatureLibrary::BatteryPower()); dropped_frame_task.uma_hacky_confusion_matrix = "Media.Learning.MediaCapabilities.DroppedFrameRatioTask.EnhancedTree"; - learning_session_.Post(FROM_HERE, &LearningSessionImpl::RegisterTask, - dropped_frame_task, - feature_factory.Run(dropped_frame_task)); + learning_session_->RegisterTask(dropped_frame_task, + feature_factory.Run(dropped_frame_task)); } } @@ -121,13 +120,11 @@ example.weight = new_stats.frames_decoded; // Add this example to both tasks. - learning_session_.Post(FROM_HERE, &LearningSessionImpl::AddExample, - kDroppedFrameRatioBaseTreeTaskName, example); - learning_session_.Post(FROM_HERE, &LearningSessionImpl::AddExample, - kDroppedFrameRatioBaseTableTaskName, example); + learning_session_->AddExample(kDroppedFrameRatioBaseTreeTaskName, example); + learning_session_->AddExample(kDroppedFrameRatioBaseTableTaskName, example); // Might fail, but that's okay. - learning_session_.Post(FROM_HERE, &LearningSessionImpl::AddExample, - kDroppedFrameRatioEnhancedTreeTaskName, example); + learning_session_->AddExample(kDroppedFrameRatioEnhancedTreeTaskName, + example); } } // namespace media
diff --git a/media/capabilities/learning_helper.h b/media/capabilities/learning_helper.h index f0db4d2..6014322 100644 --- a/media/capabilities/learning_helper.h +++ b/media/capabilities/learning_helper.h
@@ -27,11 +27,12 @@ const VideoDecodeStatsDB::DecodeStatsEntry& new_stats); private: - // Learning session for our profile. Normally, we'd not have one of these - // directly, but would instead get one that's connected to a browser profile. - // For now, however, we just instantiate one and assume that we'll be - // destroyed when the profile changes / history is cleared. - base::SequenceBound<learning::LearningSessionImpl> learning_session_; + // Learning session for our profile. This isn't the way LearningSession is + // intended to be used -- one should expect to get a LearningTaskController + // for a particular task. The LearningSession would be owned elsewhere (e.g., + // the BrowserContext). We do it this way here since LearningHelper is an + // hacky way to see if all this works. + std::unique_ptr<learning::LearningSessionImpl> learning_session_; }; } // namespace media
diff --git a/media/learning/impl/learning_session_impl.cc b/media/learning/impl/learning_session_impl.cc index 4ad7de4..79b19ba 100644 --- a/media/learning/impl/learning_session_impl.cc +++ b/media/learning/impl/learning_session_impl.cc
@@ -14,13 +14,16 @@ namespace media { namespace learning { -LearningSessionImpl::LearningSessionImpl() - : controller_factory_( - base::BindRepeating([](const LearningTask& task, - SequenceBoundFeatureProvider feature_provider) - -> std::unique_ptr<LearningTaskController> { - return std::make_unique<LearningTaskControllerImpl>( - task, DistributionReporter::Create(task), +LearningSessionImpl::LearningSessionImpl( + scoped_refptr<base::SequencedTaskRunner> task_runner) + : task_runner_(std::move(task_runner)), + controller_factory_(base::BindRepeating( + [](scoped_refptr<base::SequencedTaskRunner> task_runner, + const LearningTask& task, + SequenceBoundFeatureProvider feature_provider) + -> base::SequenceBound<LearningTaskController> { + return base::SequenceBound<LearningTaskControllerImpl>( + task_runner, task, DistributionReporter::Create(task), std::move(feature_provider)); })) {} @@ -38,9 +41,11 @@ // TODO(liberato): We shouldn't be adding examples. We should provide the // LearningTaskController instead, although ownership gets a bit weird. LearningTaskController::ObservationId id = 1; - iter->second->BeginObservation(id, example.features); - iter->second->CompleteObservation( - id, ObservationCompletion(example.target_value, example.weight)); + iter->second.Post(FROM_HERE, &LearningTaskController::BeginObservation, id, + example.features); + iter->second.Post( + FROM_HERE, &LearningTaskController::CompleteObservation, id, + ObservationCompletion(example.target_value, example.weight)); } } @@ -48,8 +53,9 @@ const LearningTask& task, SequenceBoundFeatureProvider feature_provider) { DCHECK(task_map_.count(task.name) == 0); - task_map_.emplace(task.name, - controller_factory_.Run(task, std::move(feature_provider))); + task_map_.emplace( + task.name, + controller_factory_.Run(task_runner_, task, std::move(feature_provider))); } } // namespace learning
diff --git a/media/learning/impl/learning_session_impl.h b/media/learning/impl/learning_session_impl.h index f4693df..2761fa01 100644 --- a/media/learning/impl/learning_session_impl.h +++ b/media/learning/impl/learning_session_impl.h
@@ -8,6 +8,7 @@ #include <map> #include "base/component_export.h" +#include "base/sequenced_task_runner.h" #include "base/threading/sequence_bound.h" #include "media/learning/common/learning_session.h" #include "media/learning/common/learning_task_controller.h" @@ -21,11 +22,14 @@ class COMPONENT_EXPORT(LEARNING_IMPL) LearningSessionImpl : public LearningSession { public: - LearningSessionImpl(); + // We will create LearningTaskControllers that run on |task_runner|. + LearningSessionImpl(scoped_refptr<base::SequencedTaskRunner> task_runner); ~LearningSessionImpl() override; + // Create a SequenceBound controller for |task| on |task_runner|. using CreateTaskControllerCB = - base::RepeatingCallback<std::unique_ptr<LearningTaskController>( + base::RepeatingCallback<base::SequenceBound<LearningTaskController>( + scoped_refptr<base::SequencedTaskRunner>, const LearningTask&, SequenceBoundFeatureProvider)>; @@ -42,9 +46,12 @@ SequenceBoundFeatureProvider()); private: + // Task runner on which we'll create controllers. + scoped_refptr<base::SequencedTaskRunner> task_runner_; + // [task_name] = task controller. using LearningTaskMap = - std::map<std::string, std::unique_ptr<LearningTaskController>>; + std::map<std::string, base::SequenceBound<LearningTaskController>>; LearningTaskMap task_map_; CreateTaskControllerCB controller_factory_;
diff --git a/media/learning/impl/learning_session_impl_unittest.cc b/media/learning/impl/learning_session_impl_unittest.cc index 5c55d66..1526c49 100644 --- a/media/learning/impl/learning_session_impl_unittest.cc +++ b/media/learning/impl/learning_session_impl_unittest.cc
@@ -18,11 +18,19 @@ class LearningSessionImplTest : public testing::Test { public: + class FakeLearningTaskController; + using ControllerVector = std::vector<FakeLearningTaskController*>; + using TaskRunnerVector = std::vector<base::SequencedTaskRunner*>; + class FakeLearningTaskController : public LearningTaskController { public: - FakeLearningTaskController(const LearningTask& task, + // Send ControllerVector* as void*, else it complains that args can't be + // forwarded. Adding base::Unretained() doesn't help. + FakeLearningTaskController(void* controllers, + const LearningTask& task, SequenceBoundFeatureProvider feature_provider) : feature_provider_(std::move(feature_provider)) { + static_cast<ControllerVector*>(controllers)->push_back(this); // As a complete hack, call the only public method on fp so that // we can verify that it was given to us by the session. if (!feature_provider_.is_null()) { @@ -67,20 +75,21 @@ bool* flag_ptr_ = nullptr; }; - using ControllerVector = std::vector<FakeLearningTaskController*>; - LearningSessionImplTest() { - session_ = std::make_unique<LearningSessionImpl>(); + task_runner_ = base::SequencedTaskRunnerHandle::Get(); + session_ = std::make_unique<LearningSessionImpl>(task_runner_); session_->SetTaskControllerFactoryCBForTesting(base::BindRepeating( - [](ControllerVector* controllers, const LearningTask& task, + [](ControllerVector* controllers, TaskRunnerVector* task_runners, + scoped_refptr<base::SequencedTaskRunner> task_runner, + const LearningTask& task, SequenceBoundFeatureProvider feature_provider) - -> std::unique_ptr<LearningTaskController> { - auto controller = std::make_unique<FakeLearningTaskController>( - task, std::move(feature_provider)); - controllers->push_back(controller.get()); - return controller; + -> base::SequenceBound<LearningTaskController> { + task_runners->push_back(task_runner.get()); + return base::SequenceBound<FakeLearningTaskController>( + task_runner, static_cast<void*>(controllers), task, + std::move(feature_provider)); }, - &task_controllers_)); + &task_controllers_, &task_runners_)); task_0_.name = "task_0"; task_1_.name = "task_1"; @@ -88,20 +97,32 @@ base::test::ScopedTaskEnvironment scoped_task_environment_; + scoped_refptr<base::SequencedTaskRunner> task_runner_; + std::unique_ptr<LearningSessionImpl> session_; LearningTask task_0_; LearningTask task_1_; ControllerVector task_controllers_; + TaskRunnerVector task_runners_; }; TEST_F(LearningSessionImplTest, RegisteringTasksCreatesControllers) { EXPECT_EQ(task_controllers_.size(), 0u); + EXPECT_EQ(task_runners_.size(), 0u); + session_->RegisterTask(task_0_); + scoped_task_environment_.RunUntilIdle(); EXPECT_EQ(task_controllers_.size(), 1u); + EXPECT_EQ(task_runners_.size(), 1u); + EXPECT_EQ(task_runners_[0], task_runner_.get()); + session_->RegisterTask(task_1_); + scoped_task_environment_.RunUntilIdle(); EXPECT_EQ(task_controllers_.size(), 2u); + EXPECT_EQ(task_runners_.size(), 2u); + EXPECT_EQ(task_runners_[1], task_runner_.get()); } TEST_F(LearningSessionImplTest, ExamplesAreForwardedToCorrectTask) { @@ -115,6 +136,8 @@ LabelledExample example_1({FeatureValue(321), FeatureValue(654)}, TargetValue(4321)); session_->AddExample(task_1_.name, example_1); + + scoped_task_environment_.RunUntilIdle(); EXPECT_EQ(task_controllers_[0]->example_, example_0); EXPECT_EQ(task_controllers_[1]->example_, example_1); } @@ -122,9 +145,8 @@ TEST_F(LearningSessionImplTest, FeatureProviderIsForwarded) { // Verify that a FeatureProvider actually gets forwarded to the LTC. bool flag = false; - session_->RegisterTask(task_0_, - base::SequenceBound<FakeFeatureProvider>( - base::SequencedTaskRunnerHandle::Get(), &flag)); + session_->RegisterTask( + task_0_, base::SequenceBound<FakeFeatureProvider>(task_runner_, &flag)); scoped_task_environment_.RunUntilIdle(); // Registering the task should create a FakeLearningTaskController, which will // call AddFeatures on the fake FeatureProvider.
diff --git a/net/BUILD.gn b/net/BUILD.gn index c1cf30e..10db102 100644 --- a/net/BUILD.gn +++ b/net/BUILD.gn
@@ -797,8 +797,6 @@ "http/http_network_transaction.h", "http/http_proxy_client_socket.cc", "http/http_proxy_client_socket.h", - "http/http_proxy_client_socket_wrapper.cc", - "http/http_proxy_client_socket_wrapper.h", "http/http_proxy_connect_job.cc", "http/http_proxy_connect_job.h", "http/http_request_info.cc", @@ -848,6 +846,7 @@ "http2/platform/impl/http2_flag_utils_impl.h", "http2/platform/impl/http2_flags_impl.cc", "http2/platform/impl/http2_flags_impl.h", + "http2/platform/impl/http2_logging_impl.h", "http2/platform/impl/http2_macros_impl.h", "http2/platform/impl/http2_optional_impl.h", "http2/platform/impl/http2_ptr_util_impl.h", @@ -1300,6 +1299,7 @@ "third_party/quiche/src/http2/platform/api/http2_export.h", "third_party/quiche/src/http2/platform/api/http2_flag_utils.h", "third_party/quiche/src/http2/platform/api/http2_flags.h", + "third_party/quiche/src/http2/platform/api/http2_logging.h", "third_party/quiche/src/http2/platform/api/http2_optional.h", "third_party/quiche/src/http2/platform/api/http2_ptr_util.h", "third_party/quiche/src/http2/platform/api/http2_reconstruct_object.h", @@ -4965,7 +4965,6 @@ "http/transport_security_persister_unittest.cc", "http/transport_security_state_unittest.cc", "http/url_security_manager_unittest.cc", - "http2/platform/impl/http2_mock_log_impl.h", "http2/platform/impl/http2_test_helpers_impl.cc", "http2/platform/impl/http2_test_helpers_impl.h", "log/file_net_log_observer_unittest.cc", @@ -5169,7 +5168,6 @@ "third_party/quiche/src/http2/http2_structures_test.cc", "third_party/quiche/src/http2/http2_structures_test_util.cc", "third_party/quiche/src/http2/http2_structures_test_util.h", - "third_party/quiche/src/http2/platform/api/http2_mock_log.h", "third_party/quiche/src/http2/platform/api/http2_string_utils_test.cc", "third_party/quiche/src/http2/platform/api/http2_test_helpers.h", "third_party/quiche/src/http2/test_tools/frame_parts.cc", @@ -6430,57 +6428,57 @@ ] dict = "data/fuzzer_dictionaries/net_uri_template_fuzzer.dict" } -# TODO(vasilvv): bring back when the corpus is back. -# fuzzer_test("net_qpack_decoder_fuzzer") { -# sources = [ -# "third_party/quiche/src/quic/core/qpack/fuzzer/qpack_decoder_fuzzer.cc", -# ] -# deps = [ -# ":net_fuzzer_test_support", -# ":quic_test_tools", -# ":test_support", -# "//base", -# "//net", -# ] -# seed_corpus = "third_party/quiche/src/quic/core/qpack/fuzzer/qpack_decoder_fuzzer_corpus/" -# } -# -# fuzzer_test("net_qpack_encoder_stream_receiver_fuzzer") { -# sources = [ -# "third_party/quiche/src/quic/core/qpack/fuzzer/qpack_encoder_stream_receiver_fuzzer.cc", -# ] -# deps = [ -# ":net_fuzzer_test_support", -# "//base", -# "//net", -# ] -# seed_corpus = "third_party/quiche/src/quic/core/qpack/fuzzer/qpack_encoder_stream_receiver_fuzzer_corpus/" -# } -# -# fuzzer_test("net_qpack_encoder_stream_sender_fuzzer") { -# sources = [ -# "third_party/quiche/src/quic/core/qpack/fuzzer/qpack_encoder_stream_sender_fuzzer.cc", -# ] -# deps = [ -# ":net_fuzzer_test_support", -# ":quic_test_tools", -# ":test_support", -# "//base", -# "//net", -# ] -# seed_corpus = "third_party/quiche/src/quic/core/qpack/fuzzer/qpack_encoder_stream_sender_fuzzer_corpus/" -# } -# -# fuzzer_test("net_qpack_round_trip_fuzzer") { -# sources = [ -# "third_party/quiche/src/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc", -# ] -# deps = [ -# ":net_fuzzer_test_support", -# ":quic_test_tools", -# ":test_support", -# "//base", -# "//net", -# ] -# seed_corpus = "third_party/quiche/src/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer_corpus/" -# } + +fuzzer_test("net_qpack_decoder_fuzzer") { + sources = [ + "third_party/quiche/src/quic/core/qpack/fuzzer/qpack_decoder_fuzzer.cc", + ] + deps = [ + ":net_fuzzer_test_support", + ":quic_test_tools", + ":test_support", + "//base", + "//net", + ] + seed_corpus = "third_party/quiche/src/quic/core/qpack/fuzzer/qpack_decoder_fuzzer_corpus/" +} + +fuzzer_test("net_qpack_encoder_stream_receiver_fuzzer") { + sources = [ + "third_party/quiche/src/quic/core/qpack/fuzzer/qpack_encoder_stream_receiver_fuzzer.cc", + ] + deps = [ + ":net_fuzzer_test_support", + "//base", + "//net", + ] + seed_corpus = "third_party/quiche/src/quic/core/qpack/fuzzer/qpack_encoder_stream_receiver_fuzzer_corpus/" +} + +fuzzer_test("net_qpack_encoder_stream_sender_fuzzer") { + sources = [ + "third_party/quiche/src/quic/core/qpack/fuzzer/qpack_encoder_stream_sender_fuzzer.cc", + ] + deps = [ + ":net_fuzzer_test_support", + ":quic_test_tools", + ":test_support", + "//base", + "//net", + ] + seed_corpus = "third_party/quiche/src/quic/core/qpack/fuzzer/qpack_encoder_stream_sender_fuzzer_corpus/" +} + +fuzzer_test("net_qpack_round_trip_fuzzer") { + sources = [ + "third_party/quiche/src/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc", + ] + deps = [ + ":net_fuzzer_test_support", + ":quic_test_tools", + ":test_support", + "//base", + "//net", + ] + seed_corpus = "third_party/quiche/src/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer_corpus/" +}
diff --git a/net/http/http_proxy_client_socket_wrapper.cc b/net/http/http_proxy_client_socket_wrapper.cc deleted file mode 100644 index 28177f1..0000000 --- a/net/http/http_proxy_client_socket_wrapper.cc +++ /dev/null
@@ -1,891 +0,0 @@ -// Copyright 2015 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 "net/http/http_proxy_client_socket_wrapper.h" - -#include <utility> - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback.h" -#include "base/callback_helpers.h" -#include "base/memory/weak_ptr.h" -#include "base/metrics/histogram_macros.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/values.h" -#include "net/base/http_user_agent_settings.h" -#include "net/base/proxy_delegate.h" -#include "net/http/http_proxy_client_socket.h" -#include "net/http/http_response_info.h" -#include "net/log/net_log_event_type.h" -#include "net/log/net_log_source.h" -#include "net/log/net_log_source_type.h" -#include "net/quic/quic_http_utils.h" -#include "net/quic/quic_proxy_client_socket.h" -#include "net/socket/client_socket_factory.h" -#include "net/socket/client_socket_handle.h" -#include "net/socket/socket_tag.h" -#include "net/socket/ssl_connect_job.h" -#include "net/socket/transport_connect_job.h" -#include "net/spdy/spdy_proxy_client_socket.h" -#include "net/spdy/spdy_session.h" -#include "net/spdy/spdy_session_pool.h" -#include "net/spdy/spdy_stream.h" -#include "net/ssl/ssl_cert_request_info.h" -#include "net/traffic_annotation/network_traffic_annotation.h" -#include "url/gurl.h" - -namespace net { - -HttpProxyClientSocketWrapper::HttpProxyClientSocketWrapper( - const OnProxyAuthChallengeCallback& on_proxy_auth_callback, - RequestPriority priority, - const SocketTag& socket_tag, - base::TimeDelta connect_timeout_duration, - base::TimeDelta proxy_negotiation_timeout_duration, - const CommonConnectJobParams* common_connect_job_params, - const scoped_refptr<TransportSocketParams>& transport_params, - const scoped_refptr<SSLSocketParams>& ssl_params, - quic::QuicTransportVersion quic_version, - const HostPortPair& endpoint, - HttpAuthCache* http_auth_cache, - HttpAuthHandlerFactory* http_auth_handler_factory, - SpdySessionPool* spdy_session_pool, - QuicStreamFactory* quic_stream_factory, - bool is_trusted_proxy, - bool tunnel, - const NetworkTrafficAnnotationTag& traffic_annotation, - const NetLogWithSource& net_log) - : next_state_(STATE_NONE), - on_proxy_auth_callback_(on_proxy_auth_callback), - priority_(priority), - socket_tag_(socket_tag), - connect_timeout_duration_(connect_timeout_duration), - proxy_negotiation_timeout_duration_(proxy_negotiation_timeout_duration), - transport_params_(transport_params), - ssl_params_(ssl_params), - quic_version_(quic_version), - endpoint_(endpoint), - spdy_session_pool_(spdy_session_pool), - has_restarted_(false), - tunnel_(tunnel), - common_connect_job_params_(common_connect_job_params), - using_spdy_(false), - is_trusted_proxy_(is_trusted_proxy), - has_established_connection_(false), - quic_stream_factory_(quic_stream_factory), - http_auth_controller_( - tunnel ? new HttpAuthController( - HttpAuth::AUTH_PROXY, - GURL((ssl_params_.get() ? "https://" : "http://") + - GetDestination().ToString()), - http_auth_cache, - http_auth_handler_factory, - common_connect_job_params_->host_resolver) - : nullptr), - net_log_(NetLogWithSource::Make( - net_log.net_log(), - NetLogSourceType::PROXY_CLIENT_SOCKET_WRAPPER)), - traffic_annotation_(traffic_annotation), - weak_ptr_factory_(this) { - net_log_.BeginEvent(NetLogEventType::SOCKET_ALIVE, - net_log.source().ToEventParametersCallback()); - // If doing a QUIC proxy, |quic_version| must not be - // quic::QUIC_VERSION_UNSUPPORTED, and |ssl_params| must be valid while - // |transport_params| is null. Otherwise, |quic_version| must be - // quic::QUIC_VERSION_UNSUPPORTED, and exactly one of |transport_params| or - // |ssl_params| must be set. - DCHECK(quic_version_ == quic::QUIC_VERSION_UNSUPPORTED - ? (bool)transport_params != (bool)ssl_params - : !transport_params && ssl_params); -} - -HttpProxyClientSocketWrapper::~HttpProxyClientSocketWrapper() { - // Make sure no sockets are returned to the lower level socket pools. - Disconnect(); - - net_log_.EndEvent(NetLogEventType::SOCKET_ALIVE); -} - -LoadState HttpProxyClientSocketWrapper::GetConnectLoadState() const { - switch (next_state_) { - case STATE_TCP_CONNECT: - case STATE_TCP_CONNECT_COMPLETE: - case STATE_SSL_CONNECT: - case STATE_SSL_CONNECT_COMPLETE: - return nested_connect_job_->GetLoadState(); - case STATE_HTTP_PROXY_CONNECT: - case STATE_HTTP_PROXY_CONNECT_COMPLETE: - case STATE_SPDY_PROXY_CREATE_STREAM: - case STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE: - case STATE_QUIC_PROXY_CREATE_SESSION: - case STATE_QUIC_PROXY_CREATE_STREAM: - case STATE_QUIC_PROXY_CREATE_STREAM_COMPLETE: - case STATE_RESTART_WITH_AUTH: - case STATE_RESTART_WITH_AUTH_COMPLETE: - return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL; - case STATE_BEGIN_CONNECT: - case STATE_NONE: - // May be possible for this method to be called after an error, shouldn't - // be called after a successful connect. - break; - } - return LOAD_STATE_IDLE; -} - -std::unique_ptr<HttpResponseInfo> -HttpProxyClientSocketWrapper::GetAdditionalErrorState() { - return std::move(error_response_info_); -} - -void HttpProxyClientSocketWrapper::SetPriority(RequestPriority priority) { - priority_ = priority; - - if (nested_connect_job_) - nested_connect_job_->ChangePriority(priority); - - if (spdy_stream_request_) - spdy_stream_request_->SetPriority(priority); - - if (quic_stream_request_) - quic_stream_request_->SetPriority(priority); - - if (transport_socket_) - transport_socket_->SetStreamPriority(priority); -} - -const HttpResponseInfo* HttpProxyClientSocketWrapper::GetConnectResponseInfo() - const { - if (transport_socket_) - return transport_socket_->GetConnectResponseInfo(); - return nullptr; -} - -int HttpProxyClientSocketWrapper::RestartWithAuth( - CompletionOnceCallback callback) { - // TODO(mmenke): Remove this method, once this class is merged with - // HttpProxyConnectJob. - NOTREACHED(); - return ERR_UNEXPECTED; -} - -const scoped_refptr<HttpAuthController>& -HttpProxyClientSocketWrapper::GetAuthController() const { - return http_auth_controller_; -} - -bool HttpProxyClientSocketWrapper::IsUsingSpdy() const { - if (transport_socket_) - return transport_socket_->IsUsingSpdy(); - return false; -} - -NextProto HttpProxyClientSocketWrapper::GetProxyNegotiatedProtocol() const { - if (transport_socket_) - return transport_socket_->GetProxyNegotiatedProtocol(); - return kProtoUnknown; -} - -int HttpProxyClientSocketWrapper::Connect(CompletionOnceCallback callback) { - DCHECK(!callback.is_null()); - DCHECK(connect_callback_.is_null()); - - // If connecting or previously connected and not disconnected, return OK, to - // match TCPClientSocket's behavior. - if (next_state_ != STATE_NONE || transport_socket_) - return OK; - - next_state_ = STATE_BEGIN_CONNECT; - int rv = DoLoop(OK); - if (rv == ERR_IO_PENDING) { - connect_callback_ = std::move(callback); - } else { - connect_timer_.Stop(); - } - - return rv; -} - -void HttpProxyClientSocketWrapper::Disconnect() { - connect_callback_.Reset(); - connect_timer_.Stop(); - next_state_ = STATE_NONE; - spdy_stream_request_.reset(); - quic_stream_request_.reset(); - nested_connect_job_.reset(); - transport_socket_.reset(); -} - -bool HttpProxyClientSocketWrapper::IsConnected() const { - if (transport_socket_) - return transport_socket_->IsConnected(); - // Don't return true if still connecting. Shouldn't really matter, either - // way. - return false; -} - -bool HttpProxyClientSocketWrapper::IsConnectedAndIdle() const { - if (transport_socket_) - return transport_socket_->IsConnectedAndIdle(); - return false; -} - -const NetLogWithSource& HttpProxyClientSocketWrapper::NetLog() const { - return net_log_; -} - -bool HttpProxyClientSocketWrapper::WasEverUsed() const { - // TODO(mmenke): This is a little weird. Figure out if something else should - // be done. - if (transport_socket_) - return transport_socket_->WasEverUsed(); - return false; -} - -bool HttpProxyClientSocketWrapper::WasAlpnNegotiated() const { - if (transport_socket_) - return transport_socket_->WasAlpnNegotiated(); - return false; -} - -NextProto HttpProxyClientSocketWrapper::GetNegotiatedProtocol() const { - if (transport_socket_) - return transport_socket_->GetNegotiatedProtocol(); - return kProtoUnknown; -} - -bool HttpProxyClientSocketWrapper::GetSSLInfo(SSLInfo* ssl_info) { - if (transport_socket_) - return transport_socket_->GetSSLInfo(ssl_info); - return false; -} - -void HttpProxyClientSocketWrapper::GetConnectionAttempts( - ConnectionAttempts* out) const { - // TODO(mmenke): Not clear how reconnecting for auth fits into things. - if (transport_socket_) { - transport_socket_->GetConnectionAttempts(out); - } else { - out->clear(); - } -} - -void HttpProxyClientSocketWrapper::ClearConnectionAttempts() { - if (transport_socket_) - transport_socket_->ClearConnectionAttempts(); -} - -void HttpProxyClientSocketWrapper::AddConnectionAttempts( - const ConnectionAttempts& attempts) { - if (transport_socket_) - transport_socket_->AddConnectionAttempts(attempts); -} - -int64_t HttpProxyClientSocketWrapper::GetTotalReceivedBytes() const { - return transport_socket_->GetTotalReceivedBytes(); -} - -void HttpProxyClientSocketWrapper::ApplySocketTag(const SocketTag& tag) { - // Applying a socket tag to an HttpProxyClientSocketWrapper is done by simply - // applying the socket tag to the underlying socket. - - // In the case of a connection to the proxy using HTTP/2 or HTTP/3 where the - // underlying socket may multiplex multiple streams, applying this request's - // socket tag to the multiplexed session would incorrectly apply the socket - // tag to all mutliplexed streams. In reality this would hit the CHECK(false) - // in QuicProxyClientSocket::ApplySocketTag() or - // SpdyProxyClientSocket::ApplySocketTag(). Fortunately socket tagging is only - // supported on Android without the data reduction proxy, so only simple HTTP - // proxies are supported, so proxies won't be using HTTP/2 or HTTP/3. Detect - // this case (|ssl_params_| must be set for HTTP/2 and HTTP/3 proxies) and - // enforce that a specific (non-default) tag isn't being applied. - if (ssl_params_ || - // Android also doesn't support proxy auth, so RestartWithAuth() should't - // be called so |transport_socket_| shouldn't be cleared. If - // |transport_socket_| is cleared, enforce that a specific (non-default) - // tag isn't being applied. - !transport_socket_) { - CHECK(tag == SocketTag()); - } else { - transport_socket_->ApplySocketTag(tag); - } -} - -int HttpProxyClientSocketWrapper::Read(IOBuffer* buf, - int buf_len, - CompletionOnceCallback callback) { - if (transport_socket_) - return transport_socket_->Read(buf, buf_len, std::move(callback)); - return ERR_SOCKET_NOT_CONNECTED; -} - -int HttpProxyClientSocketWrapper::ReadIfReady(IOBuffer* buf, - int buf_len, - CompletionOnceCallback callback) { - if (transport_socket_) - return transport_socket_->ReadIfReady(buf, buf_len, std::move(callback)); - return ERR_SOCKET_NOT_CONNECTED; -} - -int HttpProxyClientSocketWrapper::CancelReadIfReady() { - if (transport_socket_) - return transport_socket_->CancelReadIfReady(); - return OK; -} - -int HttpProxyClientSocketWrapper::Write( - IOBuffer* buf, - int buf_len, - CompletionOnceCallback callback, - const NetworkTrafficAnnotationTag& traffic_annotation) { - if (transport_socket_) { - return transport_socket_->Write(buf, buf_len, std::move(callback), - traffic_annotation); - } - return ERR_SOCKET_NOT_CONNECTED; -} - -int HttpProxyClientSocketWrapper::SetReceiveBufferSize(int32_t size) { - // TODO(mmenke): Should this persist across reconnects? Seems a little - // weird, and not done for normal reconnects. - if (transport_socket_) - return transport_socket_->SetReceiveBufferSize(size); - return ERR_SOCKET_NOT_CONNECTED; -} - -int HttpProxyClientSocketWrapper::SetSendBufferSize(int32_t size) { - if (transport_socket_) - return transport_socket_->SetSendBufferSize(size); - return ERR_SOCKET_NOT_CONNECTED; -} - -int HttpProxyClientSocketWrapper::GetPeerAddress(IPEndPoint* address) const { - if (transport_socket_) - return transport_socket_->GetPeerAddress(address); - return ERR_SOCKET_NOT_CONNECTED; -} - -int HttpProxyClientSocketWrapper::GetLocalAddress(IPEndPoint* address) const { - if (transport_socket_) - return transport_socket_->GetLocalAddress(address); - return ERR_SOCKET_NOT_CONNECTED; -} - -void HttpProxyClientSocketWrapper::OnConnectJobComplete(int result, - ConnectJob* job) { - DCHECK_EQ(nested_connect_job_.get(), job); - DCHECK(next_state_ == STATE_TCP_CONNECT_COMPLETE || - next_state_ == STATE_SSL_CONNECT_COMPLETE); - OnIOComplete(result); -} - -bool HttpProxyClientSocketWrapper::HasEstablishedConnection() { - if (has_established_connection_) - return true; - - // It's possible the nested connect job has established a connection, but - // hasn't completed yet (For example, an SSLConnectJob may be negotiating - // SSL). - if (nested_connect_job_) { - has_established_connection_ = - nested_connect_job_->HasEstablishedConnection(); - } - return has_established_connection_; -} - -void HttpProxyClientSocketWrapper::OnNeedsProxyAuth( - const HttpResponseInfo& response, - HttpAuthController* auth_controller, - base::OnceClosure restart_with_auth_callback, - ConnectJob* job) { - // This class can't sit on top of another proxy socket class. - NOTREACHED(); -} - -ProxyServer::Scheme HttpProxyClientSocketWrapper::GetProxyServerScheme() const { - if (quic_version_ != quic::QUIC_VERSION_UNSUPPORTED) - return ProxyServer::SCHEME_QUIC; - - if (transport_params_) - return ProxyServer::SCHEME_HTTP; - - return ProxyServer::SCHEME_HTTPS; -} - -void HttpProxyClientSocketWrapper::OnIOComplete(int result) { - int rv = DoLoop(result); - if (rv != ERR_IO_PENDING) { - connect_timer_.Stop(); - // May delete |this|. - std::move(connect_callback_).Run(rv); - } -} - -void HttpProxyClientSocketWrapper::RestartWithAuthCredentials() { - DCHECK(!connect_callback_.is_null()); - DCHECK(transport_socket_); - DCHECK_EQ(STATE_NONE, next_state_); - - // Always do this asynchronously, to avoid re-entrancy. - next_state_ = STATE_RESTART_WITH_AUTH; - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(&HttpProxyClientSocketWrapper::OnIOComplete, - weak_ptr_factory_.GetWeakPtr(), net::OK)); -} - -int HttpProxyClientSocketWrapper::DoLoop(int result) { - DCHECK_NE(next_state_, STATE_NONE); - - int rv = result; - do { - State state = next_state_; - next_state_ = STATE_NONE; - switch (state) { - case STATE_BEGIN_CONNECT: - DCHECK_EQ(OK, rv); - rv = DoBeginConnect(); - break; - case STATE_TCP_CONNECT: - DCHECK_EQ(OK, rv); - rv = DoTransportConnect(); - break; - case STATE_TCP_CONNECT_COMPLETE: - rv = DoTransportConnectComplete(rv); - break; - case STATE_SSL_CONNECT: - DCHECK_EQ(OK, rv); - rv = DoSSLConnect(); - break; - case STATE_SSL_CONNECT_COMPLETE: - rv = DoSSLConnectComplete(rv); - break; - case STATE_HTTP_PROXY_CONNECT: - DCHECK_EQ(OK, rv); - rv = DoHttpProxyConnect(); - break; - case STATE_HTTP_PROXY_CONNECT_COMPLETE: - rv = DoHttpProxyConnectComplete(rv); - break; - case STATE_SPDY_PROXY_CREATE_STREAM: - DCHECK_EQ(OK, rv); - rv = DoSpdyProxyCreateStream(); - break; - case STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE: - rv = DoSpdyProxyCreateStreamComplete(rv); - break; - case STATE_QUIC_PROXY_CREATE_SESSION: - DCHECK_EQ(OK, rv); - rv = DoQuicProxyCreateSession(); - break; - case STATE_QUIC_PROXY_CREATE_STREAM: - rv = DoQuicProxyCreateStream(rv); - break; - case STATE_QUIC_PROXY_CREATE_STREAM_COMPLETE: - rv = DoQuicProxyCreateStreamComplete(rv); - break; - case STATE_RESTART_WITH_AUTH: - DCHECK_EQ(OK, rv); - rv = DoRestartWithAuth(); - break; - case STATE_RESTART_WITH_AUTH_COMPLETE: - rv = DoRestartWithAuthComplete(rv); - break; - default: - NOTREACHED() << "bad state"; - rv = ERR_FAILED; - break; - } - } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); - - return rv; -} - -int HttpProxyClientSocketWrapper::DoBeginConnect() { - connect_start_time_ = base::TimeTicks::Now(); - SetConnectTimer(connect_timeout_duration_); - switch (GetProxyServerScheme()) { - case ProxyServer::SCHEME_QUIC: - next_state_ = STATE_QUIC_PROXY_CREATE_SESSION; - // QUIC connections are always considered to have been established. - // |has_established_connection_| is only used to start retries if a - // connection hasn't been established yet, and QUIC has its own connection - // establishment logic. - has_established_connection_ = true; - break; - case ProxyServer::SCHEME_HTTP: - next_state_ = STATE_TCP_CONNECT; - break; - case ProxyServer::SCHEME_HTTPS: - next_state_ = STATE_SSL_CONNECT; - break; - default: - NOTREACHED(); - } - return OK; -} - -int HttpProxyClientSocketWrapper::DoTransportConnect() { - next_state_ = STATE_TCP_CONNECT_COMPLETE; - nested_connect_job_ = TransportConnectJob::CreateTransportConnectJob( - transport_params_, priority_, socket_tag_, common_connect_job_params_, - this, &net_log_); - return nested_connect_job_->Connect(); -} - -int HttpProxyClientSocketWrapper::DoTransportConnectComplete(int result) { - if (result != OK) { - UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Insecure.Error", - base::TimeTicks::Now() - connect_start_time_); - // This is a special error code meaning to reuse an existing SPDY session - // rather than use a fresh socket. Overriding it with a proxy error message - // would cause the request to fail, instead of switching to using the SPDY - // session. - if (result == ERR_SPDY_SESSION_ALREADY_EXISTS) - return result; - return ERR_PROXY_CONNECTION_FAILED; - } - - has_established_connection_ = true; - - // Reset the timer to just the length of time allowed for HttpProxy handshake - // so that a fast TCP connection plus a slow HttpProxy failure doesn't take - // longer to timeout than it should. - SetConnectTimer(proxy_negotiation_timeout_duration_); - - next_state_ = STATE_HTTP_PROXY_CONNECT; - return result; -} - -int HttpProxyClientSocketWrapper::DoSSLConnect() { - DCHECK(ssl_params_); - if (tunnel_) { - SpdySessionKey key(ssl_params_->GetDirectConnectionParams()->destination(), - ProxyServer::Direct(), PRIVACY_MODE_DISABLED, - SpdySessionKey::IsProxySession::kTrue, socket_tag_); - if (spdy_session_pool_->FindAvailableSession( - key, /* enable_ip_based_pooling = */ true, - /* is_websocket = */ false, net_log_)) { - using_spdy_ = true; - next_state_ = STATE_SPDY_PROXY_CREATE_STREAM; - return OK; - } - } - next_state_ = STATE_SSL_CONNECT_COMPLETE; - nested_connect_job_ = std::make_unique<SSLConnectJob>( - priority_, socket_tag_, common_connect_job_params_, ssl_params_, this, - &net_log_); - return nested_connect_job_->Connect(); -} - -int HttpProxyClientSocketWrapper::DoSSLConnectComplete(int result) { - if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { - // Not really used to hold a socket. - // TODO(mmenke): Implement a better API to get this information. - ClientSocketHandle client_socket_handle; - nested_connect_job_->GetAdditionalErrorState(&client_socket_handle); - - DCHECK(client_socket_handle.ssl_error_response_info().cert_request_info); - UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Secure.Error", - base::TimeTicks::Now() - connect_start_time_); - error_response_info_ = std::make_unique<HttpResponseInfo>( - client_socket_handle.ssl_error_response_info()); - error_response_info_->cert_request_info->is_proxy = true; - return result; - } - - if (IsCertificateError(result)) { - UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Secure.Error", - base::TimeTicks::Now() - connect_start_time_); - // TODO(rch): allow the user to deal with proxy cert errors in the - // same way as server cert errors. - return ERR_PROXY_CERTIFICATE_INVALID; - } - // A SPDY session to the proxy completed prior to resolving the proxy - // hostname. Surface this error, and allow the delegate to retry. - // See crbug.com/334413. - if (result == ERR_SPDY_SESSION_ALREADY_EXISTS) { - DCHECK(!nested_connect_job_->socket()); - return ERR_SPDY_SESSION_ALREADY_EXISTS; - } - if (result < 0) { - UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Secure.Error", - base::TimeTicks::Now() - connect_start_time_); - return ERR_PROXY_CONNECTION_FAILED; - } - - has_established_connection_ = true; - - negotiated_protocol_ = nested_connect_job_->socket()->GetNegotiatedProtocol(); - using_spdy_ = negotiated_protocol_ == kProtoHTTP2; - - // Reset the timer to just the length of time allowed for HttpProxy handshake - // so that a fast SSL connection plus a slow HttpProxy failure doesn't take - // longer to timeout than it should. - SetConnectTimer(proxy_negotiation_timeout_duration_); - - // TODO(rch): If we ever decide to implement a "trusted" SPDY proxy - // (one that we speak SPDY over SSL to, but to which we send HTTPS - // request directly instead of through CONNECT tunnels, then we - // need to add a predicate to this if statement so we fall through - // to the else case. (HttpProxyClientSocket currently acts as - // a "trusted" SPDY proxy). - if (using_spdy_ && tunnel_) { - next_state_ = STATE_SPDY_PROXY_CREATE_STREAM; - } else { - next_state_ = STATE_HTTP_PROXY_CONNECT; - } - return result; -} - -int HttpProxyClientSocketWrapper::DoHttpProxyConnect() { - next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE; - - if (transport_params_) { - UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Insecure.Success", - base::TimeTicks::Now() - connect_start_time_); - } else { - UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Secure.Success", - base::TimeTicks::Now() - connect_start_time_); - } - - // Add a HttpProxy connection on top of the tcp socket. - transport_socket_ = - common_connect_job_params_->client_socket_factory - ->CreateProxyClientSocket( - nested_connect_job_->PassSocket(), GetUserAgent(), endpoint_, - ProxyServer(GetProxyServerScheme(), GetDestination()), - http_auth_controller_.get(), tunnel_, using_spdy_, - negotiated_protocol_, common_connect_job_params_->proxy_delegate, - ssl_params_.get() != nullptr, traffic_annotation_); - nested_connect_job_.reset(); - return transport_socket_->Connect(base::Bind( - &HttpProxyClientSocketWrapper::OnIOComplete, base::Unretained(this))); -} - -int HttpProxyClientSocketWrapper::DoHttpProxyConnectComplete(int result) { - // Always inform caller of auth requests asynchronously. - if (result == ERR_PROXY_AUTH_REQUESTED) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&HttpProxyClientSocketWrapper::OnAuthChallenge, - weak_ptr_factory_.GetWeakPtr())); - return ERR_IO_PENDING; - } - - if (result == ERR_HTTP_1_1_REQUIRED) - return ERR_PROXY_HTTP_1_1_REQUIRED; - - return result; -} - -int HttpProxyClientSocketWrapper::DoSpdyProxyCreateStream() { - DCHECK(using_spdy_); - DCHECK(tunnel_); - DCHECK(ssl_params_); - SpdySessionKey key(ssl_params_->GetDirectConnectionParams()->destination(), - ProxyServer::Direct(), PRIVACY_MODE_DISABLED, - SpdySessionKey::IsProxySession::kTrue, socket_tag_); - base::WeakPtr<SpdySession> spdy_session = - spdy_session_pool_->FindAvailableSession( - key, /* enable_ip_based_pooling = */ true, - /* is_websocket = */ false, net_log_); - // It's possible that a session to the proxy has recently been created - if (spdy_session) { - nested_connect_job_.reset(); - } else { - // Create a session direct to the proxy itself - spdy_session = spdy_session_pool_->CreateAvailableSessionFromSocket( - key, is_trusted_proxy_, nested_connect_job_->PassSocket(), - nested_connect_job_->connect_timing(), net_log_); - DCHECK(spdy_session); - nested_connect_job_.reset(); - } - - next_state_ = STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE; - spdy_stream_request_ = std::make_unique<SpdyStreamRequest>(); - return spdy_stream_request_->StartRequest( - SPDY_BIDIRECTIONAL_STREAM, spdy_session, - GURL("https://" + endpoint_.ToString()), priority_, socket_tag_, - spdy_session->net_log(), - base::Bind(&HttpProxyClientSocketWrapper::OnIOComplete, - base::Unretained(this)), - traffic_annotation_); -} - -int HttpProxyClientSocketWrapper::DoSpdyProxyCreateStreamComplete(int result) { - if (result < 0) { - spdy_stream_request_.reset(); - return result; - } - - next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE; - base::WeakPtr<SpdyStream> stream = spdy_stream_request_->ReleaseStream(); - spdy_stream_request_.reset(); - DCHECK(stream.get()); - // |transport_socket_| will set itself as |stream|'s delegate. - transport_socket_.reset( - new SpdyProxyClientSocket(stream, GetUserAgent(), endpoint_, net_log_, - http_auth_controller_.get())); - return transport_socket_->Connect(base::Bind( - &HttpProxyClientSocketWrapper::OnIOComplete, base::Unretained(this))); -} - -int HttpProxyClientSocketWrapper::DoQuicProxyCreateSession() { - DCHECK(ssl_params_); - DCHECK(tunnel_); - next_state_ = STATE_QUIC_PROXY_CREATE_STREAM; - const HostPortPair& proxy_server = - ssl_params_->GetDirectConnectionParams()->destination(); - quic_stream_request_ = - std::make_unique<QuicStreamRequest>(quic_stream_factory_); - return quic_stream_request_->Request( - proxy_server, quic_version_, ssl_params_->privacy_mode(), priority_, - socket_tag_, ssl_params_->ssl_config().GetCertVerifyFlags(), - GURL("https://" + proxy_server.ToString()), net_log_, - &quic_net_error_details_, - /*failed_on_default_network_callback=*/CompletionOnceCallback(), - base::Bind(&HttpProxyClientSocketWrapper::OnIOComplete, - base::Unretained(this))); -} - -int HttpProxyClientSocketWrapper::DoQuicProxyCreateStream(int result) { - if (result < 0) { - quic_stream_request_.reset(); - return result; - } - - next_state_ = STATE_QUIC_PROXY_CREATE_STREAM_COMPLETE; - quic_session_ = quic_stream_request_->ReleaseSessionHandle(); - quic_stream_request_.reset(); - - return quic_session_->RequestStream( - false, - base::Bind(&HttpProxyClientSocketWrapper::OnIOComplete, - base::Unretained(this)), - traffic_annotation_); -} - -int HttpProxyClientSocketWrapper::DoQuicProxyCreateStreamComplete(int result) { - if (result < 0) - return result; - - next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE; - std::unique_ptr<QuicChromiumClientStream::Handle> quic_stream = - quic_session_->ReleaseStream(); - - spdy::SpdyPriority spdy_priority = - ConvertRequestPriorityToQuicPriority(priority_); - quic_stream->SetPriority(spdy_priority); - - transport_socket_.reset(new QuicProxyClientSocket( - std::move(quic_stream), std::move(quic_session_), GetUserAgent(), - endpoint_, net_log_, http_auth_controller_.get())); - return transport_socket_->Connect(base::Bind( - &HttpProxyClientSocketWrapper::OnIOComplete, base::Unretained(this))); -} - -int HttpProxyClientSocketWrapper::DoRestartWithAuth() { - DCHECK(transport_socket_); - - next_state_ = STATE_RESTART_WITH_AUTH_COMPLETE; - return transport_socket_->RestartWithAuth(base::BindOnce( - &HttpProxyClientSocketWrapper::OnIOComplete, base::Unretained(this))); -} - -int HttpProxyClientSocketWrapper::DoRestartWithAuthComplete(int result) { - DCHECK_NE(ERR_IO_PENDING, result); - - if (result == OK && !transport_socket_->IsConnected()) - result = ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH; - - // If the connection could not be reused to attempt to send proxy auth - // credentials, try reconnecting. Do not reset the HttpAuthController in this - // case; the server may, for instance, send "Proxy-Connection: close" and - // expect that each leg of the authentication progress on separate - // connections. - bool reconnect = result == ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH; - - // If auth credentials were sent but the connection was closed, the server may - // have timed out while the user was selecting credentials. Retry once. - if (!has_restarted_ && - (result == ERR_CONNECTION_CLOSED || result == ERR_CONNECTION_RESET || - result == ERR_CONNECTION_ABORTED || - result == ERR_SOCKET_NOT_CONNECTED)) { - reconnect = true; - has_restarted_ = true; - - // Release any auth state bound to the connection. The new connection will - // start the current scheme and identity from scratch. - if (http_auth_controller_) - http_auth_controller_->OnConnectionClosed(); - } - - if (reconnect) { - // Attempt to create a new one. - transport_socket_.reset(); - using_spdy_ = false; - negotiated_protocol_ = NextProto(); - next_state_ = STATE_BEGIN_CONNECT; - return OK; - } - - // If not reconnecting, treat the result as the result of establishing a - // tunnel through the proxy. This important in the case another auth challenge - // is seen. - next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE; - return result; -} - -void HttpProxyClientSocketWrapper::SetConnectTimer(base::TimeDelta delay) { - connect_timer_.Stop(); - connect_timer_.Start(FROM_HERE, delay, this, - &HttpProxyClientSocketWrapper::ConnectTimeout); -} - -void HttpProxyClientSocketWrapper::ConnectTimeout() { - // Timer shouldn't be running if next_state_ is STATE_NONE. - DCHECK_NE(STATE_NONE, next_state_); - DCHECK(!connect_callback_.is_null()); - - if (next_state_ == STATE_TCP_CONNECT_COMPLETE || - next_state_ == STATE_SSL_CONNECT_COMPLETE) { - if (transport_params_) { - UMA_HISTOGRAM_MEDIUM_TIMES( - "Net.HttpProxy.ConnectLatency.Insecure.TimedOut", - base::TimeTicks::Now() - connect_start_time_); - } else { - UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Secure.TimedOut", - base::TimeTicks::Now() - connect_start_time_); - } - } - - CompletionOnceCallback callback = std::move(connect_callback_); - Disconnect(); - std::move(callback).Run(ERR_CONNECTION_TIMED_OUT); -} - -void HttpProxyClientSocketWrapper::OnAuthChallenge() { - connect_timer_.Stop(); - on_proxy_auth_callback_.Run( - *transport_socket_->GetConnectResponseInfo(), - transport_socket_->GetAuthController().get(), - base::BindOnce(&HttpProxyClientSocketWrapper::RestartWithAuthCredentials, - weak_ptr_factory_.GetWeakPtr())); -} - -const HostPortPair& HttpProxyClientSocketWrapper::GetDestination() { - if (transport_params_) { - return transport_params_->destination(); - } else { - return ssl_params_->GetDirectConnectionParams()->destination(); - } -} - -std::string HttpProxyClientSocketWrapper::GetUserAgent() const { - if (!common_connect_job_params_->http_user_agent_settings) - return std::string(); - return common_connect_job_params_->http_user_agent_settings->GetUserAgent(); -} - -} // namespace net
diff --git a/net/http/http_proxy_client_socket_wrapper.h b/net/http/http_proxy_client_socket_wrapper.h deleted file mode 100644 index dbc56af..0000000 --- a/net/http/http_proxy_client_socket_wrapper.h +++ /dev/null
@@ -1,273 +0,0 @@ -// Copyright 2015 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 NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_WRAPPER_H_ -#define NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_WRAPPER_H_ - -#include <stdint.h> - -#include <memory> -#include <string> - -#include "base/callback_forward.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "base/time/time.h" -#include "base/timer/timer.h" -#include "net/base/completion_callback.h" -#include "net/base/completion_once_callback.h" -#include "net/base/host_port_pair.h" -#include "net/base/load_timing_info.h" -#include "net/base/proxy_server.h" -#include "net/http/http_auth_controller.h" -#include "net/http/proxy_client_socket.h" -#include "net/log/net_log_with_source.h" -#include "net/quic/quic_stream_factory.h" -#include "net/socket/connect_job.h" -#include "net/socket/next_proto.h" -#include "net/socket/socket_tag.h" -#include "net/socket/ssl_client_socket.h" -#include "net/spdy/spdy_session.h" -#include "net/traffic_annotation/network_traffic_annotation.h" - -namespace net { - -class IOBuffer; -class HttpAuthCache; -class HttpResponseInfo; -class IOBuffer; -class SpdySessionPool; -class SSLSocketParams; -class TransportSocketParams; - -// Class that establishes connections by calling into the lower layer socket -// pools, creates a HttpProxyClientSocket, SpdyProxyClientSocket, or -// QuicProxyClientSocket, and then wraps the resulting socket. -// -// This class is needed to handle auth state across multiple connection. On -// auth challenge, this class retains auth state in its AuthController, and can -// either send the auth response to the old connection, or establish a new -// connection and send the response there. -// -// TODO(mmenke): Ideally, we'd have a central location store auth state across -// multiple connections to the same server instead. -class NET_EXPORT_PRIVATE HttpProxyClientSocketWrapper - : public ProxyClientSocket, - public ConnectJob::Delegate { - public: - using OnProxyAuthChallengeCallback = base::RepeatingCallback<void( - const HttpResponseInfo& response, - HttpAuthController* auth_controller, - base::OnceClosure restart_with_auth_callback)>; - - HttpProxyClientSocketWrapper( - const OnProxyAuthChallengeCallback& on_proxy_auth_callback, - RequestPriority priority, - const SocketTag& socket_tag, - base::TimeDelta connect_timeout_duration, - base::TimeDelta proxy_negotiation_timeout_duration, - const CommonConnectJobParams* common_connect_job_params, - const scoped_refptr<TransportSocketParams>& transport_params, - const scoped_refptr<SSLSocketParams>& ssl_params, - quic::QuicTransportVersion quic_version, - const HostPortPair& endpoint, - HttpAuthCache* http_auth_cache, - HttpAuthHandlerFactory* http_auth_handler_factory, - SpdySessionPool* spdy_session_pool, - QuicStreamFactory* quic_stream_factory, - bool is_trusted_proxy, - bool tunnel, - const NetworkTrafficAnnotationTag& traffic_annotation, - const NetLogWithSource& net_log); - - // On destruction Disconnect() is called. - ~HttpProxyClientSocketWrapper() override; - - // Returns load state while establishing a connection. Returns - // LOAD_STATE_IDLE at other times. - LoadState GetConnectLoadState() const; - - std::unique_ptr<HttpResponseInfo> GetAdditionalErrorState(); - - void SetPriority(RequestPriority priority); - - // ProxyClientSocket implementation. - const HttpResponseInfo* GetConnectResponseInfo() const override; - int RestartWithAuth(CompletionOnceCallback callback) override; - const scoped_refptr<HttpAuthController>& GetAuthController() const override; - bool IsUsingSpdy() const override; - NextProto GetProxyNegotiatedProtocol() const override; - - // StreamSocket implementation. - int Connect(CompletionOnceCallback callback) override; - void Disconnect() override; - bool IsConnected() const override; - bool IsConnectedAndIdle() const override; - const NetLogWithSource& NetLog() const override; - bool WasEverUsed() const override; - bool WasAlpnNegotiated() const override; - NextProto GetNegotiatedProtocol() const override; - bool GetSSLInfo(SSLInfo* ssl_info) override; - void GetConnectionAttempts(ConnectionAttempts* out) const override; - void ClearConnectionAttempts() override; - void AddConnectionAttempts(const ConnectionAttempts& attempts) override; - int64_t GetTotalReceivedBytes() const override; - void ApplySocketTag(const SocketTag& tag) override; - - // Socket implementation. - int Read(IOBuffer* buf, - int buf_len, - CompletionOnceCallback callback) override; - int ReadIfReady(IOBuffer* buf, - int buf_len, - CompletionOnceCallback callback) override; - int CancelReadIfReady() override; - int Write(IOBuffer* buf, - int buf_len, - CompletionOnceCallback callback, - const NetworkTrafficAnnotationTag& traffic_annotation) override; - int SetReceiveBufferSize(int32_t size) override; - int SetSendBufferSize(int32_t size) override; - int GetPeerAddress(IPEndPoint* address) const override; - int GetLocalAddress(IPEndPoint* address) const override; - - // ConnectJob::Delegate implementation. - void OnConnectJobComplete(int result, ConnectJob* job) override; - - bool HasEstablishedConnection(); - void OnNeedsProxyAuth(const HttpResponseInfo& response, - HttpAuthController* auth_controller, - base::OnceClosure restart_with_auth_callback, - ConnectJob* job) override; - - NetErrorDetails* quic_net_error_details() { return &quic_net_error_details_; } - - private: - enum State { - STATE_BEGIN_CONNECT, - STATE_TCP_CONNECT, - STATE_TCP_CONNECT_COMPLETE, - STATE_SSL_CONNECT, - STATE_SSL_CONNECT_COMPLETE, - STATE_HTTP_PROXY_CONNECT, - STATE_HTTP_PROXY_CONNECT_COMPLETE, - STATE_SPDY_PROXY_CREATE_STREAM, - STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE, - STATE_QUIC_PROXY_CREATE_SESSION, - STATE_QUIC_PROXY_CREATE_STREAM, - STATE_QUIC_PROXY_CREATE_STREAM_COMPLETE, - STATE_RESTART_WITH_AUTH, - STATE_RESTART_WITH_AUTH_COMPLETE, - STATE_NONE, - }; - - ProxyServer::Scheme GetProxyServerScheme() const; - - void OnIOComplete(int result); - - void RestartWithAuthCredentials(); - - // Runs the state transition loop. - int DoLoop(int result); - - // Determine if need to go through TCP or SSL path. - int DoBeginConnect(); - // Connecting to HTTP Proxy - int DoTransportConnect(); - int DoTransportConnectComplete(int result); - // Connecting to HTTPS Proxy - int DoSSLConnect(); - int DoSSLConnectComplete(int result); - - int DoHttpProxyConnect(); - int DoHttpProxyConnectComplete(int result); - - int DoSpdyProxyCreateStream(); - int DoSpdyProxyCreateStreamComplete(int result); - - int DoQuicProxyCreateSession(); - int DoQuicProxyCreateStream(int result); - int DoQuicProxyCreateStreamComplete(int result); - - int DoRestartWithAuth(); - int DoRestartWithAuthComplete(int result); - - void SetConnectTimer(base::TimeDelta duration); - void ConnectTimeout(); - - void OnAuthChallenge(); - - const HostPortPair& GetDestination(); - - std::string GetUserAgent() const; - - State next_state_; - - const OnProxyAuthChallengeCallback on_proxy_auth_callback_; - - RequestPriority priority_; - const SocketTag socket_tag_; - const base::TimeDelta connect_timeout_duration_; - const base::TimeDelta proxy_negotiation_timeout_duration_; - - const scoped_refptr<TransportSocketParams> transport_params_; - const scoped_refptr<SSLSocketParams> ssl_params_; - - quic::QuicTransportVersion quic_version_; - - const HostPortPair endpoint_; - SpdySessionPool* const spdy_session_pool_; - - bool has_restarted_; - const bool tunnel_; - - const CommonConnectJobParams* common_connect_job_params_; - - bool using_spdy_; - bool is_trusted_proxy_; - NextProto negotiated_protocol_; - - // Set to true once a connection has been successfully established. Remains - // true even if a new socket is being connected to retry with auth. - bool has_established_connection_; - - std::unique_ptr<HttpResponseInfo> error_response_info_; - - std::unique_ptr<ConnectJob> nested_connect_job_; - std::unique_ptr<ProxyClientSocket> transport_socket_; - - // Called when a connection is established. Also used when restarting with - // AUTH, which will invoke this when ready to restart, after reconnecting - // if necessary. - CompletionOnceCallback connect_callback_; - - std::unique_ptr<SpdyStreamRequest> spdy_stream_request_; - - QuicStreamFactory* const quic_stream_factory_; - std::unique_ptr<QuicStreamRequest> quic_stream_request_; - std::unique_ptr<QuicChromiumClientSession::Handle> quic_session_; - - scoped_refptr<HttpAuthController> http_auth_controller_; - - NetLogWithSource net_log_; - - NetErrorDetails quic_net_error_details_; - - base::OneShotTimer connect_timer_; - - // Network traffic annotation for handshaking and setup. - const NetworkTrafficAnnotationTag traffic_annotation_; - - // Time when the connection to the proxy was started. - base::TimeTicks connect_start_time_; - - base::WeakPtrFactory<HttpProxyClientSocketWrapper> weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(HttpProxyClientSocketWrapper); -}; - -} // namespace net - -#endif // NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_WRAPPER_H_
diff --git a/net/http/http_proxy_connect_job.cc b/net/http/http_proxy_connect_job.cc index 4ca5587..2379d4a 100644 --- a/net/http/http_proxy_connect_job.cc +++ b/net/http/http_proxy_connect_job.cc
@@ -10,18 +10,23 @@ #include "base/callback.h" #include "base/metrics/field_trial.h" #include "base/metrics/field_trial_params.h" +#include "base/metrics/histogram_macros.h" #include "base/no_destructor.h" #include "base/numerics/ranges.h" #include "base/optional.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" +#include "base/threading/thread_task_runner_handle.h" #include "base/values.h" #include "build/build_config.h" +#include "net/base/http_user_agent_settings.h" #include "net/base/net_errors.h" -#include "net/http/http_proxy_client_socket_wrapper.h" #include "net/log/net_log_source_type.h" #include "net/log/net_log_with_source.h" #include "net/nqe/network_quality_estimator.h" +#include "net/quic/quic_http_utils.h" +#include "net/quic/quic_proxy_client_socket.h" +#include "net/quic/quic_stream_factory.h" #include "net/socket/client_socket_factory.h" #include "net/socket/client_socket_handle.h" #include "net/socket/ssl_client_socket.h" @@ -167,7 +172,7 @@ const SocketTag& socket_tag, const CommonConnectJobParams* common_connect_job_params, const scoped_refptr<HttpProxySocketParams>& params, - Delegate* delegate, + ConnectJob::Delegate* delegate, const NetLogWithSource* net_log) : ConnectJob(priority, socket_tag, @@ -177,46 +182,64 @@ net_log, NetLogSourceType::HTTP_PROXY_CONNECT_JOB, NetLogEventType::HTTP_PROXY_CONNECT_JOB_CONNECT), - client_socket_(std::make_unique<HttpProxyClientSocketWrapper>( - base::BindRepeating(&HttpProxyConnectJob::OnNeedsProxyAuth, - base::Unretained(this)), - priority, - socket_tag, - ConnectionTimeout( - *params, - common_connect_job_params->network_quality_estimator), - kHttpProxyConnectJobTunnelTimeout, - common_connect_job_params, - params->transport_params(), - params->ssl_params(), - params->quic_version(), - params->endpoint(), - params->http_auth_cache(), - params->http_auth_handler_factory(), - params->spdy_session_pool(), - params->quic_stream_factory(), - params->is_trusted_proxy(), - params->tunnel(), - params->traffic_annotation(), - this->net_log())), - params_(std::move(params)) {} + params_(params), + next_state_(STATE_NONE), + has_restarted_(false), + using_spdy_(false), + negotiated_protocol_(kProtoUnknown), + has_established_connection_(false), + http_auth_controller_( + params_->tunnel() + ? new HttpAuthController( + HttpAuth::AUTH_PROXY, + GURL((params_->ssl_params() ? "https://" : "http://") + + GetDestination().ToString()), + params_->http_auth_cache(), + params_->http_auth_handler_factory(), + host_resolver()) + : nullptr), + weak_ptr_factory_(this) {} -HttpProxyConnectJob::~HttpProxyConnectJob() = default; +HttpProxyConnectJob::~HttpProxyConnectJob() {} LoadState HttpProxyConnectJob::GetLoadState() const { - return client_socket_->GetConnectLoadState(); + switch (next_state_) { + case STATE_TCP_CONNECT_COMPLETE: + case STATE_SSL_CONNECT_COMPLETE: + return nested_connect_job_->GetLoadState(); + case STATE_HTTP_PROXY_CONNECT: + case STATE_HTTP_PROXY_CONNECT_COMPLETE: + case STATE_SPDY_PROXY_CREATE_STREAM: + case STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE: + case STATE_QUIC_PROXY_CREATE_SESSION: + case STATE_QUIC_PROXY_CREATE_STREAM: + case STATE_QUIC_PROXY_CREATE_STREAM_COMPLETE: + case STATE_RESTART_WITH_AUTH: + case STATE_RESTART_WITH_AUTH_COMPLETE: + return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL; + // These states shouldn't be possible to be called in. + case STATE_TCP_CONNECT: + case STATE_SSL_CONNECT: + + case STATE_BEGIN_CONNECT: + case STATE_NONE: + // May be possible for this method to be called after an error, shouldn't + // be called after a successful connect. + break; + } + return LOAD_STATE_IDLE; } bool HttpProxyConnectJob::HasEstablishedConnection() const { - return client_socket_->HasEstablishedConnection(); -} + if (has_established_connection_) + return true; -void HttpProxyConnectJob::OnNeedsProxyAuth( - const HttpResponseInfo& response, - HttpAuthController* auth_controller, - base::OnceClosure restart_with_auth_callback) { - NotifyDelegateOfProxyAuth(response, auth_controller, - std::move(restart_with_auth_callback)); + // It's possible the nested connect job has established a connection, but + // hasn't completed yet (For example, an SSLConnectJob may be negotiating + // SSL). + if (nested_connect_job_) + return nested_connect_job_->HasEstablishedConnection(); + return false; } void HttpProxyConnectJob::GetAdditionalErrorState(ClientSocketHandle* handle) { @@ -226,6 +249,25 @@ } } +void HttpProxyConnectJob::OnConnectJobComplete(int result, ConnectJob* job) { + DCHECK_EQ(nested_connect_job_.get(), job); + DCHECK(next_state_ == STATE_TCP_CONNECT_COMPLETE || + next_state_ == STATE_SSL_CONNECT_COMPLETE); + OnIOComplete(result); +} + +void HttpProxyConnectJob::OnNeedsProxyAuth( + const HttpResponseInfo& response, + HttpAuthController* auth_controller, + base::OnceClosure restart_with_auth_callback, + ConnectJob* job) { + // None of the nested ConnectJob used by this class can encounter auth + // challenges. Instead, the challenges are returned by the ProxyClientSocket + // implementations after nested_connect_job_ has already established a + // connection. + NOTREACHED(); +} + base::TimeDelta HttpProxyConnectJob::ConnectionTimeout( const HttpProxySocketParams& params, const NetworkQualityEstimator* network_quality_estimator) { @@ -275,36 +317,503 @@ } int HttpProxyConnectJob::ConnectInternal() { - int result = client_socket_->Connect(base::BindOnce( - &HttpProxyConnectJob::OnConnectComplete, base::Unretained(this))); - return HandleConnectResult(result); + DCHECK_EQ(next_state_, STATE_NONE); + next_state_ = STATE_BEGIN_CONNECT; + int result = DoLoop(OK); + if (result != ERR_IO_PENDING) + HandleConnectResult(result); + return result; +} + +ProxyServer::Scheme HttpProxyConnectJob::GetProxyServerScheme() const { + if (params_->quic_version() != quic::QUIC_VERSION_UNSUPPORTED) + return ProxyServer::SCHEME_QUIC; + + if (params_->transport_params()) + return ProxyServer::SCHEME_HTTP; + + return ProxyServer::SCHEME_HTTPS; +} + +void HttpProxyConnectJob::OnIOComplete(int result) { + int rv = DoLoop(result); + if (rv != ERR_IO_PENDING) { + HandleConnectResult(rv); + + // May delete |this|. + NotifyDelegateOfCompletion(rv); + } +} + +void HttpProxyConnectJob::RestartWithAuthCredentials() { + DCHECK(transport_socket_); + DCHECK_EQ(STATE_NONE, next_state_); + + // Always do this asynchronously, to avoid re-entrancy. + next_state_ = STATE_RESTART_WITH_AUTH; + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&HttpProxyConnectJob::OnIOComplete, + weak_ptr_factory_.GetWeakPtr(), net::OK)); +} + +int HttpProxyConnectJob::DoLoop(int result) { + DCHECK_NE(next_state_, STATE_NONE); + + int rv = result; + do { + State state = next_state_; + next_state_ = STATE_NONE; + switch (state) { + case STATE_BEGIN_CONNECT: + DCHECK_EQ(OK, rv); + rv = DoBeginConnect(); + break; + case STATE_TCP_CONNECT: + DCHECK_EQ(OK, rv); + rv = DoTransportConnect(); + break; + case STATE_TCP_CONNECT_COMPLETE: + rv = DoTransportConnectComplete(rv); + break; + case STATE_SSL_CONNECT: + DCHECK_EQ(OK, rv); + rv = DoSSLConnect(); + break; + case STATE_SSL_CONNECT_COMPLETE: + rv = DoSSLConnectComplete(rv); + break; + case STATE_HTTP_PROXY_CONNECT: + DCHECK_EQ(OK, rv); + rv = DoHttpProxyConnect(); + break; + case STATE_HTTP_PROXY_CONNECT_COMPLETE: + rv = DoHttpProxyConnectComplete(rv); + break; + case STATE_SPDY_PROXY_CREATE_STREAM: + DCHECK_EQ(OK, rv); + rv = DoSpdyProxyCreateStream(); + break; + case STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE: + rv = DoSpdyProxyCreateStreamComplete(rv); + break; + case STATE_QUIC_PROXY_CREATE_SESSION: + DCHECK_EQ(OK, rv); + rv = DoQuicProxyCreateSession(); + break; + case STATE_QUIC_PROXY_CREATE_STREAM: + rv = DoQuicProxyCreateStream(rv); + break; + case STATE_QUIC_PROXY_CREATE_STREAM_COMPLETE: + rv = DoQuicProxyCreateStreamComplete(rv); + break; + case STATE_RESTART_WITH_AUTH: + DCHECK_EQ(OK, rv); + rv = DoRestartWithAuth(); + break; + case STATE_RESTART_WITH_AUTH_COMPLETE: + rv = DoRestartWithAuthComplete(rv); + break; + default: + NOTREACHED() << "bad state"; + rv = ERR_FAILED; + break; + } + } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); + + return rv; +} + +int HttpProxyConnectJob::DoBeginConnect() { + connect_start_time_ = base::TimeTicks::Now(); + ResetTimer(ConnectionTimeout(*params_, network_quality_estimator())); + switch (GetProxyServerScheme()) { + case ProxyServer::SCHEME_QUIC: + next_state_ = STATE_QUIC_PROXY_CREATE_SESSION; + // QUIC connections are always considered to have been established. + // |has_established_connection_| is only used to start retries if a + // connection hasn't been established yet, and QUIC has its own connection + // establishment logic. + has_established_connection_ = true; + break; + case ProxyServer::SCHEME_HTTP: + next_state_ = STATE_TCP_CONNECT; + break; + case ProxyServer::SCHEME_HTTPS: + next_state_ = STATE_SSL_CONNECT; + break; + default: + NOTREACHED(); + } + return OK; +} + +int HttpProxyConnectJob::DoTransportConnect() { + next_state_ = STATE_TCP_CONNECT_COMPLETE; + nested_connect_job_ = TransportConnectJob::CreateTransportConnectJob( + params_->transport_params(), priority(), socket_tag(), + common_connect_job_params(), this, &net_log()); + return nested_connect_job_->Connect(); +} + +int HttpProxyConnectJob::DoTransportConnectComplete(int result) { + if (result != OK) { + UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Insecure.Error", + base::TimeTicks::Now() - connect_start_time_); + // This is a special error code meaning to reuse an existing SPDY session + // rather than use a fresh socket. Overriding it with a proxy error message + // would cause the request to fail, instead of switching to using the SPDY + // session. + if (result == ERR_SPDY_SESSION_ALREADY_EXISTS) + return result; + return ERR_PROXY_CONNECTION_FAILED; + } + + has_established_connection_ = true; + + // Reset the timer to just the length of time allowed for HttpProxy handshake + // so that a fast TCP connection plus a slow HttpProxy failure doesn't take + // longer to timeout than it should. + ResetTimer(kHttpProxyConnectJobTunnelTimeout); + + next_state_ = STATE_HTTP_PROXY_CONNECT; + return result; +} + +int HttpProxyConnectJob::DoSSLConnect() { + DCHECK(params_->ssl_params()); + if (params_->tunnel()) { + SpdySessionKey key( + params_->ssl_params()->GetDirectConnectionParams()->destination(), + ProxyServer::Direct(), PRIVACY_MODE_DISABLED, + SpdySessionKey::IsProxySession::kTrue, socket_tag()); + if (params_->spdy_session_pool()->FindAvailableSession( + key, /* enable_ip_based_pooling = */ true, + /* is_websocket = */ false, net_log())) { + using_spdy_ = true; + next_state_ = STATE_SPDY_PROXY_CREATE_STREAM; + return OK; + } + } + next_state_ = STATE_SSL_CONNECT_COMPLETE; + nested_connect_job_ = std::make_unique<SSLConnectJob>( + priority(), socket_tag(), common_connect_job_params(), + params_->ssl_params(), this, &net_log()); + return nested_connect_job_->Connect(); +} + +int HttpProxyConnectJob::DoSSLConnectComplete(int result) { + if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { + // Not really used to hold a socket. + // TODO(mmenke): Implement a better API to get this information. + ClientSocketHandle client_socket_handle; + nested_connect_job_->GetAdditionalErrorState(&client_socket_handle); + + DCHECK(client_socket_handle.ssl_error_response_info().cert_request_info); + UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Secure.Error", + base::TimeTicks::Now() - connect_start_time_); + error_response_info_ = std::make_unique<HttpResponseInfo>( + client_socket_handle.ssl_error_response_info()); + error_response_info_->cert_request_info->is_proxy = true; + return result; + } + + if (IsCertificateError(result)) { + UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Secure.Error", + base::TimeTicks::Now() - connect_start_time_); + // TODO(rch): allow the user to deal with proxy cert errors in the + // same way as server cert errors. + return ERR_PROXY_CERTIFICATE_INVALID; + } + // A SPDY session to the proxy completed prior to resolving the proxy + // hostname. Surface this error, and allow the delegate to retry. + // See crbug.com/334413. + if (result == ERR_SPDY_SESSION_ALREADY_EXISTS) { + DCHECK(!nested_connect_job_->socket()); + return ERR_SPDY_SESSION_ALREADY_EXISTS; + } + if (result < 0) { + UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Secure.Error", + base::TimeTicks::Now() - connect_start_time_); + return ERR_PROXY_CONNECTION_FAILED; + } + + has_established_connection_ = true; + + negotiated_protocol_ = nested_connect_job_->socket()->GetNegotiatedProtocol(); + using_spdy_ = negotiated_protocol_ == kProtoHTTP2; + + // Reset the timer to just the length of time allowed for HttpProxy handshake + // so that a fast SSL connection plus a slow HttpProxy failure doesn't take + // longer to timeout than it should. + ResetTimer(kHttpProxyConnectJobTunnelTimeout); + + // TODO(rch): If we ever decide to implement a "trusted" SPDY proxy + // (one that we speak SPDY over SSL to, but to which we send HTTPS + // request directly instead of through CONNECT tunnels, then we + // need to add a predicate to this if statement so we fall through + // to the else case. (HttpProxyClientSocket currently acts as + // a "trusted" SPDY proxy). + if (using_spdy_ && params_->tunnel()) { + next_state_ = STATE_SPDY_PROXY_CREATE_STREAM; + } else { + next_state_ = STATE_HTTP_PROXY_CONNECT; + } + return result; +} + +int HttpProxyConnectJob::DoHttpProxyConnect() { + next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE; + + if (params_->transport_params()) { + UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Insecure.Success", + base::TimeTicks::Now() - connect_start_time_); + } else { + UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Secure.Success", + base::TimeTicks::Now() - connect_start_time_); + } + + // Add a HttpProxy connection on top of the tcp socket. + transport_socket_ = client_socket_factory()->CreateProxyClientSocket( + nested_connect_job_->PassSocket(), GetUserAgent(), params_->endpoint(), + ProxyServer(GetProxyServerScheme(), GetDestination()), + http_auth_controller_.get(), params_->tunnel(), using_spdy_, + negotiated_protocol_, common_connect_job_params()->proxy_delegate, + params_->ssl_params() != nullptr, params_->traffic_annotation()); + nested_connect_job_.reset(); + return transport_socket_->Connect(base::BindOnce( + &HttpProxyConnectJob::OnIOComplete, base::Unretained(this))); +} + +int HttpProxyConnectJob::DoHttpProxyConnectComplete(int result) { + // Always inform caller of auth requests asynchronously. + if (result == ERR_PROXY_AUTH_REQUESTED) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&HttpProxyConnectJob::OnAuthChallenge, + weak_ptr_factory_.GetWeakPtr())); + return ERR_IO_PENDING; + } + + if (result == ERR_HTTP_1_1_REQUIRED) + return ERR_PROXY_HTTP_1_1_REQUIRED; + + return result; +} + +int HttpProxyConnectJob::DoSpdyProxyCreateStream() { + DCHECK(using_spdy_); + DCHECK(params_->tunnel()); + DCHECK(params_->ssl_params()); + SpdySessionKey key( + params_->ssl_params()->GetDirectConnectionParams()->destination(), + ProxyServer::Direct(), PRIVACY_MODE_DISABLED, + SpdySessionKey::IsProxySession::kTrue, socket_tag()); + base::WeakPtr<SpdySession> spdy_session = + params_->spdy_session_pool()->FindAvailableSession( + key, /* enable_ip_based_pooling = */ true, + /* is_websocket = */ false, net_log()); + // It's possible that a session to the proxy has recently been created + if (spdy_session) { + nested_connect_job_.reset(); + } else { + // Create a session direct to the proxy itself + spdy_session = + params_->spdy_session_pool()->CreateAvailableSessionFromSocket( + key, params_->is_trusted_proxy(), nested_connect_job_->PassSocket(), + nested_connect_job_->connect_timing(), net_log()); + DCHECK(spdy_session); + nested_connect_job_.reset(); + } + + next_state_ = STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE; + spdy_stream_request_ = std::make_unique<SpdyStreamRequest>(); + return spdy_stream_request_->StartRequest( + SPDY_BIDIRECTIONAL_STREAM, spdy_session, + GURL("https://" + params_->endpoint().ToString()), priority(), + socket_tag(), spdy_session->net_log(), + base::BindOnce(&HttpProxyConnectJob::OnIOComplete, + base::Unretained(this)), + params_->traffic_annotation()); +} + +int HttpProxyConnectJob::DoSpdyProxyCreateStreamComplete(int result) { + if (result < 0) { + spdy_stream_request_.reset(); + return result; + } + + next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE; + base::WeakPtr<SpdyStream> stream = spdy_stream_request_->ReleaseStream(); + spdy_stream_request_.reset(); + DCHECK(stream.get()); + // |transport_socket_| will set itself as |stream|'s delegate. + transport_socket_ = std::make_unique<SpdyProxyClientSocket>( + stream, GetUserAgent(), params_->endpoint(), net_log(), + http_auth_controller_.get()); + return transport_socket_->Connect(base::BindOnce( + &HttpProxyConnectJob::OnIOComplete, base::Unretained(this))); +} + +int HttpProxyConnectJob::DoQuicProxyCreateSession() { + SSLSocketParams* ssl_params = params_->ssl_params().get(); + DCHECK(ssl_params); + DCHECK(params_->tunnel()); + + next_state_ = STATE_QUIC_PROXY_CREATE_STREAM; + const HostPortPair& proxy_server = + ssl_params->GetDirectConnectionParams()->destination(); + quic_stream_request_ = + std::make_unique<QuicStreamRequest>(params_->quic_stream_factory()); + return quic_stream_request_->Request( + proxy_server, params_->quic_version(), ssl_params->privacy_mode(), + priority(), socket_tag(), ssl_params->ssl_config().GetCertVerifyFlags(), + GURL("https://" + proxy_server.ToString()), net_log(), + &quic_net_error_details_, + /*failed_on_default_network_callback=*/CompletionOnceCallback(), + base::BindOnce(&HttpProxyConnectJob::OnIOComplete, + base::Unretained(this))); +} + +int HttpProxyConnectJob::DoQuicProxyCreateStream(int result) { + if (result < 0) { + quic_stream_request_.reset(); + return result; + } + + next_state_ = STATE_QUIC_PROXY_CREATE_STREAM_COMPLETE; + quic_session_ = quic_stream_request_->ReleaseSessionHandle(); + quic_stream_request_.reset(); + + return quic_session_->RequestStream( + false, + base::BindOnce(&HttpProxyConnectJob::OnIOComplete, + base::Unretained(this)), + params_->traffic_annotation()); +} + +int HttpProxyConnectJob::DoQuicProxyCreateStreamComplete(int result) { + if (result < 0) + return result; + + next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE; + std::unique_ptr<QuicChromiumClientStream::Handle> quic_stream = + quic_session_->ReleaseStream(); + + spdy::SpdyPriority spdy_priority = + ConvertRequestPriorityToQuicPriority(priority()); + quic_stream->SetPriority(spdy_priority); + + transport_socket_ = std::make_unique<QuicProxyClientSocket>( + std::move(quic_stream), std::move(quic_session_), GetUserAgent(), + params_->endpoint(), net_log(), http_auth_controller_.get()); + return transport_socket_->Connect(base::BindOnce( + &HttpProxyConnectJob::OnIOComplete, base::Unretained(this))); +} + +int HttpProxyConnectJob::DoRestartWithAuth() { + DCHECK(transport_socket_); + + next_state_ = STATE_RESTART_WITH_AUTH_COMPLETE; + return transport_socket_->RestartWithAuth(base::BindOnce( + &HttpProxyConnectJob::OnIOComplete, base::Unretained(this))); +} + +int HttpProxyConnectJob::DoRestartWithAuthComplete(int result) { + DCHECK_NE(ERR_IO_PENDING, result); + + if (result == OK && !transport_socket_->IsConnected()) + result = ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH; + + // If the connection could not be reused to attempt to send proxy auth + // credentials, try reconnecting. Do not reset the HttpAuthController in this + // case; the server may, for instance, send "Proxy-Connection: close" and + // expect that each leg of the authentication progress on separate + // connections. + bool reconnect = result == ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH; + + // If auth credentials were sent but the connection was closed, the server may + // have timed out while the user was selecting credentials. Retry once. + if (!has_restarted_ && + (result == ERR_CONNECTION_CLOSED || result == ERR_CONNECTION_RESET || + result == ERR_CONNECTION_ABORTED || + result == ERR_SOCKET_NOT_CONNECTED)) { + reconnect = true; + has_restarted_ = true; + + // Release any auth state bound to the connection. The new connection will + // start the current scheme and identity from scratch. + if (http_auth_controller_) + http_auth_controller_->OnConnectionClosed(); + } + + if (reconnect) { + // Attempt to create a new one. + transport_socket_.reset(); + using_spdy_ = false; + negotiated_protocol_ = NextProto(); + next_state_ = STATE_BEGIN_CONNECT; + return OK; + } + + // If not reconnecting, treat the result as the result of establishing a + // tunnel through the proxy. This is important in the case another auth + // challenge is seen. + next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE; + return result; } void HttpProxyConnectJob::ChangePriorityInternal(RequestPriority priority) { - if (client_socket_) - client_socket_->SetPriority(priority); + if (nested_connect_job_) + nested_connect_job_->ChangePriority(priority); + + if (spdy_stream_request_) + spdy_stream_request_->SetPriority(priority); + + if (quic_stream_request_) + quic_stream_request_->SetPriority(priority); + + if (transport_socket_) + transport_socket_->SetStreamPriority(priority); } -void HttpProxyConnectJob::OnConnectComplete(int result) { - DCHECK_NE(ERR_IO_PENDING, result); - result = HandleConnectResult(result); - if (result != ERR_IO_PENDING) { - NotifyDelegateOfCompletion(result); - // |this| will have been deleted at this point. +void HttpProxyConnectJob::OnTimedOutInternal() { + if (next_state_ == STATE_TCP_CONNECT_COMPLETE) { + UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Insecure.TimedOut", + base::TimeTicks::Now() - connect_start_time_); + } else if (next_state_ == STATE_SSL_CONNECT_COMPLETE) { + UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Secure.TimedOut", + base::TimeTicks::Now() - connect_start_time_); } } int HttpProxyConnectJob::HandleConnectResult(int result) { - // Stop the timer. Only needed for the ERR_PROXY_AUTH_REQUESTED case, but - // shouldn't be returning a result more than once, anyways. + if (result == OK || result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT) + SetSocket(std::move(transport_socket_)); + return result; +} + +void HttpProxyConnectJob::OnAuthChallenge() { + // Stop timer while potentially waiting for user input. ResetTimer(base::TimeDelta()); - if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) - error_response_info_ = client_socket_->GetAdditionalErrorState(); + NotifyDelegateOfProxyAuth( + *transport_socket_->GetConnectResponseInfo(), + transport_socket_->GetAuthController().get(), + base::BindOnce(&HttpProxyConnectJob::RestartWithAuthCredentials, + weak_ptr_factory_.GetWeakPtr())); +} - if (result == OK || result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT) - SetSocket(std::move(client_socket_)); - return result; +const HostPortPair& HttpProxyConnectJob::GetDestination() { + if (params_->transport_params()) { + return params_->transport_params()->destination(); + } else { + return params_->ssl_params()->GetDirectConnectionParams()->destination(); + } +} + +std::string HttpProxyConnectJob::GetUserAgent() const { + if (!http_user_agent_settings()) + return std::string(); + return http_user_agent_settings()->GetUserAgent(); } } // namespace net
diff --git a/net/http/http_proxy_connect_job.h b/net/http/http_proxy_connect_job.h index 2f3743a9..a78daaa 100644 --- a/net/http/http_proxy_connect_job.h +++ b/net/http/http_proxy_connect_job.h
@@ -14,24 +14,28 @@ #include "base/time/time.h" #include "net/base/host_port_pair.h" #include "net/base/net_export.h" +#include "net/base/request_priority.h" #include "net/http/http_auth.h" #include "net/http/http_response_info.h" +#include "net/quic/quic_chromium_client_session.h" #include "net/socket/connect_job.h" #include "net/socket/ssl_client_socket.h" -#include "net/spdy/spdy_session.h" #include "net/traffic_annotation/network_traffic_annotation.h" namespace net { class HttpAuthCache; +class HttpAuthController; class HttpAuthHandlerFactory; -class HttpProxyClientSocketWrapper; +class HttpResponseInfo; class NetworkQualityEstimator; class SocketTag; +class ProxyClientSocket; class SpdySessionPool; +class SpdyStreamRequest; class SSLSocketParams; class TransportSocketParams; -class QuicStreamFactory; +class QuicStreamRequest; // HttpProxySocketParams only needs the socket params for one of the proxy // types. The other param must be NULL. When using an HTTP proxy, @@ -97,13 +101,14 @@ // HttpProxyConnectJob optionally establishes a tunnel through the proxy // server after connecting the underlying transport socket. -class NET_EXPORT_PRIVATE HttpProxyConnectJob : public ConnectJob { +class NET_EXPORT_PRIVATE HttpProxyConnectJob : public ConnectJob, + public ConnectJob::Delegate { public: HttpProxyConnectJob(RequestPriority priority, const SocketTag& socket_tag, const CommonConnectJobParams* common_connect_job_params, const scoped_refptr<HttpProxySocketParams>& params, - Delegate* delegate, + ConnectJob::Delegate* delegate, const NetLogWithSource* net_log); ~HttpProxyConnectJob() override; @@ -111,11 +116,14 @@ LoadState GetLoadState() const override; bool HasEstablishedConnection() const override; + void GetAdditionalErrorState(ClientSocketHandle* handle) override; + + // ConnectJob::Delegate implementation. + void OnConnectJobComplete(int result, ConnectJob* job) override; void OnNeedsProxyAuth(const HttpResponseInfo& response, HttpAuthController* auth_controller, - base::OnceClosure restart_with_auth_callback); - - void GetAdditionalErrorState(ClientSocketHandle* handle) override; + base::OnceClosure restart_with_auth_callback, + ConnectJob* job) override; // Returns the connection timeout that will be used by a HttpProxyConnectJob // created with the specified parameters, given current network conditions. @@ -131,6 +139,24 @@ static void UpdateFieldTrialParametersForTesting(); private: + enum State { + STATE_BEGIN_CONNECT, + STATE_TCP_CONNECT, + STATE_TCP_CONNECT_COMPLETE, + STATE_SSL_CONNECT, + STATE_SSL_CONNECT_COMPLETE, + STATE_HTTP_PROXY_CONNECT, + STATE_HTTP_PROXY_CONNECT_COMPLETE, + STATE_SPDY_PROXY_CREATE_STREAM, + STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE, + STATE_QUIC_PROXY_CREATE_SESSION, + STATE_QUIC_PROXY_CREATE_STREAM, + STATE_QUIC_PROXY_CREATE_STREAM_COMPLETE, + STATE_RESTART_WITH_AUTH, + STATE_RESTART_WITH_AUTH_COMPLETE, + STATE_NONE, + }; + // Begins the tcp connection and the optional Http proxy tunnel. If the // request is not immediately serviceable (likely), the request will return // ERR_IO_PENDING. An OK return from this function or the callback means @@ -140,18 +166,81 @@ // a standard net error code will be returned. int ConnectInternal() override; - void ChangePriorityInternal(RequestPriority priority) override; + ProxyServer::Scheme GetProxyServerScheme() const; - void OnConnectComplete(int result); + void OnIOComplete(int result); + + void RestartWithAuthCredentials(); + + // Runs the state transition loop. + int DoLoop(int result); + + // Determine if need to go through TCP or SSL path. + int DoBeginConnect(); + // Connecting to HTTP Proxy + int DoTransportConnect(); + int DoTransportConnectComplete(int result); + // Connecting to HTTPS Proxy + int DoSSLConnect(); + int DoSSLConnectComplete(int result); + + int DoHttpProxyConnect(); + int DoHttpProxyConnectComplete(int result); + + int DoSpdyProxyCreateStream(); + int DoSpdyProxyCreateStreamComplete(int result); + + int DoQuicProxyCreateSession(); + int DoQuicProxyCreateStream(int result); + int DoQuicProxyCreateStreamComplete(int result); + + int DoRestartWithAuth(); + int DoRestartWithAuthComplete(int result); + + // ConnectJob implementation. + void ChangePriorityInternal(RequestPriority priority) override; + void OnTimedOutInternal() override; int HandleConnectResult(int result); - std::unique_ptr<HttpProxyClientSocketWrapper> client_socket_; + void OnAuthChallenge(); + + const HostPortPair& GetDestination(); + + std::string GetUserAgent() const; scoped_refptr<HttpProxySocketParams> params_; std::unique_ptr<HttpResponseInfo> error_response_info_; + State next_state_; + + bool has_restarted_; + + bool using_spdy_; + NextProto negotiated_protocol_; + + // Set to true once a connection has been successfully established. Remains + // true even if a new socket is being connected to retry with auth. + bool has_established_connection_; + + std::unique_ptr<ConnectJob> nested_connect_job_; + std::unique_ptr<ProxyClientSocket> transport_socket_; + + std::unique_ptr<SpdyStreamRequest> spdy_stream_request_; + + std::unique_ptr<QuicStreamRequest> quic_stream_request_; + std::unique_ptr<QuicChromiumClientSession::Handle> quic_session_; + + scoped_refptr<HttpAuthController> http_auth_controller_; + + NetErrorDetails quic_net_error_details_; + + // Time when the connection to the proxy was started. + base::TimeTicks connect_start_time_; + + base::WeakPtrFactory<HttpProxyConnectJob> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(HttpProxyConnectJob); };
diff --git a/net/http/http_proxy_connect_job_unittest.cc b/net/http/http_proxy_connect_job_unittest.cc index 0c6b62f..e5ce43c 100644 --- a/net/http/http_proxy_connect_job_unittest.cc +++ b/net/http/http_proxy_connect_job_unittest.cc
@@ -1188,8 +1188,7 @@ if (timeout_phase == TimeoutPhase::CONNECT) { FastForwardBy(kTinyTime); ASSERT_TRUE(test_delegate.has_result()); - EXPECT_THAT(test_delegate.WaitForResult(), - test::IsError(ERR_CONNECTION_TIMED_OUT)); + EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT)); continue; } @@ -1211,8 +1210,7 @@ if (timeout_phase == TimeoutPhase::PROXY_HANDSHAKE) { FastForwardBy(kTinyTime); ASSERT_TRUE(test_delegate.has_result()); - EXPECT_THAT(test_delegate.WaitForResult(), - test::IsError(ERR_CONNECTION_TIMED_OUT)); + EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT)); continue; } @@ -1247,7 +1245,7 @@ FastForwardBy(kTinyTime); ASSERT_TRUE(test_delegate.has_result()); EXPECT_THAT(test_delegate.WaitForResult(), - test::IsError(ERR_CONNECTION_TIMED_OUT)); + test::IsError(ERR_TIMED_OUT)); continue; } } else { @@ -1347,8 +1345,7 @@ if (timeout_phase == TimeoutPhase::CONNECT) { FastForwardBy(kTinyTime); ASSERT_TRUE(test_delegate.has_result()); - EXPECT_THAT(test_delegate.WaitForResult(), - test::IsError(ERR_CONNECTION_TIMED_OUT)); + EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT)); continue; } @@ -1371,8 +1368,7 @@ if (timeout_phase == TimeoutPhase::PROXY_HANDSHAKE) { FastForwardBy(kTinyTime); ASSERT_TRUE(test_delegate.has_result()); - EXPECT_THAT(test_delegate.WaitForResult(), - test::IsError(ERR_CONNECTION_TIMED_OUT)); + EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT)); continue; } @@ -1409,8 +1405,7 @@ if (timeout_phase == TimeoutPhase::SECOND_CONNECT) { FastForwardBy(kTinyTime); ASSERT_TRUE(test_delegate.has_result()); - EXPECT_THAT(test_delegate.WaitForResult(), - test::IsError(ERR_CONNECTION_TIMED_OUT)); + EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT)); continue; } @@ -1433,8 +1428,7 @@ if (timeout_phase == TimeoutPhase::SECOND_PROXY_HANDSHAKE) { FastForwardBy(kTinyTime); ASSERT_TRUE(test_delegate.has_result()); - EXPECT_THAT(test_delegate.WaitForResult(), - test::IsError(ERR_CONNECTION_TIMED_OUT)); + EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT)); continue; }
diff --git a/net/http2/platform/impl/http2_logging_impl.h b/net/http2/platform/impl/http2_logging_impl.h new file mode 100644 index 0000000..4ce1f26 --- /dev/null +++ b/net/http2/platform/impl/http2_logging_impl.h
@@ -0,0 +1,72 @@ +// Copyright (c) 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. + +#ifndef NET_HTTP2_PLATFORM_IMPL_HTTP2_LOGGING_IMPL_H_ +#define NET_HTTP2_PLATFORM_IMPL_HTTP2_LOGGING_IMPL_H_ + +#include "base/logging.h" +#include "build/build_config.h" +#include "net/base/net_export.h" + +#define HTTP2_LOG_IMPL(severity) HTTP2_CHROMIUM_LOG_##severity +#define HTTP2_VLOG_IMPL(verbose_level) VLOG(verbose_level) +#define HTTP2_DLOG_IMPL(severity) HTTP2_CHROMIUM_DLOG_##severity +#define HTTP2_DLOG_IF_IMPL(severity, condition) \ + HTTP2_CHROMIUM_DLOG_IF_##severity(condition) +#define HTTP2_DVLOG_IMPL(verbose_level) DVLOG(verbose_level) +#define HTTP2_DVLOG_IF_IMPL(verbose_level, condition) \ + DVLOG_IF(verbose_level, condition) + +#define HTTP2_CHROMIUM_LOG_INFO VLOG(1) +#define HTTP2_CHROMIUM_LOG_WARNING DLOG(WARNING) +#define HTTP2_CHROMIUM_LOG_ERROR DLOG(ERROR) +#define HTTP2_CHROMIUM_LOG_FATAL LOG(FATAL) +#define HTTP2_CHROMIUM_LOG_DFATAL LOG(DFATAL) + +#define HTTP2_CHROMIUM_DLOG_INFO DVLOG(1) +#define HTTP2_CHROMIUM_DLOG_WARNING DLOG(WARNING) +#define HTTP2_CHROMIUM_DLOG_ERROR DLOG(ERROR) +#define HTTP2_CHROMIUM_DLOG_FATAL DLOG(FATAL) +#define HTTP2_CHROMIUM_DLOG_DFATAL DLOG(DFATAL) + +#define HTTP2_CHROMIUM_LOG_IF_INFO(condition) VLOG_IF(1, condition) +#define HTTP2_CHROMIUM_LOG_IF_WARNING(condition) DLOG_IF(WARNING, condition) +#define HTTP2_CHROMIUM_LOG_IF_ERROR(condition) DLOG_IF(ERROR, condition) +#define HTTP2_CHROMIUM_LOG_IF_FATAL(condition) LOG_IF(FATAL, condition) +#define HTTP2_CHROMIUM_LOG_IF_DFATAL(condition) LOG_IF(DFATAL, condition) + +#define HTTP2_CHROMIUM_DLOG_IF_INFO(condition) DVLOG_IF(1, condition) +#define HTTP2_CHROMIUM_DLOG_IF_WARNING(condition) DLOG_IF(WARNING, condition) +#define HTTP2_CHROMIUM_DLOG_IF_ERROR(condition) DLOG_IF(ERROR, condition) +#define HTTP2_CHROMIUM_DLOG_IF_FATAL(condition) DLOG_IF(FATAL, condition) +#define HTTP2_CHROMIUM_DLOG_IF_DFATAL(condition) DLOG_IF(DFATAL, condition) + +#define HTTP2_LOG_INFO_IS_ON_IMPL() 0 +#ifdef NDEBUG +#define HTTP2_LOG_WARNING_IS_ON_IMPL() 0 +#define HTTP2_LOG_ERROR_IS_ON_IMPL() 0 +#else +#define HTTP2_LOG_WARNING_IS_ON_IMPL() 1 +#define HTTP2_LOG_ERROR_IS_ON_IMPL() 1 +#endif +#define HTTP2_DLOG_INFO_IS_ON_IMPL() 0 + +#if defined(OS_WIN) +// wingdi.h defines ERROR to be 0. When we call HTTP2_DLOG(ERROR), it gets +// substituted with 0, and it expands to HTTP2_CHROMIUM_DLOG_0. To allow us to +// keep using this syntax, we define this macro to do the same thing as +// HTTP2_CHROMIUM_DLOG_ERROR. +#define HTTP2_CHROMIUM_LOG_0 HTTP2_CHROMIUM_LOG_ERROR +#define HTTP2_CHROMIUM_DLOG_0 HTTP2_CHROMIUM_DLOG_ERROR +#define HTTP2_CHROMIUM_LOG_IF_0 HTTP2_CHROMIUM_LOG_IF_ERROR +#define HTTP2_CHROMIUM_DLOG_IF_0 HTTP2_CHROMIUM_DLOG_IF_ERROR +#endif + +#define HTTP2_PREDICT_FALSE_IMPL(x) x + +#define HTTP2_NOTREACHED_IMPL() NOTREACHED() + +#define HTTP2_PLOG_IMPL(severity) DVLOG(1) + +#endif // NET_HTTP2_PLATFORM_IMPL_HTTP2_LOGGING_IMPL_H_
diff --git a/net/http2/platform/impl/http2_mock_log_impl.h b/net/http2/platform/impl/http2_mock_log_impl.h deleted file mode 100644 index 254f56d..0000000 --- a/net/http2/platform/impl/http2_mock_log_impl.h +++ /dev/null
@@ -1,22 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef NET_HTTP2_PLATFORM_IMPL_HTTP2_MOCK_LOG_IMPL_H_ -#define NET_HTTP2_PLATFORM_IMPL_HTTP2_MOCK_LOG_IMPL_H_ - -#include "base/test/mock_log.h" -#include "testing/gmock/include/gmock/gmock.h" // IWYU pragma: export - -using Http2MockLogImpl = base::test::MockLog; -#define CREATE_HTTP2_MOCK_LOG_IMPL(log) Http2MockLog log - -#define EXPECT_HTTP2_LOG_CALL_IMPL(log) \ - EXPECT_CALL(log, \ - Log(testing::_, testing::_, testing::_, testing::_, testing::_)) - -#define EXPECT_HTTP2_LOG_CALL_CONTAINS_IMPL(log, level, content) \ - EXPECT_CALL(log, Log(logging::LOG_##level, testing::_, testing::_, \ - testing::_, testing::HasSubstr(content))) - -#endif // NET_HTTP2_PLATFORM_IMPL_HTTP2_MOCK_LOG_IMPL_H_
diff --git a/net/log/net_log_source_type_list.h b/net/log/net_log_source_type_list.h index 08819ab..5cc8de2b 100644 --- a/net/log/net_log_source_type_list.h +++ b/net/log/net_log_source_type_list.h
@@ -7,6 +7,7 @@ // The following line silences a presubmit warning that would otherwise be // triggered by this: // no-include-guard-because-multiply-included +// NOLINT(build/header_guard) // Used for global events which don't correspond to a particular entity. SOURCE_TYPE(NONE) @@ -30,7 +31,6 @@ SOURCE_TYPE(UDP_SOCKET) SOURCE_TYPE(CERT_VERIFIER_JOB) SOURCE_TYPE(PROXY_CLIENT_SOCKET) -SOURCE_TYPE(PROXY_CLIENT_SOCKET_WRAPPER) SOURCE_TYPE(BIDIRECTIONAL_STREAM) SOURCE_TYPE(NETWORK_QUALITY_ESTIMATOR) SOURCE_TYPE(HTTP_STREAM_JOB_CONTROLLER)
diff --git a/net/quic/quic_proxy_client_socket.cc b/net/quic/quic_proxy_client_socket.cc index e0e22c7..db69ce0 100644 --- a/net/quic/quic_proxy_client_socket.cc +++ b/net/quic/quic_proxy_client_socket.cc
@@ -153,8 +153,14 @@ } void QuicProxyClientSocket::ApplySocketTag(const SocketTag& tag) { - // |session_| can be tagged, but |stream_| cannot. - CHECK(false); + // In the case of a connection to the proxy using HTTP/2 or HTTP/3 where the + // underlying socket may multiplex multiple streams, applying this request's + // socket tag to the multiplexed session would incorrectly apply the socket + // tag to all mutliplexed streams. Fortunately socket tagging is only + // supported on Android without the data reduction proxy, so only simple HTTP + // proxies are supported, so proxies won't be using HTTP/2 or HTTP/3. Enforce + // that a specific (non-default) tag isn't being applied. + CHECK(tag == SocketTag()); } int QuicProxyClientSocket::Read(IOBuffer* buf,
diff --git a/net/socket/connect_job.cc b/net/socket/connect_job.cc index f35a8656..35afc8c 100644 --- a/net/socket/connect_job.cc +++ b/net/socket/connect_job.cc
@@ -150,9 +150,13 @@ // Make sure the socket is NULL before calling into |delegate|. SetSocket(nullptr); + OnTimedOutInternal(); + net_log_.AddEvent(NetLogEventType::CONNECT_JOB_TIMED_OUT); NotifyDelegateOfCompletion(ERR_TIMED_OUT); } +void ConnectJob::OnTimedOutInternal() {} + } // namespace net
diff --git a/net/socket/connect_job.h b/net/socket/connect_job.h index 65dc78b9..ae4159a 100644 --- a/net/socket/connect_job.h +++ b/net/socket/connect_job.h
@@ -191,6 +191,9 @@ HostResolver* host_resolver() { return common_connect_job_params_->host_resolver; } + const HttpUserAgentSettings* http_user_agent_settings() const { + return common_connect_job_params_->http_user_agent_settings; + } const SSLClientSocketContext& ssl_client_socket_context() { return common_connect_job_params_->ssl_client_socket_context; } @@ -232,6 +235,9 @@ // Alerts the delegate that the ConnectJob has timed out. void OnTimeout(); + // Invoked to notify subclasses that the has request timed out. + virtual void OnTimedOutInternal(); + const base::TimeDelta timeout_duration_; RequestPriority priority_; const SocketTag socket_tag_;
diff --git a/net/spdy/spdy_proxy_client_socket.cc b/net/spdy/spdy_proxy_client_socket.cc index 000d10a8..a8df026 100644 --- a/net/spdy/spdy_proxy_client_socket.cc +++ b/net/spdy/spdy_proxy_client_socket.cc
@@ -179,8 +179,14 @@ } void SpdyProxyClientSocket::ApplySocketTag(const SocketTag& tag) { - // Underlying SpdySession can be tagged, but |spdy_stream_| cannot. - CHECK(false); + // In the case of a connection to the proxy using HTTP/2 or HTTP/3 where the + // underlying socket may multiplex multiple streams, applying this request's + // socket tag to the multiplexed session would incorrectly apply the socket + // tag to all mutliplexed streams. Fortunately socket tagging is only + // supported on Android without the data reduction proxy, so only simple HTTP + // proxies are supported, so proxies won't be using HTTP/2 or HTTP/3. Enforce + // that a specific (non-default) tag isn't being applied. + CHECK(tag == SocketTag()); } int SpdyProxyClientSocket::Read(IOBuffer* buf,
diff --git a/remoting/signaling/grpc_support/BUILD.gn b/remoting/signaling/grpc_support/BUILD.gn index d75d923e..aa33721c 100644 --- a/remoting/signaling/grpc_support/BUILD.gn +++ b/remoting/signaling/grpc_support/BUILD.gn
@@ -31,6 +31,22 @@ ] } +source_set("test_support") { + testonly = true + + sources = [ + "grpc_async_test_server.cc", + "grpc_async_test_server.h", + "grpc_test_util.cc", + "grpc_test_util.h", + ] + + deps = [ + "//base", + "//third_party/grpc:grpcpp", + ] +} + source_set("unit_tests") { testonly = true @@ -39,6 +55,7 @@ ] deps = [ ":grpc_support", + ":test_support", ":unit_tests_grpc_library", "//testing/gtest", ]
diff --git a/remoting/signaling/grpc_support/grpc_async_dispatcher_unittest.cc b/remoting/signaling/grpc_support/grpc_async_dispatcher_unittest.cc index e6bda7fc..d4a5a16 100644 --- a/remoting/signaling/grpc_support/grpc_async_dispatcher_unittest.cc +++ b/remoting/signaling/grpc_support/grpc_async_dispatcher_unittest.cc
@@ -15,6 +15,8 @@ #include "base/test/bind_test_util.h" #include "base/threading/thread_task_runner_handle.h" #include "remoting/signaling/grpc_support/grpc_async_dispatcher_test_services.grpc.pb.h" +#include "remoting/signaling/grpc_support/grpc_async_test_server.h" +#include "remoting/signaling/grpc_support/grpc_test_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/grpc/src/include/grpcpp/grpcpp.h" @@ -22,173 +24,17 @@ namespace { -void* TagForInt(int num) { - return reinterpret_cast<void*>(num); -} +using EchoStreamResponder = test::GrpcServerStreamResponder<EchoResponse>; base::RepeatingCallback<void(const EchoResponse&)> NotReachedStreamingCallback() { return base::BindRepeating([](const EchoResponse&) { NOTREACHED(); }); } -GrpcAsyncDispatcher::RpcChannelClosedCallback -CheckStatusThenQuitRunLoopCallback(grpc::StatusCode expected_status_code, - base::RunLoop* run_loop) { - return base::BindLambdaForTesting([=](const grpc::Status& status) { - ASSERT_EQ(expected_status_code, status.error_code()); - run_loop->QuitWhenIdle(); - }); -} - -class EchoStream { - public: - EchoStream(std::unique_ptr<grpc::ServerContext> context, - grpc::ServerCompletionQueue* completion_queue, - std::unique_ptr<grpc::ServerAsyncWriter<EchoResponse>> writer); - ~EchoStream(); - - // SendEcho() must be followed by a call to OnClientReceivedEcho(). - void SendEcho(const std::string& text); - void OnClientReceivedEcho(); - void Close(const grpc::Status& status); - - private: - std::unique_ptr<grpc::ServerContext> context_; - grpc::ServerCompletionQueue* completion_queue_; - std::unique_ptr<grpc::ServerAsyncWriter<EchoResponse>> writer_; - bool closed_ = false; -}; - -EchoStream::EchoStream( - std::unique_ptr<grpc::ServerContext> context, - grpc::ServerCompletionQueue* completion_queue, - std::unique_ptr<grpc::ServerAsyncWriter<EchoResponse>> writer) - : context_(std::move(context)), - completion_queue_(completion_queue), - writer_(std::move(writer)) {} - -EchoStream::~EchoStream() { - Close(grpc::Status::OK); -} - -void EchoStream::SendEcho(const std::string& text) { +EchoResponse ResponseForText(const std::string& text) { EchoResponse response; response.set_text(text); - writer_->Write(response, this); -} - -void EchoStream::OnClientReceivedEcho() { - void* tag; - bool ok; - completion_queue_->Next(&tag, &ok); - ASSERT_TRUE(ok); - ASSERT_EQ(this, tag); -} - -void EchoStream::Close(const grpc::Status& status) { - if (closed_) { - return; - } - - writer_->Finish(status, this); - - void* tag; - bool ok; - completion_queue_->Next(&tag, &ok); - if (!ok) { - LOG(WARNING) << "Failed to finish stream. Connection might be dropped."; - } - ASSERT_EQ(this, tag); - closed_ = true; -} - -// EchoStream - -class EchoServerImpl { - public: - EchoServerImpl(); - ~EchoServerImpl(); - - void Start(); - std::shared_ptr<grpc::Channel> CreateInProcessChannel(); - void HandleOneEchoRequest(); - std::unique_ptr<EchoStream> AcceptEchoStream( - const std::string& expected_request_text); - - private: - GrpcAsyncDispatcherTestService::AsyncService async_service_; - std::unique_ptr<grpc::Server> server_; - std::unique_ptr<grpc::ServerCompletionQueue> completion_queue_; -}; - -EchoServerImpl::EchoServerImpl() = default; - -EchoServerImpl::~EchoServerImpl() { - server_->Shutdown(); - completion_queue_->Shutdown(); - - // gRPC requires draining the completion queue before destroying it. - void* tag; - bool ok; - while (completion_queue_->Next(&tag, &ok)) { - } -} - -void EchoServerImpl::Start() { - DCHECK(!server_); - grpc::ServerBuilder builder; - builder.RegisterService(&async_service_); - completion_queue_ = builder.AddCompletionQueue(); - server_ = builder.BuildAndStart(); -} - -std::shared_ptr<grpc::Channel> EchoServerImpl::CreateInProcessChannel() { - return server_->InProcessChannel(grpc::ChannelArguments()); -} - -void EchoServerImpl::HandleOneEchoRequest() { - grpc::ServerContext context; - EchoRequest request; - grpc::ServerAsyncResponseWriter<EchoResponse> responder(&context); - async_service_.RequestEcho(&context, &request, &responder, - completion_queue_.get(), completion_queue_.get(), - TagForInt(1)); - - void* tag; - bool ok; - - completion_queue_->Next(&tag, &ok); - ASSERT_TRUE(ok); - ASSERT_EQ(TagForInt(1), tag); - - EchoResponse response; - response.set_text(request.text()); - responder.Finish(response, grpc::Status::OK, TagForInt(2)); - - completion_queue_->Next(&tag, &ok); - ASSERT_TRUE(ok); - ASSERT_EQ(TagForInt(2), tag); -} - -std::unique_ptr<EchoStream> EchoServerImpl::AcceptEchoStream( - const std::string& expected_request_text) { - auto context = std::make_unique<grpc::ServerContext>(); - EchoRequest request; - auto writer = - std::make_unique<grpc::ServerAsyncWriter<EchoResponse>>(context.get()); - async_service_.RequestStreamEcho(context.get(), &request, writer.get(), - completion_queue_.get(), - completion_queue_.get(), TagForInt(3)); - - void* tag; - bool ok; - completion_queue_->Next(&tag, &ok); - EXPECT_TRUE(ok); - EXPECT_EQ(TagForInt(3), tag); - EXPECT_EQ(expected_request_text, request.text()); - - return std::make_unique<EchoStream>( - std::move(context), completion_queue_.get(), std::move(writer)); + return response; } } // namespace @@ -207,20 +53,24 @@ on_incoming_msg, GrpcAsyncDispatcher::RpcChannelClosedCallback on_channel_closed); - std::unique_ptr<EchoServerImpl> server_; - protected: + void HandleOneEchoRequest(); + std::unique_ptr<EchoStreamResponder> HandleEchoStream( + const base::Location& from_here, + const std::string& expected_request_text); + std::unique_ptr<GrpcAsyncDispatcher> dispatcher_; private: base::MessageLoop message_loop_; std::unique_ptr<GrpcAsyncDispatcherTestService::Stub> stub_; + std::unique_ptr<test::GrpcAsyncTestServer> server_; }; void GrpcAsyncDispatcherTest::SetUp() { dispatcher_ = std::make_unique<GrpcAsyncDispatcher>(); - server_ = std::make_unique<EchoServerImpl>(); - server_->Start(); + server_ = std::make_unique<test::GrpcAsyncTestServer>( + std::make_unique<GrpcAsyncDispatcherTestService::AsyncService>()); stub_ = GrpcAsyncDispatcherTestService::NewStub( server_->CreateInProcessChannel()); } @@ -256,6 +106,27 @@ std::move(on_channel_closed)); } +void GrpcAsyncDispatcherTest::HandleOneEchoRequest() { + EchoRequest request; + auto responder = server_->HandleRequest( + &GrpcAsyncDispatcherTestService::AsyncService::RequestEcho, &request); + EchoResponse response; + response.set_text(request.text()); + responder->Respond(response, grpc::Status::OK); +} + +std::unique_ptr<EchoStreamResponder> GrpcAsyncDispatcherTest::HandleEchoStream( + const base::Location& from_here, + const std::string& expected_request_text) { + EchoRequest request; + auto responder = server_->HandleStreamRequest( + &GrpcAsyncDispatcherTestService::AsyncService::RequestStreamEcho, + &request); + EXPECT_EQ(expected_request_text, request.text()) + << "Request text mismatched. Location: " << from_here.ToString(); + return responder; +} + TEST_F(GrpcAsyncDispatcherTest, DoNothing) {} TEST_F(GrpcAsyncDispatcherTest, SendOneTextAndRespond) { @@ -267,7 +138,7 @@ EXPECT_EQ("Hello", response.text()); run_loop.QuitWhenIdle(); })); - server_->HandleOneEchoRequest(); + HandleOneEchoRequest(); run_loop.Run(); } @@ -280,7 +151,7 @@ EXPECT_EQ("Hello 1", response.text()); run_loop_1.QuitWhenIdle(); })); - server_->HandleOneEchoRequest(); + HandleOneEchoRequest(); run_loop_1.Run(); base::RunLoop run_loop_2; @@ -291,7 +162,7 @@ EXPECT_EQ("Hello 2", response.text()); run_loop_2.QuitWhenIdle(); })); - server_->HandleOneEchoRequest(); + HandleOneEchoRequest(); run_loop_2.Run(); } @@ -318,8 +189,8 @@ EXPECT_EQ("Hello 2", response.text()); on_received_one_response(); })); - server_->HandleOneEchoRequest(); - server_->HandleOneEchoRequest(); + HandleOneEchoRequest(); + HandleOneEchoRequest(); run_loop.Run(); } @@ -339,8 +210,8 @@ base::RunLoop run_loop; auto scoped_stream = StartEchoStream("Hello", NotReachedStreamingCallback(), - CheckStatusThenQuitRunLoopCallback( - grpc::StatusCode::CANCELLED, &run_loop)); + test::CheckStatusThenQuitRunLoopCallback( + FROM_HERE, grpc::StatusCode::CANCELLED, &run_loop)); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindLambdaForTesting([&]() { dispatcher_.reset(); })); run_loop.Run(); @@ -348,70 +219,73 @@ TEST_F(GrpcAsyncDispatcherTest, ServerStreamImmediatelyClosedByServer) { base::RunLoop run_loop; - auto scoped_stream = StartEchoStream( - "Hello", NotReachedStreamingCallback(), - CheckStatusThenQuitRunLoopCallback(grpc::StatusCode::OK, &run_loop)); - std::unique_ptr<EchoStream> stream = server_->AcceptEchoStream("Hello"); + auto scoped_stream = + StartEchoStream("Hello", NotReachedStreamingCallback(), + test::CheckStatusThenQuitRunLoopCallback( + FROM_HERE, grpc::StatusCode::OK, &run_loop)); + auto responder = HandleEchoStream(FROM_HERE, "Hello"); base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindLambdaForTesting([&]() { stream.reset(); })); + FROM_HERE, base::BindLambdaForTesting([&]() { responder.reset(); })); run_loop.Run(); } TEST_F(GrpcAsyncDispatcherTest, ServerStreamImmediatelyClosedByServerWithError) { base::RunLoop run_loop; - auto scoped_stream = - StartEchoStream("Hello", NotReachedStreamingCallback(), - CheckStatusThenQuitRunLoopCallback( - grpc::StatusCode::UNAUTHENTICATED, &run_loop)); - std::unique_ptr<EchoStream> stream = server_->AcceptEchoStream("Hello"); + auto scoped_stream = StartEchoStream( + "Hello", NotReachedStreamingCallback(), + test::CheckStatusThenQuitRunLoopCallback( + FROM_HERE, grpc::StatusCode::UNAUTHENTICATED, &run_loop)); + auto responder = HandleEchoStream(FROM_HERE, "Hello"); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindLambdaForTesting([&]() { - stream->Close(grpc::Status(grpc::StatusCode::UNAUTHENTICATED, "")); + responder->Close(grpc::Status(grpc::StatusCode::UNAUTHENTICATED, "")); })); run_loop.Run(); } TEST_F(GrpcAsyncDispatcherTest, ServerStreamsOneMessageThenClosedByServer) { base::RunLoop run_loop; - std::unique_ptr<EchoStream> stream; + std::unique_ptr<EchoStreamResponder> responder; auto scoped_stream = StartEchoStream( "Hello", base::BindLambdaForTesting([&](const EchoResponse& response) { ASSERT_EQ("Echo 1", response.text()); - stream->OnClientReceivedEcho(); - stream.reset(); + responder->OnClientReceivedMessage(); + responder.reset(); }), - CheckStatusThenQuitRunLoopCallback(grpc::StatusCode::OK, &run_loop)); - stream = server_->AcceptEchoStream("Hello"); - stream->SendEcho("Echo 1"); + test::CheckStatusThenQuitRunLoopCallback(FROM_HERE, grpc::StatusCode::OK, + &run_loop)); + responder = HandleEchoStream(FROM_HERE, "Hello"); + responder->SendMessage(ResponseForText("Echo 1")); run_loop.Run(); } TEST_F(GrpcAsyncDispatcherTest, ServerStreamsTwoMessagesThenClosedByServer) { base::RunLoop run_loop; - std::unique_ptr<EchoStream> stream; + std::unique_ptr<EchoStreamResponder> responder; int received_messages_count = 0; auto scoped_stream = StartEchoStream( "Hello", base::BindLambdaForTesting([&](const EchoResponse& response) { if (received_messages_count == 0) { ASSERT_EQ("Echo 1", response.text()); - stream->OnClientReceivedEcho(); - stream->SendEcho("Echo 2"); + responder->OnClientReceivedMessage(); + responder->SendMessage(ResponseForText("Echo 2")); received_messages_count++; return; } if (received_messages_count == 1) { ASSERT_EQ("Echo 2", response.text()); - stream->OnClientReceivedEcho(); - stream.reset(); + responder->OnClientReceivedMessage(); + responder.reset(); received_messages_count++; return; } NOTREACHED(); }), - CheckStatusThenQuitRunLoopCallback(grpc::StatusCode::OK, &run_loop)); - stream = server_->AcceptEchoStream("Hello"); - stream->SendEcho("Echo 1"); + test::CheckStatusThenQuitRunLoopCallback(FROM_HERE, grpc::StatusCode::OK, + &run_loop)); + responder = HandleEchoStream(FROM_HERE, "Hello"); + responder->SendMessage(ResponseForText("Echo 1")); run_loop.Run(); ASSERT_EQ(2, received_messages_count); } @@ -421,9 +295,9 @@ base::RunLoop run_loop; auto scoped_stream = StartEchoStream("Hello", NotReachedStreamingCallback(), - CheckStatusThenQuitRunLoopCallback( - grpc::StatusCode::CANCELLED, &run_loop)); - std::unique_ptr<EchoStream> stream = server_->AcceptEchoStream("Hello"); + test::CheckStatusThenQuitRunLoopCallback( + FROM_HERE, grpc::StatusCode::CANCELLED, &run_loop)); + auto responder = HandleEchoStream(FROM_HERE, "Hello"); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindLambdaForTesting([&]() { dispatcher_.reset(); })); run_loop.Run(); @@ -431,12 +305,12 @@ TEST_F(GrpcAsyncDispatcherTest, ServerStreamClosedByStreamHolder) { base::RunLoop run_loop; - std::unique_ptr<EchoStream> stream; + std::unique_ptr<EchoStreamResponder> responder; std::unique_ptr<ScopedGrpcServerStream> scoped_stream = StartEchoStream("Hello", NotReachedStreamingCallback(), - CheckStatusThenQuitRunLoopCallback( - grpc::StatusCode::CANCELLED, &run_loop)); - stream = server_->AcceptEchoStream("Hello"); + test::CheckStatusThenQuitRunLoopCallback( + FROM_HERE, grpc::StatusCode::CANCELLED, &run_loop)); + responder = HandleEchoStream(FROM_HERE, "Hello"); scoped_stream.reset(); run_loop.Run(); } @@ -444,17 +318,17 @@ TEST_F(GrpcAsyncDispatcherTest, ServerStreamsOneMessageThenClosedByStreamHolder) { base::RunLoop run_loop; - std::unique_ptr<EchoStream> stream; + std::unique_ptr<EchoStreamResponder> responder; std::unique_ptr<ScopedGrpcServerStream> scoped_stream = StartEchoStream( "Hello", base::BindLambdaForTesting([&](const EchoResponse& response) { ASSERT_EQ("Echo 1", response.text()); - stream->OnClientReceivedEcho(); + responder->OnClientReceivedMessage(); scoped_stream.reset(); }), - CheckStatusThenQuitRunLoopCallback(grpc::StatusCode::CANCELLED, - &run_loop)); - stream = server_->AcceptEchoStream("Hello"); - stream->SendEcho("Echo 1"); + test::CheckStatusThenQuitRunLoopCallback( + FROM_HERE, grpc::StatusCode::CANCELLED, &run_loop)); + responder = HandleEchoStream(FROM_HERE, "Hello"); + responder->SendMessage(ResponseForText("Echo 1")); run_loop.Run(); }
diff --git a/remoting/signaling/grpc_support/grpc_async_test_server.cc b/remoting/signaling/grpc_support/grpc_async_test_server.cc new file mode 100644 index 0000000..bc65ffc39 --- /dev/null +++ b/remoting/signaling/grpc_support/grpc_async_test_server.cc
@@ -0,0 +1,39 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "remoting/signaling/grpc_support/grpc_async_test_server.h" + +#include <utility> + +#include "third_party/grpc/src/include/grpcpp/grpcpp.h" + +namespace remoting { +namespace test { + +GrpcAsyncTestServer::GrpcAsyncTestServer( + std::unique_ptr<grpc::Service> async_service) { + async_service_ = std::move(async_service); + grpc::ServerBuilder builder; + builder.RegisterService(async_service_.get()); + completion_queue_ = builder.AddCompletionQueue(); + server_ = builder.BuildAndStart(); +} + +GrpcAsyncTestServer::~GrpcAsyncTestServer() { + server_->Shutdown(); + completion_queue_->Shutdown(); + + // gRPC requires draining the completion queue before destroying it. + void* tag; + bool ok; + while (completion_queue_->Next(&tag, &ok)) { + } +} + +std::shared_ptr<grpc::Channel> GrpcAsyncTestServer::CreateInProcessChannel() { + return server_->InProcessChannel(grpc::ChannelArguments()); +} + +} // namespace test +} // namespace remoting
diff --git a/remoting/signaling/grpc_support/grpc_async_test_server.h b/remoting/signaling/grpc_support/grpc_async_test_server.h new file mode 100644 index 0000000..82140148 --- /dev/null +++ b/remoting/signaling/grpc_support/grpc_async_test_server.h
@@ -0,0 +1,109 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_SIGNALING_GRPC_SUPPORT_GRPC_ASYNC_TEST_SERVER_H_ +#define REMOTING_SIGNALING_GRPC_SUPPORT_GRPC_ASYNC_TEST_SERVER_H_ + +#include <memory> + +#include "base/macros.h" +#include "remoting/signaling/grpc_support/grpc_test_util.h" +#include "third_party/grpc/src/include/grpcpp/impl/codegen/service_type.h" +#include "third_party/grpc/src/include/grpcpp/server.h" + +namespace grpc { + +class Channel; +class ServerCompletionQueue; +class Service; + +} // namespace grpc + +namespace remoting { +namespace test { + +// Helper class to allow mocking an async server and creating in process +// channel. +class GrpcAsyncTestServer { + public: + template <typename AsyncServiceType, + typename RequestType, + typename ResponseType> + using AsyncRequestFuncPtr = + void (AsyncServiceType::*)(grpc::ServerContext*, + RequestType*, + grpc::ServerAsyncResponseWriter<ResponseType>*, + grpc::CompletionQueue*, + grpc::ServerCompletionQueue*, + void*); + template <typename AsyncServiceType, + typename RequestType, + typename ResponseType> + using AsyncServerStreamingRequestFuncPtr = + void (AsyncServiceType::*)(grpc::ServerContext*, + RequestType*, + grpc::ServerAsyncWriter<ResponseType>*, + grpc::CompletionQueue*, + grpc::ServerCompletionQueue*, + void*); + + explicit GrpcAsyncTestServer(std::unique_ptr<grpc::Service> async_service); + virtual ~GrpcAsyncTestServer(); + + std::shared_ptr<grpc::Channel> CreateInProcessChannel(); + + // Accepts a request by calling |request_func|, writes the request to + // |out_request|, and returns a responder for sending response to the client. + template <typename AsyncServiceType, + typename RequestType, + typename ResponseType> + std::unique_ptr<GrpcServerResponder<ResponseType>> HandleRequest( + AsyncRequestFuncPtr<AsyncServiceType, RequestType, ResponseType> + request_func, + RequestType* out_request) { + auto responder = std::make_unique<GrpcServerResponder<ResponseType>>( + completion_queue_.get()); + AsyncServiceType* async_service = + static_cast<AsyncServiceType*>(async_service_.get()); + (async_service->*request_func)( + responder->context(), out_request, responder->writer(), + completion_queue_.get(), completion_queue_.get(), /* event_tag */ this); + WaitForCompletionAndAssertOk(FROM_HERE, completion_queue_.get(), this); + return responder; + } + + // Accepts a request by calling |request_func|, writes the request to + // |out_request|, and returns a stream responder for sending response to the + // client. + template <typename AsyncServiceType, + typename RequestType, + typename ResponseType> + std::unique_ptr<GrpcServerStreamResponder<ResponseType>> HandleStreamRequest( + AsyncServerStreamingRequestFuncPtr<AsyncServiceType, + RequestType, + ResponseType> request_func, + RequestType* out_request) { + auto responder = std::make_unique<GrpcServerStreamResponder<ResponseType>>( + completion_queue_.get()); + AsyncServiceType* async_service = + static_cast<AsyncServiceType*>(async_service_.get()); + (async_service->*request_func)( + responder->context(), out_request, responder->writer(), + completion_queue_.get(), completion_queue_.get(), /* event_tag */ this); + WaitForCompletionAndAssertOk(FROM_HERE, completion_queue_.get(), this); + return responder; + } + + private: + std::unique_ptr<grpc::Service> async_service_; + std::unique_ptr<grpc::Server> server_; + std::unique_ptr<grpc::ServerCompletionQueue> completion_queue_; + + DISALLOW_COPY_AND_ASSIGN(GrpcAsyncTestServer); +}; + +} // namespace test +} // namespace remoting + +#endif // REMOTING_SIGNALING_GRPC_SUPPORT_GRPC_ASYNC_TEST_SERVER_H_
diff --git a/remoting/signaling/grpc_support/grpc_test_util.cc b/remoting/signaling/grpc_support/grpc_test_util.cc new file mode 100644 index 0000000..4220077 --- /dev/null +++ b/remoting/signaling/grpc_support/grpc_test_util.cc
@@ -0,0 +1,44 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "remoting/signaling/grpc_support/grpc_test_util.h" + +#include "base/run_loop.h" +#include "base/test/bind_test_util.h" + +namespace remoting { +namespace test { + +bool WaitForCompletion(const base::Location& from_here, + grpc::CompletionQueue* completion_queue, + void* expected_tag) { + void* tag; + bool ok; + + completion_queue->Next(&tag, &ok); + DCHECK_EQ(expected_tag, tag) + << "Unexpected tag. Location: " << from_here.ToString(); + return ok; +} + +void WaitForCompletionAndAssertOk(const base::Location& from_here, + grpc::CompletionQueue* completion_queue, + void* expected_tag) { + bool ok = WaitForCompletion(FROM_HERE, completion_queue, expected_tag); + DCHECK(ok) << "Event is not ok. Location: " << from_here.ToString(); +} + +GrpcAsyncDispatcher::RpcChannelClosedCallback +CheckStatusThenQuitRunLoopCallback(const base::Location& from_here, + grpc::StatusCode expected_status_code, + base::RunLoop* run_loop) { + return base::BindLambdaForTesting([=](const grpc::Status& status) { + DCHECK_EQ(expected_status_code, status.error_code()) + << "Status code mismatched. Location: " << from_here.ToString(); + run_loop->QuitWhenIdle(); + }); +} + +} // namespace test +} // namespace remoting
diff --git a/remoting/signaling/grpc_support/grpc_test_util.h b/remoting/signaling/grpc_support/grpc_test_util.h new file mode 100644 index 0000000..5fc8fbe0 --- /dev/null +++ b/remoting/signaling/grpc_support/grpc_test_util.h
@@ -0,0 +1,114 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_SIGNALING_GRPC_SUPPORT_GRPC_TEST_UTIL_H_ +#define REMOTING_SIGNALING_GRPC_SUPPORT_GRPC_TEST_UTIL_H_ + +#include <memory> + +#include "base/bind.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/macros.h" +#include "remoting/signaling/grpc_support/grpc_async_dispatcher.h" +#include "third_party/grpc/src/include/grpcpp/grpcpp.h" + +namespace base { +class RunLoop; +} // namespace base + +namespace remoting { +namespace test { + +// Block and wait until an event is received from the completion queue, and +// check if the tag matches |expected_tag|. +// Returns whether the event is marked "ok". +bool WaitForCompletion(const base::Location& from_here, + grpc::CompletionQueue* completion_queue, + void* expected_tag); +void WaitForCompletionAndAssertOk(const base::Location& from_here, + grpc::CompletionQueue* completion_queue, + void* expected_tag); + +GrpcAsyncDispatcher::RpcChannelClosedCallback +CheckStatusThenQuitRunLoopCallback(const base::Location& from_here, + grpc::StatusCode expected_status_code, + base::RunLoop* run_loop); + +// Helper class for responding to an async server request. +template <typename ResponseType> +class GrpcServerResponder { + public: + explicit GrpcServerResponder(grpc::ServerCompletionQueue* completion_queue) { + completion_queue_ = completion_queue; + } + + ~GrpcServerResponder() = default; + + void Respond(const ResponseType& response, const grpc::Status& status) { + writer_.Finish(response, status, this); + WaitForCompletionAndAssertOk(FROM_HERE, completion_queue_, this); + } + + grpc::ServerContext* context() { return &context_; } + + grpc::ServerAsyncResponseWriter<ResponseType>* writer() { return &writer_; } + + private: + grpc::ServerContext context_; + grpc::ServerCompletionQueue* completion_queue_ = nullptr; + grpc::ServerAsyncResponseWriter<ResponseType> writer_{&context_}; + + DISALLOW_COPY_AND_ASSIGN(GrpcServerResponder); +}; + +// Helper class for responding to an async server stream request. +template <typename ResponseType> +class GrpcServerStreamResponder { + public: + explicit GrpcServerStreamResponder( + grpc::ServerCompletionQueue* completion_queue) { + completion_queue_ = completion_queue; + } + + ~GrpcServerStreamResponder() { Close(grpc::Status::OK); } + + // Must be followed by a call to OnClientReceivedMessage() + void SendMessage(const ResponseType& response) { + writer_.Write(response, /* event_tag */ this); + } + + void OnClientReceivedMessage() { + WaitForCompletionAndAssertOk(FROM_HERE, completion_queue_, this); + } + + void Close(const grpc::Status& status) { + if (closed_) { + return; + } + writer_.Finish(status, /* event_tag */ this); + bool ok = WaitForCompletion(FROM_HERE, completion_queue_, this); + if (!ok) { + LOG(WARNING) << "Failed to finish stream. Connection might be dropped."; + } + closed_ = true; + } + + grpc::ServerContext* context() { return &context_; } + + grpc::ServerAsyncWriter<ResponseType>* writer() { return &writer_; } + + private: + grpc::ServerContext context_; + grpc::ServerCompletionQueue* completion_queue_ = nullptr; + grpc::ServerAsyncWriter<ResponseType> writer_{&context_}; + bool closed_ = false; + + DISALLOW_COPY_AND_ASSIGN(GrpcServerStreamResponder); +}; + +} // namespace test +} // namespace remoting + +#endif // REMOTING_SIGNALING_GRPC_SUPPORT_GRPC_TEST_UTIL_H_
diff --git a/remoting/webapp/crd/js/gnubby_auth_handler.js b/remoting/webapp/crd/js/gnubby_auth_handler.js index ebe0eaf..164a1c18 100644 --- a/remoting/webapp/crd/js/gnubby_auth_handler.js +++ b/remoting/webapp/crd/js/gnubby_auth_handler.js
@@ -56,8 +56,17 @@ var message_callback = function(response) { if (response) { this.gnubbyExtensionId_ = extensionId; + console.log('Found gnubby extension: ' + extensionId); resolve(true); } else { + if (chrome.runtime.lastError) { + console.log('Error occurred communicating with gnubby extension: ' + + extensionId + ', error: ' + + chrome.runtime.lastError.message); + } else { + console.log('Gnubby extension "' + extensionId + '" not found'); + } + if (i + 1 < exts.length) { findGnubbyExtension.call(this, exts, i+1, resolve, reject); } else {
diff --git a/services/identity/public/cpp/identity_manager.cc b/services/identity/public/cpp/identity_manager.cc index b7b6409..0740049 100644 --- a/services/identity/public/cpp/identity_manager.cc +++ b/services/identity/public/cpp/identity_manager.cc
@@ -271,8 +271,10 @@ SigninManagerBase::RegisterPrefs(registry); } -void IdentityManager::LegacySetAccountIdMigrationDone() { - account_tracker_service_->SetMigrationDone(); +std::string IdentityManager::PickAccountIdForAccount( + const std::string& gaia, + const std::string& email) const { + return account_tracker_service_->PickAccountIdForAccount(gaia, email); } IdentityManager::AccountIdMigrationState @@ -285,12 +287,6 @@ return primary_account_mutator_.get(); } -std::string IdentityManager::LegacyPickAccountIdForAccount( - const std::string& gaia, - const std::string& email) const { - return account_tracker_service_->PickAccountIdForAccount(gaia, email); -} - AccountsMutator* IdentityManager::GetAccountsMutator() { return accounts_mutator_.get(); }
diff --git a/services/identity/public/cpp/identity_manager.h b/services/identity/public/cpp/identity_manager.h index 14cbd003..84a72fa 100644 --- a/services/identity/public/cpp/identity_manager.h +++ b/services/identity/public/cpp/identity_manager.h
@@ -357,8 +357,12 @@ // Registers local state prefs used by this class. static void RegisterPrefs(PrefRegistrySimple* registry); - // Marks the migration state for account IDs as finished. - void LegacySetAccountIdMigrationDone(); + // Picks the correct account_id for the specified account depending on the + // migration state. + // TODO(https://crbug.com/883272): Remove once all platform have migrated to + // the new account_id based on gaia (currently, only Chrome OS remains). + std::string PickAccountIdForAccount(const std::string& gaia, + const std::string& email) const; // Returns the currently saved state for the migration of accounts IDs. AccountIdMigrationState GetAccountIdMigrationState() const; @@ -393,14 +397,6 @@ // state of IdentityManager. DiagnosticsProvider* GetDiagnosticsProvider(); - // Picks the correct account_id for the specified account depending on the - // migration state. - // NOTE: This method is added temporarily until when the delegate is moved - // inside the component. So, do not call this method in normal usage. - // TODO(https://crbug.com/922471): Remove the need to expose this method. - std::string LegacyPickAccountIdForAccount(const std::string& gaia, - const std::string& email) const; - // Seeds the account whose account_id is given by // AccountTrackerService::PickAccountIdForAccount() with its corresponding // account information. Returns the same value PickAccountIdForAccount()
diff --git a/services/identity/public/cpp/identity_manager_unittest.cc b/services/identity/public/cpp/identity_manager_unittest.cc index e293205..01dc48f 100644 --- a/services/identity/public/cpp/identity_manager_unittest.cc +++ b/services/identity/public/cpp/identity_manager_unittest.cc
@@ -1786,9 +1786,9 @@ gaia::ListedAccount listed_account = accounts_in_cookie_jar_info.signed_in_accounts[0]; - EXPECT_EQ(identity_manager()->LegacyPickAccountIdForAccount(kTestGaiaId, - kTestEmail), - listed_account.id); + EXPECT_EQ( + identity_manager()->PickAccountIdForAccount(kTestGaiaId, kTestEmail), + listed_account.id); EXPECT_EQ(kTestGaiaId, listed_account.gaia_id); EXPECT_EQ(kTestEmail, listed_account.email); } @@ -1816,17 +1816,17 @@ // the expected order as well. gaia::ListedAccount listed_account1 = accounts_in_cookie_jar_info.signed_in_accounts[0]; - EXPECT_EQ(identity_manager()->LegacyPickAccountIdForAccount(kTestGaiaId, - kTestEmail), - listed_account1.id); + EXPECT_EQ( + identity_manager()->PickAccountIdForAccount(kTestGaiaId, kTestEmail), + listed_account1.id); EXPECT_EQ(kTestGaiaId, listed_account1.gaia_id); EXPECT_EQ(kTestEmail, listed_account1.email); gaia::ListedAccount account_info2 = accounts_in_cookie_jar_info.signed_in_accounts[1]; - EXPECT_EQ(identity_manager()->LegacyPickAccountIdForAccount(kTestGaiaId2, - kTestEmail2), - account_info2.id); + EXPECT_EQ( + identity_manager()->PickAccountIdForAccount(kTestGaiaId2, kTestEmail2), + account_info2.id); EXPECT_EQ(kTestGaiaId2, account_info2.gaia_id); EXPECT_EQ(kTestEmail2, account_info2.email); } @@ -1874,9 +1874,9 @@ ? accounts_in_cookie_jar_info.signed_out_accounts[i++] : accounts_in_cookie_jar_info.signed_in_accounts[j++]; if (!signed_out_status.account_1) - EXPECT_EQ(identity_manager()->LegacyPickAccountIdForAccount(kTestGaiaId, - kTestEmail), - listed_account1.id); + EXPECT_EQ( + identity_manager()->PickAccountIdForAccount(kTestGaiaId, kTestEmail), + listed_account1.id); EXPECT_EQ(kTestGaiaId, listed_account1.gaia_id); EXPECT_EQ(kTestEmail, listed_account1.email); @@ -1885,8 +1885,8 @@ ? accounts_in_cookie_jar_info.signed_out_accounts[i++] : accounts_in_cookie_jar_info.signed_in_accounts[j++]; if (!signed_out_status.account_2) - EXPECT_EQ(identity_manager()->LegacyPickAccountIdForAccount(kTestGaiaId2, - kTestEmail2), + EXPECT_EQ(identity_manager()->PickAccountIdForAccount(kTestGaiaId2, + kTestEmail2), listed_account2.id); EXPECT_EQ(kTestGaiaId2, listed_account2.gaia_id); EXPECT_EQ(kTestEmail2, listed_account2.email); @@ -1970,9 +1970,9 @@ gaia::ListedAccount listed_account = updated_accounts_in_cookie_jar.signed_in_accounts[0]; - EXPECT_EQ(identity_manager()->LegacyPickAccountIdForAccount(kTestGaiaId, - kTestEmail), - listed_account.id); + EXPECT_EQ( + identity_manager()->PickAccountIdForAccount(kTestGaiaId, kTestEmail), + listed_account.id); EXPECT_EQ(kTestGaiaId, listed_account.gaia_id); EXPECT_EQ(kTestEmail, listed_account.email); } @@ -2010,17 +2010,17 @@ // the expected order as well. gaia::ListedAccount listed_account1 = updated_accounts_in_cookie_jar.signed_in_accounts[0]; - EXPECT_EQ(identity_manager()->LegacyPickAccountIdForAccount(kTestGaiaId, - kTestEmail), - listed_account1.id); + EXPECT_EQ( + identity_manager()->PickAccountIdForAccount(kTestGaiaId, kTestEmail), + listed_account1.id); EXPECT_EQ(kTestGaiaId, listed_account1.gaia_id); EXPECT_EQ(kTestEmail, listed_account1.email); gaia::ListedAccount listed_account2 = updated_accounts_in_cookie_jar.signed_in_accounts[1]; - EXPECT_EQ(identity_manager()->LegacyPickAccountIdForAccount(kTestGaiaId2, - kTestEmail2), - listed_account2.id); + EXPECT_EQ( + identity_manager()->PickAccountIdForAccount(kTestGaiaId2, kTestEmail2), + listed_account2.id); EXPECT_EQ(kTestGaiaId2, listed_account2.gaia_id); EXPECT_EQ(kTestEmail2, listed_account2.email); } @@ -2411,10 +2411,9 @@ .email); } -TEST_F(IdentityManagerTest, TestLegacyPickAccountIdForAccount) { +TEST_F(IdentityManagerTest, TestPickAccountIdForAccount) { const std::string account_id = - identity_manager()->LegacyPickAccountIdForAccount(kTestGaiaId, - kTestEmail); + identity_manager()->PickAccountIdForAccount(kTestGaiaId, kTestEmail); const bool account_id_migration_done = identity_manager()->GetAccountIdMigrationState() == IdentityManager::AccountIdMigrationState::MIGRATION_DONE; @@ -2432,8 +2431,8 @@ CoreAccountInfo account_info; account_info.email = kTestEmail; account_info.gaia = kTestGaiaId; - account_info.account_id = identity_manager()->LegacyPickAccountIdForAccount( - kTestGaiaId, kTestEmail); + account_info.account_id = + identity_manager()->PickAccountIdForAccount(kTestGaiaId, kTestEmail); // FindExtendedAccountInfoForAccount() returns empty optional if the // account_info is invalid.
diff --git a/services/shape_detection/barcode_detection_impl_mac_unittest.mm b/services/shape_detection/barcode_detection_impl_mac_unittest.mm index a3013fd..de36065d 100644 --- a/services/shape_detection/barcode_detection_impl_mac_unittest.mm +++ b/services/shape_detection/barcode_detection_impl_mac_unittest.mm
@@ -9,6 +9,7 @@ #include <string> #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/callback_forward.h" #include "base/command_line.h" #include "base/mac/mac_util.h" @@ -35,7 +36,7 @@ closure.Run(); } -std::unique_ptr<mojom::BarcodeDetection> CreateBarcodeDetectorImplMacCI( +std::unique_ptr<mojom::BarcodeDetection> CreateBarcodeDetectorImplMacCoreImage( mojom::BarcodeDetectorOptionsPtr options) { if (@available(macOS 10.10, *)) { return std::make_unique<BarcodeDetectionImplMac>(); @@ -52,23 +53,44 @@ return nullptr; } +void* LoadVisionLibrary() { + if (@available(macOS 10.13, *)) { + return dlopen("/System/Library/Frameworks/Vision.framework/Vision", + RTLD_LAZY); + } + return nullptr; +} + +using LibraryLoadCB = base::RepeatingCallback<void*(void)>; + using BarcodeDetectorFactory = - base::Callback<std::unique_ptr<mojom::BarcodeDetection>( + base::RepeatingCallback<std::unique_ptr<mojom::BarcodeDetection>( mojom::BarcodeDetectorOptionsPtr)>; const std::string kInfoString = "https://www.chromium.org"; struct TestParams { size_t num_barcodes; - const std::string barcode_value; - bool test_vision_api; + LibraryLoadCB library_load_callback; BarcodeDetectorFactory factory; + NSString* test_code_generator; } kTestParams[] = { - {1, kInfoString, false, - base::BindRepeating(&CreateBarcodeDetectorImplMacCI)}, - {1, kInfoString, true, - base::BindRepeating(&CreateBarcodeDetectorImplMacVision)}, -}; + // CoreImage only supports QR Codes. + {1, base::BindRepeating([]() { return static_cast<void*>(nullptr); }), + base::BindRepeating(&CreateBarcodeDetectorImplMacCoreImage), + @"CIQRCodeGenerator"}, + // Vision only supports a number of 1D/2D codes. Not all of them are + // available for generation, though, only a few. + {1, base::BindRepeating(&LoadVisionLibrary), + base::BindRepeating(&CreateBarcodeDetectorImplMacVision), + @"CIPDF417BarcodeGenerator"}, + {1, base::BindRepeating(&LoadVisionLibrary), + base::BindRepeating(&CreateBarcodeDetectorImplMacVision), + @"CIQRCodeGenerator"}, + {6 /* 1D barcode makes the detector find the same code several times. */, + base::BindRepeating(&LoadVisionLibrary), + base::BindRepeating(&CreateBarcodeDetectorImplMacVision), + @"CICode128BarcodeGenerator"}}; } class BarcodeDetectionImplMacTest : public TestWithParam<struct TestParams> { @@ -76,14 +98,7 @@ ~BarcodeDetectionImplMacTest() override = default; void SetUp() override { - // Only load the library if we need to for this test. - if (GetParam().test_vision_api) - return; - - if (@available(macOS 10.13, *)) { - vision_framework_ = dlopen( - "/System/Library/Frameworks/Vision.framework/Vision", RTLD_LAZY); - } + vision_framework_ = GetParam().library_load_callback.Run(); } void TearDown() override { @@ -145,10 +160,9 @@ NSData* const qr_code_data = [[NSString stringWithUTF8String:kInfoString.c_str()] dataUsingEncoding:NSISOLatin1StringEncoding]; - // TODO(mcasas): Consider using other generator types (e.g. - // CI{AztecCode,Code128Barcode,PDF417Barcode}Generator) when the minimal OS - // X is upgraded to 10.10+ (https://crbug.com/624049). - CIFilter* qr_code_generator = [CIFilter filterWithName:@"CIQRCodeGenerator"]; + + CIFilter* qr_code_generator = + [CIFilter filterWithName:GetParam().test_code_generator]; [qr_code_generator setValue:qr_code_data forKey:@"inputMessage"]; // [CIImage outputImage] is available in macOS 10.10+. Could be added to @@ -175,9 +189,9 @@ // Send the image Detect() and expect the response in callback. EXPECT_CALL(*this, Detection()).WillOnce(RunClosure(quit_closure)); impl_->Detect(bitmap, - base::Bind(&BarcodeDetectionImplMacTest::DetectCallback, - base::Unretained(this), GetParam().num_barcodes, - GetParam().barcode_value)); + base::BindOnce(&BarcodeDetectionImplMacTest::DetectCallback, + base::Unretained(this), GetParam().num_barcodes, + kInfoString)); run_loop.Run(); }
diff --git a/services/viz/privileged/interfaces/compositing/renderer_settings.mojom b/services/viz/privileged/interfaces/compositing/renderer_settings.mojom index 60118bf..9bbc445 100644 --- a/services/viz/privileged/interfaces/compositing/renderer_settings.mojom +++ b/services/viz/privileged/interfaces/compositing/renderer_settings.mojom
@@ -24,6 +24,7 @@ bool record_sk_picture; bool allow_overlays; bool requires_alpha_channel; + bool enable_shared_mem_begin_frame; [EnableIf=is_android] gfx.mojom.Size initial_screen_size;
diff --git a/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.cc b/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.cc index 23c730b..89c16d7 100644 --- a/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.cc +++ b/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.cc
@@ -31,6 +31,7 @@ out->record_sk_picture = data.record_sk_picture(); out->allow_overlays = data.allow_overlays(); out->requires_alpha_channel = data.requires_alpha_channel(); + out->enable_shared_mem_begin_frame = data.enable_shared_mem_begin_frame(); #if defined(OS_ANDROID) success = data.ReadInitialScreenSize(&out->initial_screen_size);
diff --git a/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.h b/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.h index f379f96..6b23fe6 100644 --- a/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.h +++ b/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.h
@@ -82,6 +82,11 @@ return input.requires_alpha_channel; } + static bool enable_shared_mem_begin_frame( + const viz::RendererSettings& input) { + return input.enable_shared_mem_begin_frame; + } + #if defined(OS_ANDROID) static gfx::Size initial_screen_size(const viz::RendererSettings& input) { return input.initial_screen_size;
diff --git a/sql/BUILD.gn b/sql/BUILD.gn index 3cfe156..a03d51e7 100644 --- a/sql/BUILD.gn +++ b/sql/BUILD.gn
@@ -49,6 +49,13 @@ "CoreServices.framework", ] } + + if (is_fuchsia) { + sources += [ + "vfs_wrapper_fuchsia.cc", + "vfs_wrapper_fuchsia.h", + ] + } } static_library("test_support") {
diff --git a/sql/sqlite_features_unittest.cc b/sql/sqlite_features_unittest.cc index c1b9834bc..ed481ea 100644 --- a/sql/sqlite_features_unittest.cc +++ b/sql/sqlite_features_unittest.cc
@@ -176,24 +176,6 @@ EXPECT_TRUE(!s.ColumnBool(3)) << " default FALSE added by altering the table"; } -#if defined(OS_FUCHSIA) -// If the platform cannot support SQLite mmap'ed I/O, make sure SQLite isn't -// offering to support it. -TEST_F(SQLiteFeaturesTest, NoMmap) { - // For recent versions of SQLite, SQLITE_MAX_MMAP_SIZE=0 can be used to - // disable mmap support. Alternately, sqlite3_config() could be used. In - // that case, the pragma will run successfully, but the size will always be 0. - // - // MojoVFS implements a no-op for xFileControl(). PRAGMA mmap_size is - // implemented in terms of SQLITE_FCNTL_MMAP_SIZE. In that case, the pragma - // will succeed but with no effect. - ignore_result(db().Execute("PRAGMA mmap_size = 1048576")); - sql::Statement s(db().GetUniqueStatement("PRAGMA mmap_size")); - ASSERT_TRUE(!s.Step() || !s.ColumnInt64(0)); -} -#endif // defined(OS_FUCHSIA) - -#if !defined(OS_FUCHSIA) // Verify that OS file writes are reflected in the memory mapping of a // memory-mapped file. Normally SQLite writes to memory-mapped files using // memcpy(), which should stay consistent. Our SQLite is slightly patched to @@ -265,7 +247,6 @@ ASSERT_EQ('4', m.data()[kOffset]); } } -#endif // !defined(OS_FUCHSIA) // Verify that http://crbug.com/248608 is fixed. In this bug, the // compiled regular expression is effectively cached with the prepared
diff --git a/sql/vfs_wrapper.cc b/sql/vfs_wrapper.cc index 22e31b1..5087a2e 100644 --- a/sql/vfs_wrapper.cc +++ b/sql/vfs_wrapper.cc
@@ -14,11 +14,16 @@ #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/strings/string_piece.h" +#include "build/build_config.h" #if defined(OS_MACOSX) && !defined(OS_IOS) #include "base/mac/mac_util.h" #endif +#if defined(OS_FUCHSIA) +#include "sql/vfs_wrapper_fuchsia.h" +#endif + namespace sql { namespace { @@ -36,14 +41,6 @@ return static_cast<sqlite3_vfs*>(wrapped_vfs->pAppData); } -// NOTE(shess): This structure is allocated by SQLite using malloc. Do not add -// C++ objects, they will not be correctly constructed and destructed. Instead, -// manually manage a pointer to a C++ object in Open() and Close(). -struct VfsFile { - const sqlite3_io_methods* methods; - sqlite3_file* wrapped_file; -}; - VfsFile* AsVfsFile(sqlite3_file* wrapper_file) { return reinterpret_cast<VfsFile*>(wrapper_file); } @@ -56,8 +53,16 @@ { VfsFile* file = AsVfsFile(sqlite_file); +#if defined(OS_FUCHSIA) + FuchsiaVfsUnlock(sqlite_file, SQLITE_LOCK_NONE); +#endif + int r = file->wrapped_file->pMethods->xClose(file->wrapped_file); sqlite3_free(file->wrapped_file); + + // Memory will be freed with sqlite3_free(), so the destructor needs to be + // called explicitly. + file->~VfsFile(); memset(file, '\0', sizeof(*file)); return r; } @@ -93,6 +98,8 @@ return wrapped_file->pMethods->xFileSize(wrapped_file, size); } +#if !defined(OS_FUCHSIA) + int Lock(sqlite3_file* sqlite_file, int file_lock) { sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file); @@ -111,6 +118,8 @@ return wrapped_file->pMethods->xCheckReservedLock(wrapped_file, result); } +#endif // !defined(OS_FUCHSIA) + int FileControl(sqlite3_file* sqlite_file, int op, void* arg) { sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file); @@ -209,18 +218,25 @@ // the version of the wrapped files. // // At a first glance, it might be tempting to simplify the code by - // restricting wrapping support to VFS version 3. However, this would fail - // on Fuchsia and might fail on Mac. + // restricting wrapping support to VFS version 3. However, this might fail on + // Mac. // // On Mac, SQLite built with SQLITE_ENABLE_LOCKING_STYLE ends up using a VFS // that dynamically dispatches between a few variants of sqlite3_io_methods, // based on whether the opened database is on a local or on a remote (AFS, // NFS) filesystem. Some variants return a VFS version 1 structure. - // - // Fuchsia doesn't implement POSIX locking, so it always uses dot-style - // locking, which returns VFS version 1 files. VfsFile* file = AsVfsFile(wrapper_file); + + // Call constructor explicitly since the memory is already allocated. + new (file) VfsFile(); + file->wrapped_file = wrapped_file; + +#if defined(OS_FUCHSIA) + file->file_name = file_name; + file->lock_level = SQLITE_LOCK_NONE; +#endif + if (wrapped_file->pMethods->iVersion == 1) { static const sqlite3_io_methods io_methods = { 1, @@ -230,9 +246,15 @@ Truncate, Sync, FileSize, +#if !defined(OS_FUCHSIA) Lock, Unlock, CheckReservedLock, +#else + FuchsiaVfsLock, + FuchsiaVfsUnlock, + FuchsiaVfsCheckReservedLock, +#endif FileControl, SectorSize, DeviceCharacteristics, @@ -247,9 +269,15 @@ Truncate, Sync, FileSize, +#if !defined(OS_FUCHSIA) Lock, Unlock, CheckReservedLock, +#else + FuchsiaVfsLock, + FuchsiaVfsUnlock, + FuchsiaVfsCheckReservedLock, +#endif FileControl, SectorSize, DeviceCharacteristics, @@ -269,9 +297,15 @@ Truncate, Sync, FileSize, +#if !defined(OS_FUCHSIA) Lock, Unlock, CheckReservedLock, +#else + FuchsiaVfsLock, + FuchsiaVfsUnlock, + FuchsiaVfsCheckReservedLock, +#endif FileControl, SectorSize, DeviceCharacteristics, @@ -379,10 +413,18 @@ return vfs; } - // Get the default VFS for this platform. If no default VFS, give up. - sqlite3_vfs* wrapped_vfs = sqlite3_vfs_find(nullptr); - if (!wrapped_vfs) + // Get the default VFS on all platforms except Fuchsia. + const char* base_vfs_name = nullptr; +#if defined(OS_FUCHSIA) + base_vfs_name = "unix-none"; +#endif + sqlite3_vfs* wrapped_vfs = sqlite3_vfs_find(base_vfs_name); + + // Give up if there is no VFS implementation for the current platform. + if (!wrapped_vfs) { + NOTREACHED(); return nullptr; + } std::unique_ptr<sqlite3_vfs, std::function<void(sqlite3_vfs*)>> wrapper_vfs( static_cast<sqlite3_vfs*>(sqlite3_malloc(sizeof(sqlite3_vfs))),
diff --git a/sql/vfs_wrapper.h b/sql/vfs_wrapper.h index 0ff50e6..001066d 100644 --- a/sql/vfs_wrapper.h +++ b/sql/vfs_wrapper.h
@@ -5,6 +5,9 @@ #ifndef SQL_VFS_WRAPPER_H_ #define SQL_VFS_WRAPPER_H_ +#include <string> + +#include "build/build_config.h" #include "third_party/sqlite/sqlite3.h" namespace sql { @@ -15,9 +18,22 @@ // file to associated files such as journals. <http://crbug.com/23619> and // <http://crbug.com/25959> and others. // +// On Fuchsia the wrapper adds in-process file locking (Fuchsia doesn't support +// file locking). +// // TODO(shess): On Windows, wrap xFetch() with a structured exception handler. sqlite3_vfs* VFSWrapper(); +// Internal representation of sqlite3_file for VFSWrapper. +struct VfsFile { + const sqlite3_io_methods* methods; + sqlite3_file* wrapped_file; +#if defined(OS_FUCHSIA) + std::string file_name; + int lock_level; +#endif +}; + } // namespace sql #endif // SQL_VFS_WRAPPER_H_
diff --git a/sql/vfs_wrapper_fuchsia.cc b/sql/vfs_wrapper_fuchsia.cc new file mode 100644 index 0000000..8cf3b6bd --- /dev/null +++ b/sql/vfs_wrapper_fuchsia.cc
@@ -0,0 +1,103 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sql/vfs_wrapper_fuchsia.h" + +#include "base/containers/flat_set.h" +#include "base/no_destructor.h" +#include "base/synchronization/lock.h" +#include "base/thread_annotations.h" +#include "sql/vfs_wrapper.h" + +namespace sql { + +namespace { + +// Singleton that stores locks state. +class FuchsiaFileLockManager { + public: + FuchsiaFileLockManager() = default; + + // Returns lock manager for the current process. + static FuchsiaFileLockManager* Instance() { + static base::NoDestructor<FuchsiaFileLockManager> lock_manager; + return lock_manager.get(); + } + + // Return true if the file was locked successfully. + bool Lock(const std::string& name) { + base::AutoLock lock(lock_); + + if (locked_files_.find(name) != locked_files_.end()) { + DLOG(WARNING) << "File " << name + << " is being used concurrently by multiple consumers."; + return false; + } + + locked_files_.insert(name); + return true; + } + + void Unlock(const std::string& name) { + base::AutoLock lock(lock_); + + size_t removed = locked_files_.erase(name); + DCHECK_EQ(removed, 1U); + } + + bool IsLocked(const std::string& name) { + base::AutoLock lock(lock_); + return locked_files_.find(name) != locked_files_.end(); + } + + private: + ~FuchsiaFileLockManager() = delete; + + base::Lock lock_; + + // Set of all currently locked files. + base::flat_set<std::string> locked_files_ GUARDED_BY(lock_); +}; + +} // namespace + +int FuchsiaVfsLock(sqlite3_file* sqlite_file, int file_lock) { + DCHECK(file_lock == SQLITE_LOCK_SHARED || file_lock == SQLITE_LOCK_RESERVED || + file_lock == SQLITE_LOCK_PENDING || + file_lock == SQLITE_LOCK_EXCLUSIVE); + + VfsFile* vfs_file = reinterpret_cast<VfsFile*>(sqlite_file); + + if (vfs_file->lock_level == SQLITE_LOCK_NONE) { + if (!FuchsiaFileLockManager::Instance()->Lock(vfs_file->file_name)) + return SQLITE_BUSY; + } + + vfs_file->lock_level = file_lock; + + return SQLITE_OK; +} + +int FuchsiaVfsUnlock(sqlite3_file* sqlite_file, int file_lock) { + VfsFile* vfs_file = reinterpret_cast<VfsFile*>(sqlite_file); + + if (file_lock == SQLITE_LOCK_NONE) { + if (vfs_file->lock_level != SQLITE_LOCK_NONE) + FuchsiaFileLockManager::Instance()->Unlock(vfs_file->file_name); + } else { + // Keep the file locked for the shared lock. + DCHECK(file_lock == SQLITE_LOCK_SHARED); + DCHECK(FuchsiaFileLockManager::Instance()->IsLocked(vfs_file->file_name)); + } + vfs_file->lock_level = file_lock; + + return SQLITE_OK; +} + +int FuchsiaVfsCheckReservedLock(sqlite3_file* sqlite_file, int* result) { + VfsFile* vfs_file = reinterpret_cast<VfsFile*>(sqlite_file); + return vfs_file->lock_level; +} + +} // namespace sql
diff --git a/sql/vfs_wrapper_fuchsia.h b/sql/vfs_wrapper_fuchsia.h new file mode 100644 index 0000000..e8c3c47e --- /dev/null +++ b/sql/vfs_wrapper_fuchsia.h
@@ -0,0 +1,28 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SQL_VFS_WRAPPER_FUCHSIA_H_ +#define SQL_VFS_WRAPPER_FUCHSIA_H_ + +#include "third_party/sqlite/sqlite3.h" + +namespace sql { + +// Fuchsia doesn't provide a file locking mechanism like flock(). These +// functions are used to simulate file locking. On Fuchsia profile directories +// are not expected to be shared with other processes and therefore only one +// browser process may access sqlite files. These functions are designed to +// handle the case when the same sqlite database is open more than once from the +// same browser process. In most cases databases do not need to be open more +// than once, i.e. contention is expected to be rare, so the main goal of the +// design is simplicity and not performance. The manager maintains a list of all +// currently locked files. It support only exclusive locking, i.e. only one +// client can acquire SHARED_LOCK. +int FuchsiaVfsLock(sqlite3_file* sqlite_file, int file_lock); +int FuchsiaVfsUnlock(sqlite3_file* sqlite_file, int file_lock); +int FuchsiaVfsCheckReservedLock(sqlite3_file* sqlite_file, int* result); + +} // namespace sql + +#endif // SQL_VFS_WRAPPER_FUCHSIA_H_ \ No newline at end of file
diff --git a/storage/browser/blob/blob_builder_from_stream.cc b/storage/browser/blob/blob_builder_from_stream.cc index ec63d85..d13e1fd1 100644 --- a/storage/browser/blob/blob_builder_from_stream.cc +++ b/storage/browser/blob/blob_builder_from_stream.cc
@@ -330,6 +330,10 @@ } BlobBuilderFromStream::~BlobBuilderFromStream() { + DCHECK(!callback_) << "BlobBuilderFromStream was destroyed before finishing"; +} + +void BlobBuilderFromStream::Abort() { OnError(Result::kAborted); }
diff --git a/storage/browser/blob/blob_builder_from_stream.h b/storage/browser/blob/blob_builder_from_stream.h index 7ee4d518..c313097 100644 --- a/storage/browser/blob/blob_builder_from_stream.h +++ b/storage/browser/blob/blob_builder_from_stream.h
@@ -41,9 +41,10 @@ // Finally you can pass an |length_hint| to the constructor. If this is done, // the size is used for an initial space allocation, and if the size is too // large to fit in memory anyway, the entire blob will be stored on disk. -// TODO(mek): Actually deal with length_hint. // -// If destroyed before building has finished this will not create a blob. +// If this needs to be destroyed before building has finished, you should make +// sure to call Abort() before destroying the instance. No blob will be created +// in that case. class COMPONENT_EXPORT(STORAGE_BROWSER) BlobBuilderFromStream { public: using ResultCallback = @@ -60,6 +61,8 @@ ResultCallback callback); ~BlobBuilderFromStream(); + void Abort(); + private: class WritePipeToFileHelper; class WritePipeToFutureDataHelper;
diff --git a/storage/browser/blob/blob_builder_from_stream_unittest.cc b/storage/browser/blob/blob_builder_from_stream_unittest.cc index 184f2b7..1827b9fc 100644 --- a/storage/browser/blob/blob_builder_from_stream_unittest.cc +++ b/storage/browser/blob/blob_builder_from_stream_unittest.cc
@@ -189,7 +189,7 @@ } }; -TEST_P(BlobBuilderFromStreamTest, CallbackCalledOnDeletion) { +TEST_P(BlobBuilderFromStreamTest, CallbackCalledOnAbortBeforeDeletion) { mojo::DataPipe pipe; base::RunLoop loop; @@ -204,6 +204,7 @@ loop.Quit(); })); builder_ptr = builder.get(); + builder->Abort(); builder.reset(); loop.Run(); }
diff --git a/storage/browser/blob/blob_registry_impl.cc b/storage/browser/blob/blob_registry_impl.cc index 7a92bf5..ab0e1307 100644 --- a/storage/browser/blob/blob_registry_impl.cc +++ b/storage/browser/blob/blob_registry_impl.cc
@@ -479,7 +479,11 @@ file_system_context_(std::move(file_system_context)), weak_ptr_factory_(this) {} -BlobRegistryImpl::~BlobRegistryImpl() = default; +BlobRegistryImpl::~BlobRegistryImpl() { + // BlobBuilderFromStream needs to be aborted before it can be destroyed. + for (const auto& builder : blobs_being_streamed_) + builder->Abort(); +} void BlobRegistryImpl::Bind(blink::mojom::BlobRegistryRequest request, std::unique_ptr<Delegate> delegate) {
diff --git a/storage/browser/blob/blob_registry_impl_unittest.cc b/storage/browser/blob/blob_registry_impl_unittest.cc index ea98f0e3..078368359 100644 --- a/storage/browser/blob/blob_registry_impl_unittest.cc +++ b/storage/browser/blob/blob_registry_impl_unittest.cc
@@ -8,6 +8,7 @@ #include <memory> #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/files/scoped_temp_dir.h" #include "base/rand_util.h" #include "base/run_loop.h" @@ -1039,4 +1040,13 @@ EXPECT_GE(progress_client.call_count, 1); } +TEST_F(BlobRegistryImplTest, DestroyWithUnfinishedStream) { + mojo::DataPipe pipe; + registry_->RegisterFromStream("", "", 0, std::move(pipe.consumer_handle), + nullptr, base::DoNothing()); + registry_.FlushForTesting(); + // This test just makes sure no crash happens if we're shut down while still + // creating a blob from a stream. +} + } // namespace storage
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 5078659..bac03ce 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -1413,19 +1413,18 @@ ], "experiments": [ { - "name": "Enabled" - }, - { - "name": "EnabledWithSnackbarPromo", + "name": "Enabled", "params": { - "snackbar_promo_data_savings_in_megabytes": "1024;100" - } - }, - { - "name": "SnackbarPromoOnly", - "params": { - "snackbar_promo_data_savings_in_megabytes": "1024;100" - } + "availability": "any", + "event_trigger": "name:data_saver_milestone_promo_iph_would_have_triggered;comparator:<2;window:90;storage:360", + "event_used": "name:data_saver_milestone_promo;comparator:<2;window:90;storage:360", + "session_rate": "==0", + "tracking_only": "true", + "x_milestone_promo_data_savings_in_megabytes": "1024;100" + }, + "enable_features": [ + "IPH_DataSaverMilestonePromo" + ] } ] }
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn index ea0654d4..6e921203e 100644 --- a/third_party/blink/public/BUILD.gn +++ b/third_party/blink/public/BUILD.gn
@@ -654,8 +654,6 @@ visibility_blink = [ "//third_party/blink/renderer/platform:blink_platform_public_deps" ] sources = [ - "platform/modules/background_sync/background_sync.mojom", - "platform/modules/badging/badging.mojom", "platform/modules/bluetooth/web_bluetooth.mojom", "platform/modules/credentialmanager/credential_manager.mojom", "platform/modules/geolocation/geolocation_service.mojom",
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn index 8b37469..322634d2 100644 --- a/third_party/blink/public/mojom/BUILD.gn +++ b/third_party/blink/public/mojom/BUILD.gn
@@ -18,6 +18,8 @@ "associated_interfaces/associated_interfaces.mojom", "autoplay/autoplay.mojom", "background_fetch/background_fetch.mojom", + "background_sync/background_sync.mojom", + "badging/badging.mojom", "blob/blob.mojom", "blob/blob_registry.mojom", "blob/blob_url_store.mojom",
diff --git a/third_party/blink/public/platform/modules/background_sync/OWNERS b/third_party/blink/public/mojom/background_sync/OWNERS similarity index 100% rename from third_party/blink/public/platform/modules/background_sync/OWNERS rename to third_party/blink/public/mojom/background_sync/OWNERS
diff --git a/third_party/blink/public/platform/modules/background_sync/background_sync.mojom b/third_party/blink/public/mojom/background_sync/background_sync.mojom similarity index 100% rename from third_party/blink/public/platform/modules/background_sync/background_sync.mojom rename to third_party/blink/public/mojom/background_sync/background_sync.mojom
diff --git a/third_party/blink/public/platform/modules/badging/OWNERS b/third_party/blink/public/mojom/badging/OWNERS similarity index 100% rename from third_party/blink/public/platform/modules/badging/OWNERS rename to third_party/blink/public/mojom/badging/OWNERS
diff --git a/third_party/blink/public/platform/modules/badging/badging.mojom b/third_party/blink/public/mojom/badging/badging.mojom similarity index 100% rename from third_party/blink/public/platform/modules/badging/badging.mojom rename to third_party/blink/public/mojom/badging/badging.mojom
diff --git a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc index dca2ec8..2de0484 100644 --- a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc +++ b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
@@ -348,6 +348,8 @@ // worker script fetch on the worker thread. // TODO(nhiroki): Currently |address_space| and |origin_trial_tokens| are // not updated after worker script fetch. Update them. + // TODO(crbug.com/937191): Make SharedWorkerGlobalScope use CSP list from + // the response of the main script. auto creation_params = std::make_unique<GlobalScopeCreationParams>( script_request_url_, script_type, OffMainThreadWorkerScriptFetchOption::kEnabled, name_,
diff --git a/third_party/blink/renderer/core/inspector/worker_inspector_controller.cc b/third_party/blink/renderer/core/inspector/worker_inspector_controller.cc index 57a3170a..5225b40 100644 --- a/third_party/blink/renderer/core/inspector/worker_inspector_controller.cc +++ b/third_party/blink/renderer/core/inspector/worker_inspector_controller.cc
@@ -112,7 +112,6 @@ session->Append(MakeGarbageCollected<InspectorLogAgent>( thread_->GetConsoleMessageStorage(), nullptr, session->V8Session())); if (auto* scope = DynamicTo<WorkerGlobalScope>(thread_->GlobalScope())) { - scope->EnsureFetcher(); session->Append(MakeGarbageCollected<InspectorNetworkAgent>( inspected_frames_.Get(), scope, session->V8Session())); session->Append(MakeGarbageCollected<InspectorEmulationAgent>(nullptr));
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc index 44dd8c9..7ed7118c 100644 --- a/third_party/blink/renderer/core/loader/document_loader.cc +++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -1491,6 +1491,19 @@ OriginTrialContext::FromOrCreate(document)->AddFeature( "ForceTouchEventFeatureDetectionForInspector"); } + +#if defined(OS_CHROMEOS) + // Enable Low Latency Canvas for the PDF Annotations feature of the built in + // PDF Viewer extension on Chrome OS. + const url::Origin origin = document->GetSecurityOrigin()->ToUrlOrigin(); + if (origin.scheme() == "chrome-extension" && + origin.DomainIs("mhjfbmdgcfjbbpaeojofohoefgiehjai") && + origin.port() == 0) { + OriginTrialContext::FromOrCreate(document)->AddFeature( + origin_trials::kLowLatencyCanvasTrialName); + } +#endif + OriginTrialContext::AddTokensFromHeader( document, response_.HttpHeaderField(http_names::kOriginTrial)); }
diff --git a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc index c9381a3a..9bf4ece 100644 --- a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc +++ b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc
@@ -221,6 +221,9 @@ if (!frame_->GetDocument()->IsRenderingReady()) return; + if (!anchor_node_) + return; + // If the anchor accepts keyboard focus and fragment scrolling is allowed, // move focus there to aid users relying on keyboard navigation. // If anchorNode is not focusable or fragment scrolling is not allowed,
diff --git a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.h b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.h index 3525231..774aaf76 100644 --- a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.h +++ b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.h
@@ -56,6 +56,9 @@ void Trace(blink::Visitor*) override; private: + FRIEND_TEST_ALL_PREFIXES(ElementFragmentAnchorTest, + AnchorRemovedBeforeBeginFrameCrash); + void ApplyFocusIfNeeded(); WeakMember<Node> anchor_node_;
diff --git a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor_test.cc b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor_test.cc index 965590d..cda7421 100644 --- a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor_test.cc +++ b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor_test.cc
@@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/web/web_script_source.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/core/css/css_style_declaration.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" @@ -18,8 +20,6 @@ namespace blink { -namespace { - using test::RunPendingTasks; class ElementFragmentAnchorTest : public SimTest { @@ -208,6 +208,57 @@ main_resource.Finish(); } -} // namespace +// Ensure that a BeginFrame after the element-to-focus is removed from the +// document doesn't cause a nullptr crash when the fragment anchor element has +// been removed and garbage collected. +TEST_F(ElementFragmentAnchorTest, AnchorRemovedBeforeBeginFrameCrash) { + SimRequest main_resource("https://example.com/test.html#anchor", "text/html"); + SimSubresourceRequest css_resource("https://example.com/sheet.css", + "text/css"); + LoadURL("https://example.com/test.html#anchor"); + + main_resource.Complete(R"HTML( + <!DOCTYPE html> + <link rel="stylesheet" type="text/css" href="sheet.css"> + <div style="height: 1000px;"></div> + <input id="anchor">Bottom of the page</input> + )HTML"); + + // We're still waiting on the stylesheet to load so the load event shouldn't + // yet dispatch and rendering is deferred. This will avoid invoking or + // focusing the fragment when it's first installed. + ASSERT_FALSE(GetDocument().IsRenderingReady()); + ASSERT_FALSE(GetDocument().IsLoadCompleted()); + + ASSERT_TRUE(GetDocument().View()->GetFragmentAnchor()); + ASSERT_TRUE(static_cast<ElementFragmentAnchor*>( + GetDocument().View()->GetFragmentAnchor()) + ->anchor_node_); + + // Remove the fragment anchor from the DOM and perform GC. + GetDocument().getElementById("anchor")->remove(); + v8::Isolate* isolate = ToIsolate(GetDocument().GetFrame()); + isolate->RequestGarbageCollectionForTesting( + v8::Isolate::kFullGarbageCollection); + + // Now that the element has been removed and GC'd, unblock rendering so we can + // produce a frame. + css_resource.Complete(""); + + ASSERT_TRUE(GetDocument().IsRenderingReady()); + + // We should still have a fragment anchor but its node pointer shoulld be + // gone since it's a WeakMember. + ASSERT_TRUE(GetDocument().View()->GetFragmentAnchor()); + ASSERT_FALSE(static_cast<ElementFragmentAnchor*>( + GetDocument().View()->GetFragmentAnchor()) + ->anchor_node_); + + // We'd normally focus the fragment during BeginFrame. Make sure we don't + // crash since it's been GC'd. + Compositor().BeginFrame(); + + // Non-crash is considered a pass. +} } // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/main_thread_scrolling_reasons_test.cc b/third_party/blink/renderer/core/page/scrolling/main_thread_scrolling_reasons_test.cc index 690516f..2b62015d 100644 --- a/third_party/blink/renderer/core/page/scrolling/main_thread_scrolling_reasons_test.cc +++ b/third_party/blink/renderer/core/page/scrolling/main_thread_scrolling_reasons_test.cc
@@ -16,6 +16,8 @@ #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h" #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" +#include "third_party/blink/renderer/core/testing/sim/sim_request.h" +#include "third_party/blink/renderer/core/testing/sim/sim_test.h" #include "third_party/blink/renderer/platform/graphics/graphics_layer.h" #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" #include "third_party/blink/renderer/platform/testing/url_test_helpers.h" @@ -109,52 +111,6 @@ INSTANTIATE_TEST_SUITE_P(All, MainThreadScrollingReasonsTest, testing::Bool()); TEST_P(MainThreadScrollingReasonsTest, - CustomScrollbarShouldTriggerMainThreadScroll) { - GetWebView()->GetSettings()->SetPreferCompositingToLCDTextEnabled(true); - GetWebView()->SetDeviceScaleFactor(2.f); - RegisterMockedHttpURLLoad("custom_scrollbar.html"); - NavigateTo(base_url_ + "custom_scrollbar.html"); - ForceFullCompositingUpdate(); - - Document* document = GetFrame()->GetDocument(); - Element* container = document->getElementById("container"); - Element* content = document->getElementById("content"); - DCHECK_EQ(container->getAttribute(html_names::kClassAttr), - "custom_scrollbar"); - DCHECK(container); - DCHECK(content); - - LayoutObject* layout_object = container->GetLayoutObject(); - ASSERT_TRUE(layout_object->IsBox()); - LayoutBox* box = ToLayoutBox(layout_object); - ASSERT_TRUE(box->UsesCompositedScrolling()); - CompositedLayerMapping* composited_layer_mapping = - box->Layer()->GetCompositedLayerMapping(); - // When Blink generates property trees, the main thread reasons are stored in - // scroll nodes instead of being set on the custom scrollbar layer, so we use - // the scrolling contents layer to access the main thread scrolling reasons. - GraphicsLayer* layer = - RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled() - ? composited_layer_mapping->ScrollingContentsLayer() - : composited_layer_mapping->LayerForVerticalScrollbar(); - ASSERT_TRUE(layer); - EXPECT_TRUE(layer->CcLayer()->GetMainThreadScrollingReasons()); - EXPECT_TRUE(layer->CcLayer()->GetMainThreadScrollingReasons() & - cc::MainThreadScrollingReason::kCustomScrollbarScrolling); - - // remove custom scrollbar class, the scrollbar is expected to scroll on - // impl thread as it is an overlay scrollbar. - container->removeAttribute("class"); - ForceFullCompositingUpdate(); - layer = RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled() - ? composited_layer_mapping->ScrollingContentsLayer() - : composited_layer_mapping->LayerForVerticalScrollbar(); - EXPECT_FALSE(layer->CcLayer()->GetMainThreadScrollingReasons()); - EXPECT_FALSE(layer->CcLayer()->GetMainThreadScrollingReasons() & - cc::MainThreadScrollingReason::kCustomScrollbarScrolling); -} - -TEST_P(MainThreadScrollingReasonsTest, BackgroundAttachmentFixedShouldTriggerMainThreadScroll) { // This test needs the |FastMobileScrolling| feature to be disabled // although it is stable on Android.
diff --git a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc index 311c29e..999832fa 100644 --- a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc +++ b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc
@@ -438,16 +438,10 @@ : *scrollable_area->VerticalScrollbar(); if (scrollbar.IsCustomScrollbar()) { DetachScrollbarLayer(scrollbar_graphics_layer); - scrollbar_graphics_layer->CcLayer()->AddMainThreadScrollingReasons( - cc::MainThreadScrollingReason::kCustomScrollbarScrolling); scrollbar_graphics_layer->CcLayer()->SetIsScrollbar(true); return; } - // Invalidate custom scrollbar scrolling reason in case a custom - // scrollbar becomes a non-custom one. - scrollbar_graphics_layer->CcLayer()->ClearMainThreadScrollingReasons( - cc::MainThreadScrollingReason::kCustomScrollbarScrolling); ScrollbarLayerGroup* scrollbar_layer_group = GetScrollbarLayerGroup(scrollable_area, orientation); if (!scrollbar_layer_group) {
diff --git a/third_party/blink/renderer/core/paint/image_element_timing.cc b/third_party/blink/renderer/core/paint/image_element_timing.cc index d5ddb61..f019d61 100644 --- a/third_party/blink/renderer/core/paint/image_element_timing.cc +++ b/third_party/blink/renderer/core/paint/image_element_timing.cc
@@ -48,31 +48,64 @@ const ImageResourceContent* cached_image, const PaintLayer* painting_layer) { auto result = images_notified_.insert(layout_object); - if (!result.is_new_entry) + if (!result.is_new_entry || !cached_image) return; - LocalFrame* frame = GetSupplementable()->GetFrame(); DCHECK(frame == layout_object->GetDocument().GetFrame()); - if (!frame || !cached_image) + if (!frame) return; - // Skip the computations below if the element is not same origin. + IntRect intersection_rect = + ComputeIntersectionRect(frame, layout_object, painting_layer); + const Element* element = ToElement(layout_object->GetNode()); + const AtomicString attr = + element->FastGetAttribute(html_names::kElementtimingAttr); + if (!ShouldReportElement(attr, intersection_rect)) + return; + DCHECK(GetSupplementable()->document() == &layout_object->GetDocument()); DCHECK(layout_object->GetDocument().GetSecurityOrigin()); if (!Performance::PassesTimingAllowCheck( cached_image->GetResponse(), *layout_object->GetDocument().GetSecurityOrigin(), - &layout_object->GetDocument())) + &layout_object->GetDocument())) { + WindowPerformance* performance = + DOMWindowPerformance::performance(*GetSupplementable()); + if (performance && + (performance->HasObserverFor(PerformanceEntry::kElement) || + performance->ShouldBufferEntries())) { + // Create an entry with a |startTime| of 0. + performance->AddElementTiming( + AtomicString(cached_image->Url().GetString()), intersection_rect, + TimeTicks(), cached_image->LoadResponseEnd(), attr); + } return; + } - // Compute the viewport rect. WebLayerTreeView* layerTreeView = frame->GetChromeClient().GetWebLayerTreeView(frame); if (!layerTreeView) return; - IntRect viewport = frame->View()->LayoutViewport()->VisibleContentRect(); + element_timings_.emplace_back(AtomicString(cached_image->Url().GetString()), + intersection_rect, + cached_image->LoadResponseEnd(), attr); + // Only queue a swap promise when |element_timings_| was empty. All of the + // records in |element_timings_| will be processed when the promise succeeds + // or fails, and at that time the vector is cleared. + if (element_timings_.size() == 1) { + layerTreeView->NotifySwapTime(ConvertToBaseCallback( + CrossThreadBind(&ImageElementTiming::ReportImagePaintSwapTime, + WrapCrossThreadWeakPersistent(this)))); + } +} + +IntRect ImageElementTiming::ComputeIntersectionRect( + const LocalFrame* frame, + const LayoutObject* layout_object, + const PaintLayer* painting_layer) { + viewport_ = frame->View()->LayoutViewport()->VisibleContentRect(); // Compute the visible part of the image rect. LayoutRect image_visual_rect = layout_object->FirstFragment().VisualRect(); @@ -89,30 +122,18 @@ GeometryMapper::SourceToDestinationRect(local_transform, ancestor_transform, new_visual_rect_abs); IntRect visible_new_visual_rect = RoundedIntRect(new_visual_rect_abs); - visible_new_visual_rect.Intersect(viewport); + visible_new_visual_rect.Intersect(viewport_); + return visible_new_visual_rect; +} - const Element* element = ToElement(layout_object->GetNode()); - const AtomicString attr = - element->FastGetAttribute(html_names::kElementtimingAttr); +bool ImageElementTiming::ShouldReportElement( + const AtomicString& element_timing, + const IntRect& intersection_rect) const { // Do not create an entry if 'elementtiming' is not present or the image is // below a certain size threshold. - if (attr.IsEmpty() && - visible_new_visual_rect.Size().Area() <= - viewport.Size().Area() * kImageTimingSizeThreshold) { - return; - } - - element_timings_.emplace_back(AtomicString(cached_image->Url().GetString()), - visible_new_visual_rect, - cached_image->LoadResponseEnd(), attr); - // Only queue a swap promise when |element_timings_| was empty. All of the - // records in |element_timings_| will be processed when the promise succeeds - // or fails, and at that time the vector is cleared. - if (element_timings_.size() == 1) { - layerTreeView->NotifySwapTime(ConvertToBaseCallback( - CrossThreadBind(&ImageElementTiming::ReportImagePaintSwapTime, - WrapCrossThreadWeakPersistent(this)))); - } + return !element_timing.IsEmpty() || + intersection_rect.Size().Area() > + viewport_.Size().Area() * kImageTimingSizeThreshold; } void ImageElementTiming::ReportImagePaintSwapTime(WebLayerTreeView::SwapResult,
diff --git a/third_party/blink/renderer/core/paint/image_element_timing.h b/third_party/blink/renderer/core/paint/image_element_timing.h index f009389..db9df6d 100644 --- a/third_party/blink/renderer/core/paint/image_element_timing.h +++ b/third_party/blink/renderer/core/paint/image_element_timing.h
@@ -46,6 +46,16 @@ private: friend class ImageElementTimingTest; + // Computes the intersection rect and stores the viewport rect in |viewport_|. + IntRect ComputeIntersectionRect(const LocalFrame*, + const LayoutObject*, + const PaintLayer*); + // Checks if the element must be reported, given its elementtiming attribute + // and its intersection rect. Assumes that |viewport_| has been calculated, + // i.e. that ComputeIntersectionRect() has been called. + bool ShouldReportElement(const AtomicString& element_timing, + const IntRect&) const; + // Callback for the swap promise. Reports paint timestamps. void ReportImagePaintSwapTime(WebLayerTreeView::SwapResult, base::TimeTicks timestamp); @@ -71,6 +81,8 @@ WTF::Vector<ElementTimingInfo> element_timings_; // Hashmap of LayoutObjects for which paint has already been notified. WTF::HashSet<const LayoutObject*> images_notified_; + // Current viewport rect, updated every time an intersection rect is computed. + IntRect viewport_; DISALLOW_COPY_AND_ASSIGN(ImageElementTiming); };
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc index 23cfc96..f2976ad2 100644 --- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc +++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -1617,16 +1617,6 @@ if (!object.IsBox()) return reasons; - if (auto* scrollable_area = ToLayoutBox(object).GetScrollableArea()) { - if (scrollable_area->HorizontalScrollbar() && - scrollable_area->HorizontalScrollbar()->IsCustomScrollbar()) { - reasons |= cc::MainThreadScrollingReason::kCustomScrollbarScrolling; - } else if (scrollable_area->VerticalScrollbar() && - scrollable_area->VerticalScrollbar()->IsCustomScrollbar()) { - reasons |= cc::MainThreadScrollingReason::kCustomScrollbarScrolling; - } - } - if (object.IsLayoutView()) { if (object.GetFrameView()->HasBackgroundAttachmentFixedObjects()) { reasons |=
diff --git a/third_party/blink/renderer/core/workers/global_scope_creation_params.cc b/third_party/blink/renderer/core/workers/global_scope_creation_params.cc index 138fb03..b899008 100644 --- a/third_party/blink/renderer/core/workers/global_scope_creation_params.cc +++ b/third_party/blink/renderer/core/workers/global_scope_creation_params.cc
@@ -34,7 +34,8 @@ interface_provider_info, BeginFrameProviderParams begin_frame_provider_params, const FeaturePolicy* parent_feature_policy, - base::UnguessableToken agent_cluster_id) + base::UnguessableToken agent_cluster_id, + GlobalScopeCSPApplyMode csp_apply_mode) : script_url(script_url.Copy()), script_type(script_type), off_main_thread_fetch_option(off_main_thread_fetch_option), @@ -59,17 +60,10 @@ parent_feature_policy, ParsedFeaturePolicy() /* container_policy */, starter_origin->ToUrlOrigin())), - agent_cluster_id(agent_cluster_id) { + agent_cluster_id(agent_cluster_id), + csp_apply_mode(csp_apply_mode) { switch (this->script_type) { case mojom::ScriptType::kClassic: - if (this->off_main_thread_fetch_option == - OffMainThreadWorkerScriptFetchOption::kEnabled) { - DCHECK(base::FeatureList::IsEnabled( - features::kOffMainThreadDedicatedWorkerScriptFetch) || - base::FeatureList::IsEnabled( - features::kOffMainThreadServiceWorkerScriptFetch) || - features::IsOffMainThreadSharedWorkerScriptFetchEnabled()); - } break; case mojom::ScriptType::kModule: DCHECK_EQ(this->off_main_thread_fetch_option,
diff --git a/third_party/blink/renderer/core/workers/global_scope_creation_params.h b/third_party/blink/renderer/core/workers/global_scope_creation_params.h index 04e030f..a147eae 100644 --- a/third_party/blink/renderer/core/workers/global_scope_creation_params.h +++ b/third_party/blink/renderer/core/workers/global_scope_creation_params.h
@@ -33,6 +33,16 @@ // fetch is enabled for all worker types (https://crbug.com/835717). enum class OffMainThreadWorkerScriptFetchOption { kDisabled, kEnabled }; +// Indicates where the CSP list comes from. +// https://w3c.github.io/webappsec-csp/#initialize-global-object-csp +enum class GlobalScopeCSPApplyMode { + // For dedicated workers, worklets, on-the-main-thread service workers, and + // on-the-main-thread shared workers. + kUseCreationParamsCSP, + // For off-the-main-thread service/shared workers. + kUseResponseCSP, +}; + // GlobalScopeCreationParams contains parameters for initializing // WorkerGlobalScope or WorkletGlobalScope. struct CORE_EXPORT GlobalScopeCreationParams final { @@ -61,7 +71,9 @@ service_manager::mojom::blink::InterfaceProviderPtrInfo = {}, BeginFrameProviderParams begin_frame_provider_params = {}, const FeaturePolicy* parent_feature_policy = nullptr, - base::UnguessableToken agent_cluster_id = {}); + base::UnguessableToken agent_cluster_id = {}, + GlobalScopeCSPApplyMode csp_apply_mode = + GlobalScopeCSPApplyMode::kUseCreationParamsCSP); ~GlobalScopeCreationParams() = default; @@ -149,6 +161,8 @@ // See https://tc39.github.io/ecma262/#sec-agent-clusters base::UnguessableToken agent_cluster_id; + GlobalScopeCSPApplyMode csp_apply_mode; + DISALLOW_COPY_AND_ASSIGN(GlobalScopeCreationParams); };
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.cc b/third_party/blink/renderer/core/workers/worker_global_scope.cc index 1a8721d..c84e9bd0 100644 --- a/third_party/blink/renderer/core/workers/worker_global_scope.cc +++ b/third_party/blink/renderer/core/workers/worker_global_scope.cc
@@ -472,7 +472,18 @@ // Step 12.6. "Execute the Initialize a global object's CSP list algorithm // on worker global scope and response. [CSP]" - // This is done in the constructor of WorkerGlobalScope. + // When |csp_apply_mode_| is kUseCreationParams, this is done in the + // constructor. + if (csp_apply_mode_ == GlobalScopeCSPApplyMode::kUseResponseCSP) { + if (classic_script_loader->GetContentSecurityPolicy()) { + InitContentSecurityPolicyFromVector( + classic_script_loader->GetContentSecurityPolicy()->Headers()); + } else { + // Initialize CSP with an empty list. + InitContentSecurityPolicyFromVector(Vector<CSPHeaderAndType>()); + } + BindContentSecurityPolicyToExecutionContext(); + } // Step 12.7. "Asynchronously complete the perform the fetch steps with // response." @@ -545,7 +556,8 @@ creation_params->begin_frame_provider_params)), agent_cluster_id_(creation_params->agent_cluster_id.is_empty() ? base::UnguessableToken::Create() - : creation_params->agent_cluster_id) { + : creation_params->agent_cluster_id), + csp_apply_mode_(creation_params->csp_apply_mode) { InstanceCounters::IncrementCounter( InstanceCounters::kWorkerGlobalScopeCounter); scoped_refptr<SecurityOrigin> security_origin = @@ -562,9 +574,14 @@ https_state_ = CalculateHttpsState(GetSecurityOrigin(), creation_params->starter_https_state); - InitContentSecurityPolicyFromVector( + SetOutsideContentSecurityPolicyHeaders( creation_params->content_security_policy_parsed_headers); - BindContentSecurityPolicyToExecutionContext(); + if (csp_apply_mode_ == GlobalScopeCSPApplyMode::kUseCreationParamsCSP) { + InitContentSecurityPolicyFromVector( + creation_params->content_security_policy_parsed_headers); + BindContentSecurityPolicyToExecutionContext(); + } + SetWorkerSettings(std::move(creation_params->worker_settings)); // Set the URL and referrer policy here for workers whose script is fetched
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.h b/third_party/blink/renderer/core/workers/worker_global_scope.h index c5777272f..86885bf 100644 --- a/third_party/blink/renderer/core/workers/worker_global_scope.h +++ b/third_party/blink/renderer/core/workers/worker_global_scope.h
@@ -40,6 +40,7 @@ #include "third_party/blink/renderer/core/frame/dom_timer_coordinator.h" #include "third_party/blink/renderer/core/messaging/blink_transferable_message.h" #include "third_party/blink/renderer/core/script/script.h" +#include "third_party/blink/renderer/core/workers/global_scope_creation_params.h" #include "third_party/blink/renderer/core/workers/worker_animation_frame_provider.h" #include "third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h" #include "third_party/blink/renderer/core/workers/worker_settings.h" @@ -65,7 +66,6 @@ class WorkerLocation; class WorkerNavigator; class WorkerThread; -struct GlobalScopeCreationParams; class CORE_EXPORT WorkerGlobalScope : public WorkerOrWorkletGlobalScope, @@ -200,6 +200,8 @@ mojom::ScriptType GetScriptType() const { return script_type_; } + GlobalScopeCSPApplyMode GetCSPApplyMode() const { return csp_apply_mode_; } + private: void SetWorkerSettings(std::unique_ptr<WorkerSettings>); @@ -248,6 +250,8 @@ const base::UnguessableToken agent_cluster_id_; HttpsState https_state_; + + GlobalScopeCSPApplyMode csp_apply_mode_; }; template <>
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc index 87b5d91..11259467 100644 --- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc +++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
@@ -375,9 +375,13 @@ return GetThread()->GetTaskRunner(type); } -void WorkerOrWorkletGlobalScope::InitContentSecurityPolicyFromVector( +void WorkerOrWorkletGlobalScope::SetOutsideContentSecurityPolicyHeaders( const Vector<CSPHeaderAndType>& headers) { outside_content_security_policy_parsed_headers_ = headers; +} + +void WorkerOrWorkletGlobalScope::InitContentSecurityPolicyFromVector( + const Vector<CSPHeaderAndType>& headers) { if (!GetContentSecurityPolicy()) { ContentSecurityPolicy* csp = ContentSecurityPolicy::Create(); SetContentSecurityPolicy(csp);
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h index 5c77feb..4ee3fc0 100644 --- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h +++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
@@ -129,8 +129,12 @@ scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType) override; protected: - void InitContentSecurityPolicyFromVector( - const Vector<CSPHeaderAndType>& headers); + // Sets outside's CSP used for off-main-thread top-level worker script + // fetch. + void SetOutsideContentSecurityPolicyHeaders(const Vector<CSPHeaderAndType>&); + + // Initializes inside's CSP used for subresource fetch etc. + void InitContentSecurityPolicyFromVector(const Vector<CSPHeaderAndType>&); virtual void BindContentSecurityPolicyToExecutionContext(); void FetchModuleScript(const KURL& module_url_record,
diff --git a/third_party/blink/renderer/core/workers/worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worklet_global_scope.cc index 0dc2bd2..d201631b 100644 --- a/third_party/blink/renderer/core/workers/worklet_global_scope.cc +++ b/third_party/blink/renderer/core/workers/worklet_global_scope.cc
@@ -93,6 +93,9 @@ // Step 5: "Let inheritedReferrerPolicy be outsideSettings's referrer policy." SetReferrerPolicy(creation_params->referrer_policy); + SetOutsideContentSecurityPolicyHeaders( + creation_params->content_security_policy_parsed_headers); + // https://drafts.css-houdini.org/worklets/#creating-a-workletglobalscope // Step 6: "Invoke the initialize a global object's CSP list algorithm given // workletGlobalScope."
diff --git a/third_party/blink/renderer/devtools/BUILD.gn b/third_party/blink/renderer/devtools/BUILD.gn index 1b7f2fe..98860d8 100644 --- a/third_party/blink/renderer/devtools/BUILD.gn +++ b/third_party/blink/renderer/devtools/BUILD.gn
@@ -466,6 +466,7 @@ "front_end/perf_ui/FlameChart.js", "front_end/perf_ui/GCActionDelegate.js", "front_end/perf_ui/LineLevelProfile.js", + "front_end/perf_ui/LiveHeapProfile.js", "front_end/perf_ui/NetworkPriorities.js", "front_end/perf_ui/OverviewGrid.js", "front_end/perf_ui/PieChart.js",
diff --git a/third_party/blink/renderer/devtools/front_end/help/Help.js b/third_party/blink/renderer/devtools/front_end/help/Help.js index 38e2ca0..b4846e71 100644 --- a/third_party/blink/renderer/devtools/front_end/help/Help.js +++ b/third_party/blink/renderer/devtools/front_end/help/Help.js
@@ -60,7 +60,8 @@ * @override */ run() { - Help._showReleaseNoteIfNeeded(); + if (!Host.isUnderTest()) + Help._showReleaseNoteIfNeeded(); } };
diff --git a/third_party/blink/renderer/devtools/front_end/main/Main.js b/third_party/blink/renderer/devtools/front_end/main/Main.js index 3d3214e..53e24c0a 100644 --- a/third_party/blink/renderer/devtools/front_end/main/Main.js +++ b/third_party/blink/renderer/devtools/front_end/main/Main.js
@@ -106,15 +106,16 @@ _initializeExperiments() { // Keep this sorted alphabetically: both keys and values. Runtime.experiments.register('applyCustomStylesheet', 'Allow custom UI themes'); + Runtime.experiments.register('sourcesPrettyPrint', 'Automatically pretty print in the Sources Panel'); Runtime.experiments.register('backgroundServices', 'Background web platform feature events', true); Runtime.experiments.register('blackboxJSFramesOnTimeline', 'Blackbox JavaScript frames on Timeline', true); Runtime.experiments.register('emptySourceMapAutoStepping', 'Empty sourcemap auto-stepping'); Runtime.experiments.register('inputEventsOnTimelineOverview', 'Input events on Timeline overview', true); + Runtime.experiments.register('liveHeapProfile', 'Live heap profile', true); Runtime.experiments.register('nativeHeapProfiler', 'Native memory sampling heap profiler', true); Runtime.experiments.register('protocolMonitor', 'Protocol Monitor'); Runtime.experiments.register('samplingHeapProfilerTimeline', 'Sampling heap profiler timeline', true); Runtime.experiments.register('sourceDiff', 'Source diff'); - Runtime.experiments.register('sourcesPrettyPrint', 'Automatically pretty print in the Sources Panel'); Runtime.experiments.register('splitInDrawer', 'Split in drawer', true); Runtime.experiments.register('terminalInDrawer', 'Terminal in drawer', true); @@ -128,6 +129,9 @@ Runtime.experiments.cleanUpStaleExperiments(); Runtime.experiments.setDefaultExperiments([]); + + if (Host.isUnderTest() && Runtime.queryParam('test').includes('live-line-level-heap-profile.js')) + Runtime.experiments.enableForTest('liveHeapProfile'); } /** @@ -261,10 +265,8 @@ Main.Main.time('Main._lateInitialization'); this._registerShortcuts(); Extensions.extensionServer.initializeExtensions(); - if (!Host.isUnderTest()) { - for (const extension of self.runtime.extensions('late-initialization')) - extension.instance().then(instance => (/** @type {!Common.Runnable} */ (instance)).run()); - } + for (const extension of self.runtime.extensions('late-initialization')) + extension.instance().then(instance => (/** @type {!Common.Runnable} */ (instance)).run()); Main.Main.timeEnd('Main._lateInitialization'); }
diff --git a/third_party/blink/renderer/devtools/front_end/perf_ui/LineLevelProfile.js b/third_party/blink/renderer/devtools/front_end/perf_ui/LineLevelProfile.js index 4541b619..c4ef519 100644 --- a/third_party/blink/renderer/devtools/front_end/perf_ui/LineLevelProfile.js +++ b/third_party/blink/renderer/devtools/front_end/perf_ui/LineLevelProfile.js
@@ -254,7 +254,7 @@ _createElement(type, value) { const element = createElementWithClass('div', 'text-editor-line-marker-text'); if (type === 'performance') { - const intensity = Number.constrain(Math.log10(1 + 2 * value) / 5, 0.02, 1); + const intensity = Number.constrain(Math.log10(1 + 10 * value) / 5, 0.02, 1); element.textContent = Common.UIString('%.1f', value); element.style.backgroundColor = `hsla(44, 100%, 50%, ${intensity.toFixed(3)})`; element.createChild('span', 'line-marker-units').textContent = ls`ms`;
diff --git a/third_party/blink/renderer/devtools/front_end/perf_ui/LiveHeapProfile.js b/third_party/blink/renderer/devtools/front_end/perf_ui/LiveHeapProfile.js new file mode 100644 index 0000000..2e2986d --- /dev/null +++ b/third_party/blink/renderer/devtools/front_end/perf_ui/LiveHeapProfile.js
@@ -0,0 +1,59 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @implements {Common.Runnable} + * @implements {SDK.SDKModelObserver<!SDK.HeapProfilerModel>} + */ +PerfUI.LiveHeapProfile = class { + /** + * @override + */ + run() { + SDK.targetManager.observeModels(SDK.HeapProfilerModel, this); + requestIdleCallback(() => this.onUpdateProfiles(), {timeout: 100}); + PerfUI.LiveHeapProfile.hasStartedForTest(true); + } + + /** + * @param {boolean=} started + * @return {!Promise} + */ + static hasStartedForTest(started) { + if (!PerfUI.LiveHeapProfile._startedPromise) + PerfUI.LiveHeapProfile._startedPromise = new Promise(r => PerfUI.LiveHeapProfile._startedCallback = r); + if (started) + PerfUI.LiveHeapProfile._startedCallback(); + return PerfUI.LiveHeapProfile._startedPromise; + } + + /** + * @override + * @param {!SDK.HeapProfilerModel} model + */ + modelAdded(model) { + model.startSampling(1024); + } + + /** + * @override + * @param {!SDK.HeapProfilerModel} model + */ + modelRemoved(model) { + model.stopSampling(); + } + + async onUpdateProfiles() { + const models = SDK.targetManager.models(SDK.HeapProfilerModel); + const profiles = await Promise.all(models.map(model => model.getSamplingProfile())); + const lineLevelProfile = PerfUI.LineLevelProfile.Memory.instance(); + lineLevelProfile.reset(); + for (let i = 0; i < profiles.length; ++i) { + if (profiles[i]) + lineLevelProfile.appendHeapProfile(profiles[i], models[i].target()); + } + const updateInterval = Host.isUnderTest() ? 10 : 5000; + setTimeout(() => requestIdleCallback(() => this.onUpdateProfiles(), {timeout: 100}), updateInterval); + } +};
diff --git a/third_party/blink/renderer/devtools/front_end/perf_ui/module.json b/third_party/blink/renderer/devtools/front_end/perf_ui/module.json index ce8d46c9..476922e1 100644 --- a/third_party/blink/renderer/devtools/front_end/perf_ui/module.json +++ b/third_party/blink/renderer/devtools/front_end/perf_ui/module.json
@@ -1,6 +1,11 @@ { "extensions": [ { + "type": "late-initialization", + "className": "PerfUI.LiveHeapProfile", + "experiment": "liveHeapProfile" + }, + { "type": "@SourceFrame.LineDecorator", "className": "PerfUI.LineLevelProfile.LineDecorator", "decoratorType": "performance" @@ -51,6 +56,7 @@ "FlameChart.js", "GCActionDelegate.js", "LineLevelProfile.js", + "LiveHeapProfile.js", "NetworkPriorities.js", "OverviewGrid.js", "PieChart.js",
diff --git a/third_party/blink/renderer/devtools/front_end/test_runner/TestRunner.js b/third_party/blink/renderer/devtools/front_end/test_runner/TestRunner.js index 8848d38a..32cb07e 100644 --- a/third_party/blink/renderer/devtools/front_end/test_runner/TestRunner.js +++ b/third_party/blink/renderer/devtools/front_end/test_runner/TestRunner.js
@@ -1210,6 +1210,7 @@ */ TestRunner.loadedModules = function() { return self.runtime._modules.filter(module => module._loadedForTest) + .filter(module => module.name() !== 'help') .filter(module => module.name().indexOf('test_runner') === -1); };
diff --git a/third_party/blink/renderer/devtools/front_end/text_editor/cmdevtools.css b/third_party/blink/renderer/devtools/front_end/text_editor/cmdevtools.css index b68d407..22df9a2c9 100644 --- a/third_party/blink/renderer/devtools/front_end/text_editor/cmdevtools.css +++ b/third_party/blink/renderer/devtools/front_end/text_editor/cmdevtools.css
@@ -9,12 +9,7 @@ overflow: visible !important; } -.CodeMirror-gutter-performance { - width: 60px; - background-color: white; - margin-left: 3px; -} - +.CodeMirror-gutter-performance, .CodeMirror-gutter-memory { width: 60px; background-color: white; @@ -437,7 +432,8 @@ } .CodeMirror .text-editor-line-marker-text span.line-marker-units { - color: #999; + color: #555; + font-size: 75%; margin-left: 3px; }
diff --git a/third_party/blink/renderer/modules/background_sync/sync_manager.h b/third_party/blink/renderer/modules/background_sync/sync_manager.h index 41ad808b..33eedf61 100644 --- a/third_party/blink/renderer/modules/background_sync/sync_manager.h +++ b/third_party/blink/renderer/modules/background_sync/sync_manager.h
@@ -5,7 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_BACKGROUND_SYNC_SYNC_MANAGER_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_BACKGROUND_SYNC_SYNC_MANAGER_H_ -#include "third_party/blink/public/platform/modules/background_sync/background_sync.mojom-blink.h" +#include "third_party/blink/public/mojom/background_sync/background_sync.mojom-blink.h" #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" #include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
diff --git a/third_party/blink/renderer/modules/badging/badge.h b/third_party/blink/renderer/modules/badging/badge.h index 1c53cc3..3c6ff0e 100644 --- a/third_party/blink/renderer/modules/badging/badge.h +++ b/third_party/blink/renderer/modules/badging/badge.h
@@ -5,7 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_BADGING_BADGE_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_BADGING_BADGE_H_ -#include "third_party/blink/public/platform/modules/badging/badging.mojom-blink.h" +#include "third_party/blink/public/mojom/badging/badging.mojom-blink.h" #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" #include "third_party/blink/renderer/platform/supplementable.h"
diff --git a/third_party/blink/renderer/modules/cache_storage/inspector_cache_storage_agent.cc b/third_party/blink/renderer/modules/cache_storage/inspector_cache_storage_agent.cc index eb70afd..188257b 100644 --- a/third_party/blink/renderer/modules/cache_storage/inspector_cache_storage_agent.cc +++ b/third_party/blink/renderer/modules/cache_storage/inspector_cache_storage_agent.cc
@@ -32,6 +32,7 @@ #include "third_party/blink/renderer/platform/shared_buffer.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h" #include "third_party/blink/renderer/platform/weborigin/security_origin.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/functional.h" #include "third_party/blink/renderer/platform/wtf/ref_counted.h" #include "third_party/blink/renderer/platform/wtf/time.h" @@ -368,6 +369,8 @@ }; class GetCacheKeysForRequestData { + USING_FAST_MALLOC(GetCacheKeysForRequestData); + public: GetCacheKeysForRequestData( const DataRequestParams& params,
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_path.h b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_path.h index e2ccb7d..357ad28 100644 --- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_path.h +++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_path.h
@@ -33,12 +33,15 @@ #include "third_party/blink/renderer/modules/modules_export.h" #include "third_party/blink/renderer/platform/graphics/path.h" #include "third_party/blink/renderer/platform/transforms/affine_transform.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" namespace blink { class ExceptionState; class MODULES_EXPORT CanvasPath { + DISALLOW_NEW(); + public: virtual ~CanvasPath() = default;
diff --git a/third_party/blink/renderer/modules/crypto/normalize_algorithm.cc b/third_party/blink/renderer/modules/crypto/normalize_algorithm.cc index 94db440..47f23db 100644 --- a/third_party/blink/renderer/modules/crypto/normalize_algorithm.cc +++ b/third_party/blink/renderer/modules/crypto/normalize_algorithm.cc
@@ -45,6 +45,7 @@ #include "third_party/blink/renderer/core/typed_arrays/dom_array_piece.h" #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h" #include "third_party/blink/renderer/modules/crypto/crypto_utilities.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/math_extras.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" #include "third_party/blink/renderer/platform/wtf/vector.h" @@ -220,6 +221,8 @@ // parsing of the algorithm dictionary can be recursive and it is difficult to // tell what went wrong from a failure alone. class ErrorContext { + STACK_ALLOCATED(); + public: void Add(const char* message) { messages_.push_back(message); }
diff --git a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc index 3cdea92b..0f84d908 100644 --- a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc +++ b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
@@ -405,9 +405,16 @@ String source_code; std::unique_ptr<Vector<uint8_t>> cached_meta_data; + bool is_script_installed = installed_scripts_manager_ && + installed_scripts_manager_->IsScriptInstalled( + worker_start_data_.script_url); + + // Loading the script from InstalledScriptsManager is considered as + // off-the-main-thread script fetch. const OffMainThreadWorkerScriptFetchOption off_main_thread_fetch_option = (base::FeatureList::IsEnabled( features::kOffMainThreadServiceWorkerScriptFetch) || + is_script_installed || worker_start_data_.script_type == mojom::ScriptType::kModule) ? OffMainThreadWorkerScriptFetchOption::kEnabled : OffMainThreadWorkerScriptFetchOption::kDisabled; @@ -462,9 +469,14 @@ global_scope_creation_params->v8_cache_options = kV8CacheOptionsFullCodeWithoutHeatCheck; - bool is_script_installed = installed_scripts_manager_ && - installed_scripts_manager_->IsScriptInstalled( - worker_start_data_.script_url); + // When the script is fetched on the worker thread, CSP will come from the + // response of the script. Otherwise, CSP is passed as a parameter of + // GlobalScopeCreationParams. + global_scope_creation_params->csp_apply_mode = + off_main_thread_fetch_option == + OffMainThreadWorkerScriptFetchOption::kEnabled + ? GlobalScopeCSPApplyMode::kUseResponseCSP + : GlobalScopeCSPApplyMode::kUseCreationParamsCSP; worker_thread_ = std::make_unique<ServiceWorkerThread>( ServiceWorkerGlobalScopeProxy::Create(*this, *worker_context_client_),
diff --git a/third_party/blink/renderer/modules/idle/idle_manager.cc b/third_party/blink/renderer/modules/idle/idle_manager.cc index f3edf0ac..8941581 100644 --- a/third_party/blink/renderer/modules/idle/idle_manager.cc +++ b/third_party/blink/renderer/modules/idle/idle_manager.cc
@@ -86,7 +86,6 @@ } void IdleManager::OnIdleManagerConnectionError() { - // TODO(goto): write a unittest to cover this. for (const auto& request : requests_) { request->Reject(DOMException::Create(DOMExceptionCode::kNotSupportedError, "Idle detection not available"));
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_value_wrapping_test.cc b/third_party/blink/renderer/modules/indexeddb/idb_value_wrapping_test.cc index f06e7ce3..46fe6cc 100644 --- a/third_party/blink/renderer/modules/indexeddb/idb_value_wrapping_test.cc +++ b/third_party/blink/renderer/modules/indexeddb/idb_value_wrapping_test.cc
@@ -15,6 +15,7 @@ #include "third_party/blink/renderer/modules/indexeddb/idb_key.h" #include "third_party/blink/renderer/modules/indexeddb/idb_key_path.h" #include "third_party/blink/renderer/modules/indexeddb/idb_value.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/vector.h" #include "v8/include/v8.h" @@ -183,6 +184,8 @@ // Friend class of IDBValueUnwrapper with access to its internals. class IDBValueUnwrapperReadTestHelper { + STACK_ALLOCATED(); + public: void ReadVarInt(const char* start, uint32_t buffer_size) { IDBValueUnwrapper unwrapper;
diff --git a/third_party/blink/renderer/modules/payments/payment_request_details_test.cc b/third_party/blink/renderer/modules/payments/payment_request_details_test.cc index 3c0f3245..074b5c8 100644 --- a/third_party/blink/renderer/modules/payments/payment_request_details_test.cc +++ b/third_party/blink/renderer/modules/payments/payment_request_details_test.cc
@@ -14,11 +14,14 @@ #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/weborigin/security_origin.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" namespace blink { namespace { class DetailsTestCase { + USING_FAST_MALLOC(DetailsTestCase); + public: DetailsTestCase( PaymentTestDetailToChange detail,
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter.h b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter.h index 5920612..bcfbcca 100644 --- a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter.h +++ b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter.h
@@ -5,6 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_ICE_TRANSPORT_ADAPTER_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_ICE_TRANSPORT_ADAPTER_H_ +#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/webrtc/p2p/base/p2p_transport_channel.h" namespace blink { @@ -31,6 +32,8 @@ // The ICE Agent is immediately active once this object has been constructed. It // can be stopped by deleting the IceTransportAdapter. class IceTransportAdapter { + USING_FAST_MALLOC(IceTransportAdapter); + public: // Delegate to receive callbacks from the IceTransportAdapter. The Delegate // must outlive the IceTransportAdapter.
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h index 2aad9367..72761c89 100644 --- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h +++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h
@@ -7,6 +7,7 @@ #include "base/logging.h" #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_stats.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/webrtc/rtc_base/ssl_fingerprint.h" namespace blink { @@ -22,6 +23,8 @@ // // This object should be run entirely on the webrtc worker thread. class P2PQuicTransport { + USING_FAST_MALLOC(P2PQuicTransport); + public: // A config used when starting the QUIC handshake. struct StartConfig final {
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_container_test.cc b/third_party/blink/renderer/modules/service_worker/service_worker_container_test.cc index f2838ed..57d91f60 100644 --- a/third_party/blink/renderer/modules/service_worker/service_worker_container_test.cc +++ b/third_party/blink/renderer/modules/service_worker/service_worker_container_test.cc
@@ -26,6 +26,7 @@ #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h" #include "third_party/blink/renderer/platform/weborigin/security_origin.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "v8/include/v8.h" @@ -276,6 +277,8 @@ } class StubWebServiceWorkerProvider { + DISALLOW_NEW(); + public: StubWebServiceWorkerProvider() : register_call_count_(0),
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc index f7e2c95..a963e98 100644 --- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc +++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
@@ -33,10 +33,8 @@ #include <memory> #include <utility> -#include "base/feature_list.h" #include "base/memory/ptr_util.h" #include "base/numerics/safe_conversions.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_url.h" #include "third_party/blink/renderer/bindings/core/v8/callback_promise_adapter.h" @@ -331,13 +329,13 @@ return; } - if (base::FeatureList::IsEnabled( - features::kOffMainThreadServiceWorkerScriptFetch)) { - // WorkerGlobalScope sets the URL in DidImportClassicScript() when - // off-the-main-thread fetch is enabled. Since we bypass calling - // DidImportClassicScript(), set the URL here. - InitializeURL(script_url); - } + // WorkerGlobalScope sets the response URL, referrer policy and CSP list in + // DidImportClassicScript(). Since we bypass calling + // DidImportClassicScript(), set them here. + + DCHECK_EQ(GlobalScopeCSPApplyMode::kUseResponseCSP, GetCSPApplyMode()); + + InitializeURL(script_url); DCHECK(source_code.IsEmpty()); DCHECK(!cached_meta_data);
diff --git a/third_party/blink/renderer/modules/webmidi/midi_output.cc b/third_party/blink/renderer/modules/webmidi/midi_output.cc index 01a88da..bb08257 100644 --- a/third_party/blink/renderer/modules/webmidi/midi_output.cc +++ b/third_party/blink/renderer/modules/webmidi/midi_output.cc
@@ -41,6 +41,7 @@ #include "third_party/blink/renderer/core/workers/worker_global_scope.h" #include "third_party/blink/renderer/modules/webmidi/midi_access.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" using midi::mojom::PortState; @@ -82,6 +83,8 @@ } class MessageValidator { + STACK_ALLOCATED(); + public: static bool Validate(DOMUint8Array* array, ExceptionState& exception_state,
diff --git a/third_party/blink/renderer/modules/xr/xr.cc b/third_party/blink/renderer/modules/xr/xr.cc index ef7a4f44..2c1d471 100644 --- a/third_party/blink/renderer/modules/xr/xr.cc +++ b/third_party/blink/renderer/modules/xr/xr.cc
@@ -4,6 +4,8 @@ #include "third_party/blink/renderer/modules/xr/xr.h" +#include <utility> + #include "services/metrics/public/cpp/ukm_builders.h" #include "services/service_manager/public/cpp/interface_provider.h" #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h" @@ -111,6 +113,11 @@ return environment_provider_; } +void XR::AddEnvironmentProviderErrorHandler( + EnvironmentProviderErrorCallback callback) { + environment_provider_error_callbacks_.push_back(std::move(callback)); +} + ScriptPromise XR::supportsSessionMode(ScriptState* script_state, const String& mode) { LocalFrame* frame = GetFrame(); @@ -406,6 +413,9 @@ mojo::MakeRequest(&environment_provider_, GetExecutionContext()->GetTaskRunner( TaskType::kMiscPlatformAPI))); + + environment_provider_.set_connection_error_handler(WTF::Bind( + &XR::OnEnvironmentProviderDisconnect, WrapWeakPersistent(this))); } } @@ -461,6 +471,15 @@ device_ = nullptr; } +void XR::OnEnvironmentProviderDisconnect() { + for (auto& callback : environment_provider_error_callbacks_) { + std::move(callback).Run(); + } + + environment_provider_error_callbacks_.clear(); + environment_provider_.reset(); +} + void XR::Trace(blink::Visitor* visitor) { visitor->Trace(pending_mode_queries_); visitor->Trace(pending_session_requests_);
diff --git a/third_party/blink/renderer/modules/xr/xr.h b/third_party/blink/renderer/modules/xr/xr.h index 7a936861..37b79b8 100644 --- a/third_party/blink/renderer/modules/xr/xr.h +++ b/third_party/blink/renderer/modules/xr/xr.h
@@ -71,6 +71,10 @@ int64_t GetSourceId() const { return ukm_source_id_; } + using EnvironmentProviderErrorCallback = base::OnceCallback<void()>; + void AddEnvironmentProviderErrorHandler( + EnvironmentProviderErrorCallback callback); + private: class PendingSessionQuery final : public GarbageCollected<PendingSessionQuery> { @@ -105,6 +109,8 @@ void Dispose(); + void OnEnvironmentProviderDisconnect(); + bool pending_device_ = false; // Indicates whether use of requestDevice has already been logged. @@ -123,6 +129,9 @@ HeapVector<Member<PendingSessionQuery>> pending_mode_queries_; HeapVector<Member<PendingSessionQuery>> pending_session_requests_; + Vector<EnvironmentProviderErrorCallback> + environment_provider_error_callbacks_; + Member<XRFrameProvider> frame_provider_; HeapHashSet<WeakMember<XRSession>> sessions_; device::mojom::blink::VRServicePtr service_;
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc index 6c9a30a..125f9c1 100644 --- a/third_party/blink/renderer/modules/xr/xr_session.cc +++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -54,6 +54,12 @@ const char kIncompatibleLayer[] = "XRLayer was created with a different session."; +const char kNoSpaceSpecified[] = "No XRSpace specified."; + +const char kHitTestNotSupported[] = "Device does not support hit-test!"; + +const char kDeviceDisconnected[] = "The XR device has been disconnected."; + const double kDegToRad = M_PI / 180.0; // TODO(bajones): This is something that we probably want to make configurable. @@ -362,7 +368,7 @@ if (!space) { return ScriptPromise::Reject( script_state, V8ThrowException::CreateTypeError( - script_state->GetIsolate(), "No XRSpace specified.")); + script_state->GetIsolate(), kNoSpaceSpecified)); } // TODO(https://crbug.com/846411): use space. @@ -373,9 +379,8 @@ // we want. if (!xr_->xrEnvironmentProviderPtr()) { return ScriptPromise::RejectWithDOMException( - script_state, - DOMException::Create(DOMExceptionCode::kNotSupportedError, - "Device does not support hit-test!")); + script_state, DOMException::Create(DOMExceptionCode::kNotSupportedError, + kHitTestNotSupported)); } device::mojom::blink::XRRayPtr ray_mojo = device::mojom::blink::XRRay::New(); @@ -393,12 +398,12 @@ ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); ScriptPromise promise = resolver->Promise(); - // TODO(https://crbug.com/845520): Promise should be rejected if session - // is deleted. + EnsureEnvironmentErrorHandler(); xr_->xrEnvironmentProviderPtr()->RequestHitTest( std::move(ray_mojo), - WTF::Bind(&XRSession::OnHitTestResults, WrapWeakPersistent(this), + WTF::Bind(&XRSession::OnHitTestResults, WrapPersistent(this), WrapPersistent(resolver))); + hit_test_promises_.insert(resolver); return promise; } @@ -406,6 +411,9 @@ void XRSession::OnHitTestResults( ScriptPromiseResolver* resolver, base::Optional<WTF::Vector<device::mojom::blink::XRHitResultPtr>> results) { + DCHECK(hit_test_promises_.Contains(resolver)); + hit_test_promises_.erase(resolver); + if (!results) { resolver->Reject(); return; @@ -428,6 +436,24 @@ resolver->Resolve(hit_results); } +void XRSession::EnsureEnvironmentErrorHandler() { + if (!environment_error_handler_subscribed_ && + xr_->xrEnvironmentProviderPtr()) { + environment_error_handler_subscribed_ = true; + xr_->AddEnvironmentProviderErrorHandler(WTF::Bind( + &XRSession::OnEnvironmentProviderError, WrapPersistent(this))); + } +} + +void XRSession::OnEnvironmentProviderError() { + HeapHashSet<Member<ScriptPromiseResolver>> hit_test_promises; + hit_test_promises_.swap(hit_test_promises); + for (ScriptPromiseResolver* resolver : hit_test_promises) { + resolver->Reject(DOMException::Create(DOMExceptionCode::kInvalidStateError, + kDeviceDisconnected)); + } +} + ScriptPromise XRSession::end(ScriptState* script_state) { // Don't allow a session to end twice. if (ended_) { @@ -994,6 +1020,7 @@ visitor->Trace(resize_observer_); visitor->Trace(canvas_input_provider_); visitor->Trace(callback_collection_); + visitor->Trace(hit_test_promises_); EventTargetWithInlineData::Trace(visitor); }
diff --git a/third_party/blink/renderer/modules/xr/xr_session.h b/third_party/blink/renderer/modules/xr/xr_session.h index 70eb402..d9db82d 100644 --- a/third_party/blink/renderer/modules/xr/xr_session.h +++ b/third_party/blink/renderer/modules/xr/xr_session.h
@@ -200,6 +200,9 @@ base::Optional<WTF::Vector<device::mojom::blink::XRHitResultPtr>> results); + void EnsureEnvironmentErrorHandler(); + void OnEnvironmentProviderError(); + const Member<XR> xr_; const SessionMode mode_; const String mode_string_; @@ -211,6 +214,8 @@ InputSourceMap input_sources_; Member<ResizeObserver> resize_observer_; Member<XRCanvasInputProvider> canvas_input_provider_; + bool environment_error_handler_subscribed_ = false; + HeapHashSet<Member<ScriptPromiseResolver>> hit_test_promises_; bool has_xr_focus_ = true; bool is_external_ = false;
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc index 51b99aa..28e17efa 100644 --- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc +++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -1586,13 +1586,22 @@ GLuint rgb_texture = back_color_buffer_->rgb_workaround_texture_id; DCHECK_EQ(texture_target_, GC3D_TEXTURE_RECTANGLE_ARB); if (!rgb_texture) { - rgb_texture = - gl_->CreateAndTexStorage2DSharedImageWithInternalFormatCHROMIUM( - back_color_buffer_->mailbox.name, GL_RGB); + gpu::SharedImageInterface* sii = ContextProvider()->SharedImageInterface(); + gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager = + Platform::Current()->GetGpuMemoryBufferManager(); + back_color_buffer_->rgb_workaround_mailbox = sii->CreateSharedImage( + back_color_buffer_->gpu_memory_buffer.get(), gpu_memory_buffer_manager, + storage_color_space_, + gpu::SHARED_IMAGE_USAGE_GLES2 | + gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT | + gpu::SHARED_IMAGE_USAGE_DISPLAY | gpu::SHARED_IMAGE_USAGE_SCANOUT | + gpu::SHARED_IMAGE_USAGE_RGB_EMULATION); + gl_->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData()); + rgb_texture = gl_->CreateAndTexStorage2DSharedImageCHROMIUM( + back_color_buffer_->rgb_workaround_mailbox.name); back_color_buffer_->rgb_workaround_texture_id = rgb_texture; } - gl_->EndSharedImageAccessDirectCHROMIUM(back_color_buffer_->texture_id); gl_->BeginSharedImageAccessDirectCHROMIUM( rgb_texture, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM); gl_->FramebufferTexture2D(GL_DRAW_FRAMEBUFFER_ANGLE, GL_COLOR_ATTACHMENT0, @@ -1607,9 +1616,6 @@ DCHECK(back_color_buffer_->gpu_memory_buffer); gl_->EndSharedImageAccessDirectCHROMIUM( back_color_buffer_->rgb_workaround_texture_id); - gl_->BeginSharedImageAccessDirectCHROMIUM( - back_color_buffer_->texture_id, - GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM); gl_->FramebufferTexture2D(GL_DRAW_FRAMEBUFFER_ANGLE, GL_COLOR_ATTACHMENT0, texture_target_, back_color_buffer_->texture_id, 0); // Clear the alpha channel.
diff --git a/third_party/blink/renderer/platform/wtf/decimal.cc b/third_party/blink/renderer/platform/wtf/decimal.cc index 49d9d26..0d84a5a2 100644 --- a/third_party/blink/renderer/platform/wtf/decimal.cc +++ b/third_party/blink/renderer/platform/wtf/decimal.cc
@@ -127,6 +127,8 @@ // This class is used for 128 bit unsigned integer arithmetic. class UInt128 { + STACK_ALLOCATED(); + public: UInt128(uint64_t low, uint64_t high) : high_(high), low_(low) {}
diff --git a/third_party/blink/renderer/platform/wtf/decimal_test.cc b/third_party/blink/renderer/platform/wtf/decimal_test.cc index f1b83a8..ba73f725 100644 --- a/third_party/blink/renderer/platform/wtf/decimal_test.cc +++ b/third_party/blink/renderer/platform/wtf/decimal_test.cc
@@ -33,6 +33,7 @@ #include <cfloat> #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/math_extras.h" #include "third_party/blink/renderer/platform/wtf/text/cstring.h" @@ -40,6 +41,8 @@ // Simulate core/html/forms/StepRange class DecimalStepRange { + STACK_ALLOCATED(); + public: Decimal maximum; Decimal minimum;
diff --git a/third_party/blink/renderer/platform/wtf/dtoa/bignum.h b/third_party/blink/renderer/platform/wtf/dtoa/bignum.h index 1bc51a2..f9df22a9 100644 --- a/third_party/blink/renderer/platform/wtf/dtoa/bignum.h +++ b/third_party/blink/renderer/platform/wtf/dtoa/bignum.h
@@ -28,6 +28,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_BIGNUM_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_BIGNUM_H_ +#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/dtoa/utils.h" namespace WTF { @@ -35,6 +36,8 @@ namespace double_conversion { class Bignum { + DISALLOW_NEW(); + public: // 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately. // This bignum can encode much bigger numbers, since it contains an
diff --git a/third_party/blink/renderer/platform/wtf/dtoa/cached-powers.h b/third_party/blink/renderer/platform/wtf/dtoa/cached-powers.h index fec5744a..080e2fa41 100644 --- a/third_party/blink/renderer/platform/wtf/dtoa/cached-powers.h +++ b/third_party/blink/renderer/platform/wtf/dtoa/cached-powers.h
@@ -28,6 +28,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_CACHED_POWERS_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_CACHED_POWERS_H_ +#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/dtoa/diy-fp.h" namespace WTF { @@ -35,6 +36,8 @@ namespace double_conversion { class PowersOfTenCache { + STATIC_ONLY(PowersOfTenCache); + public: // Not all powers of ten are cached. The decimal exponent of two neighboring // cached numbers will differ by kDecimalExponentDistance.
diff --git a/third_party/blink/renderer/platform/wtf/dtoa/diy-fp.h b/third_party/blink/renderer/platform/wtf/dtoa/diy-fp.h index f5199c9..701b8405 100644 --- a/third_party/blink/renderer/platform/wtf/dtoa/diy-fp.h +++ b/third_party/blink/renderer/platform/wtf/dtoa/diy-fp.h
@@ -28,6 +28,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_DIY_FP_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_DIY_FP_H_ +#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/dtoa/utils.h" namespace WTF { @@ -40,6 +41,8 @@ // Multiplication and Subtraction do not normalize their results. // DiyFp are not designed to contain special doubles (NaN and Infinity). class DiyFp { + STACK_ALLOCATED(); + public: static const int kSignificandSize = 64;
diff --git a/third_party/blink/renderer/platform/wtf/dtoa/double-conversion.h b/third_party/blink/renderer/platform/wtf/dtoa/double-conversion.h index de48a64..cf0c6f3 100644 --- a/third_party/blink/renderer/platform/wtf/dtoa/double-conversion.h +++ b/third_party/blink/renderer/platform/wtf/dtoa/double-conversion.h
@@ -28,6 +28,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_DOUBLE_CONVERSION_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_DOUBLE_CONVERSION_H_ +#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/dtoa/utils.h" namespace WTF { @@ -35,6 +36,8 @@ namespace double_conversion { class DoubleToStringConverter { + STACK_ALLOCATED(); + public: // When calling ToFixed with a double > 10^kMaxFixedDigitsBeforePoint // or a requested_digits parameter > kMaxFixedDigitsAfterPoint then the @@ -358,6 +361,8 @@ }; class StringToDoubleConverter { + STACK_ALLOCATED(); + public: // Performs the conversion. // The output parameter 'processed_characters_count' is set to the number
diff --git a/third_party/blink/renderer/platform/wtf/dtoa/double.h b/third_party/blink/renderer/platform/wtf/dtoa/double.h index eee1c2dd..386fc3eb 100644 --- a/third_party/blink/renderer/platform/wtf/dtoa/double.h +++ b/third_party/blink/renderer/platform/wtf/dtoa/double.h
@@ -44,6 +44,8 @@ // Helper functions for doubles. class Double { + STACK_ALLOCATED(); + public: static const uint64_t kSignMask = UINT64_2PART_C(0x80000000, 00000000); static const uint64_t kExponentMask = UINT64_2PART_C(0x7FF00000, 00000000);
diff --git a/third_party/blink/renderer/platform/wtf/dtoa/fixed-dtoa.cc b/third_party/blink/renderer/platform/wtf/dtoa/fixed-dtoa.cc index c99e19ce..57616a9 100644 --- a/third_party/blink/renderer/platform/wtf/dtoa/fixed-dtoa.cc +++ b/third_party/blink/renderer/platform/wtf/dtoa/fixed-dtoa.cc
@@ -38,6 +38,8 @@ // Represents a 128bit type. This class should be replaced by a native type on // platforms that support 128bit integers. class UInt128 { + STACK_ALLOCATED(); + public: UInt128() : high_bits_(0), low_bits_(0) {} UInt128(uint64_t high, uint64_t low) : high_bits_(high), low_bits_(low) {}
diff --git a/third_party/blink/renderer/platform/wtf/dtoa/utils.h b/third_party/blink/renderer/platform/wtf/dtoa/utils.h index 19a222e..d74ecd4 100644 --- a/third_party/blink/renderer/platform/wtf/dtoa/utils.h +++ b/third_party/blink/renderer/platform/wtf/dtoa/utils.h
@@ -29,6 +29,7 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_UTILS_H_ #include <string.h> +#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/assertions.h" #define UNIMPLEMENTED NOTREACHED @@ -178,6 +179,8 @@ // purpose of the class is to use safe operations that checks the // buffer bounds on all operations in debug mode. class StringBuilder { + DISALLOW_NEW(); + public: StringBuilder(char* buffer, int size) : buffer_(buffer, size), position_(0) {}
diff --git a/third_party/blink/renderer/platform/wtf/functional_test.cc b/third_party/blink/renderer/platform/wtf/functional_test.cc index 2d7352b0..b49458b 100644 --- a/third_party/blink/renderer/platform/wtf/functional_test.cc +++ b/third_party/blink/renderer/platform/wtf/functional_test.cc
@@ -33,6 +33,7 @@ #include "base/test/gtest_util.h" #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/leak_annotations.h" #include "third_party/blink/renderer/platform/wtf/ref_counted.h" #include "third_party/blink/renderer/platform/wtf/wtf_test_helper.h" @@ -122,6 +123,8 @@ }; class CountGeneration { + STACK_ALLOCATED(); + public: CountGeneration() : copies_(0) {} CountGeneration(const CountGeneration& other) : copies_(other.copies_ + 1) {}
diff --git a/third_party/blink/renderer/platform/wtf/hash_map_test.cc b/third_party/blink/renderer/platform/wtf/hash_map_test.cc index 8212fb0..2cdbcc455c 100644 --- a/third_party/blink/renderer/platform/wtf/hash_map_test.cc +++ b/third_party/blink/renderer/platform/wtf/hash_map_test.cc
@@ -30,6 +30,7 @@ #include "base/memory/ptr_util.h" #include "base/memory/scoped_refptr.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/ref_counted.h" #include "third_party/blink/renderer/platform/wtf/vector.h" #include "third_party/blink/renderer/platform/wtf/wtf_test_helper.h" @@ -217,6 +218,8 @@ } class SimpleClass { + USING_FAST_MALLOC(SimpleClass); + public: explicit SimpleClass(int v) : v_(v) {} int V() { return v_; } @@ -266,6 +269,8 @@ } class InstanceCounter { + USING_FAST_MALLOC(InstanceCounter); + public: InstanceCounter() { ++counter_; } InstanceCounter(const InstanceCounter& another) { ++counter_; }
diff --git a/third_party/blink/renderer/platform/wtf/pod_arena.h b/third_party/blink/renderer/platform/wtf/pod_arena.h index 2513b75..63b4653 100644 --- a/third_party/blink/renderer/platform/wtf/pod_arena.h +++ b/third_party/blink/renderer/platform/wtf/pod_arena.h
@@ -45,6 +45,8 @@ // destructors. class PODArena final : public RefCounted<PODArena> { + USING_FAST_MALLOC(PODArena); + public: // The arena is configured with an allocator, which is responsible // for allocating and freeing chunks of memory at a time.
diff --git a/third_party/blink/renderer/platform/wtf/pod_free_list_arena.h b/third_party/blink/renderer/platform/wtf/pod_free_list_arena.h index ff1b1e8..b1e5b225 100644 --- a/third_party/blink/renderer/platform/wtf/pod_free_list_arena.h +++ b/third_party/blink/renderer/platform/wtf/pod_free_list_arena.h
@@ -34,6 +34,8 @@ template <class T> class PODFreeListArena : public RefCounted<PODFreeListArena<T>> { + USING_FAST_MALLOC(PODFreeListArena); + public: static scoped_refptr<PODFreeListArena> Create() { return base::AdoptRef(new PODFreeListArena);
diff --git a/third_party/blink/renderer/platform/wtf/pod_interval_tree_test.cc b/third_party/blink/renderer/platform/wtf/pod_interval_tree_test.cc index 8b15458..71423e3 100644 --- a/third_party/blink/renderer/platform/wtf/pod_interval_tree_test.cc +++ b/third_party/blink/renderer/platform/wtf/pod_interval_tree_test.cc
@@ -28,6 +28,7 @@ #include "third_party/blink/renderer/platform/wtf/pod_interval_tree.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/pod_tree_test_helpers.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "third_party/blink/renderer/platform/wtf/vector.h" @@ -152,6 +153,8 @@ namespace { class EndpointType1 { + STACK_ALLOCATED(); + public: explicit EndpointType1(int value) : value_(value) {}
diff --git a/third_party/blink/renderer/platform/wtf/scoped_logger.h b/third_party/blink/renderer/platform/wtf/scoped_logger.h index f297a6b7..ee1bb201 100644 --- a/third_party/blink/renderer/platform/wtf/scoped_logger.h +++ b/third_party/blink/renderer/platform/wtf/scoped_logger.h
@@ -8,6 +8,7 @@ #include "base/gtest_prod_util.h" #include "base/logging.h" #include "base/macros.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/wtf_export.h" namespace WTF { @@ -28,6 +29,8 @@ // code. Please do not remove it. // class WTF_EXPORT ScopedLogger { + DISALLOW_NEW(); + public: // The first message is passed to the constructor. Additional messages for // the same scope can be added with log(). If condition is false, produce no
diff --git a/third_party/blink/renderer/platform/wtf/text/number_parsing_options.h b/third_party/blink/renderer/platform/wtf/text/number_parsing_options.h index d2cd46a..c03f2cb 100644 --- a/third_party/blink/renderer/platform/wtf/text/number_parsing_options.h +++ b/third_party/blink/renderer/platform/wtf/text/number_parsing_options.h
@@ -7,10 +7,14 @@ #include "base/logging.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" + namespace WTF { // Copyable and immutable object representing number parsing flags. class NumberParsingOptions { + STACK_ALLOCATED(); + public: static constexpr unsigned kNone = 0; static constexpr unsigned kAcceptTrailingGarbage = 1;
diff --git a/third_party/blink/renderer/platform/wtf/tree_node_test.cc b/third_party/blink/renderer/platform/wtf/tree_node_test.cc index da43be7..3245082 100644 --- a/third_party/blink/renderer/platform/wtf/tree_node_test.cc +++ b/third_party/blink/renderer/platform/wtf/tree_node_test.cc
@@ -27,6 +27,7 @@ #include "base/memory/scoped_refptr.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/ref_counted.h" namespace WTF { @@ -102,6 +103,8 @@ } class Trio { + STACK_ALLOCATED(); + public: Trio() : root(TestTree::Create()),
diff --git a/third_party/blink/renderer/platform/wtf/type_traits_test.cc b/third_party/blink/renderer/platform/wtf/type_traits_test.cc index 2a33105..1b6b6d47 100644 --- a/third_party/blink/renderer/platform/wtf/type_traits_test.cc +++ b/third_party/blink/renderer/platform/wtf/type_traits_test.cc
@@ -24,6 +24,8 @@ #include "base/macros.h" #include "build/build_config.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" + // No gtest tests; only static_assert checks. namespace WTF { @@ -129,6 +131,8 @@ #if !defined(COMPILER_MSVC) || defined(__clang__) class AssignmentDeleted final { + STACK_ALLOCATED(); + private: AssignmentDeleted& operator=(const AssignmentDeleted&) = delete; }; @@ -139,6 +143,8 @@ "AssignmentDeleted isn't move assignable."); class AssignmentPrivate final { + STACK_ALLOCATED(); + private: AssignmentPrivate& operator=(const AssignmentPrivate&); }; @@ -149,6 +155,8 @@ "AssignmentPrivate isn't move assignable."); class CopyAssignmentDeleted final { + STACK_ALLOCATED(); + public: CopyAssignmentDeleted& operator=(CopyAssignmentDeleted&&); @@ -162,6 +170,8 @@ "CopyAssignmentDeleted is move assignable."); class CopyAssignmentPrivate final { + STACK_ALLOCATED(); + public: CopyAssignmentPrivate& operator=(CopyAssignmentPrivate&&); @@ -175,6 +185,8 @@ "CopyAssignmentPrivate is move assignable."); class CopyAssignmentUndeclared final { + STACK_ALLOCATED(); + public: CopyAssignmentUndeclared& operator=(CopyAssignmentUndeclared&&); }; @@ -185,6 +197,8 @@ "CopyAssignmentUndeclared is move assignable."); class Assignable final { + STACK_ALLOCATED(); + public: Assignable& operator=(const Assignable&); }; @@ -204,11 +218,15 @@ #endif // !defined(COMPILER_MSVC) || defined(__clang__) class DefaultConstructorDeleted final { + STACK_ALLOCATED(); + private: DefaultConstructorDeleted() = delete; }; class DestructorDeleted final { + STACK_ALLOCATED(); + private: ~DestructorDeleted() = delete; };
diff --git a/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.h b/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.h index bd02fd17..ec028bc 100644 --- a/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.h +++ b/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.h
@@ -28,6 +28,7 @@ #include "base/allocator/partition_allocator/oom.h" #include "base/memory/scoped_refptr.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/assertions.h" #include "third_party/blink/renderer/platform/wtf/hash_set.h" #include "third_party/blink/renderer/platform/wtf/ref_counted.h" @@ -40,6 +41,8 @@ class ArrayBufferView; class WTF_EXPORT ArrayBuffer : public RefCounted<ArrayBuffer> { + USING_FAST_MALLOC(ArrayBuffer); + public: static inline scoped_refptr<ArrayBuffer> Create(unsigned num_elements, unsigned element_byte_size);
diff --git a/third_party/blink/renderer/platform/wtf/wtf_test_helper.h b/third_party/blink/renderer/platform/wtf/wtf_test_helper.h index 94bb7909..f94917cb 100644 --- a/third_party/blink/renderer/platform/wtf/wtf_test_helper.h +++ b/third_party/blink/renderer/platform/wtf/wtf_test_helper.h
@@ -8,6 +8,7 @@ #include "base/macros.h" #include "base/memory/scoped_refptr.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/hash_functions.h" #include "third_party/blink/renderer/platform/wtf/hash_set.h" #include "third_party/blink/renderer/platform/wtf/ref_counted.h" @@ -15,6 +16,8 @@ namespace WTF { class DestructCounter { + USING_FAST_MALLOC(DestructCounter); + public: explicit DestructCounter(int i, int* destruct_number) : i_(i), destruct_number_(destruct_number) {} @@ -28,6 +31,8 @@ }; class MoveOnly { + DISALLOW_NEW(); + public: explicit MoveOnly(int i = 0) : i_(i) {}
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint index ed23014..a0aa82a 100644 --- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint +++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
@@ -406,6 +406,9 @@ crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-fixed-clip.html [ Failure ] crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-isolation.html [ Failure ] +crbug.com/940033 virtual/fractional_scrolling_threaded/fast/scrolling/wheel-scrolling-over-custom-scrollbar.html [ Failure ] +crbug.com/940033 virtual/threaded/fast/scrolling/wheel-scrolling-over-custom-scrollbar.html [ Failure ] + # Missing composited layer for fixed-position crbug.com/931491 compositing/layer-creation/fixed-position-change-out-of-view-in-view.html [ Failure ] crbug.com/931491 compositing/layer-creation/fixed-position-in-fixed-overflow.html [ Failure ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index b3df8d3..66661859 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -2906,6 +2906,9 @@ # Sheriff 2019-03-07 crbug.com/939406 external/wpt/html/browsers/windows/embedded-opener-remove-frame.html [ Failure ] +# Sheriff 2019-03-08 +crbug.com/940050 virtual/threaded/external/wpt/animation-worklet/playback-rate.https.html [ Failure ] + # Untriaged failures after https://crrev.com/c/543695/. # These need to be updated but appear not to be related to that change. crbug.com/626703 http/tests/devtools/indexeddb/database-refresh-view.js [ Pass Failure ] @@ -3034,6 +3037,9 @@ crbug.com/939181 virtual/not-site-per-process/external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html [ Failure Timeout ] # ====== New tests from wpt-importer added here ====== +crbug.com/626703 [ Mac10.12 ] virtual/threaded/external/wpt/animation-worklet/playback-rate.https.html [ Timeout ] +crbug.com/626703 [ Mac10.13 ] virtual/threaded/external/wpt/animation-worklet/playback-rate.https.html [ Timeout ] +crbug.com/626703 [ Retina ] virtual/threaded/external/wpt/animation-worklet/playback-rate.https.html [ Timeout ] crbug.com/626703 external/wpt/css/css-sizing/range-percent-intrinsic-size-2a.html [ Failure ] crbug.com/626703 external/wpt/infrastructure/reftest/reftest_fuzzy.html [ Failure ] crbug.com/626703 external/wpt/infrastructure/reftest/reftest_fuzzy_1.html [ Failure ] @@ -4237,6 +4243,11 @@ # This fails because CORS check?: Error message is like: # Access to XMLHttpRequest at 'foobar://abcd' (redirected from 'http://web-platform.test:8001/xhr/resources/redirect.py?location=foobar://abcd&code=301') from origin 'http://web-platform.test:8001' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https. crbug.com/924043 virtual/omt-worker-fetch/external/wpt/xhr/send-redirect-bogus-sync.htm [ Timeout ] +# Following tests are crashing because module script fetcher for service worker +# doesn't set CSP correctly. +crbug.com/937757 external/wpt/service-workers/service-worker/update-registration-with-type.https.html [ Crash ] +crbug.com/937757 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/update-registration-with-type.https.html [ Crash ] +crbug.com/937757 virtual/outofblink-cors/external/wpt/service-workers/service-worker/update-registration-with-type.https.html [ Crash ] crbug.com/697971 [ Mac10.12 ] fast/text/selection/flexbox-selection-nested.html [ Skip ] crbug.com/697971 [ Mac10.12 ] fast/text/selection/flexbox-selection.html [ Skip ] @@ -5563,7 +5574,6 @@ crbug.com/891530 [ Linux ] virtual/android/rootscroller/remove-rootscroller-crash.html [ Pass Timeout ] crbug.com/891510 [ Linux ] fast/dom/inline-event-attributes-release.html [ Failure Pass ] crbug.com/891510 [ Win7 ] fast/dom/inline-event-attributes-release.html [ Failure Pass ] -crbug.com/892772 [ Mac ] editing/caret/caret-painting-low-dpi.html [ Failure Pass ] # Sheriff 2018-10-09 crbug.com/806357 [ Linux ] virtual/threaded/fast/events/pointerevents/pinch/pointerevent_touch-action-pinch_zoom_touch.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/badging/badge-error.html b/third_party/blink/web_tests/badging/badge-error.html index 679220d..3203dc6a 100644 --- a/third_party/blink/web_tests/badging/badge-error.html +++ b/third_party/blink/web_tests/badging/badge-error.html
@@ -5,7 +5,7 @@ <script src="../resources/testharness.js"></script> <script src="../resources/testharnessreport.js"></script> <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script> -<script src="file:///gen/third_party/blink/public/platform/modules/badging/badging.mojom.js"></script> +<script src="file:///gen/third_party/blink/public/mojom/badging/badging.mojom.js"></script> <script src="resources/mock-badge-service.js"></script> </head> <body>
diff --git a/third_party/blink/web_tests/badging/badge-success.html b/third_party/blink/web_tests/badging/badge-success.html index 20301cc..48337cc3 100644 --- a/third_party/blink/web_tests/badging/badge-success.html +++ b/third_party/blink/web_tests/badging/badge-success.html
@@ -5,7 +5,7 @@ <script src="../resources/testharness.js"></script> <script src="../resources/testharnessreport.js"></script> <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script> -<script src="file:///gen/third_party/blink/public/platform/modules/badging/badging.mojom.js"></script> +<script src="file:///gen/third_party/blink/public/mojom/badging/badging.mojom.js"></script> <script src="resources/mock-badge-service.js"></script> </head> <body>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json index 8e6055e..5d07795 100644 --- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json +++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
@@ -179993,6 +179993,11 @@ {} ] ], + "portals/resources/portal-inside-iframe.html": [ + [ + {} + ] + ], "portals/resources/portals-rendering-portal.html": [ [ {} @@ -188443,6 +188448,11 @@ {} ] ], + "svg/painting/parsing/stroke-dashoffset-valid-expected.txt": [ + [ + {} + ] + ], "svg/painting/parsing/stroke-linejoin-valid-expected.txt": [ [ {} @@ -188458,6 +188468,11 @@ {} ] ], + "svg/painting/parsing/stroke-width-valid-expected.txt": [ + [ + {} + ] + ], "svg/painting/reftests/markers-orient-001-ref.svg": [ [ {} @@ -274844,6 +274859,12 @@ {} ] ], + "portals/portals-activate-inside-iframe.html": [ + [ + "/portals/portals-activate-inside-iframe.html", + {} + ] + ], "portals/portals-activate-no-browsing-context.html": [ [ "/portals/portals-activate-no-browsing-context.html", @@ -446433,6 +446454,10 @@ "33d91e37d9d9ac77c5243a60b42ce841645d248e", "testharness" ], + "portals/portals-activate-inside-iframe.html": [ + "3c9149f485d1ac44b8d2303a4448a78ea7525243", + "testharness" + ], "portals/portals-activate-no-browsing-context.html": [ "6eebca9f9d982ffd38a96bb72ff0173bcfb07903", "testharness" @@ -446497,6 +446522,10 @@ "5043a158ea74ef173f166c0580f9c1a27242bd14", "support" ], + "portals/resources/portal-inside-iframe.html": [ + "5db75d5b5fd5c12d5a77181ee1cac48f76657a57", + "support" + ], "portals/resources/portals-rendering-portal.html": [ "1b6f23f512da5bb7d1c7b5b85e48277470d2e146", "support" @@ -463433,8 +463462,12 @@ "64e2eec764a13bab403172a1568d085d745033d2", "testharness" ], + "svg/painting/parsing/stroke-dashoffset-valid-expected.txt": [ + "a683e2f4a8a7d8d528dee4453b30899b59622dd5", + "support" + ], "svg/painting/parsing/stroke-dashoffset-valid.svg": [ - "ff913e06e4f849b5b14fac050986f697df9c81d8", + "f34774e68d7afa7a336ed7cbd30b44695451d74d", "testharness" ], "svg/painting/parsing/stroke-invalid.svg": [ @@ -463506,15 +463539,19 @@ "testharness" ], "svg/painting/parsing/stroke-width-computed.svg": [ - "71dead0942fc2e660f6ffe239f00ef502c1a8728", + "0d14a6651482baff1b9168f304306b28d6edc910", "testharness" ], "svg/painting/parsing/stroke-width-invalid.svg": [ "0d3f63d077f29a0a36f6443164dc7f24421a3f62", "testharness" ], + "svg/painting/parsing/stroke-width-valid-expected.txt": [ + "9ec8028894ac20908757d53235c2a1438061fad2", + "support" + ], "svg/painting/parsing/stroke-width-valid.svg": [ - "02bca189f74fe91088ebe913f848b80dfc24868c", + "f90781284dcf54b2e864e0607ae3e880a40531e1", "testharness" ], "svg/painting/parsing/text-rendering-computed.svg": [ @@ -471390,7 +471427,7 @@ "support" ], "web-animations/animation-model/animation-types/accumulation-per-property-expected.txt": [ - "d0cbf2ee61320ac964f756246c000bea5abf292a", + "c977a68d039195b8636e68ad9402b922b41f0ad2", "support" ], "web-animations/animation-model/animation-types/accumulation-per-property.html": [ @@ -471398,7 +471435,7 @@ "testharness" ], "web-animations/animation-model/animation-types/addition-per-property-expected.txt": [ - "93e84763eae24547cfc17393e190c95b0396ef9d", + "fa04c367c70d927c15314fcd371325512f414350", "support" ], "web-animations/animation-model/animation-types/addition-per-property.html": [ @@ -471410,7 +471447,7 @@ "testharness" ], "web-animations/animation-model/animation-types/interpolation-per-property-expected.txt": [ - "05e1e1d2d5c0d7238f71972548e7ec1e0b9038ce", + "e7ede0a800b5b9bdf004282b6e3d7e0feec05c1d", "support" ], "web-animations/animation-model/animation-types/interpolation-per-property.html": [ @@ -471418,11 +471455,11 @@ "testharness" ], "web-animations/animation-model/animation-types/property-list.js": [ - "2d45574a61fcb5d44147ba78dd2b9a79c09762ac", + "8d9b296ff74fdd907f94da417bfce1d9021eca48", "support" ], "web-animations/animation-model/animation-types/property-types.js": [ - "64a7eb4762754adf693074ee1bc7780c81ffbe3e", + "80d3b8f77ff5589f2f4dc5ee69b4fa102786f85a", "support" ], "web-animations/animation-model/animation-types/visibility.html": [
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/cross-origin-element.sub.html b/third_party/blink/web_tests/external/wpt/element-timing/cross-origin-element.sub.html index ed820d0..a122819f 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/cross-origin-element.sub.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/cross-origin-element.sub.html
@@ -1,15 +1,27 @@ <!DOCTYPE HTML> <meta charset=utf-8> -<title>Element Timing: do NOT observe cross-origin images</title> +<title>Element Timing: observe cross-origin images but without startTime</title> <body> +<style> +body { + margin: 0; +} +</style> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="resources/element-timing-helpers.js"></script> <script> async_test((t) => { + const pathname = 'http://{{domains[www]}}:{{ports[http][1]}}' + + '/element-timing/resources/square100.png'; const observer = new PerformanceObserver( t.step_func_done((entryList) => { - assert_unreached("We should not observe a cross origin element."); + assert_equals(entryList.getEntries().length, 1); + const entry = entryList.getEntries()[0]; + checkElement(entry, pathname, 'my_image', 0); + assert_equals(entry.startTime, 0, + 'The startTime of a cross-origin image should be 0.'); + checkRect(entry, [0, 100, 0, 100]); }) ); observer.observe({entryTypes: ['element']}); @@ -19,15 +31,8 @@ window.onload = t.step_func(() => { // Add a cross origin image resource. const img = document.createElement('img'); - img.src = 'http://{{domains[www]}}:{{ports[http][1]}}' - + '/element-timing/resources/square100.png'; + img.src = pathname; img.setAttribute('elementtiming', 'my_image'); - img.onload = t.step_func(() => { - t.step_timeout( () => { - // After some wait, assume observer did not receive the entry, so the test passes. - t.done(); - }, 100); - }); document.body.appendChild(img); }); }, 'Cross-origin image element is NOT observable.');
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/observe-child-element.html b/third_party/blink/web_tests/external/wpt/element-timing/observe-child-element.html index 83cc2ef..9166a4b 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/observe-child-element.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/observe-child-element.html
@@ -1,6 +1,6 @@ <!DOCTYPE HTML> <meta charset=utf-8> -<title>Element Timing: observe elements from same-origin iframes</title> +<title>Element Timing: do NOT observe elements from same-origin iframes</title> <body> <style> body {
diff --git a/third_party/blink/web_tests/external/wpt/idle-detection/interceptor.https.html b/third_party/blink/web_tests/external/wpt/idle-detection/interceptor.https.html new file mode 100644 index 0000000..03595afc --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/idle-detection/interceptor.https.html
@@ -0,0 +1,133 @@ +<!DOCTYPE html> +<link rel="help" href="https://github.com/inexorabletash/idle-detection"> +<title>Tests the Idle Detection API</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script> +<script src="/gen/mojo/public/mojom/base/string16.mojom.js"></script> +<script src="/gen/mojo/public/mojom/base/time.mojom.js"></script> +<script src="/gen/third_party/blink/public/platform/modules/idle/idle_manager.mojom.js"></script> +<script src="mock.js"></script> +<script> +'use strict'; + +promise_test(async t => { + // Basic test that expects navigator.idle.query() to call internally + // addMonitor, which in turn will return an ACTIVE state. + expect(addMonitor).andReturn((threshold, monitorPtr) => { + return Promise.resolve({ + state: { + user: UserIdleState.ACTIVE, + screen: ScreenIdleState.LOCKED + } + }); + }); + + let status = await navigator.idle.query({threshold: 10}); + + assert_equals(status.state.user, "active"); + assert_equals(status.state.screen, "locked"); +}, 'query()'); + +promise_test(async t => { + // Verifies that an event is thrown when a change of state from IDLE to ACTIVE + // is detected. + expect(addMonitor).andReturn((threshold, monitorPtr) => { + t.step_timeout(() => { + monitorPtr.update({ + user: UserIdleState.IDLE, + screen: ScreenIdleState.UNLOCKED + }); + }, 0); + return Promise.resolve({ + state: { + user: UserIdleState.ACTIVE, + screen: ScreenIdleState.UNLOCKED + } + }); + }); + + let monitor = await navigator.idle.query({threshold: 10}); + + await new EventWatcher(t, monitor, ["change"]).wait_for("change"); + + assert_equals(monitor.state.user, "idle"); + assert_equals(monitor.state.screen, "unlocked"); +}, 'updates once'); + + +promise_test(async t => { + // Simulates the user being active, going idle and then going back active + // again. + expect(addMonitor).andReturn((threshold, monitorPtr) => { + // Updates the client once with the user idle. + t.step_timeout(() => { + monitorPtr.update({ + user: UserIdleState.IDLE, + screen: ScreenIdleState.UNLOCKED + }); + }, 0); + // Updates the client a second time with the user active. + t.step_timeout(() => { + monitorPtr.update({ + user: UserIdleState.ACTIVE, + screen: ScreenIdleState.UNLOCKED + }); + }, 1); + return Promise.resolve({ + state: { + user: UserIdleState.ACTIVE, + screen: ScreenIdleState.UNLOCKED + } + }); + }); + + let monitor = await navigator.idle.query({threshold: 10}); + + let watcher = new EventWatcher(t, monitor, ["change"]); + + // waits for the first event. + await watcher.wait_for("change"); + assert_equals(monitor.state.user, "idle"); + + // waits for the second event. + await watcher.wait_for("change"); + assert_equals(monitor.state.user, "active"); +}, 'updates twice'); + +promise_test(async t => { + // Simulates a locked screen. + expect(addMonitor).andReturn((threshold, monitorPtr) => { + return Promise.resolve({ + state: { + user: UserIdleState.ACTIVE, + screen: ScreenIdleState.LOCKED + } + }); + }); + + let monitor = await navigator.idle.query({threshold: 10}); + + assert_equals(monitor.state.screen, "locked"); +}, 'locked screen'); + +promise_test(async t => { + // Simulates the service becoming unavailable. + expect(addMonitor).andReturn((threshold, monitorPtr) => { + return new Promise((resolve, reject) => { + // leave the renderer deliberately hanging by not resolve()-ing. + }); + }); + + let error = new Promise((resolve, reject) => { + navigator.idle.query({threshold: 10}) + .then((e) => {reject("unexpected response :(")}) + .catch((e) => {resolve(e.message)}); + }); + + // simulates what happens when the service is unavailable. + close(); + + assert_equals(await error, "Idle detection not available"); +}, "service unavailable"); +</script> \ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/idle-detection/mock.js b/third_party/blink/web_tests/external/wpt/idle-detection/mock.js new file mode 100644 index 0000000..d88d1ad --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/idle-detection/mock.js
@@ -0,0 +1,83 @@ +/** + * This is a testing framework that enables us to test the user idle detection + * by intercepting the connection between the renderer and the browser and + * exposing a mocking API for tests. + * + * Usage: + * + * 1) Include <script src="mock.js"></script> in your file. + * 2) Set expectations + * expect(addMonitor).andReturn((threshold, monitorPtr, callback) => { + * // mock behavior + * }) + * 3) Call navigator.idle.query() + * + * The mocking API is blink agnostic and is designed such that other engines + * could implement it too. Here are the symbols that are exposed to tests: + * + * - function addMonitor(): the main/only function that can be mocked. + * - function expect(): the main/only function that enables us to mock it. + * - function close(): disconnects the interceptor. + * - enum UserIdleState {IDLE, ACTIVE}: blink agnostic constants. + * - enum ScreenIdleState {LOCKED, UNLOCKED}: blink agnostic constants. + */ + +class FakeIdleMonitor { + addMonitor(threshold, monitorPtr, callback) { + return this.handler.addMonitor(threshold, monitorPtr); + } + setHandler(handler) { + this.handler = handler; + return this; + } + setBinding(binding) { + this.binding = binding; + return this; + } + close() { + this.binding.close(); + } +} + +const UserIdleState = {}; +const ScreenIdleState = {}; + +function addMonitor(threshold, monitorPtr, callback) { + throw new Error("expected to be overriden by tests"); +} + +async function close() { + interceptor.close(); +} + +function expect(call) { + return { + andReturn(callback) { + let handler = {}; + handler[call.name] = callback; + interceptor.setHandler(handler); + } + } +} + +function intercept() { + let result = new FakeIdleMonitor(); + + let binding = new mojo.Binding(blink.mojom.IdleManager, result); + let interceptor = new MojoInterfaceInterceptor(blink.mojom.IdleManager.name); + interceptor.oninterfacerequest = (e) => { + binding.bind(e.handle); + } + + interceptor.start(); + + UserIdleState.ACTIVE = blink.mojom.UserIdleState.kActive; + UserIdleState.IDLE = blink.mojom.UserIdleState.kIdle; + ScreenIdleState.LOCKED = blink.mojom.ScreenIdleState.kLocked; + ScreenIdleState.UNLOCKED = blink.mojom.ScreenIdleState.kUnlocked; + + result.setBinding(binding); + return result; +} + +const interceptor = intercept(); \ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/webxr-test.js b/third_party/blink/web_tests/external/wpt/resources/chromium/webxr-test.js index c6c21a6..8816273 100644 --- a/third_party/blink/web_tests/external/wpt/resources/chromium/webxr-test.js +++ b/third_party/blink/web_tests/external/wpt/resources/chromium/webxr-test.js
@@ -328,11 +328,18 @@ } getEnvironmentIntegrationProvider(environmentProviderRequest) { - let environmentProviderBinding = new mojo.AssociatedBinding( + this.environmentProviderBinding_ = new mojo.AssociatedBinding( device.mojom.XREnvironmentIntegrationProvider, this, environmentProviderRequest); } + // Note that if getEnvironmentProvider hasn't finished running yet this will + // be undefined. It's recommended that you allow a successful task to post + // first before attempting to close. + closeEnvironmentIntegrationProvider() { + this.environmentProviderBinding_.close(); + } + updateSessionGeometry(frame_size, display_rotation) { // This function must exist to ensure that calls to it do not crash, but we // do not have any use for this data at present. @@ -429,4 +436,4 @@ } } -let XRTest = new ChromeXRTest(); \ No newline at end of file +let XRTest = new ChromeXRTest();
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid-expected.txt b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid-expected.txt new file mode 100644 index 0000000..a683e2f --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid-expected.txt
@@ -0,0 +1,9 @@ +This is a testharness.js-based test. +FAIL e.style['stroke-dashoffset'] = "0" should set the property value assert_equals: serialization should be canonical expected "0px" but got "0" +PASS e.style['stroke-dashoffset'] = "10px" should set the property value +PASS e.style['stroke-dashoffset'] = "-20%" should set the property value +FAIL e.style['stroke-dashoffset'] = "30" should set the property value assert_equals: serialization should be canonical expected "30px" but got "30" +PASS e.style['stroke-dashoffset'] = "40Q" should set the property value +PASS e.style['stroke-dashoffset'] = "calc(2em + 3ex)" should set the property value +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid.svg b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid.svg index ff913e0..f34774e 100644 --- a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid.svg +++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid.svg
@@ -13,10 +13,10 @@ <h:script src="/css/support/parsing-testcommon.js"/> <script><![CDATA[ -test_valid_value("stroke-dashoffset", "0"); +test_valid_value("stroke-dashoffset", "0", "0px"); test_valid_value("stroke-dashoffset", "10px"); test_valid_value("stroke-dashoffset", "-20%"); -test_valid_value("stroke-dashoffset", "30"); +test_valid_value("stroke-dashoffset", "30", "30px"); test_valid_value("stroke-dashoffset", "40Q", "40q"); test_valid_value("stroke-dashoffset", "calc(2em + 3ex)");
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-computed.svg b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-computed.svg index 71dead09..0d14a665 100644 --- a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-computed.svg +++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-computed.svg
@@ -50,9 +50,9 @@ target.style.strokeWidth = length; const ref = document.getElementById('ref'); - ref.style.wordSpacing = length; + ref.style.textIndent = length; - assert_equals(getComputedStyle(target).strokeWidth, getComputedStyle(ref).wordSpacing); + assert_equals(getComputedStyle(target).strokeWidth, getComputedStyle(ref).textIndent); }, 'stroke-width computes ' + lengthUnit + ' lengths'); }
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid-expected.txt b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid-expected.txt new file mode 100644 index 0000000..9ec8028 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid-expected.txt
@@ -0,0 +1,10 @@ +This is a testharness.js-based test. +FAIL e.style['stroke-width'] = "0" should set the property value assert_equals: serialization should be canonical expected "0px" but got "0" +FAIL e.style['stroke-width'] = "10" should set the property value assert_equals: serialization should be canonical expected "10px" but got "10" +PASS e.style['stroke-width'] = "1px" should set the property value +PASS e.style['stroke-width'] = "calc(2em + 3ex)" should set the property value +PASS e.style['stroke-width'] = "4%" should set the property value +PASS e.style['stroke-width'] = "5vmin" should set the property value +PASS e.style['stroke-width'] = "calc(50% + 60px)" should set the property value +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid.svg b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid.svg index 02bca18..f9078128 100644 --- a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid.svg +++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid.svg
@@ -13,8 +13,8 @@ <h:script src="/css/support/parsing-testcommon.js"/> <script><![CDATA[ -test_valid_value("stroke-width", "0"); -test_valid_value("stroke-width", "10"); +test_valid_value("stroke-width", "0", "0px"); +test_valid_value("stroke-width", "10", "10px"); test_valid_value("stroke-width", "1px"); test_valid_value("stroke-width", "calc(2em + 3ex)"); test_valid_value("stroke-width", "4%");
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-expected.txt index d0cbf2e..c977a68 100644 --- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-expected.txt +++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-expected.txt
@@ -1,5 +1,5 @@ This is a testharness.js-based test. -Found 560 tests; 394 PASS, 166 FAIL, 0 TIMEOUT, 0 NOTRUN. +Found 560 tests; 396 PASS, 164 FAIL, 0 TIMEOUT, 0 NOTRUN. PASS Setup PASS align-content (type: discrete) has testAccumulation function PASS align-content: "flex-end" onto "flex-start" @@ -443,10 +443,10 @@ FAIL stop-opacity: [0, 1] number assert_equals: The value should be 0.6 at 0ms expected "0.6" but got "0.3" FAIL stop-opacity: [0, 1] number (clamped) assert_equals: The value should be 1 at 0ms expected "1" but got "0.3" PASS stroke-dasharray (type: dasharray) has testAccumulation function -FAIL stroke-dasharray: dasharray assert_equals: The value should be 1, 2, 3, 4, 5 at 0ms expected "1, 2, 3, 4, 5" but got "1px, 2px, 3px, 4px, 5px" +PASS stroke-dasharray: dasharray PASS stroke-dasharray (type: discrete) has testAccumulation function -FAIL stroke-dasharray: "10, 20" onto "none" assert_equals: The value should be 10, 20 at 0ms expected "10, 20" but got "10px, 20px" -PASS stroke-dasharray: "none" onto "10, 20" +PASS stroke-dasharray: "10px, 20px" onto "none" +PASS stroke-dasharray: "none" onto "10px, 20px" PASS stroke-linecap (type: discrete) has testAccumulation function PASS stroke-linecap: "square" onto "round" PASS stroke-linecap: "round" onto "square"
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-expected.txt index 93e8476..fa04c36 100644 --- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-expected.txt +++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-expected.txt
@@ -1,5 +1,5 @@ This is a testharness.js-based test. -Found 556 tests; 524 PASS, 32 FAIL, 0 TIMEOUT, 0 NOTRUN. +Found 556 tests; 525 PASS, 31 FAIL, 0 TIMEOUT, 0 NOTRUN. PASS Setup PASS align-content (type: discrete) has testAddition function PASS align-content: "flex-end" onto "flex-start" @@ -443,10 +443,10 @@ PASS stop-opacity: [0, 1] number PASS stop-opacity: [0, 1] number (clamped) PASS stroke-dasharray (type: dasharray) has testAddition function -FAIL stroke-dasharray: dasharray assert_equals: The value should be 1, 2, 3, 4, 5 at 0ms expected "1, 2, 3, 4, 5" but got "7px, calc(30% + 2px), 5px, 10px, calc(30% + 5px), 3px, 8px, calc(30% + 3px), 6px, 11px, calc(30% + 1px), 4px, 9px, calc(30% + 4px), 7px" +FAIL stroke-dasharray: dasharray assert_equals: The value should be 1px, 2px, 3px, 4px, 5px at 0ms expected "1px, 2px, 3px, 4px, 5px" but got "7px, calc(30% + 2px), 5px, 10px, calc(30% + 5px), 3px, 8px, calc(30% + 3px), 6px, 11px, calc(30% + 1px), 4px, 9px, calc(30% + 4px), 7px" PASS stroke-dasharray (type: discrete) has testAddition function -FAIL stroke-dasharray: "10, 20" onto "none" assert_equals: The value should be 10, 20 at 0ms expected "10, 20" but got "10px, 20px" -PASS stroke-dasharray: "none" onto "10, 20" +PASS stroke-dasharray: "10px, 20px" onto "none" +PASS stroke-dasharray: "none" onto "10px, 20px" PASS stroke-linecap (type: discrete) has testAddition function PASS stroke-linecap: "square" onto "round" PASS stroke-linecap: "round" onto "square"
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/interpolation-per-property-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/interpolation-per-property-expected.txt index 05e1e1d2..e7ede0a 100644 --- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/interpolation-per-property-expected.txt +++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/interpolation-per-property-expected.txt
@@ -1,5 +1,5 @@ This is a testharness.js-based test. -Found 693 tests; 640 PASS, 53 FAIL, 0 TIMEOUT, 0 NOTRUN. +Found 693 tests; 646 PASS, 47 FAIL, 0 TIMEOUT, 0 NOTRUN. PASS Setup PASS align-content (type: discrete) has testInterpolation function PASS align-content uses discrete animation when animating between "flex-start" and "flex-end" with linear easing @@ -548,13 +548,13 @@ PASS stop-opacity supports animating as a [0, 1] number PASS stroke-dasharray (type: dasharray) has testInterpolation function PASS stroke-dasharray supports animating as a percentage -FAIL stroke-dasharray supports animating as a positive number assert_equals: The value should be 1.3 at 500ms expected "1.3" but got "1.3px" -FAIL stroke-dasharray supports animating as a dasharray (mismatched length) assert_equals: The value should be 6, 12, 8, 12, 10, 6, 10, 16, 4, 8, 14, 10 at 500ms expected "6, 12, 8, 12, 10, 6, 10, 16, 4, 8, 14, 10" but got "6px, 12px, 8px, 12px, 10px, 6px, 10px, 16px, 4px, 8px, 14px, 10px" -FAIL stroke-dasharray supports animating as a dasharray (mixed number and percentage) assert_equals: The value should be 4, 40%, 4, 6 at 500ms expected "4, 40%, 4, 6" but got "4px, 40%, 4px, 6px" +PASS stroke-dasharray supports animating as a positive number +PASS stroke-dasharray supports animating as a dasharray (mismatched length) +PASS stroke-dasharray supports animating as a dasharray (mixed lengths and percentages) PASS stroke-dasharray (type: discrete) has testInterpolation function -FAIL stroke-dasharray uses discrete animation when animating between "none" and "10, 20" with linear easing assert_equals: The value should be 10, 20 at 500ms expected "10, 20" but got "10px, 20px" -FAIL stroke-dasharray uses discrete animation when animating between "none" and "10, 20" with effect easing assert_equals: The value should be 10, 20 at 960ms expected "10, 20" but got "10px, 20px" -FAIL stroke-dasharray uses discrete animation when animating between "none" and "10, 20" with keyframe easing assert_equals: The value should be 10, 20 at 960ms expected "10, 20" but got "10px, 20px" +PASS stroke-dasharray uses discrete animation when animating between "none" and "10px, 20px" with linear easing +PASS stroke-dasharray uses discrete animation when animating between "none" and "10px, 20px" with effect easing +PASS stroke-dasharray uses discrete animation when animating between "none" and "10px, 20px" with keyframe easing PASS stroke-linecap (type: discrete) has testInterpolation function PASS stroke-linecap uses discrete animation when animating between "round" and "square" with linear easing PASS stroke-linecap uses discrete animation when animating between "round" and "square" with effect easing
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-list.js b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-list.js index 2d45574..8d9b296f 100644 --- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-list.js +++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-list.js
@@ -1218,7 +1218,7 @@ // https://svgwg.org/svg2-draft/painting.html#StrokeDasharrayProperty types: [ 'dasharray', - { type: 'discrete', options: [ [ 'none', '10, 20' ] ] } + { type: 'discrete', options: [ [ 'none', '10px, 20px' ] ] } ] }, 'stroke-dashoffset': {
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-types.js b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-types.js index 64a7eb47..80d3b8f 100644 --- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-types.js +++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-types.js
@@ -473,14 +473,14 @@ }; const positiveNumberType = { - testInterpolation: (property, setup) => { + testInterpolation: (property, setup, expectedUnit='') => { test(t => { const idlName = propertyToIDL(property); const target = createTestElement(t, setup); const animation = target.animate({ [idlName]: [1.1, 1.5] }, { duration: 1000, fill: 'both' }); testAnimationSamples(animation, idlName, - [{ time: 500, expected: '1.3' }]); + [{ time: 500, expected: '1.3' + expectedUnit }]); }, `${property} supports animating as a positive number`); }, @@ -2622,7 +2622,7 @@ const dasharrayType = { testInterpolation: (property, setup) => { percentageType.testInterpolation(property, setup); - positiveNumberType.testInterpolation(property, setup); + positiveNumberType.testInterpolation(property, setup, 'px'); test(t => { const idlName = propertyToIDL(property); @@ -2633,7 +2633,7 @@ { duration: 1000, fill: 'both' }); testAnimationSamples( animation, idlName, - [{ time: 500, expected: '6, 12, 8, 12, 10, 6, 10, 16, 4, 8, 14, 10' }]); + [{ time: 500, expected: '6px, 12px, 8px, 12px, 10px, 6px, 10px, 16px, 4px, 8px, 14px, 10px' }]); }, `${property} supports animating as a dasharray (mismatched length)`); test(t => { @@ -2645,8 +2645,8 @@ { duration: 1000, fill: 'both' }); testAnimationSamples( animation, idlName, - [{ time: 500, expected: '4, 40%, 4, 6' }]); - }, `${property} supports animating as a dasharray (mixed number and percentage)`); + [{ time: 500, expected: '4px, 40%, 4px, 6px' }]); + }, `${property} supports animating as a dasharray (mixed lengths and percentages)`); }, @@ -2665,7 +2665,7 @@ { duration: 1000, composite }); testAnimationSamples( animation, idlName, - [{ time: 0, expected: '1, 2, 3, 4, 5' }]); + [{ time: 0, expected: '1px, 2px, 3px, 4px, 5px' }]); }, `${property}: dasharray`); },
diff --git a/third_party/blink/web_tests/fast/scrolling/wheel-scrolling-over-custom-scrollbar.html b/third_party/blink/web_tests/fast/scrolling/wheel-scrolling-over-custom-scrollbar.html new file mode 100644 index 0000000..51d1ca86 --- /dev/null +++ b/third_party/blink/web_tests/fast/scrolling/wheel-scrolling-over-custom-scrollbar.html
@@ -0,0 +1,83 @@ +<!DOCTYPE> +<title>Wheel scrolling over custom scrollbar should scroll its scroller</title> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script src="../../resources/gesture-util.js"></script> +<style> +::-webkit-scrollbar { + width: 50px; + height: 50px; + background-color: grey; +} +html,body { + margin: 0; +} +.wheelDiv { + width: 100px; + height: 100px; + overflow: auto; + border: 1px solid black; + margin: 20px; +} +.slow { + clip: rect(0px, 1000px, 500px, 0px); +} +.fast { + will-change: transform; +} +.space { + height: 1000px; +} +</style> + +<div class="wheelDiv slow"> + <div class="space">MAIN-THREAD SCROLLING</div> +</div> + +<div class="wheelDiv fast"> + <div class="space">COMPOSITOR-THREAD SCROLLING</div> +</div> + +<script> + +function injectInput() { + return smoothScroll(10, x, y, GestureSourceType.MOUSE_INPUT, "down"); +} + +window.onload = () => { + const divSlow = document.querySelector('.slow'); + const divFast = document.querySelector('.fast'); + const rectSlow = divSlow.getBoundingClientRect(); + const rectFast = divFast.getBoundingClientRect(); + + promise_test (async () => { + await waitForCompositorCommit(); + + const distance = 10; + const source_type = GestureSourceType.MOUSE_INPUT; //Mouse == Wheel + const direction = 'down'; + const use_precise_deltas = true; + let x = rectSlow.right - 30; + let y = rectSlow.top + 50; + + await smoothScroll(distance, + x, y, + source_type, + direction, + SPEED_INSTANT, + use_precise_deltas); + + assert_greater_than(divSlow.scrollTop, 0, "Main-thread scrolling div didn't scroll."); + + x = rectFast.right - 30; + y = rectFast.top + 50; + await smoothScroll(distance, + x, y, + source_type, + direction, + SPEED_INSTANT, + use_precise_deltas); + assert_greater_than(divFast.scrollTop, 0, "Compositor-thread scrolling div didn't scroll."); + }, "Test wheel scrolling over custom scrollbars"); +} +</script>
diff --git a/third_party/blink/web_tests/http/tests/devtools/profiler/live-line-level-heap-profile-expected.txt b/third_party/blink/web_tests/http/tests/devtools/profiler/live-line-level-heap-profile-expected.txt new file mode 100644 index 0000000..392a6c2 --- /dev/null +++ b/third_party/blink/web_tests/http/tests/devtools/profiler/live-line-level-heap-profile-expected.txt
@@ -0,0 +1,5 @@ +Tests that the live line-level heap profile is shown in the text editor. + +allocator.js +Memory annotation added to line 12. +
diff --git a/third_party/blink/web_tests/http/tests/devtools/profiler/live-line-level-heap-profile.js b/third_party/blink/web_tests/http/tests/devtools/profiler/live-line-level-heap-profile.js new file mode 100644 index 0000000..09ed03d7 --- /dev/null +++ b/third_party/blink/web_tests/http/tests/devtools/profiler/live-line-level-heap-profile.js
@@ -0,0 +1,29 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +(async function() { + TestRunner.addResult(`Tests that the live line-level heap profile is shown in the text editor.\n`); + await self.runtime.loadModulePromise('perf_ui'); + await PerfUI.LiveHeapProfile.hasStartedForTest(); + await TestRunner.loadModule('sources_test_runner'); + await TestRunner.showPanel('sources'); + + await TestRunner.evaluateInPagePromise(` + let dump = new Array(10000).fill(42).map(x => Date.now() + '42'); + //# sourceURL=allocator.js`); + + TestRunner.addSniffer(SourceFrame.SourcesTextEditor.prototype, 'setGutterDecoration', decorationAdded, true); + SourcesTestRunner.showScriptSource('allocator.js', frameRevealed); + + function decorationAdded(line, type, element) { + if (line !== 12 || type !== 'CodeMirror-gutter-memory' || !element.textContent || !element.style.backgroundColor) + return; + TestRunner.addResult(`Memory annotation added to line ${line}.`); + TestRunner.completeTest(); + } + + function frameRevealed(frame) { + TestRunner.addResult(TestRunner.formatters.formatAsURL(frame.uiSourceCode().url())); + } +})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-js/timeline-js-line-level-profile-expected.txt b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-js/timeline-js-line-level-profile-expected.txt index bef9e0b..842c89dc 100644 --- a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-js/timeline-js-line-level-profile-expected.txt +++ b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-js/timeline-js-line-level-profile-expected.txt
@@ -1,11 +1,11 @@ Tests that a line-level CPU profile is shown in the text editor. .../devtools/tracing/resources/empty.js -99 CodeMirror-gutter-performance 10.0ms rgba(255, 187, 0, 0.263) -101 CodeMirror-gutter-performance 1900.0ms rgba(255, 187, 0, 0.718) -0 CodeMirror-gutter-performance 100.0ms rgba(255, 187, 0, 0.463) -1 CodeMirror-gutter-performance 200.0ms rgba(255, 187, 0, 0.52) -2 CodeMirror-gutter-performance 300.0ms rgba(255, 187, 0, 0.557) -3 CodeMirror-gutter-performance 400.0ms rgba(255, 187, 0, 0.58) -54 CodeMirror-gutter-performance 220.0ms rgba(255, 187, 0, 0.53) +99 CodeMirror-gutter-performance 10.0ms rgba(255, 187, 0, 0.4) +101 CodeMirror-gutter-performance 1900.0ms rgba(255, 187, 0, 0.855) +0 CodeMirror-gutter-performance 100.0ms rgba(255, 187, 0, 0.6) +1 CodeMirror-gutter-performance 200.0ms rgba(255, 187, 0, 0.66) +2 CodeMirror-gutter-performance 300.0ms rgba(255, 187, 0, 0.694) +3 CodeMirror-gutter-performance 400.0ms rgba(255, 187, 0, 0.72) +54 CodeMirror-gutter-performance 220.0ms rgba(255, 187, 0, 0.67)
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/editing/caret/caret-painting-low-dpi-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/editing/caret/caret-painting-low-dpi-expected.png index d313333..50584fc 100644 --- a/third_party/blink/web_tests/platform/mac-mac10.10/editing/caret/caret-painting-low-dpi-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac10.10/editing/caret/caret-painting-low-dpi-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/editing/caret/caret-painting-low-dpi-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/editing/caret/caret-painting-low-dpi-expected.png index f75e7339..c0d7422 100644 --- a/third_party/blink/web_tests/platform/mac-mac10.11/editing/caret/caret-painting-low-dpi-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac10.11/editing/caret/caret-painting-low-dpi-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/caret/caret-painting-low-dpi-expected.png b/third_party/blink/web_tests/platform/mac/editing/caret/caret-painting-low-dpi-expected.png index 60f1e2bc6..b32819b8 100644 --- a/third_party/blink/web_tests/platform/mac/editing/caret/caret-painting-low-dpi-expected.png +++ b/third_party/blink/web_tests/platform/mac/editing/caret/caret-painting-low-dpi-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/service-worker-csp-connect.https-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/service-worker-csp-connect.https-expected.txt deleted file mode 100644 index 21f1401..0000000 --- a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/service-worker-csp-connect.https-expected.txt +++ /dev/null
@@ -1,7 +0,0 @@ -This is a testharness.js-based test. -PASS CSP test for connect-src in ServiceWorkerGlobalScope -PASS importScripts test for connect-src -FAIL Fetch test for connect-src assert_unreached: unexpected rejection: assert_unreached: fetch should fail. Reached unreachable code Reached unreachable code -FAIL Redirected fetch test for connect-src assert_unreached: unexpected rejection: assert_unreached: Redirected fetch should fail. Reached unreachable code Reached unreachable code -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/service-worker-csp-default.https-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/service-worker-csp-default.https-expected.txt deleted file mode 100644 index d8854f6..0000000 --- a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/service-worker-csp-default.https-expected.txt +++ /dev/null
@@ -1,7 +0,0 @@ -This is a testharness.js-based test. -PASS CSP test for default-src in ServiceWorkerGlobalScope -FAIL importScripts test for default-src assert_true: Importing the other origins script should fail. expected true got false -FAIL Fetch test for default-src assert_unreached: unexpected rejection: assert_unreached: fetch should fail. Reached unreachable code Reached unreachable code -FAIL Redirected fetch test for default-src assert_unreached: unexpected rejection: assert_unreached: Redirected fetch should fail. Reached unreachable code Reached unreachable code -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/service-worker-csp-script.https-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/service-worker-csp-script.https-expected.txt deleted file mode 100644 index 22b5481..0000000 --- a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/service-worker-csp-script.https-expected.txt +++ /dev/null
@@ -1,7 +0,0 @@ -This is a testharness.js-based test. -PASS CSP test for script-src in ServiceWorkerGlobalScope -FAIL importScripts test for script-src assert_true: Importing the other origins script should fail. expected true got false -PASS Fetch test for script-src -PASS Redirected fetch test for script-src -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/xr/xrSession_environmentProviderDisconnect.html b/third_party/blink/web_tests/xr/xrSession_environmentProviderDisconnect.html new file mode 100644 index 0000000..86a9a6f --- /dev/null +++ b/third_party/blink/web_tests/xr/xrSession_environmentProviderDisconnect.html
@@ -0,0 +1,66 @@ +<!DOCTYPE html> +<script src="../resources/testharness.js"></script> +<script src="../resources/testharnessreport.js"></script> +<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script> +<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script> +<script src="../external/wpt/resources/chromium/webxr-test.js"></script> +<script src="../xr/resources/xr-internal-device-mocking.js"></script> +<script src="../xr/resources/xr-test-utils.js"></script> +<script src="../xr/resources/test-constants.js"></script> +<canvas id="webgl-canvas"></canvas> + +<script> + +let testName = "Outstanding promises get rejected if environmentProvider disconencts"; + +let fakeDeviceInitParams = { supportsImmersive: false, + supportsEnvironmentIntegration: true }; + +let requestSessionOptions = [ {mode: 'legacy-inline-ar' } ]; +let refSpace = undefined; + +let ray = new XRRay({x : 0.0, y : 0.0, z : 0.0}, {x : 1.0, y : 0.0, z: 0.0}); + +// Override the xr-internal-device-mock for requestHitTest so that we can +// also return a promise that never resolves or rejects. +// This is so that we can simulate a disconnect while the mojo call is still +// outstanding. +let immediatelyResolveHitTest = true; +MockRuntime.prototype.requestHitTest = function(Ray) { + if (immediatelyResolveHitTest) { + var hit = new device.mojom.XRHitResult(); + hit.hitMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; + hit_results = {results: [hit]}; + return Promise.resolve(hit_results); + } + + return new Promise((resolve,reject) => { }); +} + +let testFunction = function(session, t, controller) { + return session.requestReferenceSpace({ type: "stationary", subtype: "eye-level" }) + .then((referenceSpace) => { + refSpace = referenceSpace; + + // Request a first hit test to ensure that all of the mojo connections are + // up and running. + return session.requestHitTest(ray, refSpace); + }) + .then(() => { + immediatelyResolveHitTest = false; + let hitTestPromise = session.requestHitTest(ray, refSpace); + controller.closeEnvironmentIntegrationProvider(); + return hitTestPromise; + }) + .then(() => { + assert_unreached("HitTestPromise should not resolve"); + }, + (err) => { + assert_equals(err.name, "InvalidStateError"); + }); +}; + +xr_session_promise_test( + testFunction, fakeDeviceInitParams, requestSessionOptions, testName); + +</script>
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index c66b6eb..78478702 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -93,10 +93,10 @@ 'chromium.chrome': { 'chromeos-amd64-generic-google-rel': 'official_cros_chrome_sdk', - 'Google Chrome ChromeOS': 'official_goma_chromeos', - 'Google Chrome Linux x64': 'official_goma', - 'Google Chrome Mac': 'official_goma', - 'Google Chrome Win': 'official_goma_x86', + 'linux-chromeos-google-rel': 'official_goma_chromeos', + 'linux-google-rel': 'official_goma', + 'mac-google-rel': 'official_goma', + 'win-google-rel': 'official_goma_x86', }, 'chromium.chromedriver': {
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml index 0ad97760..f751b2d 100644 --- a/tools/metrics/actions/actions.xml +++ b/tools/metrics/actions/actions.xml
@@ -11727,6 +11727,13 @@ </description> </action> +<action name="MobileNTPExploreSites"> + <owner>dewittj@chromium.org</owner> + <description> + Recorded when a user clicks on an Explore Sites category tile on the NTP. + </description> +</action> + <action name="MobileNTPForeignSession"> <obsolete> Deprecated as of 01/2017. Replaced with @@ -11737,8 +11744,8 @@ </action> <action name="MobileNTPMostVisited"> - <owner>Please list the metric's owners. Add more owner tags as needed.</owner> - <description>Please enter the description of this user action.</description> + <owner>dewittj@chromium.org</owner> + <description>Recorded when a user clicks on a Most Visited tile.</description> </action> <action name="MobileNTPOpenedInNewTab"> @@ -21444,6 +21451,8 @@ <suffix name="ContextualSuggestions" label="For ContextualSuggestions feature."/> <suffix name="DataSaverDetail" label="For DataSaverDetail feature."/> + <suffix name="DataSaverMilestonePromo" + label="For DataSaverMilestonePromo feature."/> <suffix name="DataSaverPreview" label="For DataSaverPreview feature."/> <suffix name="DownloadHome" label="For DownloadHome feature."/> <suffix name="DownloadPage" label="For DownloadPage feature."/>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index d846ae0e..b66568f 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -2227,6 +2227,21 @@ <int value="6" label="PIP free-resize"/> </enum> +<enum name="AshPipPosition"> + <summary> + Defines approximate positions of the Picture-in-picture window + </summary> + <int value="0" label="Middle"/> + <int value="1" label="Top middle"/> + <int value="2" label="Middle left"/> + <int value="3" label="Middle right"/> + <int value="4" label="Bottom middle"/> + <int value="5" label="Top left"/> + <int value="6" label="Top right"/> + <int value="7" label="Bottom left"/> + <int value="8" label="Bottom right"/> +</enum> + <enum name="AssistantButtonId"> <int value="1" label="kBack"/> <int value="2" label="kClose"/> @@ -4054,7 +4069,7 @@ label="OBSOLETE_WC_CONTENT_WITH_CERT_ERRORS_BAD_SECURITY_INFO"/> <int value="102" label="RFMF_RENDERER_FAKED_ITS_OWN_DEATH"/> <int value="103" label="DWNLD_INVALID_SAVABLE_RESOURCE_LINKS_RESPONSE"/> - <int value="104" label="DWNLD_INVALID_SERIALIZE_AS_MHTML_RESPONSE"/> + <int value="104" label="OBSOLETE_DWNLD_INVALID_SERIALIZE_AS_MHTML_RESPONSE"/> <int value="105" label="BDH_DEVICE_NOT_ALLOWED_FOR_ORIGIN"/> <int value="106" label="OBSOLETE_ACI_WRONG_STORAGE_PARTITION"/> <int value="107" label="OBSOLETE_RDHI_WRONG_STORAGE_PARTITION"/> @@ -18439,6 +18454,8 @@ <int value="1321" label="AUTOTESTPRIVATE_GETARCSTATE"/> <int value="1322" label="AUTOTESTPRIVATE_ISTABLETMODEENABLED"/> <int value="1323" label="AUTOTESTPRIVATE_SETTABLETMODEENABLED"/> + <int value="1324" label="AUTOTESTPRIVATE_GETSHELFAUTOHIDEBEHAVIOR"/> + <int value="1325" label="AUTOTESTPRIVATE_SETSHELFAUTOHIDEBEHAVIOR"/> </enum> <enum name="ExtensionIconState"> @@ -32003,6 +32020,7 @@ <int value="-1167992523" label="DesktopPWAsCustomTabUI:disabled"/> <int value="-1166715563" label="ChromeOSAssistant:disabled"/> <int value="-1165191758" label="ForegroundNotificationManager:disabled"/> + <int value="-1163777157" label="ContextualSearchSimplifiedServer:disabled"/> <int value="-1162944097" label="enable-color-correct-rendering"/> <int value="-1161409696" label="MediaRemotingEncrypted:enabled"/> <int value="-1161384421" label="ContextualSuggestionsAboveArticles:enabled"/> @@ -32199,6 +32217,7 @@ <int value="-834661509" label="ModalPermissionPrompts:disabled"/> <int value="-832561975" label="enable-picture-in-picture"/> <int value="-825942229" label="tab-management-experiment-type-elderberry"/> + <int value="-824199802" label="ContextualSearchSimplifiedServer:enabled"/> <int value="-823165021" label="MaterialDesignUserMenu:enabled"/> <int value="-820041355" label="enable-transition-compositing"/> <int value="-816984237" label="OfflinePagesAsyncDownload:enabled"/> @@ -39203,6 +39222,7 @@ <int value="9" label="Clicked on Learn More"/> <int value="10" label="Clicked on the Refresh button in the all dismissed state"/> + <int value="11" label="Opened an explore sites tile"/> </enum> <enum name="NewTabPageActioniOS">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 92e69ce..40d25d3 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -5474,6 +5474,14 @@ </summary> </histogram> +<histogram name="Ash.Pip.Position" enum="AshPipPosition" + expires_after="2019-09-01"> + <owner>edcourtney@chromium.org</owner> + <summary> + The position that a Picture-in-picture window was moved to by a user drag. + </summary> +</histogram> + <histogram name="Ash.PowerButtonScreenshot.DelayBetweenAccelKeyPressed" units="ms"> <owner>warx@chromium.org</owner> @@ -139213,6 +139221,8 @@ label="In product help for contextual suggestions."/> <suffix name="IPH_DataSaverDetail" label="In product help data saver detail."/> + <suffix name="IPH_DataSaverMilestonePromo" + label="In product help data saver milestone promo."/> <suffix name="IPH_DataSaverPreview" label="In product help data saver preview."/> <suffix name="IPH_DownloadHome" label="In product help download home."/>
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn index be0c397..43bfcf4 100644 --- a/ui/base/BUILD.gn +++ b/ui/base/BUILD.gn
@@ -11,7 +11,7 @@ import("//build/util/branding.gni") import("//testing/test.gni") import("//tools/grit/grit_rule.gni") -import("//ui/base/mpris/features.gni") +import("//ui/base/mpris/buildflags/buildflags.gni") import("//ui/base/ui_features.gni") import("//ui/ozone/ozone.gni") @@ -359,6 +359,12 @@ "accelerators/global_media_keys_listener_win.h", "accelerators/media_keys_listener_win.cc", ] + } else if (use_mpris) { + sources += [ + "accelerators/media_keys_listener_linux.cc", + "accelerators/mpris_media_keys_listener.cc", + "accelerators/mpris_media_keys_listener.h", + ] } else { sources += [ "accelerators/media_keys_listener_stub.cc" ] } @@ -453,6 +459,13 @@ } } + if (use_mpris) { + deps += [ + "//dbus", + "//ui/base/mpris", + ] + } + if (use_x11 && use_aura) { sources += [ "cursor/cursor_loader_x11.cc", @@ -1052,7 +1065,11 @@ } if (use_mpris) { - deps += [ "//ui/base/mpris:unit_tests" ] + sources += [ "accelerators/mpris_media_keys_listener_unittest.cc" ] + deps += [ + "//ui/base/mpris:test_support", + "//ui/base/mpris:unit_tests", + ] } if (is_chromeos) {
diff --git a/ui/base/accelerators/media_keys_listener_linux.cc b/ui/base/accelerators/media_keys_listener_linux.cc new file mode 100644 index 0000000..7586095 --- /dev/null +++ b/ui/base/accelerators/media_keys_listener_linux.cc
@@ -0,0 +1,29 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/base/accelerators/media_keys_listener.h" + +#include "ui/base/accelerators/mpris_media_keys_listener.h" + +namespace ui { + +std::unique_ptr<MediaKeysListener> MediaKeysListener::Create( + MediaKeysListener::Delegate* delegate, + MediaKeysListener::Scope scope) { + DCHECK(delegate); + + if (scope == Scope::kGlobal) { + if (!MprisMediaKeysListener::has_instance()) { + auto listener = std::make_unique<MprisMediaKeysListener>(delegate); + listener->Initialize(); + return std::move(listener); + } + // We shouldn't try to create more than one MprisMediaKeysListener + // instance. + NOTREACHED(); + } + return nullptr; +} + +} // namespace ui
diff --git a/ui/base/accelerators/mpris_media_keys_listener.cc b/ui/base/accelerators/mpris_media_keys_listener.cc new file mode 100644 index 0000000..7edd491 --- /dev/null +++ b/ui/base/accelerators/mpris_media_keys_listener.cc
@@ -0,0 +1,123 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/base/accelerators/mpris_media_keys_listener.h" + +#include "ui/base/accelerators/accelerator.h" +#include "ui/base/mpris/mpris_service.h" + +namespace ui { + +// static +bool MprisMediaKeysListener::has_instance_ = false; + +MprisMediaKeysListener::MprisMediaKeysListener( + MediaKeysListener::Delegate* delegate) + : delegate_(delegate) { + DCHECK(delegate_); + DCHECK(!has_instance_); + has_instance_ = true; +} + +MprisMediaKeysListener::~MprisMediaKeysListener() { + DCHECK(has_instance_); + has_instance_ = false; +} + +void MprisMediaKeysListener::Initialize() { + // |service_| can be set for tests. + if (!service_) + service_ = mpris::MprisService::GetInstance(); + DCHECK(service_); + + service_->AddObserver(this); +} + +bool MprisMediaKeysListener::StartWatchingMediaKey(KeyboardCode key_code) { + DCHECK(IsMediaKeycode(key_code)); + + key_codes_.insert(key_code); + + DCHECK(service_); + + switch (key_code) { + case VKEY_MEDIA_PLAY_PAUSE: + service_->SetCanPlay(true); + service_->SetCanPause(true); + break; + case VKEY_MEDIA_NEXT_TRACK: + service_->SetCanGoNext(true); + break; + case VKEY_MEDIA_PREV_TRACK: + service_->SetCanGoPrevious(true); + break; + case VKEY_MEDIA_STOP: + // No properties need to be changed. + break; + default: + NOTREACHED(); + } + + return true; +} + +void MprisMediaKeysListener::StopWatchingMediaKey(KeyboardCode key_code) { + DCHECK(IsMediaKeycode(key_code)); + + key_codes_.erase(key_code); + + DCHECK(service_); + + switch (key_code) { + case VKEY_MEDIA_PLAY_PAUSE: + service_->SetCanPlay(false); + service_->SetCanPause(false); + break; + case VKEY_MEDIA_NEXT_TRACK: + service_->SetCanGoNext(false); + break; + case VKEY_MEDIA_PREV_TRACK: + service_->SetCanGoPrevious(false); + break; + case VKEY_MEDIA_STOP: + // No properties need to be changed. + break; + default: + NOTREACHED(); + } +} + +void MprisMediaKeysListener::OnNext() { + MaybeSendKeyCode(VKEY_MEDIA_NEXT_TRACK); +} + +void MprisMediaKeysListener::OnPrevious() { + MaybeSendKeyCode(VKEY_MEDIA_PREV_TRACK); +} + +void MprisMediaKeysListener::OnPause() { + MaybeSendKeyCode(VKEY_MEDIA_PLAY_PAUSE); +} + +void MprisMediaKeysListener::OnPlayPause() { + MaybeSendKeyCode(VKEY_MEDIA_PLAY_PAUSE); +} + +void MprisMediaKeysListener::OnStop() { + MaybeSendKeyCode(VKEY_MEDIA_STOP); +} + +void MprisMediaKeysListener::OnPlay() { + MaybeSendKeyCode(VKEY_MEDIA_PLAY_PAUSE); +} + +void MprisMediaKeysListener::MaybeSendKeyCode(KeyboardCode key_code) { + if (!key_codes_.contains(key_code)) + return; + + Accelerator accelerator(key_code, /*modifiers=*/0); + delegate_->OnMediaKeysAccelerator(accelerator); +} + +} // namespace ui
diff --git a/ui/base/accelerators/mpris_media_keys_listener.h b/ui/base/accelerators/mpris_media_keys_listener.h new file mode 100644 index 0000000..166b249 --- /dev/null +++ b/ui/base/accelerators/mpris_media_keys_listener.h
@@ -0,0 +1,66 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_ACCELERATORS_MPRIS_MEDIA_KEYS_LISTENER_H_ +#define UI_BASE_ACCELERATORS_MPRIS_MEDIA_KEYS_LISTENER_H_ + +#include "base/containers/flat_set.h" +#include "ui/base/accelerators/media_keys_listener.h" +#include "ui/base/mpris/mpris_service_observer.h" +#include "ui/base/ui_base_export.h" +#include "ui/events/keycodes/keyboard_codes.h" + +namespace mpris { +class MprisService; +} // namespace mpris + +namespace ui { + +// Implementation of MediaKeysListener that uses MprisService to globally listen +// for media key presses. It only allows for a single instance to be created in +// order to prevent conflicts from multiple listeners. +class UI_BASE_EXPORT MprisMediaKeysListener + : public MediaKeysListener, + public mpris::MprisServiceObserver { + public: + explicit MprisMediaKeysListener(MediaKeysListener::Delegate* delegate); + ~MprisMediaKeysListener() override; + + static bool has_instance() { return has_instance_; } + + // Connects with the MprisService. + void Initialize(); + + // MediaKeysListener implementation. + bool StartWatchingMediaKey(KeyboardCode key_code) override; + void StopWatchingMediaKey(KeyboardCode key_code) override; + + // mpris::MprisServiceObserver implementation. + void OnNext() override; + void OnPrevious() override; + void OnPause() override; + void OnPlayPause() override; + void OnStop() override; + void OnPlay() override; + + void SetMprisServiceForTesting(mpris::MprisService* service) { + service_ = service; + } + + private: + static bool has_instance_; + + // Sends the key code to the delegate if the delegate has asked for it. + void MaybeSendKeyCode(KeyboardCode key_code); + + MediaKeysListener::Delegate* delegate_; + base::flat_set<KeyboardCode> key_codes_; + mpris::MprisService* service_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(MprisMediaKeysListener); +}; + +} // namespace ui + +#endif // UI_BASE_ACCELERATORS_MPRIS_MEDIA_KEYS_LISTENER_H_
diff --git a/ui/base/accelerators/mpris_media_keys_listener_unittest.cc b/ui/base/accelerators/mpris_media_keys_listener_unittest.cc new file mode 100644 index 0000000..29b570e --- /dev/null +++ b/ui/base/accelerators/mpris_media_keys_listener_unittest.cc
@@ -0,0 +1,121 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/base/accelerators/mpris_media_keys_listener.h" + +#include <memory> + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/accelerators/accelerator.h" +#include "ui/base/mpris/mock_mpris_service.h" + +using testing::_; +using testing::WithArg; + +namespace ui { + +namespace { + +class MockMediaKeysListenerDelegate : public MediaKeysListener::Delegate { + public: + MockMediaKeysListenerDelegate() = default; + ~MockMediaKeysListenerDelegate() override = default; + + // MediaKeysListener::Delegate implementation. + MOCK_METHOD1(OnMediaKeysAccelerator, void(const Accelerator& accelerator)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockMediaKeysListenerDelegate); +}; + +} // anonymous namespace + +class MprisMediaKeysListenerTest : public testing::Test { + public: + MprisMediaKeysListenerTest() = default; + ~MprisMediaKeysListenerTest() override = default; + + void SetUp() override { + listener_ = std::make_unique<MprisMediaKeysListener>(&delegate_); + listener_->SetMprisServiceForTesting(&mock_mpris_service_); + } + + protected: + mpris::MockMprisService& mock_mpris_service() { return mock_mpris_service_; } + MockMediaKeysListenerDelegate& delegate() { return delegate_; } + MprisMediaKeysListener* listener() { return listener_.get(); } + + private: + mpris::MockMprisService mock_mpris_service_; + MockMediaKeysListenerDelegate delegate_; + std::unique_ptr<MprisMediaKeysListener> listener_; + + DISALLOW_COPY_AND_ASSIGN(MprisMediaKeysListenerTest); +}; + +TEST_F(MprisMediaKeysListenerTest, ListensToMprisService) { + EXPECT_CALL(mock_mpris_service(), AddObserver(listener())); + listener()->Initialize(); +} + +TEST_F(MprisMediaKeysListenerTest, SimplePlayPauseTest) { + // Should be set to true when we start listening for the key. + EXPECT_CALL(mock_mpris_service(), SetCanPlay(true)); + EXPECT_CALL(mock_mpris_service(), SetCanPause(true)); + + EXPECT_CALL(delegate(), OnMediaKeysAccelerator(_)) + .WillOnce(WithArg<0>([](const Accelerator& accelerator) { + EXPECT_EQ(ui::VKEY_MEDIA_PLAY_PAUSE, accelerator.key_code()); + })); + + listener()->Initialize(); + listener()->StartWatchingMediaKey(ui::VKEY_MEDIA_PLAY_PAUSE); + + // Simulate media key press. + listener()->OnPlayPause(); +} + +TEST_F(MprisMediaKeysListenerTest, KeyCanBeReRegistered) { + EXPECT_CALL(mock_mpris_service(), SetCanGoNext(true)).Times(2); + EXPECT_CALL(mock_mpris_service(), SetCanGoNext(false)); + EXPECT_CALL(delegate(), OnMediaKeysAccelerator(_)) + .WillOnce(WithArg<0>([](const Accelerator& accelerator) { + EXPECT_EQ(ui::VKEY_MEDIA_NEXT_TRACK, accelerator.key_code()); + })); + + listener()->Initialize(); + + // Start listening to register the key. + listener()->StartWatchingMediaKey(ui::VKEY_MEDIA_NEXT_TRACK); + + // Stop listening to unregister the key. + listener()->StopWatchingMediaKey(ui::VKEY_MEDIA_NEXT_TRACK); + + // Start listening to re-register the key. + listener()->StartWatchingMediaKey(ui::VKEY_MEDIA_NEXT_TRACK); + + // Simulate media key press. + listener()->OnNext(); +} + +TEST_F(MprisMediaKeysListenerTest, ListenForMultipleKeys) { + // Should be set to true when we start listening for the key. + EXPECT_CALL(mock_mpris_service(), SetCanPlay(true)); + EXPECT_CALL(mock_mpris_service(), SetCanPause(true)); + EXPECT_CALL(mock_mpris_service(), SetCanGoPrevious(true)); + + // Should receive the key presses. + EXPECT_CALL(delegate(), OnMediaKeysAccelerator(_)).Times(2); + + listener()->Initialize(); + listener()->StartWatchingMediaKey(ui::VKEY_MEDIA_PLAY_PAUSE); + listener()->StartWatchingMediaKey(ui::VKEY_MEDIA_PREV_TRACK); + + // Simulate media key press. + listener()->OnPlayPause(); + listener()->OnPrevious(); +} + +} // namespace ui
diff --git a/ui/base/mpris/BUILD.gn b/ui/base/mpris/BUILD.gn index 5d75a52..0e417f812 100644 --- a/ui/base/mpris/BUILD.gn +++ b/ui/base/mpris/BUILD.gn
@@ -2,18 +2,12 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//build/buildflag_header.gni") -import("//ui/base/mpris/features.gni") - -buildflag_header("buildflags") { - header = "buildflags.h" - flags = [ "USE_MPRIS=$use_mpris" ] -} - component("mpris") { sources = [ "mpris_service.cc", "mpris_service.h", + "mpris_service_impl.cc", + "mpris_service_impl.h", "mpris_service_observer.h", ] @@ -29,7 +23,7 @@ source_set("unit_tests") { testonly = true sources = [ - "mpris_service_unittest.cc", + "mpris_service_impl_unittest.cc", ] deps = [ ":mpris", @@ -42,3 +36,18 @@ "//testing/gtest", ] } + +static_library("test_support") { + testonly = true + + sources = [ + "mock_mpris_service.cc", + "mock_mpris_service.h", + ] + + deps = [ + ":mpris", + "//base", + "//testing/gmock", + ] +}
diff --git a/ui/base/mpris/buildflags/BUILD.gn b/ui/base/mpris/buildflags/BUILD.gn new file mode 100644 index 0000000..0c77239 --- /dev/null +++ b/ui/base/mpris/buildflags/BUILD.gn
@@ -0,0 +1,11 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/buildflag_header.gni") +import("//ui/base/mpris/buildflags/buildflags.gni") + +buildflag_header("buildflags") { + header = "buildflags.h" + flags = [ "USE_MPRIS=$use_mpris" ] +}
diff --git a/ui/base/mpris/features.gni b/ui/base/mpris/buildflags/buildflags.gni similarity index 100% rename from ui/base/mpris/features.gni rename to ui/base/mpris/buildflags/buildflags.gni
diff --git a/ui/base/mpris/mock_mpris_service.cc b/ui/base/mpris/mock_mpris_service.cc new file mode 100644 index 0000000..756785f9 --- /dev/null +++ b/ui/base/mpris/mock_mpris_service.cc
@@ -0,0 +1,13 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/base/mpris/mock_mpris_service.h" + +namespace mpris { + +MockMprisService::MockMprisService() = default; + +MockMprisService::~MockMprisService() = default; + +} // namespace mpris
diff --git a/ui/base/mpris/mock_mpris_service.h b/ui/base/mpris/mock_mpris_service.h new file mode 100644 index 0000000..6f95fa8 --- /dev/null +++ b/ui/base/mpris/mock_mpris_service.h
@@ -0,0 +1,40 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_MPRIS_MOCK_MPRIS_SERVICE_H_ +#define UI_BASE_MPRIS_MOCK_MPRIS_SERVICE_H_ + +#include <string> + +#include "base/macros.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "ui/base/mpris/mpris_service.h" + +namespace mpris { + +class MprisServiceObserver; + +// Mock implementation of MprisService for testing. +class MockMprisService : public MprisService { + public: + MockMprisService(); + ~MockMprisService() override; + + // MprisService implementation. + MOCK_METHOD0(StartService, void()); + MOCK_METHOD1(AddObserver, void(MprisServiceObserver* observer)); + MOCK_METHOD1(RemoveObserver, void(MprisServiceObserver* observer)); + MOCK_METHOD1(SetCanGoNext, void(bool value)); + MOCK_METHOD1(SetCanGoPrevious, void(bool value)); + MOCK_METHOD1(SetCanPlay, void(bool value)); + MOCK_METHOD1(SetCanPause, void(bool value)); + MOCK_CONST_METHOD0(GetServiceName, std::string()); + + private: + DISALLOW_COPY_AND_ASSIGN(MockMprisService); +}; + +} // namespace mpris + +#endif // UI_BASE_MPRIS_MOCK_MPRIS_SERVICE_H_
diff --git a/ui/base/mpris/mpris_service.cc b/ui/base/mpris/mpris_service.cc index f04e80e..f9f4df0 100644 --- a/ui/base/mpris/mpris_service.cc +++ b/ui/base/mpris/mpris_service.cc
@@ -4,31 +4,10 @@ #include "ui/base/mpris/mpris_service.h" -#include <memory> -#include <utility> -#include <vector> - -#include "base/bind.h" -#include "base/process/process.h" -#include "base/unguessable_token.h" -#include "base/values.h" -#include "components/dbus/dbus_thread_linux.h" -#include "dbus/bus.h" -#include "dbus/exported_object.h" -#include "dbus/message.h" -#include "dbus/object_path.h" -#include "dbus/property.h" -#include "dbus/values_util.h" -#include "ui/base/mpris/mpris_service_observer.h" +#include "ui/base/mpris/mpris_service_impl.h" namespace mpris { -namespace { - -constexpr int kNumMethodsToExport = 14; - -} // namespace - #if defined(GOOGLE_CHROME_BUILD) const char kMprisAPIServiceNamePrefix[] = "org.mpris.MediaPlayer2.chrome.instance"; @@ -41,383 +20,10 @@ const char kMprisAPIPlayerInterfaceName[] = "org.mpris.MediaPlayer2.Player"; // static -MprisService* MprisService::instance_ = nullptr; - -// static MprisService* MprisService::GetInstance() { - return instance_; + return MprisServiceImpl::GetInstance(); } -MprisService::MprisService() - : service_name_(std::string(kMprisAPIServiceNamePrefix) + - std::to_string(base::Process::Current().Pid())) { - DCHECK_EQ(nullptr, instance_); - instance_ = this; - - InitializeProperties(); -} - -MprisService::~MprisService() { - DCHECK_EQ(instance_, this); - instance_ = nullptr; - - if (bus_) { - dbus_thread_linux::GetTaskRunner()->PostTask( - FROM_HERE, base::BindOnce(&dbus::Bus::ShutdownAndBlock, bus_)); - } -} - -void MprisService::StartService() { - InitializeDbusInterface(); -} - -void MprisService::AddObserver(MprisServiceObserver* observer) { - observers_.AddObserver(observer); - - // If the service is already ready, inform the observer. - if (service_ready_) - observer->OnServiceReady(); -} - -void MprisService::RemoveObserver(MprisServiceObserver* observer) { - observers_.RemoveObserver(observer); -} - -void MprisService::SetCanGoNext(bool value) { - SetPropertyInternal(media_player2_player_properties_, "CanGoNext", - base::Value(value)); -} - -void MprisService::SetCanGoPrevious(bool value) { - SetPropertyInternal(media_player2_player_properties_, "CanGoPrevious", - base::Value(value)); -} - -void MprisService::SetCanPlay(bool value) { - SetPropertyInternal(media_player2_player_properties_, "CanPlay", - base::Value(value)); -} - -void MprisService::SetCanPause(bool value) { - SetPropertyInternal(media_player2_player_properties_, "CanPause", - base::Value(value)); -} - -std::string MprisService::GetServiceName() const { - return service_name_; -} - -void MprisService::InitializeProperties() { - // org.mpris.MediaPlayer2 interface properties. - media_player2_properties_["CanQuit"] = base::Value(false); - media_player2_properties_["CanRaise"] = base::Value(false); - media_player2_properties_["HasTrackList"] = base::Value(false); -#if defined(GOOGLE_CHROME_BUILD) - media_player2_properties_["Identity"] = base::Value("Chrome"); -#else - media_player2_properties_["Identity"] = base::Value("Chromium"); -#endif - media_player2_properties_["SupportedUriSchemes"] = - base::Value(base::Value::Type::LIST); - media_player2_properties_["SupportedMimeTypes"] = - base::Value(base::Value::Type::LIST); - - // org.mpris.MediaPlayer2.Player interface properties. - media_player2_player_properties_["PlaybackStatus"] = base::Value("Stopped"); - media_player2_player_properties_["Rate"] = base::Value(1.0); - media_player2_player_properties_["Metadata"] = - base::Value(base::Value::DictStorage()); - media_player2_player_properties_["Volume"] = base::Value(1.0); - media_player2_player_properties_["Position"] = base::Value(0); - media_player2_player_properties_["MinimumRate"] = base::Value(1.0); - media_player2_player_properties_["MaximumRate"] = base::Value(1.0); - media_player2_player_properties_["CanGoNext"] = base::Value(false); - media_player2_player_properties_["CanGoPrevious"] = base::Value(false); - media_player2_player_properties_["CanPlay"] = base::Value(false); - media_player2_player_properties_["CanPause"] = base::Value(false); - media_player2_player_properties_["CanSeek"] = base::Value(false); - media_player2_player_properties_["CanControl"] = base::Value(true); -} - -void MprisService::InitializeDbusInterface() { - // Bus may be set for testing. - if (!bus_) { - dbus::Bus::Options bus_options; - bus_options.bus_type = dbus::Bus::SESSION; - bus_options.connection_type = dbus::Bus::PRIVATE; - bus_options.dbus_task_runner = dbus_thread_linux::GetTaskRunner(); - bus_ = base::MakeRefCounted<dbus::Bus>(bus_options); - } - - exported_object_ = - bus_->GetExportedObject(dbus::ObjectPath(kMprisAPIObjectPath)); - int num_methods_attempted_to_export = 0; - - // Helper lambdas for exporting methods while keeping track of the number of - // exported methods. - auto export_method = - [&](const std::string& interface_name, const std::string& method_name, - dbus::ExportedObject::MethodCallCallback method_call_callback) { - exported_object_->ExportMethod( - interface_name, method_name, method_call_callback, - base::BindRepeating(&MprisService::OnExported, - base::Unretained(this))); - num_methods_attempted_to_export++; - }; - auto export_unhandled_method = [&](const std::string& interface_name, - const std::string& method_name) { - export_method( - interface_name, method_name, - base::BindRepeating(&MprisService::DoNothing, base::Unretained(this))); - }; - - // Set up org.mpris.MediaPlayer2 interface. - // https://specifications.freedesktop.org/mpris-spec/2.2/Media_Player.html - export_unhandled_method(kMprisAPIInterfaceName, "Raise"); - export_unhandled_method(kMprisAPIInterfaceName, "Quit"); - - // Set up org.mpris.MediaPlayer2.Player interface. - // https://specifications.freedesktop.org/mpris-spec/2.2/Player_Interface.html - export_method( - kMprisAPIPlayerInterfaceName, "Next", - base::BindRepeating(&MprisService::Next, base::Unretained(this))); - export_method( - kMprisAPIPlayerInterfaceName, "Previous", - base::BindRepeating(&MprisService::Previous, base::Unretained(this))); - export_method( - kMprisAPIPlayerInterfaceName, "Pause", - base::BindRepeating(&MprisService::Pause, base::Unretained(this))); - export_method( - kMprisAPIPlayerInterfaceName, "PlayPause", - base::BindRepeating(&MprisService::PlayPause, base::Unretained(this))); - export_method( - kMprisAPIPlayerInterfaceName, "Stop", - base::BindRepeating(&MprisService::Stop, base::Unretained(this))); - export_method( - kMprisAPIPlayerInterfaceName, "Play", - base::BindRepeating(&MprisService::Play, base::Unretained(this))); - export_unhandled_method(kMprisAPIPlayerInterfaceName, "Seek"); - export_unhandled_method(kMprisAPIPlayerInterfaceName, "SetPosition"); - export_unhandled_method(kMprisAPIPlayerInterfaceName, "OpenUri"); - - // Set up org.freedesktop.DBus.Properties interface. - // https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties - export_method(dbus::kPropertiesInterface, dbus::kPropertiesGetAll, - base::BindRepeating(&MprisService::GetAllProperties, - base::Unretained(this))); - export_method( - dbus::kPropertiesInterface, dbus::kPropertiesGet, - base::BindRepeating(&MprisService::GetProperty, base::Unretained(this))); - export_unhandled_method(dbus::kPropertiesInterface, dbus::kPropertiesSet); - - DCHECK_EQ(kNumMethodsToExport, num_methods_attempted_to_export); -} - -void MprisService::OnExported(const std::string& interface_name, - const std::string& method_name, - bool success) { - if (success) - num_methods_exported_++; - - // Still waiting for more methods to finish exporting. - if (num_methods_exported_ < kNumMethodsToExport) - return; - - bus_->RequestOwnership( - service_name_, dbus::Bus::ServiceOwnershipOptions::REQUIRE_PRIMARY, - base::BindRepeating(&MprisService::OnOwnership, base::Unretained(this))); -} - -void MprisService::OnOwnership(const std::string& service_name, bool success) { - DCHECK(success); - service_ready_ = true; - - for (MprisServiceObserver& obs : observers_) - obs.OnServiceReady(); -} - -void MprisService::Next(dbus::MethodCall* method_call, - dbus::ExportedObject::ResponseSender response_sender) { - for (MprisServiceObserver& obs : observers_) - obs.OnNext(); - response_sender.Run(dbus::Response::FromMethodCall(method_call)); -} - -void MprisService::Previous( - dbus::MethodCall* method_call, - dbus::ExportedObject::ResponseSender response_sender) { - for (MprisServiceObserver& obs : observers_) - obs.OnPrevious(); - response_sender.Run(dbus::Response::FromMethodCall(method_call)); -} - -void MprisService::Pause(dbus::MethodCall* method_call, - dbus::ExportedObject::ResponseSender response_sender) { - for (MprisServiceObserver& obs : observers_) - obs.OnPause(); - response_sender.Run(dbus::Response::FromMethodCall(method_call)); -} - -void MprisService::PlayPause( - dbus::MethodCall* method_call, - dbus::ExportedObject::ResponseSender response_sender) { - for (MprisServiceObserver& obs : observers_) - obs.OnPlayPause(); - response_sender.Run(dbus::Response::FromMethodCall(method_call)); -} - -void MprisService::Stop(dbus::MethodCall* method_call, - dbus::ExportedObject::ResponseSender response_sender) { - for (MprisServiceObserver& obs : observers_) - obs.OnStop(); - response_sender.Run(dbus::Response::FromMethodCall(method_call)); -} - -void MprisService::Play(dbus::MethodCall* method_call, - dbus::ExportedObject::ResponseSender response_sender) { - for (MprisServiceObserver& obs : observers_) - obs.OnPlay(); - response_sender.Run(dbus::Response::FromMethodCall(method_call)); -} - -void MprisService::DoNothing( - dbus::MethodCall* method_call, - dbus::ExportedObject::ResponseSender response_sender) { - response_sender.Run(dbus::Response::FromMethodCall(method_call)); -} - -// org.freedesktop.DBus.Properties.GetAll(in STRING interface_name, -// out DICT<STRING,VARIANT> props); -void MprisService::GetAllProperties( - dbus::MethodCall* method_call, - dbus::ExportedObject::ResponseSender response_sender) { - dbus::MessageReader reader(method_call); - std::string interface; - if (!reader.PopString(&interface)) { - response_sender.Run(nullptr); - return; - } - - std::unique_ptr<dbus::Response> response = - dbus::Response::FromMethodCall(method_call); - dbus::MessageWriter writer(response.get()); - - if (interface == kMprisAPIInterfaceName) { - AddPropertiesToWriter(&writer, media_player2_properties_); - } else if (interface == kMprisAPIPlayerInterfaceName) { - AddPropertiesToWriter(&writer, media_player2_player_properties_); - } else if (interface == dbus::kPropertiesInterface) { - // There are no properties to give for this interface. - PropertyMap empty_properties_map; - AddPropertiesToWriter(&writer, empty_properties_map); - } else { - // The given interface is not supported, so return a null response. - response_sender.Run(nullptr); - return; - } - - response_sender.Run(std::move(response)); -} - -// org.freedesktop.DBus.Properties.Get(in STRING interface_name, -// in STRING property_name, -// out VARIANT value); -void MprisService::GetProperty( - dbus::MethodCall* method_call, - dbus::ExportedObject::ResponseSender response_sender) { - dbus::MessageReader reader(method_call); - std::string interface; - if (!reader.PopString(&interface)) { - response_sender.Run(nullptr); - return; - } - - std::string property_name; - if (!reader.PopString(&property_name)) { - response_sender.Run(nullptr); - return; - } - - std::unique_ptr<dbus::Response> response = - dbus::Response::FromMethodCall(method_call); - dbus::MessageWriter writer(response.get()); - - bool success = false; - if (interface == kMprisAPIInterfaceName) { - auto property_iter = media_player2_properties_.find(property_name); - if (property_iter != media_player2_properties_.end()) { - dbus::AppendValueDataAsVariant(&writer, property_iter->second); - success = true; - } - } else if (interface == kMprisAPIPlayerInterfaceName) { - auto property_iter = media_player2_player_properties_.find(property_name); - if (property_iter != media_player2_player_properties_.end()) { - dbus::AppendValueDataAsVariant(&writer, property_iter->second); - success = true; - } - } - - // If we don't support the given property, return a null response. - if (!success) { - response_sender.Run(nullptr); - return; - } - - response_sender.Run(std::move(response)); -} - -void MprisService::AddPropertiesToWriter(dbus::MessageWriter* writer, - const PropertyMap& properties) { - DCHECK(writer); - - dbus::MessageWriter array_writer(nullptr); - dbus::MessageWriter dict_entry_writer(nullptr); - - writer->OpenArray("{sv}", &array_writer); - - for (auto& property : properties) { - array_writer.OpenDictEntry(&dict_entry_writer); - dict_entry_writer.AppendString(property.first); - dbus::AppendValueDataAsVariant(&dict_entry_writer, property.second); - array_writer.CloseContainer(&dict_entry_writer); - } - - writer->CloseContainer(&array_writer); -} - -void MprisService::SetPropertyInternal(PropertyMap& property_map, - const std::string& property_name, - const base::Value& new_value) { - if (property_map[property_name] == new_value) - return; - - property_map[property_name] = new_value.Clone(); - - PropertyMap changed_properties; - changed_properties[property_name] = new_value.Clone(); - EmitPropertiesChangedSignal(changed_properties); -} - -void MprisService::EmitPropertiesChangedSignal( - const PropertyMap& changed_properties) { - if (!bus_ || !exported_object_) - return; - - // |signal| follows the PropertiesChanged API: - // org.freedesktop.DBus.Properties.PropertiesChanged( - // STRING interface_name, - // DICT<STRING,VARIANT> changed_properties, - // ARRAY<STRING> invalidated_properties); - dbus::Signal signal(dbus::kPropertiesInterface, dbus::kPropertiesChanged); - dbus::MessageWriter writer(&signal); - writer.AppendString(kMprisAPIPlayerInterfaceName); - - AddPropertiesToWriter(&writer, changed_properties); - - std::vector<std::string> empty_invalidated_properties; - writer.AppendArrayOfStrings(empty_invalidated_properties); - - exported_object_->SendSignal(&signal); -} +MprisService::~MprisService() = default; } // namespace mpris
diff --git a/ui/base/mpris/mpris_service.h b/ui/base/mpris/mpris_service.h index 03f66fe..f6103ec 100644 --- a/ui/base/mpris/mpris_service.h +++ b/ui/base/mpris/mpris_service.h
@@ -8,20 +8,6 @@ #include <string> #include "base/component_export.h" -#include "base/containers/flat_map.h" -#include "base/memory/scoped_refptr.h" -#include "base/observer_list.h" -#include "dbus/bus.h" -#include "dbus/exported_object.h" - -namespace base { -class Value; -} // namespace base - -namespace dbus { -class MessageWriter; -class MethodCall; -} // namespace dbus namespace mpris { @@ -36,103 +22,26 @@ // https://specifications.freedesktop.org/mpris-spec/latest/ class COMPONENT_EXPORT(MPRIS) MprisService { public: - MprisService(); - ~MprisService(); - + // Returns the singleton instance, creating if necessary. static MprisService* GetInstance(); // Starts the DBus service. - void StartService(); + virtual void StartService() = 0; - void AddObserver(MprisServiceObserver* observer); - void RemoveObserver(MprisServiceObserver* observer); + virtual void AddObserver(MprisServiceObserver* observer) = 0; + virtual void RemoveObserver(MprisServiceObserver* observer) = 0; // Setters for properties. - void SetCanGoNext(bool value); - void SetCanGoPrevious(bool value); - void SetCanPlay(bool value); - void SetCanPause(bool value); + virtual void SetCanGoNext(bool value) = 0; + virtual void SetCanGoPrevious(bool value) = 0; + virtual void SetCanPlay(bool value) = 0; + virtual void SetCanPause(bool value) = 0; // Returns the generated service name. - std::string GetServiceName() const; + virtual std::string GetServiceName() const = 0; - // Used for testing with a mock DBus Bus. - void SetBusForTesting(scoped_refptr<dbus::Bus> bus) { bus_ = bus; } - - private: - // MprisService can only have one instance. This holds a pointer to that - // instance. - static MprisService* instance_; - - void InitializeProperties(); - void InitializeDbusInterface(); - void OnExported(const std::string& interface_name, - const std::string& method_name, - bool success); - void OnOwnership(const std::string& service_name, bool success); - - // org.mpris.MediaPlayer2.Player interface. - void Next(dbus::MethodCall* method_call, - dbus::ExportedObject::ResponseSender response_sender); - void Previous(dbus::MethodCall* method_call, - dbus::ExportedObject::ResponseSender response_sender); - void Pause(dbus::MethodCall* method_call, - dbus::ExportedObject::ResponseSender response_sender); - void PlayPause(dbus::MethodCall* method_call, - dbus::ExportedObject::ResponseSender response_sender); - void Stop(dbus::MethodCall* method_call, - dbus::ExportedObject::ResponseSender response_sender); - void Play(dbus::MethodCall* method_call, - dbus::ExportedObject::ResponseSender response_sender); - - // Used for API methods we don't support. - void DoNothing(dbus::MethodCall* method_call, - dbus::ExportedObject::ResponseSender response_sender); - - // org.freedesktop.DBus.Properties interface. - void GetAllProperties(dbus::MethodCall* method_call, - dbus::ExportedObject::ResponseSender response_sender); - void GetProperty(dbus::MethodCall* method_call, - dbus::ExportedObject::ResponseSender response_sender); - - using PropertyMap = base::flat_map<std::string, base::Value>; - - // Sets a value on the given PropertyMap and sends a PropertiesChanged signal - // if necessary. - void SetPropertyInternal(PropertyMap& property_map, - const std::string& property_name, - const base::Value& new_value); - - // Emits a org.freedesktop.DBus.Properties.PropertiesChanged signal for the - // given map of changed properties. - void EmitPropertiesChangedSignal(const PropertyMap& changed_properties); - - // Writes all properties onto writer. - void AddPropertiesToWriter(dbus::MessageWriter* writer, - const PropertyMap& properties); - - // Map of org.mpris.MediaPlayer2 interface properties. - PropertyMap media_player2_properties_; - - // Map of org.mpris.MediaPlayer2.Player interface properties. - PropertyMap media_player2_player_properties_; - - scoped_refptr<dbus::Bus> bus_; - dbus::ExportedObject* exported_object_; - - // The generated service name given to |bus_| when requesting ownership. - const std::string service_name_; - - // The number of methods that have been successfully exported through - // |exported_object_|. - int num_methods_exported_ = 0; - - // True if we have finished creating the DBus service and received ownership. - bool service_ready_ = false; - - base::ObserverList<MprisServiceObserver> observers_; - - DISALLOW_COPY_AND_ASSIGN(MprisService); + protected: + virtual ~MprisService(); }; } // namespace mpris
diff --git a/ui/base/mpris/mpris_service_impl.cc b/ui/base/mpris/mpris_service_impl.cc new file mode 100644 index 0000000..1ba28f8 --- /dev/null +++ b/ui/base/mpris/mpris_service_impl.cc
@@ -0,0 +1,410 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/base/mpris/mpris_service_impl.h" + +#include <memory> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/process/process.h" +#include "base/unguessable_token.h" +#include "base/values.h" +#include "components/dbus/dbus_thread_linux.h" +#include "dbus/bus.h" +#include "dbus/exported_object.h" +#include "dbus/message.h" +#include "dbus/object_path.h" +#include "dbus/property.h" +#include "dbus/values_util.h" +#include "ui/base/mpris/mpris_service_observer.h" + +namespace mpris { + +namespace { + +constexpr int kNumMethodsToExport = 14; + +} // namespace + +// static +MprisServiceImpl* MprisServiceImpl::GetInstance() { + return base::Singleton<MprisServiceImpl>::get(); +} + +MprisServiceImpl::MprisServiceImpl() + : service_name_(std::string(kMprisAPIServiceNamePrefix) + + std::to_string(base::Process::Current().Pid())) { + InitializeProperties(); +} + +MprisServiceImpl::~MprisServiceImpl() { + if (bus_) { + dbus_thread_linux::GetTaskRunner()->PostTask( + FROM_HERE, base::BindOnce(&dbus::Bus::ShutdownAndBlock, bus_)); + } +} + +void MprisServiceImpl::StartService() { + InitializeDbusInterface(); +} + +void MprisServiceImpl::AddObserver(MprisServiceObserver* observer) { + observers_.AddObserver(observer); + + // If the service is already ready, inform the observer. + if (service_ready_) + observer->OnServiceReady(); +} + +void MprisServiceImpl::RemoveObserver(MprisServiceObserver* observer) { + observers_.RemoveObserver(observer); +} + +void MprisServiceImpl::SetCanGoNext(bool value) { + SetPropertyInternal(media_player2_player_properties_, "CanGoNext", + base::Value(value)); +} + +void MprisServiceImpl::SetCanGoPrevious(bool value) { + SetPropertyInternal(media_player2_player_properties_, "CanGoPrevious", + base::Value(value)); +} + +void MprisServiceImpl::SetCanPlay(bool value) { + SetPropertyInternal(media_player2_player_properties_, "CanPlay", + base::Value(value)); +} + +void MprisServiceImpl::SetCanPause(bool value) { + SetPropertyInternal(media_player2_player_properties_, "CanPause", + base::Value(value)); +} + +std::string MprisServiceImpl::GetServiceName() const { + return service_name_; +} + +void MprisServiceImpl::InitializeProperties() { + // org.mpris.MediaPlayer2 interface properties. + media_player2_properties_["CanQuit"] = base::Value(false); + media_player2_properties_["CanRaise"] = base::Value(false); + media_player2_properties_["HasTrackList"] = base::Value(false); + +#if defined(GOOGLE_CHROME_BUILD) + media_player2_properties_["Identity"] = base::Value("Chrome"); +#else + media_player2_properties_["Identity"] = base::Value("Chromium"); +#endif + media_player2_properties_["SupportedUriSchemes"] = + base::Value(base::Value::Type::LIST); + media_player2_properties_["SupportedMimeTypes"] = + base::Value(base::Value::Type::LIST); + + // org.mpris.MediaPlayer2.Player interface properties. + media_player2_player_properties_["PlaybackStatus"] = base::Value("Stopped"); + media_player2_player_properties_["Rate"] = base::Value(1.0); + media_player2_player_properties_["Metadata"] = + base::Value(base::Value::DictStorage()); + media_player2_player_properties_["Volume"] = base::Value(1.0); + media_player2_player_properties_["Position"] = base::Value(0); + media_player2_player_properties_["MinimumRate"] = base::Value(1.0); + media_player2_player_properties_["MaximumRate"] = base::Value(1.0); + media_player2_player_properties_["CanGoNext"] = base::Value(false); + media_player2_player_properties_["CanGoPrevious"] = base::Value(false); + media_player2_player_properties_["CanPlay"] = base::Value(false); + media_player2_player_properties_["CanPause"] = base::Value(false); + media_player2_player_properties_["CanSeek"] = base::Value(false); + media_player2_player_properties_["CanControl"] = base::Value(true); +} + +void MprisServiceImpl::InitializeDbusInterface() { + // Bus may be set for testing. + if (!bus_) { + dbus::Bus::Options bus_options; + bus_options.bus_type = dbus::Bus::SESSION; + bus_options.connection_type = dbus::Bus::PRIVATE; + bus_options.dbus_task_runner = dbus_thread_linux::GetTaskRunner(); + bus_ = base::MakeRefCounted<dbus::Bus>(bus_options); + } + + exported_object_ = + bus_->GetExportedObject(dbus::ObjectPath(kMprisAPIObjectPath)); + int num_methods_attempted_to_export = 0; + + // Helper lambdas for exporting methods while keeping track of the number of + // exported methods. + auto export_method = + [&](const std::string& interface_name, const std::string& method_name, + dbus::ExportedObject::MethodCallCallback method_call_callback) { + exported_object_->ExportMethod( + interface_name, method_name, method_call_callback, + base::BindRepeating(&MprisServiceImpl::OnExported, + base::Unretained(this))); + num_methods_attempted_to_export++; + }; + auto export_unhandled_method = [&](const std::string& interface_name, + const std::string& method_name) { + export_method(interface_name, method_name, + base::BindRepeating(&MprisServiceImpl::DoNothing, + base::Unretained(this))); + }; + + // Set up org.mpris.MediaPlayer2 interface. + // https://specifications.freedesktop.org/mpris-spec/2.2/Media_Player.html + export_unhandled_method(kMprisAPIInterfaceName, "Raise"); + export_unhandled_method(kMprisAPIInterfaceName, "Quit"); + + // Set up org.mpris.MediaPlayer2.Player interface. + // https://specifications.freedesktop.org/mpris-spec/2.2/Player_Interface.html + export_method( + kMprisAPIPlayerInterfaceName, "Next", + base::BindRepeating(&MprisServiceImpl::Next, base::Unretained(this))); + export_method( + kMprisAPIPlayerInterfaceName, "Previous", + base::BindRepeating(&MprisServiceImpl::Previous, base::Unretained(this))); + export_method( + kMprisAPIPlayerInterfaceName, "Pause", + base::BindRepeating(&MprisServiceImpl::Pause, base::Unretained(this))); + export_method(kMprisAPIPlayerInterfaceName, "PlayPause", + base::BindRepeating(&MprisServiceImpl::PlayPause, + base::Unretained(this))); + export_method( + kMprisAPIPlayerInterfaceName, "Stop", + base::BindRepeating(&MprisServiceImpl::Stop, base::Unretained(this))); + export_method( + kMprisAPIPlayerInterfaceName, "Play", + base::BindRepeating(&MprisServiceImpl::Play, base::Unretained(this))); + export_unhandled_method(kMprisAPIPlayerInterfaceName, "Seek"); + export_unhandled_method(kMprisAPIPlayerInterfaceName, "SetPosition"); + export_unhandled_method(kMprisAPIPlayerInterfaceName, "OpenUri"); + + // Set up org.freedesktop.DBus.Properties interface. + // https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties + export_method(dbus::kPropertiesInterface, dbus::kPropertiesGetAll, + base::BindRepeating(&MprisServiceImpl::GetAllProperties, + base::Unretained(this))); + export_method(dbus::kPropertiesInterface, dbus::kPropertiesGet, + base::BindRepeating(&MprisServiceImpl::GetProperty, + base::Unretained(this))); + export_unhandled_method(dbus::kPropertiesInterface, dbus::kPropertiesSet); + + DCHECK_EQ(kNumMethodsToExport, num_methods_attempted_to_export); +} + +void MprisServiceImpl::OnExported(const std::string& interface_name, + const std::string& method_name, + bool success) { + if (success) + num_methods_exported_++; + + // Still waiting for more methods to finish exporting. + if (num_methods_exported_ < kNumMethodsToExport) + return; + + bus_->RequestOwnership(service_name_, + dbus::Bus::ServiceOwnershipOptions::REQUIRE_PRIMARY, + base::BindRepeating(&MprisServiceImpl::OnOwnership, + base::Unretained(this))); +} + +void MprisServiceImpl::OnOwnership(const std::string& service_name, + bool success) { + DCHECK(success); + service_ready_ = true; + + for (MprisServiceObserver& obs : observers_) + obs.OnServiceReady(); +} + +void MprisServiceImpl::Next( + dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + for (MprisServiceObserver& obs : observers_) + obs.OnNext(); + response_sender.Run(dbus::Response::FromMethodCall(method_call)); +} + +void MprisServiceImpl::Previous( + dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + for (MprisServiceObserver& obs : observers_) + obs.OnPrevious(); + response_sender.Run(dbus::Response::FromMethodCall(method_call)); +} + +void MprisServiceImpl::Pause( + dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + for (MprisServiceObserver& obs : observers_) + obs.OnPause(); + response_sender.Run(dbus::Response::FromMethodCall(method_call)); +} + +void MprisServiceImpl::PlayPause( + dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + for (MprisServiceObserver& obs : observers_) + obs.OnPlayPause(); + response_sender.Run(dbus::Response::FromMethodCall(method_call)); +} + +void MprisServiceImpl::Stop( + dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + for (MprisServiceObserver& obs : observers_) + obs.OnStop(); + response_sender.Run(dbus::Response::FromMethodCall(method_call)); +} + +void MprisServiceImpl::Play( + dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + for (MprisServiceObserver& obs : observers_) + obs.OnPlay(); + response_sender.Run(dbus::Response::FromMethodCall(method_call)); +} + +void MprisServiceImpl::DoNothing( + dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + response_sender.Run(dbus::Response::FromMethodCall(method_call)); +} + +// org.freedesktop.DBus.Properties.GetAll(in STRING interface_name, +// out DICT<STRING,VARIANT> props); +void MprisServiceImpl::GetAllProperties( + dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + dbus::MessageReader reader(method_call); + std::string interface; + if (!reader.PopString(&interface)) { + response_sender.Run(nullptr); + return; + } + + std::unique_ptr<dbus::Response> response = + dbus::Response::FromMethodCall(method_call); + dbus::MessageWriter writer(response.get()); + + if (interface == kMprisAPIInterfaceName) { + AddPropertiesToWriter(&writer, media_player2_properties_); + } else if (interface == kMprisAPIPlayerInterfaceName) { + AddPropertiesToWriter(&writer, media_player2_player_properties_); + } else if (interface == dbus::kPropertiesInterface) { + // There are no properties to give for this interface. + PropertyMap empty_properties_map; + AddPropertiesToWriter(&writer, empty_properties_map); + } else { + // The given interface is not supported, so return a null response. + response_sender.Run(nullptr); + return; + } + + response_sender.Run(std::move(response)); +} + +// org.freedesktop.DBus.Properties.Get(in STRING interface_name, +// in STRING property_name, +// out VARIANT value); +void MprisServiceImpl::GetProperty( + dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + dbus::MessageReader reader(method_call); + std::string interface; + if (!reader.PopString(&interface)) { + response_sender.Run(nullptr); + return; + } + + std::string property_name; + if (!reader.PopString(&property_name)) { + response_sender.Run(nullptr); + return; + } + + std::unique_ptr<dbus::Response> response = + dbus::Response::FromMethodCall(method_call); + dbus::MessageWriter writer(response.get()); + + bool success = false; + if (interface == kMprisAPIInterfaceName) { + auto property_iter = media_player2_properties_.find(property_name); + if (property_iter != media_player2_properties_.end()) { + dbus::AppendValueDataAsVariant(&writer, property_iter->second); + success = true; + } + } else if (interface == kMprisAPIPlayerInterfaceName) { + auto property_iter = media_player2_player_properties_.find(property_name); + if (property_iter != media_player2_player_properties_.end()) { + dbus::AppendValueDataAsVariant(&writer, property_iter->second); + success = true; + } + } + + // If we don't support the given property, return a null response. + if (!success) { + response_sender.Run(nullptr); + return; + } + + response_sender.Run(std::move(response)); +} + +void MprisServiceImpl::AddPropertiesToWriter(dbus::MessageWriter* writer, + const PropertyMap& properties) { + DCHECK(writer); + + dbus::MessageWriter array_writer(nullptr); + dbus::MessageWriter dict_entry_writer(nullptr); + + writer->OpenArray("{sv}", &array_writer); + + for (auto& property : properties) { + array_writer.OpenDictEntry(&dict_entry_writer); + dict_entry_writer.AppendString(property.first); + dbus::AppendValueDataAsVariant(&dict_entry_writer, property.second); + array_writer.CloseContainer(&dict_entry_writer); + } + + writer->CloseContainer(&array_writer); +} + +void MprisServiceImpl::SetPropertyInternal(PropertyMap& property_map, + const std::string& property_name, + const base::Value& new_value) { + if (property_map[property_name] == new_value) + return; + + property_map[property_name] = new_value.Clone(); + + PropertyMap changed_properties; + changed_properties[property_name] = new_value.Clone(); + EmitPropertiesChangedSignal(changed_properties); +} + +void MprisServiceImpl::EmitPropertiesChangedSignal( + const PropertyMap& changed_properties) { + if (!bus_ || !exported_object_) + return; + + // |signal| follows the PropertiesChanged API: + // org.freedesktop.DBus.Properties.PropertiesChanged( + // STRING interface_name, + // DICT<STRING,VARIANT> changed_properties, + // ARRAY<STRING> invalidated_properties); + dbus::Signal signal(dbus::kPropertiesInterface, dbus::kPropertiesChanged); + dbus::MessageWriter writer(&signal); + writer.AppendString(kMprisAPIPlayerInterfaceName); + + AddPropertiesToWriter(&writer, changed_properties); + + std::vector<std::string> empty_invalidated_properties; + writer.AppendArrayOfStrings(empty_invalidated_properties); + + exported_object_->SendSignal(&signal); +} + +} // namespace mpris
diff --git a/ui/base/mpris/mpris_service_impl.h b/ui/base/mpris/mpris_service_impl.h new file mode 100644 index 0000000..e0cdbae0 --- /dev/null +++ b/ui/base/mpris/mpris_service_impl.h
@@ -0,0 +1,128 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_MPRIS_MPRIS_SERVICE_IMPL_H_ +#define UI_BASE_MPRIS_MPRIS_SERVICE_IMPL_H_ + +#include <string> + +#include "base/component_export.h" +#include "base/containers/flat_map.h" +#include "base/memory/scoped_refptr.h" +#include "base/memory/singleton.h" +#include "base/observer_list.h" +#include "dbus/bus.h" +#include "dbus/exported_object.h" +#include "ui/base/mpris/mpris_service.h" + +namespace base { +class Value; +} // namespace base + +namespace dbus { +class MessageWriter; +class MethodCall; +} // namespace dbus + +namespace mpris { + +class MprisServiceObserver; + +// A D-Bus service conforming to the MPRIS spec: +// https://specifications.freedesktop.org/mpris-spec/latest/ +class COMPONENT_EXPORT(MPRIS) MprisServiceImpl : public MprisService { + public: + MprisServiceImpl(); + ~MprisServiceImpl() override; + + static MprisServiceImpl* GetInstance(); + + // MprisService implementation. + void StartService() override; + void AddObserver(MprisServiceObserver* observer) override; + void RemoveObserver(MprisServiceObserver* observer) override; + void SetCanGoNext(bool value) override; + void SetCanGoPrevious(bool value) override; + void SetCanPlay(bool value) override; + void SetCanPause(bool value) override; + std::string GetServiceName() const override; + + // Used for testing with a mock DBus Bus. + void SetBusForTesting(scoped_refptr<dbus::Bus> bus) { bus_ = bus; } + + private: + void InitializeProperties(); + void InitializeDbusInterface(); + void OnExported(const std::string& interface_name, + const std::string& method_name, + bool success); + void OnOwnership(const std::string& service_name, bool success); + + // org.mpris.MediaPlayer2.Player interface. + void Next(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender); + void Previous(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender); + void Pause(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender); + void PlayPause(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender); + void Stop(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender); + void Play(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender); + + // Used for API methods we don't support. + void DoNothing(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender); + + // org.freedesktop.DBus.Properties interface. + void GetAllProperties(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender); + void GetProperty(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender); + + using PropertyMap = base::flat_map<std::string, base::Value>; + + // Sets a value on the given PropertyMap and sends a PropertiesChanged signal + // if necessary. + void SetPropertyInternal(PropertyMap& property_map, + const std::string& property_name, + const base::Value& new_value); + + // Emits a org.freedesktop.DBus.Properties.PropertiesChanged signal for the + // given map of changed properties. + void EmitPropertiesChangedSignal(const PropertyMap& changed_properties); + + // Writes all properties onto writer. + void AddPropertiesToWriter(dbus::MessageWriter* writer, + const PropertyMap& properties); + + // Map of org.mpris.MediaPlayer2 interface properties. + PropertyMap media_player2_properties_; + + // Map of org.mpris.MediaPlayer2.Player interface properties. + PropertyMap media_player2_player_properties_; + + scoped_refptr<dbus::Bus> bus_; + dbus::ExportedObject* exported_object_; + + // The generated service name given to |bus_| when requesting ownership. + const std::string service_name_; + + // The number of methods that have been successfully exported through + // |exported_object_|. + int num_methods_exported_ = 0; + + // True if we have finished creating the DBus service and received ownership. + bool service_ready_ = false; + + base::ObserverList<MprisServiceObserver> observers_; + + DISALLOW_COPY_AND_ASSIGN(MprisServiceImpl); +}; + +} // namespace mpris + +#endif // UI_BASE_MPRIS_MPRIS_SERVICE_IMPL_H_
diff --git a/ui/base/mpris/mpris_service_unittest.cc b/ui/base/mpris/mpris_service_impl_unittest.cc similarity index 87% rename from ui/base/mpris/mpris_service_unittest.cc rename to ui/base/mpris/mpris_service_impl_unittest.cc index 9e679d2..ba9c738 100644 --- a/ui/base/mpris/mpris_service_unittest.cc +++ b/ui/base/mpris/mpris_service_impl_unittest.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/base/mpris/mpris_service.h" +#include "ui/base/mpris/mpris_service_impl.h" #include <memory> @@ -47,12 +47,12 @@ MOCK_METHOD0(OnPlay, void()); }; -class MprisServiceTest : public testing::Test, public MprisServiceObserver { +class MprisServiceImplTest : public testing::Test, public MprisServiceObserver { public: - MprisServiceTest() + MprisServiceImplTest() : scoped_task_environment_( base::test::ScopedTaskEnvironment::MainThreadType::UI) {} - ~MprisServiceTest() override = default; + ~MprisServiceImplTest() override = default; void SetUp() override { StartMprisServiceAndWaitForReady(); } @@ -71,7 +71,7 @@ // Call the method and await a response. player_interface_exported_methods_[method_name].Run( - &method_call, base::BindRepeating(&MprisServiceTest::OnResponse, + &method_call, base::BindRepeating(&MprisServiceImplTest::OnResponse, base::Unretained(this))); response_wait_loop_->Run(); } @@ -85,7 +85,7 @@ private: void StartMprisServiceAndWaitForReady() { service_wait_loop_ = std::make_unique<base::RunLoop>(); - service_ = std::make_unique<mpris::MprisService>(); + service_ = std::make_unique<mpris::MprisServiceImpl>(); SetUpMocks(); @@ -111,14 +111,14 @@ GetExportedObject(dbus::ObjectPath(kMprisAPIObjectPath))) .WillOnce(Return(mock_exported_object_.get())); EXPECT_CALL(*mock_bus_, RequestOwnership(service_->GetServiceName(), _, _)) - .WillOnce(Invoke(this, &MprisServiceTest::OnOwnership)); + .WillOnce(Invoke(this, &MprisServiceImplTest::OnOwnership)); // The service must call ShutdownAndBlock in order to properly clean up the // DBus service. EXPECT_CALL(*mock_bus_, ShutdownAndBlock()); EXPECT_CALL(*mock_exported_object_, ExportMethod(_, _, _, _)) - .WillRepeatedly(Invoke(this, &MprisServiceTest::OnExported)); + .WillRepeatedly(Invoke(this, &MprisServiceImplTest::OnExported)); } // Tell the service that ownership was successful. @@ -155,65 +155,65 @@ base::test::ScopedTaskEnvironment scoped_task_environment_; std::unique_ptr<base::RunLoop> service_wait_loop_; std::unique_ptr<base::RunLoop> response_wait_loop_; - std::unique_ptr<MprisService> service_; + std::unique_ptr<MprisServiceImpl> service_; scoped_refptr<dbus::MockBus> mock_bus_; scoped_refptr<dbus::MockExportedObject> mock_exported_object_; base::flat_map<std::string, dbus::ExportedObject::MethodCallCallback> player_interface_exported_methods_; - DISALLOW_COPY_AND_ASSIGN(MprisServiceTest); + DISALLOW_COPY_AND_ASSIGN(MprisServiceImplTest); }; -TEST_F(MprisServiceTest, ObserverNotifiedOfServiceReadyWhenAdded) { +TEST_F(MprisServiceImplTest, ObserverNotifiedOfServiceReadyWhenAdded) { MockMprisServiceObserver observer; EXPECT_CALL(observer, OnServiceReady()); AddObserver(&observer); } -TEST_F(MprisServiceTest, ObserverNotifiedOfNextCalls) { +TEST_F(MprisServiceImplTest, ObserverNotifiedOfNextCalls) { MockMprisServiceObserver observer; EXPECT_CALL(observer, OnNext()); AddObserver(&observer); CallMediaPlayer2PlayerMethodAndBlock("Next"); } -TEST_F(MprisServiceTest, ObserverNotifiedOfPreviousCalls) { +TEST_F(MprisServiceImplTest, ObserverNotifiedOfPreviousCalls) { MockMprisServiceObserver observer; EXPECT_CALL(observer, OnPrevious()); AddObserver(&observer); CallMediaPlayer2PlayerMethodAndBlock("Previous"); } -TEST_F(MprisServiceTest, ObserverNotifiedOfPauseCalls) { +TEST_F(MprisServiceImplTest, ObserverNotifiedOfPauseCalls) { MockMprisServiceObserver observer; EXPECT_CALL(observer, OnPause()); AddObserver(&observer); CallMediaPlayer2PlayerMethodAndBlock("Pause"); } -TEST_F(MprisServiceTest, ObserverNotifiedOfPlayPauseCalls) { +TEST_F(MprisServiceImplTest, ObserverNotifiedOfPlayPauseCalls) { MockMprisServiceObserver observer; EXPECT_CALL(observer, OnPlayPause()); AddObserver(&observer); CallMediaPlayer2PlayerMethodAndBlock("PlayPause"); } -TEST_F(MprisServiceTest, ObserverNotifiedOfStopCalls) { +TEST_F(MprisServiceImplTest, ObserverNotifiedOfStopCalls) { MockMprisServiceObserver observer; EXPECT_CALL(observer, OnStop()); AddObserver(&observer); CallMediaPlayer2PlayerMethodAndBlock("Stop"); } -TEST_F(MprisServiceTest, ObserverNotifiedOfPlayCalls) { +TEST_F(MprisServiceImplTest, ObserverNotifiedOfPlayCalls) { MockMprisServiceObserver observer; EXPECT_CALL(observer, OnPlay()); AddObserver(&observer); CallMediaPlayer2PlayerMethodAndBlock("Play"); } -TEST_F(MprisServiceTest, ChangingPropertyEmitsSignal) { +TEST_F(MprisServiceImplTest, ChangingPropertyEmitsSignal) { // The returned signal should give the changed property. EXPECT_CALL(*GetExportedObject(), SendSignal(_)) .WillOnce(WithArg<0>([](dbus::Signal* signal) {
diff --git a/ui/gl/init/create_gr_gl_interface.cc b/ui/gl/init/create_gr_gl_interface.cc index bdba564..6ceaab45 100644 --- a/ui/gl/init/create_gr_gl_interface.cc +++ b/ui/gl/init/create_gr_gl_interface.cc
@@ -55,6 +55,19 @@ #endif } +template <typename R, typename... Args> +GrGLFunction<R GR_GL_FUNCTION_TYPE(Args...)> bind_slow_with_flush_on_mac( + R(GL_BINDING_CALL* func)(Args...), + gl::ProgressReporter* progress_reporter) { + if (!progress_reporter) { + return bind_with_flush_on_mac(func); + } + return [func, progress_reporter](Args... args) { + ScopedProgressReporter scoped_reporter(progress_reporter); + return bind_with_flush_on_mac(func)(args...); + }; +} + const GLubyte* GetStringHook(const char* version_string, GLenum name) { switch (name) { case GL_VERSION: @@ -161,8 +174,8 @@ functions->fColorMask = gl->glColorMaskFn; functions->fCompileShader = bind_slow(gl->glCompileShaderFn, progress_reporter); - functions->fCompressedTexImage2D = - bind_slow(gl->glCompressedTexImage2DFn, progress_reporter); + functions->fCompressedTexImage2D = bind_slow_with_flush_on_mac( + gl->glCompressedTexImage2DFn, progress_reporter); functions->fCompressedTexSubImage2D = bind_slow(gl->glCompressedTexSubImage2DFn, progress_reporter); functions->fCopyTexSubImage2D = @@ -177,7 +190,7 @@ functions->fDeleteQueries = gl->glDeleteQueriesFn; functions->fDeleteSamplers = gl->glDeleteSamplersFn; functions->fDeleteShader = bind_slow(gl->glDeleteShaderFn, progress_reporter); - functions->fDeleteTextures = gl->glDeleteTexturesFn; + functions->fDeleteTextures = bind_with_flush_on_mac(gl->glDeleteTexturesFn); functions->fDepthMask = gl->glDepthMaskFn; functions->fDisable = gl->glDisableFn; functions->fDisableVertexAttribArray = gl->glDisableVertexAttribArrayFn; @@ -256,13 +269,14 @@ functions->fStencilOpSeparate = gl->glStencilOpSeparateFn; functions->fTexBuffer = gl->glTexBufferFn; functions->fTexBufferRange = gl->glTexBufferRangeFn; - functions->fTexImage2D = bind_slow(gl->glTexImage2DFn, progress_reporter); + functions->fTexImage2D = + bind_slow_with_flush_on_mac(gl->glTexImage2DFn, progress_reporter); functions->fTexParameterf = gl->glTexParameterfFn; functions->fTexParameterfv = gl->glTexParameterfvFn; functions->fTexParameteri = gl->glTexParameteriFn; functions->fTexParameteriv = gl->glTexParameterivFn; - functions->fTexStorage2D = gl->glTexStorage2DEXTFn; - functions->fTexSubImage2D = gl->glTexSubImage2DFn; + functions->fTexStorage2D = bind_with_flush_on_mac(gl->glTexStorage2DEXTFn); + functions->fTexSubImage2D = bind_with_flush_on_mac(gl->glTexSubImage2DFn); // GL 4.5 or GL_ARB_texture_barrier or GL_NV_texture_barrier // functions->fTextureBarrier = gl->glTextureBarrierFn; @@ -319,20 +333,22 @@ bind_with_flush_on_mac(gl->glBindFramebufferEXTFn); functions->fFramebufferTexture2D = gl->glFramebufferTexture2DEXTFn; functions->fCheckFramebufferStatus = gl->glCheckFramebufferStatusEXTFn; - functions->fDeleteFramebuffers = - bind_slow(gl->glDeleteFramebuffersEXTFn, progress_reporter); - functions->fRenderbufferStorage = gl->glRenderbufferStorageEXTFn; + functions->fDeleteFramebuffers = bind_slow_with_flush_on_mac( + gl->glDeleteFramebuffersEXTFn, progress_reporter); + functions->fRenderbufferStorage = + bind_with_flush_on_mac(gl->glRenderbufferStorageEXTFn); functions->fGenRenderbuffers = gl->glGenRenderbuffersEXTFn; - functions->fDeleteRenderbuffers = gl->glDeleteRenderbuffersEXTFn; + functions->fDeleteRenderbuffers = + bind_with_flush_on_mac(gl->glDeleteRenderbuffersEXTFn); functions->fFramebufferRenderbuffer = gl->glFramebufferRenderbufferEXTFn; functions->fBindRenderbuffer = gl->glBindRenderbufferEXTFn; functions->fRenderbufferStorageMultisample = - gl->glRenderbufferStorageMultisampleFn; + bind_with_flush_on_mac(gl->glRenderbufferStorageMultisampleFn); functions->fFramebufferTexture2DMultisample = gl->glFramebufferTexture2DMultisampleEXTFn; functions->fRenderbufferStorageMultisampleES2EXT = - gl->glRenderbufferStorageMultisampleEXTFn; - functions->fBlitFramebuffer = gl->glBlitFramebufferFn; + bind_with_flush_on_mac(gl->glRenderbufferStorageMultisampleEXTFn); + functions->fBlitFramebuffer = bind_with_flush_on_mac(gl->glBlitFramebufferFn); functions->fMatrixLoadf = gl->glMatrixLoadfEXTFn; functions->fMatrixLoadIdentity = gl->glMatrixLoadIdentityEXTFn;
diff --git a/ui/message_center/views/message_view_context_menu_controller.cc b/ui/message_center/views/message_view_context_menu_controller.cc index ff7bf791..6d738fc 100644 --- a/ui/message_center/views/message_view_context_menu_controller.cc +++ b/ui/message_center/views/message_view_context_menu_controller.cc
@@ -19,7 +19,7 @@ MessageViewContextMenuController::~MessageViewContextMenuController() = default; -void MessageViewContextMenuController::ShowContextMenuForView( +void MessageViewContextMenuController::ShowContextMenuForViewImpl( views::View* source, const gfx::Point& point, ui::MenuSourceType source_type) {
diff --git a/ui/message_center/views/message_view_context_menu_controller.h b/ui/message_center/views/message_view_context_menu_controller.h index 8ee27f0..00bf172 100644 --- a/ui/message_center/views/message_view_context_menu_controller.h +++ b/ui/message_center/views/message_view_context_menu_controller.h
@@ -30,9 +30,9 @@ private: // Overridden from views::ContextMenuController: - void ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; // Callback for MenuRunner void OnMenuClosed();
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn index 9277a77..bf59e9e 100644 --- a/ui/views/BUILD.gn +++ b/ui/views/BUILD.gn
@@ -73,6 +73,7 @@ "animation/flood_fill_ink_drop_ripple.h", "animation/ink_drop.h", "animation/ink_drop_animation_ended_reason.h", + "animation/ink_drop_event_handler.h", "animation/ink_drop_highlight.h", "animation/ink_drop_highlight_observer.h", "animation/ink_drop_host_view.h", @@ -287,6 +288,7 @@ "animation/flood_fill_ink_drop_ripple.cc", "animation/ink_drop.cc", "animation/ink_drop_animation_ended_reason.cc", + "animation/ink_drop_event_handler.cc", "animation/ink_drop_highlight.cc", "animation/ink_drop_host_view.cc", "animation/ink_drop_impl.cc", @@ -308,6 +310,7 @@ "bubble/tooltip_icon.cc", "button_drag_utils.cc", "color_chooser/color_chooser_view.cc", + "context_menu_controller.cc", "controls/animated_image_view.cc", "controls/button/button.cc", "controls/button/checkbox.cc", @@ -561,10 +564,6 @@ "linux_ui/status_icon_linux.cc", ] sources += [ "widget/desktop_aura/desktop_window_tree_host_chromeos.cc" ] - - # TODO(edinkadric): Add EditableCombobox support on ChromeOS. - public -= [ "controls/editable_combobox/editable_combobox.h" ] - sources -= [ "controls/editable_combobox/editable_combobox.cc" ] } if (is_win) { @@ -1144,11 +1143,6 @@ ] } - if (is_chromeos) { - # TODO(edinkadric): Add EditableCombobox support on ChromeOS. - sources -= [ "controls/editable_combobox/editable_combobox_unittest.cc" ] - } - if (is_win) { public_deps = [ "//build/win:default_exe_manifest",
diff --git a/ui/views/animation/OWNERS b/ui/views/animation/OWNERS index be29560..a6dc7101 100644 --- a/ui/views/animation/OWNERS +++ b/ui/views/animation/OWNERS
@@ -1 +1,2 @@ per-file *ink*=mohsen@chromium.org +per-file *ink*=pbos@chromium.org
diff --git a/ui/views/animation/ink_drop_event_handler.cc b/ui/views/animation/ink_drop_event_handler.cc new file mode 100644 index 0000000..975728b --- /dev/null +++ b/ui/views/animation/ink_drop_event_handler.cc
@@ -0,0 +1,89 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/animation/ink_drop_event_handler.h" + +#include <memory> + +#include "ui/events/scoped_target_handler.h" +#include "ui/views/animation/ink_drop.h" +#include "ui/views/animation/ink_drop_state.h" +#include "ui/views/view.h" + +namespace views { +InkDropEventHandler::InkDropEventHandler(View* host_view, Delegate* delegate) + : target_handler_( + std::make_unique<ui::ScopedTargetHandler>(host_view, this)), + host_view_(host_view), + delegate_(delegate) {} + +InkDropEventHandler::~InkDropEventHandler() = default; + +void InkDropEventHandler::OnGestureEvent(ui::GestureEvent* event) { + if (!host_view_->enabled() || !delegate_->SupportsGestureEvents()) + return; + + InkDropState current_ink_drop_state = + delegate_->GetInkDrop()->GetTargetInkDropState(); + + InkDropState ink_drop_state = InkDropState::HIDDEN; + switch (event->type()) { + case ui::ET_GESTURE_TAP_DOWN: + if (current_ink_drop_state == InkDropState::ACTIVATED) + return; + ink_drop_state = InkDropState::ACTION_PENDING; + // The ui::ET_GESTURE_TAP_DOWN event needs to be marked as handled so + // that subsequent events for the gesture are sent to |this|. + event->SetHandled(); + break; + case ui::ET_GESTURE_LONG_PRESS: + if (current_ink_drop_state == InkDropState::ACTIVATED) + return; + ink_drop_state = InkDropState::ALTERNATE_ACTION_PENDING; + break; + case ui::ET_GESTURE_LONG_TAP: + ink_drop_state = InkDropState::ALTERNATE_ACTION_TRIGGERED; + break; + case ui::ET_GESTURE_END: + case ui::ET_GESTURE_SCROLL_BEGIN: + case ui::ET_GESTURE_TAP_CANCEL: + if (current_ink_drop_state == InkDropState::ACTIVATED) + return; + ink_drop_state = InkDropState::HIDDEN; + break; + default: + return; + } + + if (ink_drop_state == InkDropState::HIDDEN && + (current_ink_drop_state == InkDropState::ACTION_TRIGGERED || + current_ink_drop_state == InkDropState::ALTERNATE_ACTION_TRIGGERED || + current_ink_drop_state == InkDropState::DEACTIVATED || + current_ink_drop_state == InkDropState::HIDDEN)) { + // These InkDropStates automatically transition to the HIDDEN state so we + // don't make an explicit call. Explicitly animating to HIDDEN in this + // case would prematurely pre-empt these animations. + return; + } + delegate_->AnimateInkDrop(ink_drop_state, event); +} + +void InkDropEventHandler::OnMouseEvent(ui::MouseEvent* event) { + switch (event->type()) { + case ui::ET_MOUSE_ENTERED: + delegate_->GetInkDrop()->SetHovered(true); + break; + case ui::ET_MOUSE_EXITED: + delegate_->GetInkDrop()->SetHovered(false); + break; + case ui::ET_MOUSE_DRAGGED: + delegate_->GetInkDrop()->SetHovered( + host_view_->GetLocalBounds().Contains(event->location())); + break; + default: + break; + } +} + +} // namespace views
diff --git a/ui/views/animation/ink_drop_event_handler.h b/ui/views/animation/ink_drop_event_handler.h new file mode 100644 index 0000000..2abb6365 --- /dev/null +++ b/ui/views/animation/ink_drop_event_handler.h
@@ -0,0 +1,65 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_ANIMATION_INK_DROP_EVENT_HANDLER_H_ +#define UI_VIEWS_ANIMATION_INK_DROP_EVENT_HANDLER_H_ + +#include <memory> + +#include "ui/events/event_handler.h" +#include "ui/views/views_export.h" + +namespace ui { +class LocatedEvent; +class ScopedTargetHandler; +} // namespace ui + +namespace views { +class View; +class InkDrop; +enum class InkDropState; + +// This class handles ink-drop changes due to events on its host. +class VIEWS_EXPORT InkDropEventHandler : public ui::EventHandler { + public: + // Delegate class that allows InkDropEventHandler to be used with InkDrops + // that are hosted in multiple ways. + class Delegate { + public: + // Gets the InkDrop (or stub) that should react to incoming events. + virtual InkDrop* GetInkDrop() = 0; + // Start animating the InkDrop to another target state. + // TODO(pbos): Consider moving the implementation of + // InkDropHostView::AnimateInkDrop into InkDropEventHandler. In this case + // InkDropHostView would forward AnimateInkDrop into + // InkDropEventHandler::AnimateInkDrop. + virtual void AnimateInkDrop(InkDropState state, + const ui::LocatedEvent* event) = 0; + // Returns true if gesture events should affect the InkDrop. + virtual bool SupportsGestureEvents() const = 0; + }; + + InkDropEventHandler(View* host_view, Delegate* delegate); + ~InkDropEventHandler() override; + + private: + // ui::EventHandler: + void OnGestureEvent(ui::GestureEvent* event) override; + void OnMouseEvent(ui::MouseEvent* event) override; + + // Allows |this| to handle all GestureEvents on |host_view_|. + std::unique_ptr<ui::ScopedTargetHandler> target_handler_; + + // The host view. + View* const host_view_; + + // Delegate used to get the InkDrop, etc. + Delegate* const delegate_; + + DISALLOW_COPY_AND_ASSIGN(InkDropEventHandler); +}; + +} // namespace views + +#endif // UI_VIEWS_ANIMATION_INK_DROP_EVENT_HANDLER_H_
diff --git a/ui/views/animation/ink_drop_host_view.cc b/ui/views/animation/ink_drop_host_view.cc index 5d1b0db..98abaa1c 100644 --- a/ui/views/animation/ink_drop_host_view.cc +++ b/ui/views/animation/ink_drop_host_view.cc
@@ -22,97 +22,6 @@ namespace views { -// An EventHandler that is guaranteed to be invoked and is not prone to -// InkDropHostView descendents who do not call -// InkDropHostView::OnGestureEvent() and InkDropHostView::OnMouseEvent(). -// Only one instance of this class can exist at any given time for each ink drop -// host view. -class InkDropHostView::InkDropEventHandler : public ui::EventHandler { - public: - explicit InkDropEventHandler(InkDropHostView* host_view) - : target_handler_( - std::make_unique<ui::ScopedTargetHandler>(host_view, this)), - host_view_(host_view) {} - - ~InkDropEventHandler() override = default; - - // ui::EventHandler: - void OnGestureEvent(ui::GestureEvent* event) override { - if (!host_view_->enabled() || host_view_->ink_drop_mode_ != InkDropMode::ON) - return; - - InkDropState current_ink_drop_state = - host_view_->GetInkDrop()->GetTargetInkDropState(); - - InkDropState ink_drop_state = InkDropState::HIDDEN; - switch (event->type()) { - case ui::ET_GESTURE_TAP_DOWN: - if (current_ink_drop_state == InkDropState::ACTIVATED) - return; - ink_drop_state = InkDropState::ACTION_PENDING; - // The ui::ET_GESTURE_TAP_DOWN event needs to be marked as handled so - // that subsequent events for the gesture are sent to |this|. - event->SetHandled(); - break; - case ui::ET_GESTURE_LONG_PRESS: - if (current_ink_drop_state == InkDropState::ACTIVATED) - return; - ink_drop_state = InkDropState::ALTERNATE_ACTION_PENDING; - break; - case ui::ET_GESTURE_LONG_TAP: - ink_drop_state = InkDropState::ALTERNATE_ACTION_TRIGGERED; - break; - case ui::ET_GESTURE_END: - case ui::ET_GESTURE_SCROLL_BEGIN: - case ui::ET_GESTURE_TAP_CANCEL: - if (current_ink_drop_state == InkDropState::ACTIVATED) - return; - ink_drop_state = InkDropState::HIDDEN; - break; - default: - return; - } - - if (ink_drop_state == InkDropState::HIDDEN && - (current_ink_drop_state == InkDropState::ACTION_TRIGGERED || - current_ink_drop_state == InkDropState::ALTERNATE_ACTION_TRIGGERED || - current_ink_drop_state == InkDropState::DEACTIVATED || - current_ink_drop_state == InkDropState::HIDDEN)) { - // These InkDropStates automatically transition to the HIDDEN state so we - // don't make an explicit call. Explicitly animating to HIDDEN in this - // case would prematurely pre-empt these animations. - return; - } - host_view_->AnimateInkDrop(ink_drop_state, event); - } - - void OnMouseEvent(ui::MouseEvent* event) override { - switch (event->type()) { - case ui::ET_MOUSE_ENTERED: - host_view_->GetInkDrop()->SetHovered(true); - break; - case ui::ET_MOUSE_EXITED: - host_view_->GetInkDrop()->SetHovered(false); - break; - case ui::ET_MOUSE_DRAGGED: - host_view_->GetInkDrop()->SetHovered( - host_view_->GetLocalBounds().Contains(event->location())); - break; - default: - break; - } - } - - private: - // Allows |this| to handle all GestureEvents on |host_view_|. - std::unique_ptr<ui::ScopedTargetHandler> target_handler_; - - // The host view to cache ui::Events to when animating the ink drop. - InkDropHostView* host_view_; - - DISALLOW_COPY_AND_ASSIGN(InkDropEventHandler); -}; - class InkDropHostView::InkDropViewObserver : public ViewObserver { public: explicit InkDropViewObserver(InkDropHostView* parent) : parent_(parent) { @@ -136,8 +45,27 @@ DISALLOW_COPY_AND_ASSIGN(InkDropViewObserver); }; +InkDropHostView::InkDropHostViewEventHandlerDelegate:: + InkDropHostViewEventHandlerDelegate(InkDropHostView* host_view) + : host_view_(host_view) {} + +InkDrop* InkDropHostView::InkDropHostViewEventHandlerDelegate::GetInkDrop() { + return host_view_->GetInkDrop(); +} +void InkDropHostView::InkDropHostViewEventHandlerDelegate::AnimateInkDrop( + InkDropState state, + const ui::LocatedEvent* event) { + host_view_->AnimateInkDrop(state, event); +} + +bool InkDropHostView::InkDropHostViewEventHandlerDelegate:: + SupportsGestureEvents() const { + return host_view_->ink_drop_mode_ == InkDropMode::ON; +} + InkDropHostView::InkDropHostView() - : ink_drop_event_handler_(std::make_unique<InkDropEventHandler>(this)), + : ink_drop_event_handler_delegate_(this), + ink_drop_event_handler_(this, &ink_drop_event_handler_delegate_), ink_drop_view_observer_(std::make_unique<InkDropViewObserver>(this)) {} InkDropHostView::~InkDropHostView() {
diff --git a/ui/views/animation/ink_drop_host_view.h b/ui/views/animation/ink_drop_host_view.h index 4e20d53..6473e37 100644 --- a/ui/views/animation/ink_drop_host_view.h +++ b/ui/views/animation/ink_drop_host_view.h
@@ -10,6 +10,7 @@ #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/size.h" +#include "ui/views/animation/ink_drop_event_handler.h" #include "ui/views/view.h" namespace gfx { @@ -187,10 +188,26 @@ static gfx::Size CalculateLargeInkDropSize(const gfx::Size& small_size); private: - class InkDropEventHandler; class InkDropViewObserver; friend class test::InkDropHostViewTestApi; + class InkDropHostViewEventHandlerDelegate + : public InkDropEventHandler::Delegate { + public: + explicit InkDropHostViewEventHandlerDelegate(InkDropHostView* host_view); + + // InkDropEventHandler: + InkDrop* GetInkDrop() override; + void AnimateInkDrop(InkDropState state, + const ui::LocatedEvent* event) override; + + bool SupportsGestureEvents() const override; + + private: + // The host view. + InkDropHostView* const host_view_; + }; + // The last user Event to trigger an ink drop ripple animation. std::unique_ptr<ui::LocatedEvent> last_ripple_triggering_event_; @@ -202,7 +219,8 @@ // Intentionally declared after |ink_drop_| so that it doesn't access a // destroyed |ink_drop_| during destruction. - const std::unique_ptr<InkDropEventHandler> ink_drop_event_handler_; + InkDropHostViewEventHandlerDelegate ink_drop_event_handler_delegate_; + InkDropEventHandler ink_drop_event_handler_; // Used to observe changes to the host through the ViewObserver API. const std::unique_ptr<InkDropViewObserver> ink_drop_view_observer_;
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.cc b/ui/views/bubble/bubble_dialog_delegate_view.cc index c27df5f..de7bb17 100644 --- a/ui/views/bubble/bubble_dialog_delegate_view.cc +++ b/ui/views/bubble/bubble_dialog_delegate_view.cc
@@ -66,6 +66,7 @@ ? Widget::InitParams::TRANSLUCENT_WINDOW : Widget::InitParams::OPAQUE_WINDOW; bubble_params.accept_events = bubble->accept_events(); + bubble_params.remove_standard_frame = true; // Use a window default shadow if the bubble doesn't provides its own. if (bubble->GetShadow() == BubbleBorder::NO_ASSETS) bubble_params.shadow_type = Widget::InitParams::SHADOW_TYPE_DEFAULT;
diff --git a/ui/views/context_menu_controller.cc b/ui/views/context_menu_controller.cc new file mode 100644 index 0000000..5d4ff83 --- /dev/null +++ b/ui/views/context_menu_controller.cc
@@ -0,0 +1,23 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/context_menu_controller.h" + +#include "base/auto_reset.h" + +namespace views { + +void ContextMenuController::ShowContextMenuForView( + View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) { + // Use a boolean flag to early-exit out of re-entrant behavior. + if (is_opening_) + return; + base::AutoReset<bool> auto_reset_is_opening(&is_opening_, true); + + ShowContextMenuForViewImpl(source, point, source_type); +} + +} // namespace views
diff --git a/ui/views/context_menu_controller.h b/ui/views/context_menu_controller.h index dd137a9..1b1ae45 100644 --- a/ui/views/context_menu_controller.h +++ b/ui/views/context_menu_controller.h
@@ -28,14 +28,26 @@ // implementation for mouse processing. class VIEWS_EXPORT ContextMenuController { public: - // Invoked to show the context menu for |source|. - // |point| is in screen coordinates. - virtual void ShowContextMenuForView(View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) = 0; + // Invoked to show the context menu for |source|. |point| is in screen + // coordinates. This method also prevents reentrant calls. + void ShowContextMenuForView(View* source, + const gfx::Point& point, + ui::MenuSourceType source_type); protected: virtual ~ContextMenuController() {} + + private: + // Subclasses should override this method. + virtual void ShowContextMenuForViewImpl(View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) = 0; + + // Used as a flag to prevent a re-entrancy in ShowContextMenuForView(). + // This is most relevant to Linux, where spawning the textfield context menu + // spins a nested message loop that processes input events, which may attempt + // to trigger another context menu. + bool is_opening_ = false; }; } // namespace views
diff --git a/ui/views/controls/button/button_unittest.cc b/ui/views/controls/button/button_unittest.cc index c7c5e50..8a3fce29 100644 --- a/ui/views/controls/button/button_unittest.cc +++ b/ui/views/controls/button/button_unittest.cc
@@ -51,9 +51,9 @@ ~TestContextMenuController() override {} // ContextMenuController: - void ShowContextMenuForView(View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override {} + void ShowContextMenuForViewImpl(View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override {} private: DISALLOW_COPY_AND_ASSIGN(TestContextMenuController);
diff --git a/ui/views/controls/label.cc b/ui/views/controls/label.cc index ef1ed109..53149a1 100644 --- a/ui/views/controls/label.cc +++ b/ui/views/controls/label.cc
@@ -643,9 +643,9 @@ ClearDisplayText(); } -void Label::ShowContextMenuForView(View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) { +void Label::ShowContextMenuForViewImpl(View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) { if (!GetRenderTextForSelectionController()) return;
diff --git a/ui/views/controls/label.h b/ui/views/controls/label.h index 771034cc..cad3c4e 100644 --- a/ui/views/controls/label.h +++ b/ui/views/controls/label.h
@@ -267,9 +267,9 @@ friend class LabelSelectionTest; // ContextMenuController overrides: - void ShowContextMenuForView(View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; // WordLookupClient overrides: bool GetWordLookupDataAtPoint(const gfx::Point& point,
diff --git a/ui/views/controls/scrollbar/base_scroll_bar.cc b/ui/views/controls/scrollbar/base_scroll_bar.cc index 26ddd90..6119914 100644 --- a/ui/views/controls/scrollbar/base_scroll_bar.cc +++ b/ui/views/controls/scrollbar/base_scroll_bar.cc
@@ -258,9 +258,9 @@ ScrollBarContextMenuCommand_ScrollNext }; -void BaseScrollBar::ShowContextMenuForView(View* source, - const gfx::Point& p, - ui::MenuSourceType source_type) { +void BaseScrollBar::ShowContextMenuForViewImpl(View* source, + const gfx::Point& p, + ui::MenuSourceType source_type) { Widget* widget = GetWidget(); gfx::Rect widget_bounds = widget->GetWindowBoundsInScreen(); gfx::Point temp_pt(p.x() - widget_bounds.x(), p.y() - widget_bounds.y());
diff --git a/ui/views/controls/scrollbar/base_scroll_bar.h b/ui/views/controls/scrollbar/base_scroll_bar.h index f2c0a7d..bdf2f1e1 100644 --- a/ui/views/controls/scrollbar/base_scroll_bar.h +++ b/ui/views/controls/scrollbar/base_scroll_bar.h
@@ -86,9 +86,9 @@ bool OnScroll(float dx, float dy) override; // ContextMenuController overrides: - void ShowContextMenuForView(View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; // ui::SimpleMenuModel::Delegate overrides: bool IsCommandIdChecked(int id) const override;
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc index 0ef9830a..956424f3 100644 --- a/ui/views/controls/textfield/textfield.cc +++ b/ui/views/controls/textfield/textfield.cc
@@ -1189,9 +1189,9 @@ //////////////////////////////////////////////////////////////////////////////// // Textfield, ContextMenuController overrides: -void Textfield::ShowContextMenuForView(View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) { +void Textfield::ShowContextMenuForViewImpl(View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) { UpdateContextMenu(); context_menu_runner_->RunMenuAt(GetWidget(), NULL, gfx::Rect(point, gfx::Size()),
diff --git a/ui/views/controls/textfield/textfield.h b/ui/views/controls/textfield/textfield.h index 161c0a1e..9dc3e7e 100644 --- a/ui/views/controls/textfield/textfield.h +++ b/ui/views/controls/textfield/textfield.h
@@ -292,9 +292,9 @@ void OnCompositionTextConfirmedOrCleared() override; // ContextMenuController overrides: - void ShowContextMenuForView(View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; // DragController overrides: void WriteDragDataForView(View* sender,
diff --git a/ui/views/examples/tree_view_example.cc b/ui/views/examples/tree_view_example.cc index 6626f709a..5bc774b 100644 --- a/ui/views/examples/tree_view_example.cc +++ b/ui/views/examples/tree_view_example.cc
@@ -151,9 +151,10 @@ return true; } -void TreeViewExample::ShowContextMenuForView(View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) { +void TreeViewExample::ShowContextMenuForViewImpl( + View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) { context_menu_model_.reset(new ui::SimpleMenuModel(this)); context_menu_model_->AddItem(ID_EDIT, ASCIIToUTF16("Edit")); context_menu_model_->AddItem(ID_REMOVE, ASCIIToUTF16("Remove"));
diff --git a/ui/views/examples/tree_view_example.h b/ui/views/examples/tree_view_example.h index 53d3051..6677b0f 100644 --- a/ui/views/examples/tree_view_example.h +++ b/ui/views/examples/tree_view_example.h
@@ -60,9 +60,9 @@ bool CanEdit(TreeView* tree_view, ui::TreeModelNode* node) override; // ContextMenuController: - void ShowContextMenuForView(View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override; + void ShowContextMenuForViewImpl(View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; // SimpleMenuModel::Delegate: bool IsCommandIdChecked(int command_id) const override;
diff --git a/ui/views/mus/desktop_window_tree_host_mus.cc b/ui/views/mus/desktop_window_tree_host_mus.cc index 85f4ffa..70a9ff8 100644 --- a/ui/views/mus/desktop_window_tree_host_mus.cc +++ b/ui/views/mus/desktop_window_tree_host_mus.cc
@@ -347,6 +347,11 @@ SetBoundsInDIP(params.bounds); } + // If the standard frame is not used, the frame area (rendered by the client) + // should be handled by the client, so it shouldn't update the client area + // by itself. See https://crbug.com/935338. + auto_update_client_area_ = !params.remove_standard_frame; + cursor_manager_owner_ = std::make_unique<CursorManagerOwner>(window()); InitHost();
diff --git a/ui/views/mus/desktop_window_tree_host_mus.h b/ui/views/mus/desktop_window_tree_host_mus.h index 56cea18..5df1a1cd 100644 --- a/ui/views/mus/desktop_window_tree_host_mus.h +++ b/ui/views/mus/desktop_window_tree_host_mus.h
@@ -40,11 +40,6 @@ // Called when the window was deleted on the server. void ServerDestroyedWindow() { CloseNow(); } - // Controls whether the client area is automatically updated as necessary. - void set_auto_update_client_area(bool value) { - auto_update_client_area_ = value; - } - private: class WindowTreeHostWindowObserver;
diff --git a/ui/views/widget/root_view_unittest.cc b/ui/views/widget/root_view_unittest.cc index b655e20..2609a8c 100644 --- a/ui/views/widget/root_view_unittest.cc +++ b/ui/views/widget/root_view_unittest.cc
@@ -94,9 +94,9 @@ } // ContextMenuController: - void ShowContextMenuForView(View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) override { + void ShowContextMenuForViewImpl(View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override { show_context_menu_calls_++; menu_source_view_ = source; menu_source_type_ = source_type;
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_list.html b/ui/webui/resources/cr_components/certificate_manager/certificate_list.html index 299da91b..8748868 100644 --- a/ui/webui/resources/cr_components/certificate_manager/certificate_list.html +++ b/ui/webui/resources/cr_components/certificate_manager/certificate_list.html
@@ -1,5 +1,6 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html"> <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html"> @@ -10,7 +11,7 @@ <dom-module id="certificate-list"> <template> - <style include="certificate-shared iron-flex"> + <style include="certificate-shared iron-flex paper-button-style"> .button-box { align-items: center; display: flex;
diff --git a/ui/webui/resources/cr_elements/shared_style_css.html b/ui/webui/resources/cr_elements/shared_style_css.html index f0a0b5d..ad605f60 100644 --- a/ui/webui/resources/cr_elements/shared_style_css.html +++ b/ui/webui/resources/cr_elements/shared_style_css.html
@@ -9,6 +9,16 @@ <dom-module id="cr-shared-style"> <template> <style include="cr-hidden-style cr-icons"> + html, + :host { + --scrollable-border-color: var(--google-grey-refresh-300); + } + + html[dark], + :host-context([dark]) { + --scrollable-border-color: var(--google-grey-refresh-700); + } + [actionable] { @apply --cr-actionable; } @@ -39,10 +49,10 @@ overflow-y: auto; } [scrollable].is-scrolled { - border-top-color: var(--google-grey-300); + border-top-color: var(--scrollable-border-color); } [scrollable].can-scroll:not(.scrolled-to-bottom) { - border-bottom-color: var(--google-grey-300); + border-bottom-color: var(--scrollable-border-color); } [scrollable] iron-list > :not(.no-outline):focus { @apply --cr-list-item-focus;