diff --git a/DEPS b/DEPS index 9cc54a0..2d67b774 100644 --- a/DEPS +++ b/DEPS
@@ -105,11 +105,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': 'e8161dd154d7b5adc3bce71a615263dc1635dfc8', + 'skia_revision': 'b090b2b26803179f5a271efb142c747a1bba6225', # 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': 'e1220339a6c864b71394ad9f98537df0e55f3796', + 'v8_revision': '104cb05682bfbac1a4cf15462a4da7b6a590b499', # 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. @@ -117,7 +117,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': '4d42ef39658b9808f4e18c077b10202a7b50b33b', + 'angle_revision': '801719235b132b3bd53af35fba36dfe9238cf14f', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling build tools # and whatever else without interference from each other. @@ -125,11 +125,11 @@ # 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': 'fe5861bf5bb1f761b5d765abfb0c371e75202aa2', + 'swiftshader_revision': '108f3e10f093d8cb27b12adc9cc697bee300c6f9', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. - 'pdfium_revision': 'aaff8f8b902ac3058b44b0f11a236df606082383', + 'pdfium_revision': '1881cb7591c7154c65efaa62a8ead989113a6c23', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling openmax_dl # and whatever else without interference from each other. @@ -604,7 +604,7 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'b56a43a9064c35e81be989f5e86a19d9159d5edf', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '515e7fe037902d6434c5d5ff2c077b747c56b0b7', 'src/third_party/devtools-node-modules': Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'), @@ -1098,7 +1098,7 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '7ca87fb1d3da3b3d2060886e8c58e726d74c8219', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + 'bb081a62dc30a49fa77cfdbb4ae470c5137fa547', + Var('webrtc_git') + '/src.git' + '@' + '7cd2a9514837cfb63f636fa2d213e7941fc76f2e', 'src/third_party/xdg-utils': { 'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java index 224a89c..3fd8ec8e 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java
@@ -257,8 +257,8 @@ // Now long press on some texts and see if the text handles show up. DOMUtils.longPressNode(popupContents.getWebContents(), "plain_text"); - SelectionPopupController controller = - SelectionPopupController.fromWebContents(popupContents.getWebContents()); + SelectionPopupController controller = ThreadUtils.runOnUiThreadBlocking( + () -> SelectionPopupController.fromWebContents(popupContents.getWebContents())); assertWaitForSelectActionBarStatus(true, controller); Assert.assertTrue(ThreadUtils.runOnUiThreadBlocking(() -> controller.hasSelection()));
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/WebViewModalDialogOverrideTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewModalDialogOverrideTest.java index 5781fd0..157da35 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/WebViewModalDialogOverrideTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewModalDialogOverrideTest.java
@@ -20,6 +20,7 @@ import org.chromium.android_webview.JsPromptResultReceiver; import org.chromium.android_webview.JsResultReceiver; import org.chromium.android_webview.test.util.AwTestTouchUtils; +import org.chromium.base.ThreadUtils; import org.chromium.base.test.util.CallbackHelper; import org.chromium.base.test.util.Feature; import org.chromium.content_public.browser.GestureListenerManager; @@ -194,8 +195,10 @@ private void tapViewAndWait(AwTestContainerView view) throws Throwable { final TapGestureStateListener tapGestureStateListener = new TapGestureStateListener(); int callCount = tapGestureStateListener.getCallCount(); - GestureListenerManager.fromWebContents(view.getWebContents()) - .addListener(tapGestureStateListener); + ThreadUtils.runOnUiThreadBlocking(() -> { + GestureListenerManager.fromWebContents(view.getWebContents()) + .addListener(tapGestureStateListener); + }); AwTestTouchUtils.simulateTouchCenterOfView(view); tapGestureStateListener.waitForTap(callCount);
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc index 1d3f42d..6ec9014 100644 --- a/ash/shelf/shelf_layout_manager.cc +++ b/ash/shelf/shelf_layout_manager.cc
@@ -272,6 +272,9 @@ SetState(SHELF_VISIBLE); } else if (Shell::Get()->screen_pinning_controller()->IsPinned()) { SetState(SHELF_HIDDEN); + } else if (Shell::Get()->window_selector_controller() && + Shell::Get()->window_selector_controller()->IsSelecting()) { + SetState(SHELF_VISIBLE); } else { // TODO(zelidrag): Verify shelf drag animation still shows on the device // when we are in SHELF_AUTO_HIDE_ALWAYS_HIDDEN. @@ -497,6 +500,16 @@ MaybeUpdateShelfBackground(AnimationChangeType::IMMEDIATE); } +void ShelfLayoutManager::OnOverviewModeStarting() { + UpdateVisibilityState(); + MaybeUpdateShelfBackground(AnimationChangeType::ANIMATE); +} + +void ShelfLayoutManager::OnOverviewModeEnded() { + UpdateVisibilityState(); + MaybeUpdateShelfBackground(AnimationChangeType::ANIMATE); +} + void ShelfLayoutManager::OnSplitViewModeStarted() { MaybeUpdateShelfBackground(AnimationChangeType::ANIMATE); } @@ -559,6 +572,11 @@ return SHELF_BACKGROUND_OVERLAP; } + if (Shell::Get()->window_selector_controller() && + Shell::Get()->window_selector_controller()->IsSelecting()) { + return SHELF_BACKGROUND_OVERLAP; + } + // If split view mode is active, make the shelf fully opapue. if (Shell::Get()->IsSplitViewModeActive()) return SHELF_BACKGROUND_SPLIT_VIEW;
diff --git a/ash/shelf/shelf_layout_manager.h b/ash/shelf/shelf_layout_manager.h index 3373a0d..04b6bc2 100644 --- a/ash/shelf/shelf_layout_manager.h +++ b/ash/shelf/shelf_layout_manager.h
@@ -155,6 +155,8 @@ void OnPinnedStateChanged(aura::Window* pinned_window) override; void OnAppListVisibilityChanged(bool shown, aura::Window* root_window) override; + void OnOverviewModeStarting() override; + void OnOverviewModeEnded() override; void OnSplitViewModeStarted() override; void OnSplitViewModeEnded() override;
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc index 08f0297..d35aea0 100644 --- a/ash/shelf/shelf_layout_manager_unittest.cc +++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -35,6 +35,7 @@ #include "ash/wallpaper/wallpaper_controller.h" #include "ash/window_factory.h" #include "ash/wm/lock_state_controller.h" +#include "ash/wm/overview/window_selector_controller.h" #include "ash/wm/splitview/split_view_controller.h" #include "ash/wm/tablet_mode/tablet_mode_controller.h" #include "ash/wm/window_state.h" @@ -1006,6 +1007,36 @@ EXPECT_EQ(SHELF_BACKGROUND_OVERLAP, GetShelfWidget()->GetBackgroundType()); } +// Tests that the shelf should be visible when in overview mode. +TEST_F(ShelfLayoutManagerTest, VisibleInOverview) { + std::unique_ptr<aura::Window> window(CreateTestWindow()); + window->Show(); + Shelf* shelf = GetPrimaryShelf(); + shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS); + EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState()); + EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState()); + + // LayoutShelf() forces the animation to completion, at which point the + // shelf should go off the screen. + GetShelfLayoutManager()->LayoutShelf(); + display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay(); + EXPECT_EQ(display.bounds().bottom() - kShelfAutoHideSize, + GetShelfWidget()->GetWindowBoundsInScreen().y()); + + WindowSelectorController* window_selector_controller = + Shell::Get()->window_selector_controller(); + // Tests that the shelf is visible when in overview mode and its color is + // overlap. + window_selector_controller->ToggleOverview(); + EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState()); + EXPECT_EQ(SHELF_BACKGROUND_OVERLAP, GetShelfWidget()->GetBackgroundType()); + + // Test that on exiting overview mode, the shelf returns to auto hide state. + window_selector_controller->ToggleOverview(); + EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState()); + EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState()); +} + // Assertions around SetAutoHideBehavior. TEST_F(ShelfLayoutManagerTest, SetAutoHideBehavior) { Shelf* shelf = GetPrimaryShelf();
diff --git a/base/task/task_scheduler/environment_config.cc b/base/task/task_scheduler/environment_config.cc index bca9c38..0bb59ab 100644 --- a/base/task/task_scheduler/environment_config.cc +++ b/base/task/task_scheduler/environment_config.cc
@@ -28,13 +28,13 @@ return false; #if !defined(OS_ANDROID) - // When thread priority can't be increased, run all threads with a normal - // priority to avoid priority inversions on shutdown (TaskScheduler increases - // background threads priority to normal on shutdown while resolving remaining - // shutdown blocking tasks). + // When thread priority can't be increased to NORMAL, run all threads with a + // NORMAL priority to avoid priority inversions on shutdown (TaskScheduler + // increases BACKGROUND threads priority to NORMAL on shutdown while resolving + // remaining shutdown blocking tasks). // // This is ignored on Android, because it doesn't have a clean shutdown phase. - if (!PlatformThread::CanIncreaseCurrentThreadPriority()) + if (!PlatformThread::CanIncreaseThreadPriority(ThreadPriority::NORMAL)) return false; #endif // defined(OS_ANDROID)
diff --git a/base/threading/platform_thread.h b/base/threading/platform_thread.h index faeb858..029cca5 100644 --- a/base/threading/platform_thread.h +++ b/base/threading/platform_thread.h
@@ -196,9 +196,9 @@ // and |thread_handle| is invalidated after this call. static void Detach(PlatformThreadHandle thread_handle); - // Returns true if SetCurrentThreadPriority() can be used to increase the - // priority of the current thread. - static bool CanIncreaseCurrentThreadPriority(); + // Returns true if SetCurrentThreadPriority() should be able to increase the + // priority of a thread to |priority|. + static bool CanIncreaseThreadPriority(ThreadPriority priority); // Toggles the current thread's priority at runtime. //
diff --git a/base/threading/platform_thread_fuchsia.cc b/base/threading/platform_thread_fuchsia.cc index eb06795c..2c642ad 100644 --- a/base/threading/platform_thread_fuchsia.cc +++ b/base/threading/platform_thread_fuchsia.cc
@@ -31,7 +31,7 @@ } // static -bool PlatformThread::CanIncreaseCurrentThreadPriority() { +bool PlatformThread::CanIncreaseThreadPriority(ThreadPriority priority) { return false; }
diff --git a/base/threading/platform_thread_mac.mm b/base/threading/platform_thread_mac.mm index 39d979d..c470ce0f 100644 --- a/base/threading/platform_thread_mac.mm +++ b/base/threading/platform_thread_mac.mm
@@ -148,7 +148,7 @@ } // anonymous namespace // static -bool PlatformThread::CanIncreaseCurrentThreadPriority() { +bool PlatformThread::CanIncreaseThreadPriority(ThreadPriority priority) { return true; }
diff --git a/base/threading/platform_thread_posix.cc b/base/threading/platform_thread_posix.cc index 2466b78..83b61b5 100644 --- a/base/threading/platform_thread_posix.cc +++ b/base/threading/platform_thread_posix.cc
@@ -240,10 +240,13 @@ #if !defined(OS_MACOSX) && !defined(OS_FUCHSIA) // static -bool PlatformThread::CanIncreaseCurrentThreadPriority() { +bool PlatformThread::CanIncreaseThreadPriority(ThreadPriority priority) { #if defined(OS_NACL) return false; #else + // TODO(fdoray): Also check if the target priority is within the range allowed + // by RLIMIT_NICE. https://crbug.com/816389 + // Only root can raise thread priority on POSIX environment. On Linux, users // who have CAP_SYS_NICE permission also can raise the thread priority, but // libcap.so would be needed to check the capability.
diff --git a/base/threading/platform_thread_unittest.cc b/base/threading/platform_thread_unittest.cc index da6f4eb..e4c50e65 100644 --- a/base/threading/platform_thread_unittest.cc +++ b/base/threading/platform_thread_unittest.cc
@@ -220,88 +220,69 @@ namespace { -constexpr ThreadPriority kAllThreadPriorities[] = { - ThreadPriority::REALTIME_AUDIO, ThreadPriority::DISPLAY, - ThreadPriority::NORMAL, ThreadPriority::BACKGROUND}; +bool CanIncreaseThreadPriorityWrapper(ThreadPriority priority) { +#if defined(OS_ANDROID) + // TODO(fdoray): PlatformThread::CanIncreaseThreadPriority(...) incorrectly + // returns false on Android. There should be a cross-POSIX implementation of + // this method that checks RLIMIT_NICE to determine whether it is possible + // to increase thread priority. https://crbug.com/872820 + return true; +#else + return PlatformThread::CanIncreaseThreadPriority(priority); +#endif +} class ThreadPriorityTestThread : public FunctionTestThread { public: - explicit ThreadPriorityTestThread() = default; + explicit ThreadPriorityTestThread(ThreadPriority from, ThreadPriority to) + : from_(from), to_(to) {} ~ThreadPriorityTestThread() override = default; private: void RunTest() override { -#if !defined(OS_ANDROID) - // TODO(fdoray): PlatformThread::CanIncreaseCurrentThreadPriority() - // incorrectly returns false on Android. There should be a cross-POSIX - // implementation of this method that checks RLIMIT_NICE to determine - // whether it is possible to increase thread priority. - // https://crbug.com/872820 - if (PlatformThread::CanIncreaseCurrentThreadPriority()) { -#endif - // On platforms that support increasing thread priority, test transition - // between every possible pair of priorities. - for (auto first_priority : kAllThreadPriorities) { - for (auto second_priority : kAllThreadPriorities) { - PlatformThread::SetCurrentThreadPriority(first_priority); - EXPECT_EQ(first_priority, PlatformThread::GetCurrentThreadPriority()); + EXPECT_EQ(PlatformThread::GetCurrentThreadPriority(), + ThreadPriority::NORMAL); + PlatformThread::SetCurrentThreadPriority(from_); + EXPECT_EQ(PlatformThread::GetCurrentThreadPriority(), from_); + PlatformThread::SetCurrentThreadPriority(to_); - PlatformThread::SetCurrentThreadPriority(second_priority); - EXPECT_EQ(second_priority, - PlatformThread::GetCurrentThreadPriority()); - } - } -#if !defined(OS_ANDROID) + if (static_cast<int>(to_) <= static_cast<int>(from_) || + CanIncreaseThreadPriorityWrapper(to_)) { + EXPECT_EQ(PlatformThread::GetCurrentThreadPriority(), to_); } else { - // On platforms that don't support increasing thread priority, the only - // valid transition is NORMAL -> BACKGROUND (it isn't possible to get a - // thread running with a higher priority than NORMAL). - EXPECT_EQ(ThreadPriority::NORMAL, - PlatformThread::GetCurrentThreadPriority()); - - // Verify that transition to DISPLAY or REALTIME_AUDIO is impossible. - PlatformThread::SetCurrentThreadPriority(ThreadPriority::DISPLAY); - EXPECT_EQ(ThreadPriority::NORMAL, - PlatformThread::GetCurrentThreadPriority()); - PlatformThread::SetCurrentThreadPriority(ThreadPriority::REALTIME_AUDIO); - EXPECT_EQ(ThreadPriority::NORMAL, - PlatformThread::GetCurrentThreadPriority()); - - // Verify that transition to BACKGROUND is possible. - PlatformThread::SetCurrentThreadPriority(ThreadPriority::BACKGROUND); - EXPECT_EQ(ThreadPriority::BACKGROUND, - PlatformThread::GetCurrentThreadPriority()); - - // Verify that transition to NORMAL, DISPLAY or REALTIME_AUDIO is - // impossible. - PlatformThread::SetCurrentThreadPriority(ThreadPriority::NORMAL); - EXPECT_EQ(ThreadPriority::BACKGROUND, - PlatformThread::GetCurrentThreadPriority()); - PlatformThread::SetCurrentThreadPriority(ThreadPriority::DISPLAY); - EXPECT_EQ(ThreadPriority::BACKGROUND, - PlatformThread::GetCurrentThreadPriority()); - PlatformThread::SetCurrentThreadPriority(ThreadPriority::REALTIME_AUDIO); - EXPECT_EQ(ThreadPriority::BACKGROUND, - PlatformThread::GetCurrentThreadPriority()); + EXPECT_NE(PlatformThread::GetCurrentThreadPriority(), to_); } -#endif // !defined(OS_ANDROID) } + const ThreadPriority from_; + const ThreadPriority to_; + DISALLOW_COPY_AND_ASSIGN(ThreadPriorityTestThread); }; void TestSetCurrentThreadPriority() { - ThreadPriorityTestThread thread; - PlatformThreadHandle handle; + constexpr ThreadPriority kAllThreadPriorities[] = { + ThreadPriority::REALTIME_AUDIO, ThreadPriority::DISPLAY, + ThreadPriority::NORMAL, ThreadPriority::BACKGROUND}; - ASSERT_FALSE(thread.IsRunning()); - ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); - thread.WaitForTerminationReady(); - ASSERT_TRUE(thread.IsRunning()); + for (auto from : kAllThreadPriorities) { + if (static_cast<int>(from) <= static_cast<int>(ThreadPriority::NORMAL) || + CanIncreaseThreadPriorityWrapper(from)) { + for (auto to : kAllThreadPriorities) { + ThreadPriorityTestThread thread(from, to); + PlatformThreadHandle handle; - thread.MarkForTermination(); - PlatformThread::Join(handle); - ASSERT_FALSE(thread.IsRunning()); + ASSERT_FALSE(thread.IsRunning()); + ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); + thread.WaitForTerminationReady(); + ASSERT_TRUE(thread.IsRunning()); + + thread.MarkForTermination(); + PlatformThread::Join(handle); + ASSERT_FALSE(thread.IsRunning()); + } + } + } } } // namespace
diff --git a/base/threading/platform_thread_win.cc b/base/threading/platform_thread_win.cc index a40cbb4..0db57c0 100644 --- a/base/threading/platform_thread_win.cc +++ b/base/threading/platform_thread_win.cc
@@ -271,7 +271,7 @@ } // static -bool PlatformThread::CanIncreaseCurrentThreadPriority() { +bool PlatformThread::CanIncreaseThreadPriority(ThreadPriority priority) { return true; }
diff --git a/chrome/VERSION b/chrome/VERSION index 1fb51f9..59dd56b 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=71 MINOR=0 -BUILD=3545 +BUILD=3546 PATCH=0
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 10504ec..df0635f4f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -118,6 +118,7 @@ import org.chromium.chrome.browser.tab.BrowserControlsVisibilityDelegate; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabDelegateFactory; +import org.chromium.chrome.browser.tab.TabRedirectHandler; import org.chromium.chrome.browser.tab.TabStateBrowserControlsVisibilityDelegate; import org.chromium.chrome.browser.tabmodel.AsyncTabParamsManager; import org.chromium.chrome.browser.tabmodel.ChromeTabCreator; @@ -1235,7 +1236,7 @@ // can be handled by other applications (e.g. www.youtube.com links). Tab currentTab = getActivityTab(); if (currentTab != null) { - currentTab.getTabRedirectHandler().updateIntent(intent); + TabRedirectHandler.from(currentTab).updateIntent(intent); int transitionType = PageTransition.LINK | PageTransition.FROM_API; LoadUrlParams loadUrlParams = new LoadUrlParams(url); loadUrlParams.setIntentReceivedTimestamp(mIntentHandlingTimeMs);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java index 4e61db7..5d4e464 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -275,7 +275,7 @@ mInProductHelp.setParentView(parentView); - mTabRedirectHandler = new TabRedirectHandler(mActivity); + mTabRedirectHandler = TabRedirectHandler.create(mActivity); mPolicy.initialize();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java index bbd9cc6..e2f818a2 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -84,6 +84,7 @@ import org.chromium.chrome.browser.tab.EmptyTabObserver; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabDelegateFactory; +import org.chromium.chrome.browser.tab.TabRedirectHandler; import org.chromium.chrome.browser.tabmodel.AsyncTabParams; import org.chromium.chrome.browser.tabmodel.AsyncTabParamsManager; import org.chromium.chrome.browser.tabmodel.ChromeTabCreator; @@ -755,7 +756,7 @@ } private void initializeMainTab(Tab tab) { - tab.getTabRedirectHandler().updateIntent(getIntent()); + TabRedirectHandler.from(tab).updateIntent(getIntent()); tab.getView().requestFocus(); mTabObserver = new CustomTabObserver(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java index a381634..59666ee5 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java
@@ -45,6 +45,7 @@ import org.chromium.chrome.browser.tab.EmptyTabObserver; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabObserver; +import org.chromium.chrome.browser.tab.TabRedirectHandler; import org.chromium.chrome.browser.util.IntentUtils; import org.chromium.chrome.browser.util.UrlUtilities; import org.chromium.chrome.browser.webapps.WebappActivity; @@ -641,9 +642,8 @@ if (!hasValidTab() || mTab.getWebContents() == null) return false; InstantAppsHandler handler = InstantAppsHandler.getInstance(); - Intent intent = mTab.getTabRedirectHandler() != null - ? mTab.getTabRedirectHandler().getInitialIntent() - : null; + TabRedirectHandler redirect = TabRedirectHandler.getOrNull(mTab); + Intent intent = redirect != null ? redirect.getInitialIntent() : null; // TODO(mariakhomenko): consider also handling NDEF_DISCOVER action redirects. if (isIncomingRedirect && intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) { // Set the URL the redirect was resolved to for checking the existence of the
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java index cdb3b45..6d43da68 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java
@@ -23,6 +23,7 @@ import org.chromium.base.library_loader.LibraryLoader; import org.chromium.chrome.browser.fullscreen.FullscreenHtmlApiHandler.FullscreenHtmlApiDelegate; import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tab.TabBrowserControlsOffsetHelper; import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType; import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver; @@ -543,7 +544,8 @@ private boolean shouldShowAndroidControls() { if (mBrowserControlsAndroidViewHidden) return false; - if (getTab() != null && getTab().getControlsOffsetHelper().isControlsOffsetOverridden()) { + if (getTab() != null + && TabBrowserControlsOffsetHelper.from(getTab()).isControlsOffsetOverridden()) { return true; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenManager.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenManager.java index c3679cd3..de73edf 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenManager.java
@@ -10,6 +10,7 @@ import org.chromium.chrome.browser.fullscreen.FullscreenHtmlApiHandler.FullscreenHtmlApiDelegate; import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tab.TabBrowserControlsOffsetHelper; /** * Manages the basic fullscreen functionality required by a Tab. @@ -111,12 +112,18 @@ if (mTab == tab) return; // Remove the fullscreen manager from the old tab before setting the new tab. - if (mTab != null) mTab.setFullscreenManager(null); + setFullscreenManager(null); mTab = tab; // Initialize the new tab with the correct fullscreen manager reference. - if (mTab != null) mTab.setFullscreenManager(this); + setFullscreenManager(this); + } + + private void setFullscreenManager(FullscreenManager manager) { + if (mTab == null) return; + mTab.setFullscreenManager(manager); + TabBrowserControlsOffsetHelper.from(mTab).resetPositions(); } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java index c221e5d..ff1ef7c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java
@@ -109,7 +109,7 @@ if (mDialogContainer == null) initDialogContainer(); setBrowserControlsAccess(true); // Don't show the dialog container before browser controls are guaranteed fully visible. - if (mActiveTab.getControlsOffsetHelper().areBrowserControlsFullyVisible()) { + if (TabBrowserControlsOffsetHelper.from(mActiveTab).areBrowserControlsFullyVisible()) { runEnterAnimation(dialogView); } else { mRunEnterAnimationOnCallback = true; @@ -224,7 +224,7 @@ assert mActiveTab != null : "Tab modal dialogs should be shown on top of an active tab."; - mActiveTab.getControlsOffsetHelper().addObserver(this); + TabBrowserControlsOffsetHelper.from(mActiveTab).addObserver(this); // Hide contextual search panel so that bottom toolbar will not be // obscured and back press is not overridden. ContextualSearchManager contextualSearchManager = @@ -259,7 +259,7 @@ } menuButton.setEnabled(false); } else { - mActiveTab.getControlsOffsetHelper().removeObserver(this); + TabBrowserControlsOffsetHelper.from(mActiveTab).removeObserver(this); // Show the action bar back if it was dismissed when the dialogs were showing. if (mDidClearTextControls) { mDidClearTextControls = false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorController.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorController.java index 4dd38aaa..6f6dc493f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorController.java
@@ -228,8 +228,7 @@ return; } - Drawable icon = - AppCompatResources.getDrawable(activity, R.drawable.ic_offline_pin_blue_white); + Drawable icon = AppCompatResources.getDrawable(activity, R.drawable.ic_offline_pin_white); Snackbar snackbar = Snackbar.make(activity.getString(R.string.offline_indicator_offline_title), this, Snackbar.TYPE_ACTION, Snackbar.UMA_OFFLINE_INDICATOR)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java index 2f02662..c84f99c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java
@@ -98,7 +98,7 @@ TabRedirectHandler tabRedirectHandler = null; if (navigationParams.isMainFrame) { - tabRedirectHandler = mTab.getTabRedirectHandler(); + tabRedirectHandler = TabRedirectHandler.from(mTab); } else if (navigationParams.isExternalProtocol) { // Only external protocol navigations are intercepted for iframe navigations. Since // we do not see all previous navigations for the iframe, we can not build a complete @@ -110,7 +110,7 @@ // not covering the case where a gesture is carried over via a redirect. This is // currently not feasible because we do not see all navigations for iframes and it is // better to error on the side of caution and require direct user gestures for iframes. - tabRedirectHandler = new TabRedirectHandler(associatedActivity); + tabRedirectHandler = TabRedirectHandler.create(associatedActivity); } else { assert false; return false; @@ -200,8 +200,9 @@ // redirect history to be consistent. NavigationController navigationController = webContents.getNavigationController(); - int indexBeforeRedirection = mTab.getTabRedirectHandler() - .getLastCommittedEntryIndexBeforeStartingNavigation(); + int indexBeforeRedirection = + TabRedirectHandler.from(mTab) + .getLastCommittedEntryIndexBeforeStartingNavigation(); int lastCommittedEntryIndex = getLastCommittedEntryIndex(); for (int i = lastCommittedEntryIndex - 1; i > indexBeforeRedirection; --i) { boolean ret = navigationController.removeEntryAtIndex(i); @@ -229,8 +230,9 @@ // navigation is invalid, it means that this navigation is the first one since this tab was // created. // In such case, we would like to close this tab. - if (mTab.getTabRedirectHandler().isOnNavigation()) { - return mTab.getTabRedirectHandler().getLastCommittedEntryIndexBeforeStartingNavigation() + if (TabRedirectHandler.from(mTab).isOnNavigation()) { + return TabRedirectHandler.from(mTab) + .getLastCommittedEntryIndexBeforeStartingNavigation() == TabRedirectHandler.INVALID_ENTRY_INDEX; } return false; @@ -262,9 +264,10 @@ mTab.getTabModelSelector().closeTab(mTab); } }); - } else if (mTab.getTabRedirectHandler().isOnNavigation()) { - int lastCommittedEntryIndexBeforeNavigation = mTab.getTabRedirectHandler() - .getLastCommittedEntryIndexBeforeStartingNavigation(); + } else if (TabRedirectHandler.from(mTab).isOnNavigation()) { + int lastCommittedEntryIndexBeforeNavigation = + TabRedirectHandler.from(mTab) + .getLastCommittedEntryIndexBeforeStartingNavigation(); if (getLastCommittedEntryIndex() > lastCommittedEntryIndexBeforeNavigation) { // http://crbug/426679 : we want to go back to the last committed entry index which // was saved before this navigation, and remove the empty entries from the
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java index 931a0b62..b4b2919 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -349,8 +349,6 @@ */ private boolean mIsNativePageCommitPending; - private TabRedirectHandler mTabRedirectHandler; - private FullscreenManager mFullscreenManager; private final TabBrowserControlsOffsetHelper mControlsOffsetHelper; @@ -564,6 +562,10 @@ return mUserDataHost; } + Context getContext() { + return mThemedApplicationContext; + } + /** * Creates an instance of a {@link Tab}. * @@ -616,7 +618,6 @@ restoreFieldsFromState(frozenState); } - mTabRedirectHandler = new TabRedirectHandler(mThemedApplicationContext); addObserver(mTabObserver); if (incognito) { @@ -1301,8 +1302,6 @@ if (mTabUma != null) mTabUma.onHide(); - mTabRedirectHandler.clear(); - // Allow this tab's NativePage to be frozen if it stays hidden for a while. NativePageAssassin.getInstance().tabHidden(this); @@ -2902,7 +2901,6 @@ */ public void setFullscreenManager(FullscreenManager manager) { mFullscreenManager = manager; - mControlsOffsetHelper.resetPositions(); } /** @@ -3038,22 +3036,6 @@ } /** - * @return the TabRedirectHandler for the tab. - */ - public TabRedirectHandler getTabRedirectHandler() { - return mTabRedirectHandler; - } - - /** - * Sets the TabRedirectHandler for the tab. - * - * @param tabRedirectHandler the TabRedirectHandler - */ - public void setTabRedirectHandler(TabRedirectHandler tabRedirectHandler) { - mTabRedirectHandler = tabRedirectHandler; - } - - /** * @return the AppBannerManager. */ public AppBannerManager getAppBannerManager() { @@ -3377,13 +3359,6 @@ return nativeAreRendererInputEventsIgnored(mNativeTabAndroid); } - /** - * @return The {@link TabBrowserControlsOffsetHelper} for this tab. - */ - public TabBrowserControlsOffsetHelper getControlsOffsetHelper() { - return mControlsOffsetHelper; - } - @CalledByNative private void showMediaDownloadInProductHelp(int x, int y, int width, int height) { Rect rect = new Rect(x, y, x + width, y + height);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsOffsetHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsOffsetHelper.java index cffced6..46732ea 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsOffsetHelper.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsOffsetHelper.java
@@ -154,7 +154,7 @@ /** * Resets the controls positions in {@link FullscreenManager} to the cached positions. */ - void resetPositions() { + public void resetPositions() { resetControlsOffsetOverridden(); if (mTab.getFullscreenManager() == null) return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabRedirectHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabRedirectHandler.java index a7a3e9e..2ecddbf 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabRedirectHandler.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabRedirectHandler.java
@@ -12,6 +12,8 @@ import android.provider.Browser; import android.text.TextUtils; +import org.chromium.base.UserData; +import org.chromium.base.UserDataHost; import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.LaunchIntentDispatcher; import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider; @@ -25,7 +27,8 @@ /** * This class contains the logic to determine effective navigation/redirect. */ -public class TabRedirectHandler { +public class TabRedirectHandler extends EmptyTabObserver implements UserData { + private static final Class<TabRedirectHandler> USER_DATA_KEY = TabRedirectHandler.class; /** * An invalid entry index. */ @@ -54,10 +57,56 @@ private final Context mContext; - public TabRedirectHandler(Context context) { + /** + * Returns {@link TabRedirectHandler} that hangs on to a given {@link Tab}. + * If not present, creates a new instance and associate it with the {@link UserDataHost} + * that the {@link Tab} manages. + * @param tab Tab instance that the TabRedirectHandler hangs on to. + * @return TabRedirectHandler for a given Tab. + */ + public static TabRedirectHandler from(Tab tab) { + UserDataHost host = tab.getUserDataHost(); + TabRedirectHandler handler = host.getUserData(USER_DATA_KEY); + if (handler == null) { + handler = new TabRedirectHandler(tab.getContext()); + host.setUserData(USER_DATA_KEY, handler); + tab.addObserver(handler); + } + return handler; + } + + /** + * @return {@link TabRedirectHandler} hanging to the given {@link Tab}, + * or {@code null} if there is no instance available. + */ + public static TabRedirectHandler getOrNull(Tab tab) { + return tab.getUserDataHost().getUserData(USER_DATA_KEY); + } + + /** + * Replace {@link TabRedirectHandler} instance for the Tab with the new one. + * @return Old {@link TabRedirectHandler} associated with the Tab. Could be {@code null}. + */ + public static TabRedirectHandler swapFor(Tab tab, TabRedirectHandler newHandler) { + UserDataHost host = tab.getUserDataHost(); + TabRedirectHandler oldHandler = host.getUserData(TabRedirectHandler.class); + host.setUserData(TabRedirectHandler.class, newHandler); + return oldHandler; + } + + public static TabRedirectHandler create(Context context) { + return new TabRedirectHandler(context); + } + + protected TabRedirectHandler(Context context) { mContext = context; } + @Override + public void onHidden(Tab tab) { + clear(); + } + /** * Updates |mIntentHistory| and |mLastIntentUpdatedTime|. If |intent| comes from chrome and * currently |mIsOnEffectiveIntentRedirectChain| is true, that means |intent| was sent from
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegate.java index a2b5779..e6ef88f1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegate.java
@@ -29,13 +29,13 @@ @Override public void onTopControlsChanged(float topControlsOffsetY, float topContentOffsetY) { - mTab.getControlsOffsetHelper().onOffsetsChanged( + TabBrowserControlsOffsetHelper.from(mTab).onOffsetsChanged( topControlsOffsetY, Float.NaN, topContentOffsetY); } @Override public void onBottomControlsChanged(float bottomControlsOffsetY, float bottomContentOffsetY) { - mTab.getControlsOffsetHelper().onOffsetsChanged( + TabBrowserControlsOffsetHelper.from(mTab).onOffsetsChanged( Float.NaN, bottomControlsOffsetY, Float.NaN); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java index 592e4bfd..bd8e2439 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java
@@ -19,6 +19,7 @@ import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabDelegateFactory; +import org.chromium.chrome.browser.tab.TabRedirectHandler; import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType; import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType; import org.chromium.chrome.browser.util.IntentUtils; @@ -152,7 +153,7 @@ tab.initialize(null, mTabContentManager, delegateFactory, !openInForeground, false); tab.loadUrl(loadUrlParams); } - tab.getTabRedirectHandler().updateIntent(intent); + TabRedirectHandler.from(tab).updateIntent(intent); if (intent != null && intent.hasExtra(ServiceTabLauncher.LAUNCH_REQUEST_ID_EXTRA)) { ServiceTabLauncher.onWebContentsForRequestAvailable( intent.getIntExtra(ServiceTabLauncher.LAUNCH_REQUEST_ID_EXTRA, 0),
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrShell.java b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrShell.java index f8c990d..cab3d8db 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrShell.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrShell.java
@@ -494,15 +494,14 @@ private void initializeTabForVR() { if (mTab == null) return; // Make sure we are not redirecting to another app, i.e. out of VR mode. - mNonVrTabRedirectHandler = mTab.getTabRedirectHandler(); - mTab.setTabRedirectHandler(mTabRedirectHandler); + mNonVrTabRedirectHandler = TabRedirectHandler.swapFor(mTab, mTabRedirectHandler); assert mTab.getWindowAndroid() == mContentVrWindowAndroid; configWebContentsImeForVr(mTab.getWebContents()); } private void restoreTabFromVR() { if (mTab == null) return; - mTab.setTabRedirectHandler(mNonVrTabRedirectHandler); + TabRedirectHandler.swapFor(mTab, mNonVrTabRedirectHandler); mNonVrTabRedirectHandler = null; restoreWebContentsImeFromVr(mTab.getWebContents()); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ContentViewFocusTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ContentViewFocusTest.java index cd1500f..8213a8b 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ContentViewFocusTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ContentViewFocusTest.java
@@ -30,11 +30,13 @@ import org.chromium.chrome.test.util.ChromeTabUtils; import org.chromium.chrome.test.util.OverviewModeBehaviorWatcher; import org.chromium.content_public.browser.ViewEventSink; +import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContentsObserver; import org.chromium.content_public.browser.test.util.Criteria; import org.chromium.content_public.browser.test.util.CriteriaHelper; import org.chromium.content_public.browser.test.util.TestTouchUtils; import org.chromium.content_public.browser.test.util.TouchCommon; +import org.chromium.content_public.browser.test.util.WebContentsUtils; import org.chromium.ui.test.util.UiRestriction; import java.util.ArrayDeque; @@ -197,9 +199,10 @@ @Test @MediumTest public void testPauseTriggersBlur() throws Exception { + final WebContents webContents = mActivityTestRule.getWebContents(); final CallbackHelper onTitleUpdatedHelper = new CallbackHelper(); final WebContentsObserver observer = - new WebContentsObserver(mActivityTestRule.getWebContents()) { + new WebContentsObserver(webContents) { @Override public void titleWasSet(String title) { mTitle = title; @@ -210,7 +213,7 @@ String url = UrlUtils.getIsolatedTestFileUrl( "chrome/test/data/android/content_view_focus/content_view_blur_focus.html"); mActivityTestRule.loadUrl(url); - ViewEventSink eventSink = ViewEventSink.from(mActivityTestRule.getWebContents()); + ViewEventSink eventSink = WebContentsUtils.getViewEventSink(webContents); onTitleUpdatedHelper.waitForCallback(callCount); Assert.assertEquals("initial", mTitle); callCount = onTitleUpdatedHelper.getCallCount();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ModalDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ModalDialogTest.java index 62ff9dc70..93a3fc1 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ModalDialogTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ModalDialogTest.java
@@ -28,13 +28,13 @@ import org.chromium.chrome.R; import org.chromium.chrome.test.ChromeActivityTestRule; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; -import org.chromium.content_public.browser.GestureListenerManager; import org.chromium.content_public.browser.GestureStateListener; import org.chromium.content_public.browser.test.util.Criteria; import org.chromium.content_public.browser.test.util.CriteriaHelper; import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer; import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper; import org.chromium.content_public.browser.test.util.TouchCommon; +import org.chromium.content_public.browser.test.util.WebContentsUtils; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; @@ -241,18 +241,14 @@ } } - private GestureListenerManager getGestureListenerManager() { - return GestureListenerManager.fromWebContents( - mActivityTestRule.getActivity().getCurrentWebContents()); - } - /** * Taps on a view and waits for a callback. */ private void tapViewAndWait() throws InterruptedException, TimeoutException { final TapGestureStateListener tapGestureStateListener = new TapGestureStateListener(); int callCount = tapGestureStateListener.getCallCount(); - getGestureListenerManager().addListener(tapGestureStateListener); + WebContentsUtils.getGestureListenerManager(mActivityTestRule.getWebContents()) + .addListener(tapGestureStateListener); TouchCommon.singleClickView(mActivityTestRule.getActivity().getActivityTab().getView()); tapGestureStateListener.waitForTap(callCount);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupTest.java index 0c44407..99a7fdd 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupTest.java
@@ -31,6 +31,7 @@ import org.chromium.content_public.browser.test.util.DOMUtils; import org.chromium.content_public.browser.test.util.TestInputMethodManagerWrapper; import org.chromium.content_public.browser.test.util.TouchCommon; +import org.chromium.content_public.browser.test.util.WebContentsUtils; import org.chromium.ui.DropdownPopupWindowInterface; import org.chromium.ui.R; @@ -151,7 +152,7 @@ // brought up. final WebContents webContents = mActivityTestRule.getActivity().getCurrentWebContents(); final ViewGroup view = webContents.getViewAndroidDelegate().getContainerView(); - final ImeAdapter imeAdapter = ImeAdapter.fromWebContents(webContents); + final ImeAdapter imeAdapter = WebContentsUtils.getImeAdapter(webContents); TestInputMethodManagerWrapper immw = TestInputMethodManagerWrapper.create(imeAdapter); imeAdapter.setInputMethodManagerWrapper(immw);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java index 92d41796..f72542c 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java
@@ -455,7 +455,7 @@ mDelegate.add(new IntentActivity(YOUTUBE_MOBILE_URL, YOUTUBE_PACKAGE_NAME)); mDelegate.add(new IntentActivity(YOUTUBE_URL, YOUTUBE_PACKAGE_NAME)); - TabRedirectHandler redirectHandler = new TabRedirectHandler(mContext); + TabRedirectHandler redirectHandler = TabRedirectHandler.create(mContext); Intent ytIntent = Intent.parseUri(YOUTUBE_URL, Intent.URI_INTENT_SCHEME); Intent fooIntent = Intent.parseUri("http://foo.com/", Intent.URI_INTENT_SCHEME); int transTypeLinkFromIntent = PageTransition.LINK @@ -501,7 +501,7 @@ testInitialIntentHeadingToChrome() throws URISyntaxException { mDelegate.add(new IntentActivity(YOUTUBE_MOBILE_URL, YOUTUBE_PACKAGE_NAME)); - TabRedirectHandler redirectHandler = new TabRedirectHandler(mContext); + TabRedirectHandler redirectHandler = TabRedirectHandler.create(mContext); Intent fooIntent = Intent.parseUri("http://foo.com/", Intent.URI_INTENT_SCHEME); fooIntent.setPackage(mContext.getPackageName()); int transTypeLinkFromIntent = PageTransition.LINK @@ -535,7 +535,7 @@ testIntentForCustomTab() throws URISyntaxException { mDelegate.add(new IntentActivity(YOUTUBE_URL, YOUTUBE_PACKAGE_NAME)); - TabRedirectHandler redirectHandler = new TabRedirectHandler(mContext); + TabRedirectHandler redirectHandler = TabRedirectHandler.create(mContext); int transTypeLinkFromIntent = PageTransition.LINK | PageTransition.FROM_API; // In Custom Tabs, if the first url is not a redirect, stay in chrome. @@ -611,7 +611,7 @@ @SmallTest public void testInstantAppsIntent_customTabRedirect() throws Exception { - TabRedirectHandler redirectHandler = new TabRedirectHandler(mContext); + TabRedirectHandler redirectHandler = TabRedirectHandler.create(mContext); int transTypeLinkFromIntent = PageTransition.LINK | PageTransition.FROM_API; // In Custom Tabs, if the first url is a redirect, don't allow it to intent out, unless @@ -637,7 +637,7 @@ public void testInstantAppsIntent_incomingIntentRedirect() throws Exception { int transTypeLinkFromIntent = PageTransition.LINK | PageTransition.FROM_API; - TabRedirectHandler redirectHandler = new TabRedirectHandler(mContext); + TabRedirectHandler redirectHandler = TabRedirectHandler.create(mContext); Intent fooIntent = Intent.parseUri("http://instantappenabled.com", Intent.URI_INTENT_SCHEME); redirectHandler.updateIntent(fooIntent); @@ -815,7 +815,7 @@ @Test @SmallTest public void testFallbackUrl_RedirectToIntentToMarket() { - TabRedirectHandler redirectHandler = new TabRedirectHandler(mContext); + TabRedirectHandler redirectHandler = TabRedirectHandler.create(mContext); redirectHandler.updateNewUrlLoading(PageTransition.TYPED, false, false, 0, 0); checkUrl("http://goo.gl/abcdefg") @@ -898,7 +898,7 @@ public void testFallback_UseFallbackUrlForRedirectionFromTypedInUrl() { mDelegate.add(new IntentActivity(YOUTUBE_MOBILE_URL, YOUTUBE_PACKAGE_NAME)); - TabRedirectHandler redirectHandler = new TabRedirectHandler(mContext); + TabRedirectHandler redirectHandler = TabRedirectHandler.create(mContext); redirectHandler.updateNewUrlLoading(PageTransition.TYPED, false, false, 0, 0); checkUrl("http://goo.gl/abcdefg") @@ -924,7 +924,7 @@ // We cannot resolve any intent, so fall-back URL will be used. mDelegate.setCanResolveActivityForExternalSchemes(false); - TabRedirectHandler redirectHandler = new TabRedirectHandler(mContext); + TabRedirectHandler redirectHandler = TabRedirectHandler.create(mContext); redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0); checkUrl(INTENT_URL_WITH_CHAIN_FALLBACK_URL) @@ -958,7 +958,7 @@ public void testIgnoreEffectiveRedirectFromUserTyping() { mDelegate.add(new IntentActivity(YOUTUBE_MOBILE_URL, YOUTUBE_PACKAGE_NAME)); - TabRedirectHandler redirectHandler = new TabRedirectHandler(mContext); + TabRedirectHandler redirectHandler = TabRedirectHandler.create(mContext); redirectHandler.updateNewUrlLoading(PageTransition.TYPED, false, false, 0, 0); checkUrl(YOUTUBE_MOBILE_URL) @@ -984,7 +984,7 @@ public void testNavigationFromLinkWithoutUserGesture() { mDelegate.add(new IntentActivity(YOUTUBE_MOBILE_URL, YOUTUBE_PACKAGE_NAME)); - TabRedirectHandler redirectHandler = new TabRedirectHandler(mContext); + TabRedirectHandler redirectHandler = TabRedirectHandler.create(mContext); redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 1, 0); checkUrl(YOUTUBE_MOBILE_URL) @@ -1156,7 +1156,7 @@ public void testNavigationFromReload() { mDelegate.add(new IntentActivity(YOUTUBE_MOBILE_URL, YOUTUBE_PACKAGE_NAME)); - TabRedirectHandler redirectHandler = new TabRedirectHandler(mContext); + TabRedirectHandler redirectHandler = TabRedirectHandler.create(mContext); redirectHandler.updateNewUrlLoading(PageTransition.RELOAD, false, false, 1, 0); checkUrl(YOUTUBE_MOBILE_URL) @@ -1180,7 +1180,7 @@ public void testNavigationWithForwardBack() { mDelegate.add(new IntentActivity(YOUTUBE_MOBILE_URL, YOUTUBE_PACKAGE_NAME)); - TabRedirectHandler redirectHandler = new TabRedirectHandler(mContext); + TabRedirectHandler redirectHandler = TabRedirectHandler.create(mContext); redirectHandler.updateNewUrlLoading( PageTransition.FORM_SUBMIT | PageTransition.FORWARD_BACK, false, false, 1, 0);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java index 1962b63..948a02a 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java
@@ -48,13 +48,13 @@ import org.chromium.content_public.browser.GestureListenerManager; import org.chromium.content_public.browser.GestureStateListener; import org.chromium.content_public.browser.SelectionPopupController; -import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.test.util.Criteria; import org.chromium.content_public.browser.test.util.CriteriaHelper; import org.chromium.content_public.browser.test.util.JavaScriptUtils; import org.chromium.content_public.browser.test.util.TestTouchUtils; import org.chromium.content_public.browser.test.util.TouchCommon; import org.chromium.content_public.browser.test.util.UiUtils; +import org.chromium.content_public.browser.test.util.WebContentsUtils; import org.chromium.net.test.EmbeddedTestServer; import java.util.concurrent.TimeUnit; @@ -279,9 +279,8 @@ }; Tab tab = mActivityTestRule.getActivity().getActivityTab(); - WebContents webContents = tab.getWebContents(); GestureListenerManager gestureListenerManager = - GestureListenerManager.fromWebContents(webContents); + WebContentsUtils.getGestureListenerManager(tab.getWebContents()); gestureListenerManager.addListener(scrollListener); final CallbackHelper viewportCallback = new CallbackHelper();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTestUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTestUtils.java index f49fa89..c6e22a5 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTestUtils.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTestUtils.java
@@ -20,6 +20,7 @@ import org.chromium.content_public.browser.test.util.Criteria; import org.chromium.content_public.browser.test.util.CriteriaHelper; import org.chromium.content_public.browser.test.util.TouchCommon; +import org.chromium.content_public.browser.test.util.WebContentsUtils; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; @@ -151,7 +152,7 @@ }; GestureListenerManager gestureListenerManager = - GestureListenerManager.fromWebContents(webContents); + WebContentsUtils.getGestureListenerManager(webContents); gestureListenerManager.addListener(scrollEndListener); for (int i = 0; i < 10; i++) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabRedirectHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabRedirectHandlerTest.java index 1dd594f..1f0fd9e0 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabRedirectHandlerTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabRedirectHandlerTest.java
@@ -62,7 +62,7 @@ @SmallTest @Feature({"IntentHandling"}) public void testRealIntentRedirect() { - TabRedirectHandler handler = new TabRedirectHandler(mContext); + TabRedirectHandler handler = TabRedirectHandler.create(mContext); handler.updateIntent(sYtIntent); Assert.assertFalse(handler.isOnNavigation()); @@ -82,7 +82,7 @@ @SmallTest @Feature({"IntentHandling"}) public void testEffectiveIntentRedirect() { - TabRedirectHandler handler = new TabRedirectHandler(mContext); + TabRedirectHandler handler = TabRedirectHandler.create(mContext); handler.updateIntent(sYtIntent); Assert.assertFalse(handler.isOnNavigation()); @@ -102,7 +102,7 @@ @SmallTest @Feature({"IntentHandling"}) public void testNoIntent() { - TabRedirectHandler handler = new TabRedirectHandler(mContext); + TabRedirectHandler handler = TabRedirectHandler.create(mContext); handler.updateIntent(null); Assert.assertFalse(handler.isOnNavigation()); @@ -122,7 +122,7 @@ @SmallTest @Feature({"IntentHandling"}) public void testClear() { - TabRedirectHandler handler = new TabRedirectHandler(mContext); + TabRedirectHandler handler = TabRedirectHandler.create(mContext); handler.updateIntent(sYtIntent); Assert.assertFalse(handler.isOnNavigation()); @@ -147,7 +147,7 @@ @SmallTest @Feature({"IntentHandling"}) public void testNonLinkFromIntent() { - TabRedirectHandler handler = new TabRedirectHandler(mContext); + TabRedirectHandler handler = TabRedirectHandler.create(mContext); handler.updateIntent(sYtIntent); Assert.assertFalse(handler.isOnNavigation()); @@ -167,7 +167,7 @@ @SmallTest @Feature({"IntentHandling"}) public void testUserInteraction() { - TabRedirectHandler handler = new TabRedirectHandler(mContext); + TabRedirectHandler handler = TabRedirectHandler.create(mContext); handler.updateIntent(sYtIntent); Assert.assertFalse(handler.isOnNavigation()); @@ -196,7 +196,7 @@ @SmallTest @Feature({"IntentHandling"}) public void testIntentFromChrome() { - TabRedirectHandler handler = new TabRedirectHandler(mContext); + TabRedirectHandler handler = TabRedirectHandler.create(mContext); Intent fooIntent = new Intent(sFooIntent); fooIntent.putExtra(Browser.EXTRA_APPLICATION_ID, TEST_PACKAGE_NAME); handler.updateIntent(fooIntent); @@ -228,7 +228,7 @@ @SmallTest @Feature({"IntentHandling"}) public void testNavigationFromUserTyping() { - TabRedirectHandler handler = new TabRedirectHandler(mContext); + TabRedirectHandler handler = TabRedirectHandler.create(mContext); handler.updateIntent(sYtIntent); Assert.assertFalse(handler.isOnNavigation()); Assert.assertFalse(handler.isNavigationFromUserTyping()); @@ -254,7 +254,7 @@ @SmallTest @Feature({"IntentHandling"}) public void testIntentHavingChromePackageName() { - TabRedirectHandler handler = new TabRedirectHandler(mContext); + TabRedirectHandler handler = TabRedirectHandler.create(mContext); Intent fooIntent = new Intent(sFooIntent); fooIntent.setPackage(TEST_PACKAGE_NAME); handler.updateIntent(fooIntent); @@ -289,7 +289,7 @@ ///////////////////////////////////////////////////// // 1. 3XX redirection should not override URL loading. ///////////////////////////////////////////////////// - TabRedirectHandler handler = new TabRedirectHandler(mContext); + TabRedirectHandler handler = TabRedirectHandler.create(mContext); handler.updateIntent(sYtIntent); Assert.assertFalse(handler.shouldNotOverrideUrlLoading()); @@ -303,7 +303,7 @@ ///////////////////////////////////////////////////// // 2. Effective redirection should not override URL loading. ///////////////////////////////////////////////////// - handler = new TabRedirectHandler(mContext); + handler = TabRedirectHandler.create(mContext); handler.updateIntent(sYtIntent); Assert.assertFalse(handler.shouldNotOverrideUrlLoading()); @@ -330,7 +330,7 @@ @Feature({"IntentHandling"}) @RetryOnFailure public void testNavigationFromLinkWithoutUserGesture() { - TabRedirectHandler handler = new TabRedirectHandler(mContext); + TabRedirectHandler handler = TabRedirectHandler.create(mContext); handler.updateIntent(sYtIntent); Assert.assertFalse(handler.isOnNavigation()); Assert.assertFalse(handler.shouldStayInChrome(false)); @@ -363,7 +363,7 @@ @Feature({"IntentHandling"}) @RetryOnFailure public void testNavigationFromReload() { - TabRedirectHandler handler = new TabRedirectHandler(mContext); + TabRedirectHandler handler = TabRedirectHandler.create(mContext); handler.updateIntent(sYtIntent); Assert.assertFalse(handler.isOnNavigation()); Assert.assertFalse(handler.shouldStayInChrome(false)); @@ -396,7 +396,7 @@ @Feature({"IntentHandling"}) @RetryOnFailure public void testNavigationWithForwardBack() { - TabRedirectHandler handler = new TabRedirectHandler(mContext); + TabRedirectHandler handler = TabRedirectHandler.create(mContext); handler.updateIntent(sYtIntent); Assert.assertFalse(handler.isOnNavigation()); Assert.assertFalse(handler.shouldStayInChrome(false)); @@ -431,7 +431,7 @@ // User interaction time could be uninitialized when a new document activity is opened after // clicking a link. In that case, the value is 0. final long uninitializedUserInteractionTime = 0; - TabRedirectHandler handler = new TabRedirectHandler(mContext); + TabRedirectHandler handler = TabRedirectHandler.create(mContext); Assert.assertFalse(handler.isOnNavigation()); handler.updateNewUrlLoading(PageTransition.LINK, false, true,
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt index 21b93c7..bb8c9f98 100644 --- a/chrome/android/profiles/newest.txt +++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@ -chromeos-chrome-amd64-71.0.3543.0_rc-r1.afdo.bz2 \ No newline at end of file +chromeos-chrome-amd64-71.0.3544.0_rc-r1.afdo.bz2 \ No newline at end of file
diff --git a/chrome/app/chrome_manifest.json b/chrome/app/chrome_manifest.json index 444d1b9e..d8ae07b21f 100644 --- a/chrome/app/chrome_manifest.json +++ b/chrome/app/chrome_manifest.json
@@ -2,7 +2,8 @@ "name": "chrome", "display_name": "Chrome", "options" : { - "instance_sharing" : "shared_instance_across_users" + "instance_sharing": "shared_instance_across_users", + "can_connect_to_other_services_with_any_instance_name": true }, "interface_provider_specs": { "service_manager:connector": { @@ -24,8 +25,7 @@ "requires": { "chrome_renderer": [ "browser" ], "service_manager": [ - "service_manager:client_process", - "service_manager:instance_name" + "service_manager:client_process" ] } }
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 5153d6e..826d068 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -3588,14 +3588,6 @@ FEATURE_VALUE_TYPE(features::kDirectManipulationStylus)}, #endif // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) -#if !defined(OS_ANDROID) - {"remove-deprecared-gaia-signin-endpoint", - flag_descriptions::kRemoveUsageOfDeprecatedGaiaSigninEndpointName, - flag_descriptions::kRemoveUsageOfDeprecatedGaiaSigninEndpointDescription, - kOsWin | kOsMac | kOsLinux, - FEATURE_VALUE_TYPE(features::kRemoveUsageOfDeprecatedGaiaSigninEndpoint)}, -#endif - #if defined(OS_ANDROID) {"third-party-doodles", flag_descriptions::kThirdPartyDoodlesName, flag_descriptions::kThirdPartyDoodlesDescription, kOsAndroid,
diff --git a/chrome/browser/background_fetch/background_fetch_delegate_impl.cc b/chrome/browser/background_fetch/background_fetch_delegate_impl.cc index 56026fe..5b65c0c 100644 --- a/chrome/browser/background_fetch/background_fetch_delegate_impl.cc +++ b/chrome/browser/background_fetch/background_fetch_delegate_impl.cc
@@ -46,7 +46,9 @@ offline_content_aggregator_->RegisterProvider(provider_namespace_, this); } -BackgroundFetchDelegateImpl::~BackgroundFetchDelegateImpl() = default; +BackgroundFetchDelegateImpl::~BackgroundFetchDelegateImpl() { + offline_content_aggregator_->UnregisterProvider(provider_namespace_); +} download::DownloadService* BackgroundFetchDelegateImpl::GetDownloadService() { if (download_service_)
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index ec30cd0d..3e7abde 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -4493,7 +4493,8 @@ content::RenderFrameHost* frame, bool is_navigation, const GURL& url, - network::mojom::URLLoaderFactoryRequest* factory_request) { + network::mojom::URLLoaderFactoryRequest* factory_request, + bool* bypass_redirect_checks) { DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService)); bool use_proxy = false; @@ -4505,8 +4506,12 @@ // NOTE: Some unit test environments do not initialize // BrowserContextKeyedAPI factories for e.g. WebRequest. if (web_request_api) { - use_proxy |= web_request_api->MaybeProxyURLLoaderFactory( - frame, is_navigation, factory_request); + bool use_proxy_for_web_request = + web_request_api->MaybeProxyURLLoaderFactory(frame, is_navigation, + factory_request); + if (bypass_redirect_checks) + *bypass_redirect_checks = use_proxy_for_web_request; + use_proxy |= use_proxy_for_web_request; } #endif
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h index a063582..ad21627 100644 --- a/chrome/browser/chrome_content_browser_client.h +++ b/chrome/browser/chrome_content_browser_client.h
@@ -439,7 +439,8 @@ content::RenderFrameHost* frame, bool is_navigation, const GURL& url, - network::mojom::URLLoaderFactoryRequest* factory_request) override; + network::mojom::URLLoaderFactoryRequest* factory_request, + bool* bypass_redirect_checks) override; std::vector<std::unique_ptr<content::URLLoaderRequestInterceptor>> WillCreateURLLoaderRequestInterceptors( content::NavigationUIData* navigation_ui_data,
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index 0bcd2fb..b4e1b72 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -562,6 +562,8 @@ "chrome_service_name.h", "crostini/crostini_manager.cc", "crostini/crostini_manager.h", + "crostini/crostini_manager_factory.cc", + "crostini/crostini_manager_factory.h", "crostini/crostini_mime_types_service.cc", "crostini/crostini_mime_types_service.h", "crostini/crostini_mime_types_service_factory.cc",
diff --git a/chrome/browser/chromeos/android_sms/BUILD.gn b/chrome/browser/chromeos/android_sms/BUILD.gn index f0026bd9..3531206c 100644 --- a/chrome/browser/chromeos/android_sms/BUILD.gn +++ b/chrome/browser/chromeos/android_sms/BUILD.gn
@@ -35,6 +35,8 @@ "//chrome/browser/chromeos:chromeos", "//chromeos:chromeos", "//chromeos/components/proximity_auth/logging", + "//chromeos/services/multidevice_setup/public/cpp:cpp", + "//chromeos/services/multidevice_setup/public/cpp:prefs", "//components/keyed_service/content:content", "//components/keyed_service/core:core", "//components/session_manager/core:core", @@ -68,6 +70,7 @@ ":android_sms", ":android_sms_urls", ":test_support", + "//chromeos/services/multidevice_setup/public/cpp:test_support", "//content/public/browser", "//content/test:test_support", "//testing/gtest",
diff --git a/chrome/browser/chromeos/android_sms/android_sms_service.cc b/chrome/browser/chromeos/android_sms/android_sms_service.cc index 420c7ca9..1611ded 100644 --- a/chrome/browser/chromeos/android_sms/android_sms_service.cc +++ b/chrome/browser/chromeos/android_sms/android_sms_service.cc
@@ -5,9 +5,15 @@ #include "chrome/browser/chromeos/android_sms/android_sms_service.h" #include "chrome/browser/chromeos/android_sms/android_sms_urls.h" #include "chrome/browser/chromeos/android_sms/connection_establisher_impl.h" +#include "chrome/browser/chromeos/multidevice_setup/multidevice_setup_client_factory.h" +#include "chrome/browser/chromeos/profiles/profile_helper.h" +#include "chromeos/services/multidevice_setup/public/cpp/prefs.h" #include "components/session_manager/core/session_manager.h" #include "content/public/browser/storage_partition.h" +using chromeos::multidevice_setup::MultiDeviceSetupClient; +using chromeos::multidevice_setup::MultiDeviceSetupClientFactory; + namespace chromeos { namespace android_sms { @@ -37,8 +43,14 @@ browser_context_, GetAndroidMessagesURL()); content::ServiceWorkerContext* service_worker_context = storage_partition->GetServiceWorkerContext(); + + MultiDeviceSetupClient* multidevice_setup_client = + MultiDeviceSetupClientFactory::GetForProfile( + Profile::FromBrowserContext(browser_context_)); + connection_manager_ = std::make_unique<ConnectionManager>( - service_worker_context, std::make_unique<ConnectionEstablisherImpl>()); + service_worker_context, std::make_unique<ConnectionEstablisherImpl>(), + multidevice_setup_client); } } // namespace android_sms
diff --git a/chrome/browser/chromeos/android_sms/android_sms_service_factory.cc b/chrome/browser/chromeos/android_sms/android_sms_service_factory.cc index 30f78ae..6e4622aa 100644 --- a/chrome/browser/chromeos/android_sms/android_sms_service_factory.cc +++ b/chrome/browser/chromeos/android_sms/android_sms_service_factory.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include "chrome/browser/chromeos/android_sms/android_sms_service_factory.h" +#include "chrome/browser/chromeos/multidevice_setup/multidevice_setup_client_factory.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chromeos/chromeos_features.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" @@ -28,7 +29,10 @@ AndroidSmsServiceFactory::AndroidSmsServiceFactory() : BrowserContextKeyedServiceFactory( "AndroidSmsService", - BrowserContextDependencyManager::GetInstance()) {} + BrowserContextDependencyManager::GetInstance()) { + DependsOn(chromeos::multidevice_setup::MultiDeviceSetupClientFactory:: + GetInstance()); +} AndroidSmsServiceFactory::~AndroidSmsServiceFactory() = default;
diff --git a/chrome/browser/chromeos/android_sms/connection_manager.cc b/chrome/browser/chromeos/android_sms/connection_manager.cc index 9021b0b..f5122d53 100644 --- a/chrome/browser/chromeos/android_sms/connection_manager.cc +++ b/chrome/browser/chromeos/android_sms/connection_manager.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/chromeos/android_sms/connection_manager.h" #include "chrome/browser/chromeos/android_sms/android_sms_urls.h" +#include "chromeos/components/proximity_auth/logging/logging.h" #include "components/session_manager/core/session_manager.h" namespace chromeos { @@ -12,15 +13,20 @@ ConnectionManager::ConnectionManager( content::ServiceWorkerContext* service_worker_context, - std::unique_ptr<ConnectionEstablisher> connection_establisher) + std::unique_ptr<ConnectionEstablisher> connection_establisher, + MultiDeviceSetupClient* multidevice_setup_client) : service_worker_context_(service_worker_context), - connection_establisher_(std::move(connection_establisher)) { + connection_establisher_(std::move(connection_establisher)), + multidevice_setup_client_(multidevice_setup_client) { service_worker_context_->AddObserver(this); - connection_establisher_->EstablishConnection(service_worker_context_); + multidevice_setup_client_->AddObserver(this); + UpdateAndroidSmsFeatureState(multidevice_setup_client->GetFeatureState( + multidevice_setup::mojom::Feature::kMessages)); } ConnectionManager::~ConnectionManager() { service_worker_context_->RemoveObserver(this); + multidevice_setup_client_->RemoveObserver(this); } void ConnectionManager::OnVersionActivated(int64_t version_id, @@ -30,7 +36,8 @@ prev_active_version_id_ = active_version_id_; active_version_id_ = version_id; - connection_establisher_->EstablishConnection(service_worker_context_); + if (is_android_sms_enabled_) + connection_establisher_->EstablishConnection(service_worker_context_); } void ConnectionManager::OnVersionRedundant(int64_t version_id, @@ -60,7 +67,36 @@ if (active_version_id_ != version_id) return; - connection_establisher_->EstablishConnection(service_worker_context_); + if (is_android_sms_enabled_) + connection_establisher_->EstablishConnection(service_worker_context_); +} + +void ConnectionManager::OnFeatureStatesChanged( + const MultiDeviceSetupClient::FeatureStatesMap& feature_states_map) { + const auto it = + feature_states_map.find(multidevice_setup::mojom::Feature::kMessages); + if (it == feature_states_map.end()) + return; + + UpdateAndroidSmsFeatureState(it->second); +} + +void ConnectionManager::UpdateAndroidSmsFeatureState( + multidevice_setup::mojom::FeatureState feature_state) { + bool is_enabled = + feature_state == multidevice_setup::mojom::FeatureState::kEnabledByUser; + if (is_android_sms_enabled_ == is_enabled) + return; + + PA_LOG(INFO) << "ConnectionManager::UpdateAndroidSmsFeatureState enabled: " + << is_enabled; + if (is_enabled) { + connection_establisher_->EstablishConnection(service_worker_context_); + } else { + service_worker_context_->StopAllServiceWorkersForOrigin( + GetAndroidMessagesURL()); + } + is_android_sms_enabled_ = is_enabled; } } // namespace android_sms
diff --git a/chrome/browser/chromeos/android_sms/connection_manager.h b/chrome/browser/chromeos/android_sms/connection_manager.h index 6b11077..8209ed4 100644 --- a/chrome/browser/chromeos/android_sms/connection_manager.h +++ b/chrome/browser/chromeos/android_sms/connection_manager.h
@@ -7,10 +7,13 @@ #include "base/gtest_prod_util.h" #include "chrome/browser/chromeos/android_sms/connection_establisher.h" +#include "chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/service_worker_context.h" #include "content/public/browser/service_worker_context_observer.h" +using chromeos::multidevice_setup::MultiDeviceSetupClient; + namespace chromeos { namespace android_sms { @@ -33,11 +36,13 @@ // connection as required. E.g., The service worker will not establish a // connection if it's is already connected to the Android Messages for Web page // or if a connection already exists. -class ConnectionManager : public content::ServiceWorkerContextObserver { +class ConnectionManager : public content::ServiceWorkerContextObserver, + public MultiDeviceSetupClient::Observer { public: ConnectionManager( content::ServiceWorkerContext* service_worker_context, - std::unique_ptr<ConnectionEstablisher> connection_establisher); + std::unique_ptr<ConnectionEstablisher> connection_establisher, + MultiDeviceSetupClient* multidevice_setup_client); ~ConnectionManager() override; private: @@ -46,8 +51,16 @@ void OnVersionRedundant(int64_t version_id, const GURL& scope) override; void OnNoControllees(int64_t version_id, const GURL& scope) override; + // MultideviceSetupClient::Observer: + void OnFeatureStatesChanged(const MultiDeviceSetupClient::FeatureStatesMap& + feature_state_map) override; + + void UpdateAndroidSmsFeatureState( + multidevice_setup::mojom::FeatureState feature_state); + content::ServiceWorkerContext* service_worker_context_; std::unique_ptr<ConnectionEstablisher> connection_establisher_; + MultiDeviceSetupClient* multidevice_setup_client_; // Version ID of the Android Messages for Web service worker that's currently // active i.e., capable of handling messages and controlling pages. @@ -57,6 +70,8 @@ // service worker. base::Optional<int64_t> prev_active_version_id_; + bool is_android_sms_enabled_ = false; + DISALLOW_COPY_AND_ASSIGN(ConnectionManager); };
diff --git a/chrome/browser/chromeos/android_sms/connection_manager_unittest.cc b/chrome/browser/chromeos/android_sms/connection_manager_unittest.cc index c16bda7..badb971 100644 --- a/chrome/browser/chromeos/android_sms/connection_manager_unittest.cc +++ b/chrome/browser/chromeos/android_sms/connection_manager_unittest.cc
@@ -5,6 +5,7 @@ #include "chrome/browser/chromeos/android_sms/connection_manager.h" #include "chrome/browser/chromeos/android_sms/android_sms_urls.h" #include "chrome/browser/chromeos/android_sms/fake_connection_establisher.h" +#include "chromeos/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h" #include "content/public/test/fake_service_worker_context.h" #include "testing/gtest/include/gtest/gtest.h" @@ -23,14 +24,21 @@ ~ConnectionManagerTest() override = default; void SetUp() override { + fake_service_worker_context_ = + std::make_unique<content::FakeServiceWorkerContext>(); + fake_multidevice_setup_client_ = + std::make_unique<multidevice_setup::FakeMultiDeviceSetupClient>(); + } + + void SetupConnectionManager(bool initial_feature_state) { + SetFeatureState(initial_feature_state); std::unique_ptr<FakeConnectionEstablisher> fake_connection_establisher = std::make_unique<FakeConnectionEstablisher>(); fake_connection_establisher_ = fake_connection_establisher.get(); - fake_service_worker_context_ = - std::make_unique<content::FakeServiceWorkerContext>(); connection_manager_ = std::make_unique<ConnectionManager>( fake_service_worker_context_.get(), - std::move(fake_connection_establisher)); + std::move(fake_connection_establisher), + fake_multidevice_setup_client_.get()); } void VerifyNumEstablishConnectionCalls(size_t count) { @@ -42,11 +50,20 @@ EXPECT_EQ(fake_service_worker_context_.get(), service_worker_context); } + void SetFeatureState(bool enable) { + fake_multidevice_setup_client_->SetFeatureState( + multidevice_setup::mojom::Feature::kMessages, + enable ? multidevice_setup::mojom::FeatureState::kEnabledByUser + : multidevice_setup::mojom::FeatureState::kDisabledByUser); + } + content::FakeServiceWorkerContext* fake_service_worker_context() const { return fake_service_worker_context_.get(); } private: + std::unique_ptr<multidevice_setup::FakeMultiDeviceSetupClient> + fake_multidevice_setup_client_; FakeConnectionEstablisher* fake_connection_establisher_; std::unique_ptr<content::FakeServiceWorkerContext> fake_service_worker_context_; @@ -55,6 +72,7 @@ }; TEST_F(ConnectionManagerTest, ConnectOnActivate) { + SetupConnectionManager(true /* initial_feature_state */); fake_service_worker_context()->NotifyObserversOnVersionActivated( kDummyVersionId, GetAndroidMessagesURL()); @@ -65,6 +83,7 @@ } TEST_F(ConnectionManagerTest, ConnectOnNoControllees) { + SetupConnectionManager(true /* initial_feature_state */); // Notify Activation so that Connection manager is tracking the version ID. fake_service_worker_context()->NotifyObserversOnVersionActivated( kDummyVersionId, GetAndroidMessagesURL()); @@ -78,6 +97,7 @@ } TEST_F(ConnectionManagerTest, IgnoreRedundantVersion) { + SetupConnectionManager(true /* initial_feature_state */); fake_service_worker_context()->NotifyObserversOnVersionActivated( kDummyVersionId, GetAndroidMessagesURL()); @@ -94,6 +114,7 @@ } TEST_F(ConnectionManagerTest, ConnectOnNoControlleesWithNoActive) { + SetupConnectionManager(true /* initial_feature_state */); // Verify that connection establishing is attempted when there are no // controllees for a version ID even if the activate notification was missed. fake_service_worker_context()->NotifyObserversOnNoControllees( @@ -103,6 +124,7 @@ } TEST_F(ConnectionManagerTest, IgnoreOnNoControlleesInvalidId) { + SetupConnectionManager(true /* initial_feature_state */); fake_service_worker_context()->NotifyObserversOnVersionActivated( kDummyVersionId, GetAndroidMessagesURL()); @@ -114,6 +136,7 @@ } TEST_F(ConnectionManagerTest, InvalidScope) { + SetupConnectionManager(true /* initial_feature_state */); GURL invalid_scope("https://example.com"); // Verify that OnVersionActivated and OnNoControllees with invalid scope // are ignored @@ -134,6 +157,39 @@ VerifyNumEstablishConnectionCalls(3u); } +TEST_F(ConnectionManagerTest, FeatureStateInitDisabled) { + // Verify that connection is not established on initialization + // if the feature is not enabled. + SetupConnectionManager(false /* initial_feature_state */); + VerifyNumEstablishConnectionCalls(0u); + + SetFeatureState(true); + VerifyNumEstablishConnectionCalls(1u); +} + +TEST_F(ConnectionManagerTest, FeatureStateChange) { + SetupConnectionManager(true /* initial_feature_state */); + fake_service_worker_context()->NotifyObserversOnVersionActivated( + kDummyVersionId, GetAndroidMessagesURL()); + + // Verify that disabling feature stops the service worker. + SetFeatureState(false); + VerifyNumEstablishConnectionCalls(2u); + const auto& stop_calls = fake_service_worker_context() + ->stop_all_service_workers_for_origin_calls(); + ASSERT_EQ(1u, stop_calls.size()); + EXPECT_EQ(GetAndroidMessagesURL(), stop_calls[0]); + + // Verify that subsequent service worker events do not trigger connection. + fake_service_worker_context()->NotifyObserversOnNoControllees( + kDummyVersionId, GetAndroidMessagesURL()); + VerifyNumEstablishConnectionCalls(2u); + + // Verify that enabling feature establishes connection again. + SetFeatureState(true); + VerifyNumEstablishConnectionCalls(3u); +} + } // namespace android_sms } // namespace chromeos
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc index 517d6335..3991925 100644 --- a/chrome/browser/chromeos/crostini/crostini_manager.cc +++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -14,6 +14,7 @@ #include "base/sys_info.h" #include "base/task/post_task.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/chromeos/crostini/crostini_manager_factory.h" #include "chrome/browser/chromeos/crostini/crostini_remover.h" #include "chrome/browser/chromeos/crostini/crostini_util.h" #include "chrome/browser/chromeos/file_manager/path_util.h" @@ -55,95 +56,47 @@ return chromeos::DBusThreadManager::Get()->GetConciergeClient(); } -class CrostiniRestarter; - -class CrostiniRestarterService : public KeyedService { - public: - CrostiniRestarterService() = default; - ~CrostiniRestarterService() override = default; - - CrostiniManager::RestartId Register( - Profile* profile, - std::string vm_name, - std::string container_name, - CrostiniManager::RestartCrostiniCallback callback, - CrostiniManager::RestartObserver* observer); - - void RunPendingCallbacks(CrostiniRestarter* restarter, - ConciergeClientResult result); - - // Aborts restart_id. A "next" restarter with the same <vm_name, - // container_name> will run, if there is one. - void Abort(CrostiniManager::RestartId restart_id); - - private: - void ErasePending(CrostiniRestarter* restarter); - - std::map<CrostiniManager::RestartId, scoped_refptr<CrostiniRestarter>> - restarter_map_; - - // Restarts by <vm_name, container_name>. Only one restarter flow is actually - // running. Other restarters will just have their callback called when the - // running restarter completes. - std::multimap<std::pair<std::string, std::string>, CrostiniManager::RestartId> - pending_map_; -}; - -class CrostiniRestarterServiceFactory - : public BrowserContextKeyedServiceFactory { - public: - static CrostiniRestarterService* GetForProfile(Profile* profile) { - return static_cast<CrostiniRestarterService*>( - GetInstance()->GetServiceForBrowserContext(profile, true)); +void OnConciergeServiceAvailable( + CrostiniManager::StartConciergeCallback callback, + bool success) { + if (!success) { + LOG(ERROR) << "Concierge service did not become available"; + std::move(callback).Run(success); + return; } - static CrostiniRestarterServiceFactory* GetInstance() { - static base::NoDestructor<CrostiniRestarterServiceFactory> factory; - return factory.get(); - } + VLOG(1) << "Concierge service announced availability"; + VLOG(1) << "Waiting for Cicerone to announce availability."; - private: - friend class base::NoDestructor<CrostiniRestarterServiceFactory>; + GetCiceroneClient()->WaitForServiceToBeAvailable(std::move(callback)); +} - CrostiniRestarterServiceFactory() - : BrowserContextKeyedServiceFactory( - "CrostiniRestarterService", - BrowserContextDependencyManager::GetInstance()) {} - ~CrostiniRestarterServiceFactory() override = default; +} // namespace - // BrowserContextKeyedServiceFactory: - KeyedService* BuildServiceInstanceFor( - content::BrowserContext* context) const override { - return new CrostiniRestarterService(); - } -}; - -class CrostiniRestarter : public base::RefCountedThreadSafe<CrostiniRestarter>, - public chromeos::disks::DiskMountManager::Observer { +class CrostiniManager::CrostiniRestarter + : public base::RefCountedThreadSafe<CrostiniRestarter>, + public chromeos::disks::DiskMountManager::Observer { public: - CrostiniRestarter(CrostiniRestarterService* restarter_service, - Profile* profile, + CrostiniRestarter(Profile* profile, + base::WeakPtr<CrostiniManager> crostini_manager, std::string vm_name, - std::string cryptohome_id, std::string container_name, std::string container_username, CrostiniManager::RestartCrostiniCallback callback) : profile_(profile), + crostini_manager_(crostini_manager), vm_name_(std::move(vm_name)), - cryptohome_id_(std::move(cryptohome_id)), container_name_(std::move(container_name)), container_username_(std::move(container_username)), callback_(std::move(callback)), - restart_id_(next_restart_id_++), - restarter_service_(restarter_service) {} + restart_id_(next_restart_id_++) {} void Restart() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (is_aborted_) return; - - CrostiniManager* crostini_manager = CrostiniManager::GetInstance(); + is_running_ = true; // Skip to the end immediately if testing. - if (crostini_manager->skip_restart_for_testing()) { + if (crostini_manager_->skip_restart_for_testing()) { content::BrowserThread::PostTask( content::BrowserThread::UI, FROM_HERE, base::BindOnce(&CrostiniRestarter::SetUpLxdContainerUserFinished, @@ -152,7 +105,7 @@ return; } - crostini_manager->InstallTerminaComponent(base::BindOnce( + crostini_manager_->InstallTerminaComponent(base::BindOnce( &CrostiniRestarter::LoadComponentFinished, base::WrapRefCounted(this))); } @@ -169,6 +122,16 @@ observer_list_.Clear(); } + void OnContainerDownloading(int download_percent) { + if (!is_running_) { + return; + } + // Tell observers. + for (auto& observer : observer_list_) { + observer.OnContainerDownloading(download_percent); + } + } + CrostiniManager::RestartId restart_id() const { return restart_id_; } std::string vm_name() const { return vm_name_; } std::string container_name() const { return container_name_; } @@ -183,7 +146,7 @@ } void FinishRestart(ConciergeClientResult result) { - restarter_service_->RunPendingCallbacks(this, result); + crostini_manager_->FinishRestart(this, result); } void LoadComponentFinished(bool is_successful) { @@ -200,7 +163,7 @@ FinishRestart(client_result); return; } - CrostiniManager::GetInstance()->StartConcierge( + crostini_manager_->StartConcierge( base::BindOnce(&CrostiniRestarter::ConciergeStarted, this)); } @@ -220,8 +183,8 @@ FinishRestart(client_result); return; } - CrostiniManager::GetInstance()->CreateDiskImage( - cryptohome_id_, base::FilePath(vm_name_), + crostini_manager_->CreateDiskImage( + base::FilePath(vm_name_), vm_tools::concierge::StorageLocation::STORAGE_CRYPTOHOME_ROOT, base::BindOnce(&CrostiniRestarter::CreateDiskImageFinished, this)); } @@ -240,8 +203,8 @@ FinishRestart(result); return; } - CrostiniManager::GetInstance()->StartTerminaVm( - cryptohome_id_, vm_name_, result_path, + crostini_manager_->StartTerminaVm( + vm_name_, result_path, base::BindOnce(&CrostiniRestarter::StartTerminaVmFinished, this)); } @@ -258,14 +221,17 @@ FinishRestart(result); return; } - CrostiniManager::GetInstance()->CreateLxdContainer( - vm_name_, container_name_, cryptohome_id_, + crostini_manager_->CreateLxdContainer( + vm_name_, container_name_, base::BindOnce(&CrostiniRestarter::CreateLxdContainerFinished, this)); } void CreateLxdContainerFinished(ConciergeClientResult result) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - // TODO(timloh): Does this need an observer callback? + // Tell observers. + for (auto& observer : observer_list_) { + observer.OnContainerCreated(result); + } if (is_aborted_) return; if (result != ConciergeClientResult::SUCCESS) { @@ -273,8 +239,8 @@ FinishRestart(result); return; } - CrostiniManager::GetInstance()->StartLxdContainer( - vm_name_, container_name_, cryptohome_id_, + crostini_manager_->StartLxdContainer( + vm_name_, container_name_, base::BindOnce(&CrostiniRestarter::StartLxdContainerFinished, this)); } @@ -288,8 +254,8 @@ FinishRestart(result); return; } - CrostiniManager::GetInstance()->SetUpLxdContainerUser( - vm_name_, container_name_, cryptohome_id_, container_username_, + crostini_manager_->SetUpLxdContainerUser( + vm_name_, container_name_, container_username_, base::BindOnce(&CrostiniRestarter::SetUpLxdContainerUserFinished, this)); } @@ -311,8 +277,8 @@ // If default termina/penguin, then do sshfs mount, else we are finished. if (vm_name_ == kCrostiniDefaultVmName && container_name_ == kCrostiniDefaultContainerName) { - CrostiniManager::GetInstance()->GetContainerSshKeys( - vm_name_, container_name_, cryptohome_id_, + crostini_manager_->GetContainerSshKeys( + vm_name_, container_name_, base::BindOnce(&CrostiniRestarter::GetContainerSshKeysFinished, this)); } else { @@ -394,8 +360,11 @@ } Profile* profile_; + // This isn't accessed after the CrostiniManager is destroyed, but we still + // use a WeakPtr for safety. + base::WeakPtr<CrostiniManager> crostini_manager_; + std::string vm_name_; - std::string cryptohome_id_; std::string container_name_; std::string container_username_; std::string source_path_; @@ -403,148 +372,56 @@ base::ObserverList<CrostiniManager::RestartObserver>::Unchecked observer_list_; CrostiniManager::RestartId restart_id_; - CrostiniRestarterService* restarter_service_; bool is_aborted_ = false; + bool is_running_ = false; static CrostiniManager::RestartId next_restart_id_; }; -CrostiniManager::RestartId CrostiniRestarter::next_restart_id_ = 0; +CrostiniManager::RestartId + CrostiniManager::CrostiniRestarter::next_restart_id_ = 0; -CrostiniManager::RestartId CrostiniRestarterService::Register( - Profile* profile, - std::string vm_name, - std::string container_name, - CrostiniManager::RestartCrostiniCallback callback, - CrostiniManager::RestartObserver* observer) { - auto restarter = base::MakeRefCounted<CrostiniRestarter>( - this, profile, std::move(vm_name), CryptohomeIdForProfile(profile), - std::move(container_name), ContainerUserNameForProfile(profile), - std::move(callback)); - if (observer) - restarter->AddObserver(observer); - auto key = std::make_pair(restarter->vm_name(), restarter->container_name()); - pending_map_.emplace(key, restarter->restart_id()); - restarter_map_[restarter->restart_id()] = restarter; - if (pending_map_.count(key) > 1) { - VLOG(1) << "Already restarting vm " << vm_name << ", container " - << container_name; - } else { - restarter->Restart(); - } - return restarter->restart_id(); -} - -void CrostiniRestarterService::RunPendingCallbacks( - CrostiniRestarter* restarter, - ConciergeClientResult result) { - auto key = std::make_pair(restarter->vm_name(), restarter->container_name()); - auto range = pending_map_.equal_range(key); - std::vector<scoped_refptr<CrostiniRestarter>> pending_restarters; - // Erase first, because restarter->RunCallback() may modify our maps. - for (auto it = range.first; it != range.second; ++it) { - CrostiniManager::RestartId restart_id = it->second; - pending_restarters.emplace_back(restarter_map_[restart_id]); - restarter_map_.erase(restart_id); - } - pending_map_.erase(range.first, range.second); - for (const auto& pending_restarter : pending_restarters) { - pending_restarter->RunCallback(result); - } -} - -void CrostiniRestarterService::Abort(CrostiniManager::RestartId restart_id) { - auto it = restarter_map_.find(restart_id); - if (it == restarter_map_.end()) { - // This can happen if a user cancels the install flow at the exact right - // moment, for example. - LOG(ERROR) << "Aborting a restarter that already finished"; +void CrostiniManager::SetVmState(std::string vm_name, VmState vm_state) { + auto vm_info = running_vms_.find(std::move(vm_name)); + if (vm_info != running_vms_.end()) { + vm_info->second.first = vm_state; return; } - it->second->Abort(); - ErasePending(it->second.get()); - // Erasing |it| also invalidates |it|, so make a key from |it| now. - auto key = - std::make_pair(it->second->vm_name(), it->second->container_name()); - restarter_map_.erase(it); - // Kick off the "next" (in no order) pending Restart() if any. - auto pending_it = pending_map_.find(key); - if (pending_it != pending_map_.end()) { - auto restarter = restarter_map_[pending_it->second]; - restarter->Restart(); + // This can happen normally when StopVm is called right after start up. + LOG(WARNING) << "Attempted to set state for unknown vm: " << vm_name; +} + +bool CrostiniManager::IsVmRunning(std::string vm_name) { + auto vm_info = running_vms_.find(std::move(vm_name)); + if (vm_info != running_vms_.end()) { + return vm_info->second.first == VmState::STARTED; } -} - -void CrostiniRestarterService::ErasePending(CrostiniRestarter* restarter) { - // Erase from pending_map_ - auto key = std::make_pair(restarter->vm_name(), restarter->container_name()); - auto range = pending_map_.equal_range(key); - for (auto it = range.first; it != range.second; ++it) { - if (it->second == restarter->restart_id()) { - pending_map_.erase(it); - return; - } - } - NOTREACHED(); -} - -void OnConciergeServiceAvailable( - CrostiniManager::StartConciergeCallback callback, - bool success) { - if (!success) { - LOG(ERROR) << "Concierge service did not become available"; - std::move(callback).Run(success); - return; - } - VLOG(1) << "Concierge service announced availability"; - VLOG(1) << "Waiting for Cicerone to announce availability."; - - GetCiceroneClient()->WaitForServiceToBeAvailable(std::move(callback)); -} - -} // namespace - -// static -CrostiniManager* CrostiniManager::GetInstance() { - return base::Singleton<CrostiniManager>::get(); -} - -bool CrostiniManager::IsVmRunning(Profile* profile, std::string vm_name) { - return running_vms_.find(std::make_pair(CryptohomeIdForProfile(profile), - std::move(vm_name))) != - running_vms_.end(); + return false; } base::Optional<vm_tools::concierge::VmInfo> CrostiniManager::GetVmInfo( - Profile* profile, std::string vm_name) { - auto it = running_vms_.find( - std::make_pair(CryptohomeIdForProfile(profile), std::move(vm_name))); + auto it = running_vms_.find(std::move(vm_name)); if (it != running_vms_.end()) - return it->second; + return it->second.second; return base::nullopt; } void CrostiniManager::AddRunningVmForTesting( - std::string owner_id, std::string vm_name, vm_tools::concierge::VmInfo vm_info) { - auto key = std::make_pair(std::move(owner_id), std::move(vm_name)); - running_vms_[key] = std::move(vm_info); + running_vms_[std::move(vm_name)] = + std::make_pair(VmState::STARTED, std::move(vm_info)); } -bool CrostiniManager::IsContainerRunning(Profile* profile, - std::string vm_name, +bool CrostiniManager::IsContainerRunning(std::string vm_name, std::string container_name) { - return IsContainerRunning(CryptohomeIdForProfile(profile), std::move(vm_name), - std::move(container_name)); -} - -bool CrostiniManager::IsContainerRunning(std::string owner_id, - std::string vm_name, - std::string container_name) { - auto range = running_containers_.equal_range( - std::make_pair(std::move(owner_id), std::move(vm_name))); + if (!IsVmRunning(vm_name)) { + return false; + } + // TODO(jopra): Ensure the container not marked running if the vm is not + // running. + auto range = running_containers_.equal_range(std::move(vm_name)); for (auto it = range.first; it != range.second; ++it) { if (it->second == container_name) { return true; @@ -553,27 +430,30 @@ return false; } -void CrostiniManager::ResetForTesting() { - running_vms_.clear(); - running_containers_.clear(); +CrostiniManager* CrostiniManager::GetForProfile(Profile* profile) { + return CrostiniManagerFactory::GetForProfile(profile); } -CrostiniManager::CrostiniManager() : weak_ptr_factory_(this) { - // Cicerone/ConciergeClient and its observer_list_ will be destroyed together. - // We add, but don't need to remove the observer. (Doing so would force a - // "destroyed before" dependency on the owner of Cicerone/ConciergeClient). +CrostiniManager::CrostiniManager(Profile* profile) + : profile_(profile), + owner_id_(CryptohomeIdForProfile(profile)), + weak_ptr_factory_(this) { + DCHECK(!profile_->IsOffTheRecord()); GetCiceroneClient()->AddObserver(this); GetConciergeClient()->AddObserver(this); } -CrostiniManager::~CrostiniManager() {} +CrostiniManager::~CrostiniManager() { + GetCiceroneClient()->RemoveObserver(this); + GetConciergeClient()->RemoveObserver(this); +} bool CrostiniManager::IsCrosTerminaInstalled() const { return is_cros_termina_registered_; } -void CrostiniManager::MaybeUpgradeCrostini(Profile* profile) { - if (!IsCrostiniAllowedForProfile(profile)) { +void CrostiniManager::MaybeUpgradeCrostini() { + if (!IsCrostiniAllowedForProfile(profile_)) { return; } auto* component_manager = @@ -725,17 +605,9 @@ } void CrostiniManager::CreateDiskImage( - const std::string& cryptohome_id, const base::FilePath& disk_path, vm_tools::concierge::StorageLocation storage_location, CreateDiskImageCallback callback) { - if (cryptohome_id.empty()) { - LOG(ERROR) << "Cryptohome id cannot be empty"; - std::move(callback).Run(ConciergeClientResult::CLIENT_ERROR, - base::FilePath()); - return; - } - std::string disk_path_string = disk_path.AsUTF8Unsafe(); if (disk_path_string.empty()) { LOG(ERROR) << "Disk path cannot be empty"; @@ -745,7 +617,7 @@ } vm_tools::concierge::CreateDiskImageRequest request; - request.set_cryptohome_id(std::move(cryptohome_id)); + request.set_cryptohome_id(CryptohomeIdForProfile(profile_)); request.set_disk_path(std::move(disk_path_string)); // The type of disk image to be created. request.set_image_type(vm_tools::concierge::DISK_IMAGE_QCOW2); @@ -793,16 +665,9 @@ } void CrostiniManager::DestroyDiskImage( - const std::string& cryptohome_id, const base::FilePath& disk_path, vm_tools::concierge::StorageLocation storage_location, DestroyDiskImageCallback callback) { - if (cryptohome_id.empty()) { - LOG(ERROR) << "Cryptohome id cannot be empty"; - std::move(callback).Run(ConciergeClientResult::CLIENT_ERROR); - return; - } - std::string disk_path_string = disk_path.AsUTF8Unsafe(); if (disk_path_string.empty()) { LOG(ERROR) << "Disk path cannot be empty"; @@ -811,7 +676,7 @@ } vm_tools::concierge::DestroyDiskImageRequest request; - request.set_cryptohome_id(std::move(cryptohome_id)); + request.set_cryptohome_id(CryptohomeIdForProfile(profile_)); request.set_disk_path(std::move(disk_path_string)); if (storage_location != vm_tools::concierge::STORAGE_CRYPTOHOME_ROOT && @@ -829,18 +694,9 @@ weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } -void CrostiniManager::ListVmDisks( - // The cryptohome id for the user's encrypted storage. - const std::string& cryptohome_id, - ListVmDisksCallback callback) { - if (cryptohome_id.empty()) { - LOG(ERROR) << "Cryptohome id cannot be empty"; - std::move(callback).Run(ConciergeClientResult::CLIENT_ERROR, 0); - return; - } - +void CrostiniManager::ListVmDisks(ListVmDisksCallback callback) { vm_tools::concierge::ListVmDisksRequest request; - request.set_cryptohome_id(std::move(cryptohome_id)); + request.set_cryptohome_id(CryptohomeIdForProfile(profile_)); request.set_storage_location(vm_tools::concierge::STORAGE_CRYPTOHOME_ROOT); GetConciergeClient()->ListVmDisks( @@ -849,16 +705,9 @@ weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } -void CrostiniManager::StartTerminaVm(std::string owner_id, - std::string name, +void CrostiniManager::StartTerminaVm(std::string name, const base::FilePath& disk_path, StartTerminaVmCallback callback) { - if (owner_id.empty()) { - LOG(ERROR) << "owner_id is required"; - std::move(callback).Run(ConciergeClientResult::CLIENT_ERROR); - return; - } - if (name.empty()) { LOG(ERROR) << "name is required"; std::move(callback).Run(ConciergeClientResult::CLIENT_ERROR); @@ -875,7 +724,7 @@ vm_tools::concierge::StartVmRequest request; request.set_name(std::move(name)); request.set_start_termina(true); - request.set_owner_id(std::move(owner_id)); + request.set_owner_id(owner_id_); vm_tools::concierge::DiskImage* disk_image = request.add_disks(); disk_image->set_path(std::move(disk_path_string)); @@ -884,37 +733,32 @@ disk_image->set_do_mount(false); GetConciergeClient()->StartTerminaVm( - request, - base::BindOnce(&CrostiniManager::OnStartTerminaVm, - weak_ptr_factory_.GetWeakPtr(), request.owner_id(), - request.name(), std::move(callback))); + request, base::BindOnce(&CrostiniManager::OnStartTerminaVm, + weak_ptr_factory_.GetWeakPtr(), request.name(), + std::move(callback))); } -void CrostiniManager::StopVm(Profile* profile, - std::string name, - StopVmCallback callback) { +void CrostiniManager::StopVm(std::string name, StopVmCallback callback) { if (name.empty()) { LOG(ERROR) << "name is required"; std::move(callback).Run(ConciergeClientResult::CLIENT_ERROR); return; } - std::string owner_id = CryptohomeIdForProfile(profile); + SetVmState(name, VmState::STOPPING); vm_tools::concierge::StopVmRequest request; - request.set_owner_id(owner_id); + request.set_owner_id(owner_id_); request.set_name(name); GetConciergeClient()->StopVm( std::move(request), base::BindOnce(&CrostiniManager::OnStopVm, weak_ptr_factory_.GetWeakPtr(), - std::move(owner_id), std::move(name), - std::move(callback))); + std::move(name), std::move(callback))); } void CrostiniManager::CreateLxdContainer(std::string vm_name, std::string container_name, - std::string owner_id, CrostiniResultCallback callback) { if (vm_name.empty()) { LOG(ERROR) << "vm_name is required"; @@ -926,11 +770,6 @@ std::move(callback).Run(ConciergeClientResult::CLIENT_ERROR); return; } - if (owner_id.empty()) { - LOG(ERROR) << "cryptohome_id is required"; - std::move(callback).Run(ConciergeClientResult::CLIENT_ERROR); - return; - } if (!GetCiceroneClient()->IsLxdContainerCreatedSignalConnected() || !GetCiceroneClient()->IsLxdContainerDownloadingSignalConnected()) { LOG(ERROR) @@ -942,20 +781,18 @@ vm_tools::cicerone::CreateLxdContainerRequest request; request.set_vm_name(std::move(vm_name)); request.set_container_name(std::move(container_name)); - request.set_owner_id(std::move(owner_id)); + request.set_owner_id(owner_id_); request.set_image_server(kCrostiniDefaultImageServerUrl); request.set_image_alias(kCrostiniDefaultImageAlias); GetCiceroneClient()->CreateLxdContainer( std::move(request), base::BindOnce(&CrostiniManager::OnCreateLxdContainer, - weak_ptr_factory_.GetWeakPtr(), request.owner_id(), - request.vm_name(), request.container_name(), - std::move(callback))); + weak_ptr_factory_.GetWeakPtr(), request.vm_name(), + request.container_name(), std::move(callback))); } void CrostiniManager::StartLxdContainer(std::string vm_name, std::string container_name, - std::string owner_id, CrostiniResultCallback callback) { if (vm_name.empty()) { LOG(ERROR) << "vm_name is required"; @@ -967,11 +804,6 @@ std::move(callback).Run(ConciergeClientResult::CLIENT_ERROR); return; } - if (owner_id.empty()) { - LOG(ERROR) << "cryptohome_id is required"; - std::move(callback).Run(ConciergeClientResult::CLIENT_ERROR); - return; - } if (!GetCiceroneClient()->IsContainerStartedSignalConnected() || !GetCiceroneClient()->IsContainerShutdownSignalConnected()) { LOG(ERROR) << "Async call to StartLxdContainer can't complete when signals " @@ -982,18 +814,16 @@ vm_tools::cicerone::StartLxdContainerRequest request; request.set_vm_name(std::move(vm_name)); request.set_container_name(std::move(container_name)); - request.set_owner_id(std::move(owner_id)); + request.set_owner_id(owner_id_); GetCiceroneClient()->StartLxdContainer( std::move(request), base::BindOnce(&CrostiniManager::OnStartLxdContainer, - weak_ptr_factory_.GetWeakPtr(), request.owner_id(), - request.vm_name(), request.container_name(), - std::move(callback))); + weak_ptr_factory_.GetWeakPtr(), request.vm_name(), + request.container_name(), std::move(callback))); } void CrostiniManager::SetUpLxdContainerUser(std::string vm_name, std::string container_name, - std::string owner_id, std::string container_username, CrostiniResultCallback callback) { if (vm_name.empty()) { @@ -1006,11 +836,6 @@ std::move(callback).Run(ConciergeClientResult::CLIENT_ERROR); return; } - if (owner_id.empty()) { - LOG(ERROR) << "cryptohome_id is required"; - std::move(callback).Run(ConciergeClientResult::CLIENT_ERROR); - return; - } if (container_username.empty()) { LOG(ERROR) << "container_username is required"; std::move(callback).Run(ConciergeClientResult::CLIENT_ERROR); @@ -1019,25 +844,23 @@ vm_tools::cicerone::SetUpLxdContainerUserRequest request; request.set_vm_name(std::move(vm_name)); request.set_container_name(std::move(container_name)); - request.set_owner_id(std::move(owner_id)); + request.set_owner_id(owner_id_); request.set_container_username(std::move(container_username)); GetCiceroneClient()->SetUpLxdContainerUser( std::move(request), base::BindOnce(&CrostiniManager::OnSetUpLxdContainerUser, - weak_ptr_factory_.GetWeakPtr(), request.owner_id(), - request.vm_name(), request.container_name(), - std::move(callback))); + weak_ptr_factory_.GetWeakPtr(), request.vm_name(), + request.container_name(), std::move(callback))); } void CrostiniManager::LaunchContainerApplication( - Profile* profile, std::string vm_name, std::string container_name, std::string desktop_file_id, const std::vector<std::string>& files, LaunchContainerApplicationCallback callback) { vm_tools::cicerone::LaunchContainerApplicationRequest request; - request.set_owner_id(CryptohomeIdForProfile(profile)); + request.set_owner_id(owner_id_); request.set_vm_name(std::move(vm_name)); request.set_container_name(std::move(container_name)); request.set_desktop_file_id(std::move(desktop_file_id)); @@ -1052,7 +875,6 @@ } void CrostiniManager::GetContainerAppIcons( - Profile* profile, std::string vm_name, std::string container_name, std::vector<std::string> desktop_file_ids, @@ -1060,7 +882,7 @@ int scale, GetContainerAppIconsCallback callback) { vm_tools::cicerone::ContainerAppIconRequest request; - request.set_owner_id(CryptohomeIdForProfile(profile)); + request.set_owner_id(owner_id_); request.set_vm_name(std::move(vm_name)); request.set_container_name(std::move(container_name)); google::protobuf::RepeatedPtrField<std::string> ids( @@ -1077,7 +899,6 @@ } void CrostiniManager::InstallLinuxPackage( - Profile* profile, std::string vm_name, std::string container_name, std::string package_path, @@ -1093,7 +914,7 @@ } vm_tools::cicerone::InstallLinuxPackageRequest request; - request.set_owner_id(CryptohomeIdForProfile(profile)); + request.set_owner_id(owner_id_); request.set_vm_name(std::move(vm_name)); request.set_container_name(std::move(container_name)); request.set_file_path(std::move(package_path)); @@ -1107,12 +928,11 @@ void CrostiniManager::GetContainerSshKeys( std::string vm_name, std::string container_name, - std::string cryptohome_id, GetContainerSshKeysCallback callback) { vm_tools::concierge::ContainerSshKeysRequest request; request.set_vm_name(std::move(vm_name)); request.set_container_name(std::move(container_name)); - request.set_cryptohome_id(std::move(cryptohome_id)); + request.set_cryptohome_id(CryptohomeIdForProfile(profile_)); GetConciergeClient()->GetContainerSshKeys( std::move(request), @@ -1169,12 +989,14 @@ return launch_params; } +// static Browser* CrostiniManager::CreateContainerTerminal( const AppLaunchParams& launch_params, const GURL& vsh_in_crosh_url) { return CreateApplicationWindow(launch_params, vsh_in_crosh_url); } +// static void CrostiniManager::ShowContainerTerminal( const AppLaunchParams& launch_params, const GURL& vsh_in_crosh_url, @@ -1183,65 +1005,89 @@ } void CrostiniManager::LaunchContainerTerminal( - Profile* profile, const std::string& vm_name, const std::string& container_name, const std::vector<std::string>& terminal_args) { GURL vsh_in_crosh_url = - GenerateVshInCroshUrl(profile, vm_name, container_name, terminal_args); - AppLaunchParams launch_params = GenerateTerminalAppLaunchParams(profile); + GenerateVshInCroshUrl(profile_, vm_name, container_name, terminal_args); + AppLaunchParams launch_params = GenerateTerminalAppLaunchParams(profile_); OpenApplicationWindow(launch_params, vsh_in_crosh_url); } CrostiniManager::RestartId CrostiniManager::RestartCrostini( - Profile* profile, std::string vm_name, std::string container_name, RestartCrostiniCallback callback, RestartObserver* observer) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - DCHECK(!profile->IsOffTheRecord()); - return CrostiniRestarterServiceFactory::GetForProfile(profile)->Register( - profile, std::move(vm_name), std::move(container_name), - std::move(callback), observer); + + auto restarter = base::MakeRefCounted<CrostiniRestarter>( + profile_, weak_ptr_factory_.GetWeakPtr(), std::move(vm_name), + std::move(container_name), ContainerUserNameForProfile(profile_), + std::move(callback)); + if (observer) + restarter->AddObserver(observer); + auto key = std::make_pair(restarter->vm_name(), restarter->container_name()); + restarters_by_container_.emplace(key, restarter->restart_id()); + restarters_by_id_[restarter->restart_id()] = restarter; + if (restarters_by_container_.count(key) > 1) { + VLOG(1) << "Already restarting vm " << vm_name << ", container " + << container_name; + } else { + restarter->Restart(); + } + return restarter->restart_id(); } void CrostiniManager::AbortRestartCrostini( - Profile* profile, CrostiniManager::RestartId restart_id) { - CrostiniRestarterServiceFactory::GetForProfile(profile)->Abort(restart_id); + auto restarter_it = restarters_by_id_.find(restart_id); + if (restarter_it == restarters_by_id_.end()) { + // This can happen if a user cancels the install flow at the exact right + // moment, for example. + LOG(ERROR) << "Aborting a restarter that already finished"; + return; + } + restarter_it->second->Abort(); + + auto key = std::make_pair(restarter_it->second->vm_name(), + restarter_it->second->container_name()); + auto range = restarters_by_container_.equal_range(key); + for (auto it = range.first; it != range.second; ++it) { + if (it->second == restart_id) { + restarters_by_container_.erase(it); + break; + } + } + + // This invalidates the iterator and potentially destroys the restarter, so + // those shouldn't be accessed after this. + restarters_by_id_.erase(restarter_it); + + // Kick off the "next" (in no order) pending Restart() if any. + auto pending_it = restarters_by_container_.find(key); + if (pending_it != restarters_by_container_.end()) { + auto restarter = restarters_by_id_[pending_it->second]; + restarter->Restart(); + } } void CrostiniManager::AddShutdownContainerCallback( - Profile* profile, std::string vm_name, std::string container_name, ShutdownContainerCallback shutdown_callback) { shutdown_container_callbacks_.emplace( - std::make_tuple(CryptohomeIdForProfile(profile), vm_name, container_name), - std::move(shutdown_callback)); + std::make_tuple(vm_name, container_name), std::move(shutdown_callback)); } void CrostiniManager::AddInstallLinuxPackageProgressObserver( - Profile* profile, InstallLinuxPackageProgressObserver* observer) { - install_linux_package_progress_observers_.emplace( - CryptohomeIdForProfile(profile), observer); + install_linux_package_progress_observers_.AddObserver(observer); } void CrostiniManager::RemoveInstallLinuxPackageProgressObserver( - Profile* profile, InstallLinuxPackageProgressObserver* observer) { - auto range = install_linux_package_progress_observers_.equal_range( - CryptohomeIdForProfile(profile)); - for (auto it = range.first; it != range.second; ++it) { - if (it->second == observer) { - install_linux_package_progress_observers_.erase(it); - return; - } - } - - NOTREACHED(); + install_linux_package_progress_observers_.RemoveObserver(observer); } void CrostiniManager::OnCreateDiskImage( @@ -1309,7 +1155,6 @@ } void CrostiniManager::OnStartTerminaVm( - std::string owner_id, std::string vm_name, StartTerminaVmCallback callback, base::Optional<vm_tools::concierge::StartVmResponse> reply) { @@ -1325,38 +1170,40 @@ std::move(callback).Run(ConciergeClientResult::VM_START_FAILED); return; } - // Wait for the Tremplin signal if the vm isn't already marked "running". - auto key = std::make_pair(owner_id, vm_name); - if (running_vms_.find(key) == running_vms_.end()) { - VLOG(1) << "Awaiting TremplinStartedSignal for " << owner_id << ", " - << vm_name; - // Record the container start and run the callback after the VM starts. - tremplin_started_callbacks_.emplace( - key, base::BindOnce(&CrostiniManager::OnStartTremplin, - weak_ptr_factory_.GetWeakPtr(), key, - std::move(response.vm_info()), std::move(callback), - ConciergeClientResult::SUCCESS)); + // If the vm is already marked "running" run the callback. + if (IsVmRunning(vm_name)) { + std::move(callback).Run(ConciergeClientResult::SUCCESS); return; } - std::move(callback).Run(ConciergeClientResult::SUCCESS); + + // Otherwise, record the container start and run the callback after the VM + // starts. + VLOG(1) << "Awaiting TremplinStartedSignal for " << owner_id_ << ", " + << vm_name; + running_vms_[vm_name] = + std::make_pair(VmState::STARTING, std::move(response.vm_info())); + + tremplin_started_callbacks_.emplace( + vm_name, + base::BindOnce(&CrostiniManager::OnStartTremplin, + weak_ptr_factory_.GetWeakPtr(), vm_name, + std::move(callback), ConciergeClientResult::SUCCESS)); } -void CrostiniManager::OnStartTremplin(std::pair<std::string, std::string> key, - vm_tools::concierge::VmInfo vm_info, +void CrostiniManager::OnStartTremplin(std::string vm_name, StartTerminaVmCallback callback, ConciergeClientResult result) { // Record the running vm. - VLOG(1) << "Received TremplinStartedSignal, VM: " << key.first << ", " - << key.second; - running_vms_[key] = std::move(vm_info); + VLOG(1) << "Received TremplinStartedSignal, VM: " << owner_id_ << ", " + << vm_name; + SetVmState(vm_name, VmState::STARTED); // Run the original callback. std::move(callback).Run(result); } void CrostiniManager::OnStopVm( - std::string owner_id, std::string vm_name, StopVmCallback callback, base::Optional<vm_tools::concierge::StopVmResponse> reply) { @@ -1381,32 +1228,33 @@ } } // Remove from running_vms_. - auto key = std::make_pair(std::move(owner_id), std::move(vm_name)); - running_vms_.erase(key); + running_vms_.erase(vm_name); // Remove containers from running_containers_ - running_containers_.erase(key); + running_containers_.erase(std::move(vm_name)); std::move(callback).Run(ConciergeClientResult::SUCCESS); } void CrostiniManager::OnContainerStarted( const vm_tools::cicerone::ContainerStartedSignal& signal) { + if (signal.owner_id() != owner_id_) + return; // Find the callbacks to call, then erase them from the map. - auto range = start_container_callbacks_.equal_range(std::make_tuple( - signal.owner_id(), signal.vm_name(), signal.container_name())); + auto range = start_container_callbacks_.equal_range( + std::make_tuple(signal.vm_name(), signal.container_name())); for (auto it = range.first; it != range.second; ++it) { std::move(it->second).Run(ConciergeClientResult::SUCCESS); } start_container_callbacks_.erase(range.first, range.second); - running_containers_.emplace( - std::make_pair(signal.owner_id(), signal.vm_name()), - signal.container_name()); + running_containers_.emplace(signal.vm_name(), signal.container_name()); } void CrostiniManager::OnContainerStartupFailed( const vm_tools::concierge::ContainerStartedSignal& signal) { + if (signal.owner_id() != owner_id_) + return; // Find the callbacks to call, then erase them from the map. - auto range = start_container_callbacks_.equal_range(std::make_tuple( - signal.owner_id(), signal.vm_name(), signal.container_name())); + auto range = start_container_callbacks_.equal_range( + std::make_tuple(signal.vm_name(), signal.container_name())); for (auto it = range.first; it != range.second; ++it) { std::move(it->second).Run(ConciergeClientResult::CONTAINER_START_FAILED); } @@ -1415,9 +1263,11 @@ void CrostiniManager::OnContainerShutdown( const vm_tools::cicerone::ContainerShutdownSignal& signal) { + if (signal.owner_id() != owner_id_) + return; // Find the callbacks to call, then erase them from the map. - auto range = shutdown_container_callbacks_.equal_range(std::make_tuple( - signal.owner_id(), signal.vm_name(), signal.container_name())); + auto range = shutdown_container_callbacks_.equal_range( + std::make_tuple(signal.vm_name(), signal.container_name())); for (auto it = range.first; it != range.second; ++it) { std::move(it->second).Run(); } @@ -1426,6 +1276,8 @@ void CrostiniManager::OnInstallLinuxPackageProgress( const vm_tools::cicerone::InstallLinuxPackageProgressSignal& signal) { + if (signal.owner_id() != owner_id_) + return; if (signal.progress_percent() < 0 || signal.progress_percent() > 100) { LOG(ERROR) << "Received install progress with invalid progress of " << signal.progress_percent() << "%."; @@ -1450,17 +1302,14 @@ NOTREACHED(); } - auto range = - install_linux_package_progress_observers_.equal_range(signal.owner_id()); - for (auto it = range.first; it != range.second; ++it) { - it->second->OnInstallLinuxPackageProgress( + for (auto& observer : install_linux_package_progress_observers_) { + observer.OnInstallLinuxPackageProgress( signal.vm_name(), signal.container_name(), status, signal.progress_percent(), signal.failure_details()); } } void CrostiniManager::OnCreateLxdContainer( - std::string owner_id, std::string vm_name, std::string container_name, CrostiniResultCallback callback, @@ -1473,13 +1322,12 @@ vm_tools::cicerone::CreateLxdContainerResponse response = reply.value(); if (response.status() == vm_tools::cicerone::CreateLxdContainerResponse::CREATING) { - VLOG(1) << "Awaiting LxdContainerCreatedSignal for " << owner_id << ", " + VLOG(1) << "Awaiting LxdContainerCreatedSignal for " << owner_id_ << ", " << vm_name << ", " << container_name; // The callback will be called when we receive the LxdContainerCreated // signal. create_lxd_container_callbacks_.emplace( - std::make_tuple(owner_id, vm_name, container_name), - std::move(callback)); + std::make_tuple(vm_name, container_name), std::move(callback)); return; } if (response.status() != @@ -1492,7 +1340,6 @@ } void CrostiniManager::OnStartLxdContainer( - std::string owner_id, std::string vm_name, std::string container_name, CrostiniResultCallback callback, @@ -1516,7 +1363,6 @@ } void CrostiniManager::OnSetUpLxdContainerUser( - std::string owner_id, std::string vm_name, std::string container_name, CrostiniResultCallback callback, @@ -1538,10 +1384,9 @@ return; } - if (!IsContainerRunning(owner_id, vm_name, container_name)) { - start_container_callbacks_.emplace( - std::make_tuple(owner_id, vm_name, container_name), - std::move(callback)); + if (!IsContainerRunning(vm_name, container_name)) { + start_container_callbacks_.emplace(std::make_tuple(vm_name, container_name), + std::move(callback)); return; } std::move(callback).Run(ConciergeClientResult::SUCCESS); @@ -1549,9 +1394,11 @@ void CrostiniManager::OnLxdContainerCreated( const vm_tools::cicerone::LxdContainerCreatedSignal& signal) { + if (signal.owner_id() != owner_id_) + return; // Find the callbacks to call, then erase them from the map. - auto range = create_lxd_container_callbacks_.equal_range(std::make_tuple( - signal.owner_id(), signal.vm_name(), signal.container_name())); + auto range = create_lxd_container_callbacks_.equal_range( + std::make_tuple(signal.vm_name(), signal.container_name())); for (auto it = range.first; it != range.second; ++it) { std::move(it->second).Run(ConciergeClientResult::SUCCESS); } @@ -1559,13 +1406,24 @@ } void CrostiniManager::OnLxdContainerDownloading( - const vm_tools::cicerone::LxdContainerDownloadingSignal& signal) {} + const vm_tools::cicerone::LxdContainerDownloadingSignal& signal) { + if (owner_id_ != signal.owner_id()) { + return; + } + auto range = restarters_by_container_.equal_range( + std::make_pair(signal.vm_name(), signal.container_name())); + for (auto it = range.first; it != range.second; ++it) { + restarters_by_id_[it->second]->OnContainerDownloading( + signal.download_progress()); + } +} void CrostiniManager::OnTremplinStarted( const vm_tools::cicerone::TremplinStartedSignal& signal) { + if (signal.owner_id() != owner_id_) + return; // Find the callbacks to call, then erase them from the map. - auto range = tremplin_started_callbacks_.equal_range( - std::make_pair(signal.owner_id(), signal.vm_name())); + auto range = tremplin_started_callbacks_.equal_range(signal.vm_name()); for (auto it = range.first; it != range.second; ++it) { std::move(it->second).Run(); } @@ -1659,14 +1517,30 @@ response.host_private_key(), response.hostname()); } -void CrostiniManager::RemoveCrostini(Profile* profile, - std::string vm_name, +void CrostiniManager::RemoveCrostini(std::string vm_name, std::string container_name, RemoveCrostiniCallback callback) { auto crostini_remover = base::MakeRefCounted<CrostiniRemover>( - profile, std::move(vm_name), std::move(container_name), + profile_, std::move(vm_name), std::move(container_name), std::move(callback)); crostini_remover->RemoveCrostini(); } +void CrostiniManager::FinishRestart(CrostiniRestarter* restarter, + ConciergeClientResult result) { + auto key = std::make_pair(restarter->vm_name(), restarter->container_name()); + auto range = restarters_by_container_.equal_range(key); + std::vector<scoped_refptr<CrostiniRestarter>> pending_restarters; + // Erase first, because restarter->RunCallback() may modify our maps. + for (auto it = range.first; it != range.second; ++it) { + CrostiniManager::RestartId restart_id = it->second; + pending_restarters.emplace_back(restarters_by_id_[restart_id]); + restarters_by_id_.erase(restart_id); + } + restarters_by_container_.erase(range.first, range.second); + for (const auto& pending_restarter : pending_restarters) { + pending_restarter->RunCallback(result); + } +} + } // namespace crostini
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.h b/chrome/browser/chromeos/crostini/crostini_manager.h index 93ac231..77d5bbd 100644 --- a/chrome/browser/chromeos/crostini/crostini_manager.h +++ b/chrome/browser/chromeos/crostini/crostini_manager.h
@@ -8,7 +8,6 @@ #include <map> #include <set> #include <string> -#include <tuple> #include <utility> #include <vector> @@ -23,6 +22,7 @@ #include "chromeos/dbus/cicerone_client.h" #include "chromeos/dbus/concierge/service.pb.h" #include "chromeos/dbus/concierge_client.h" +#include "components/keyed_service/core/keyed_service.h" class Profile; @@ -55,6 +55,12 @@ INSTALLING, }; +enum class VmState { + STARTING, + STARTED, + STOPPING, +}; + // Return type when getting app icons from within a container. struct Icon { std::string desktop_file_id; @@ -84,7 +90,8 @@ // communication with the Cicerone service and both should remain as thin as // possible. The existence of Cicerone is abstracted behind this class and // only the Concierge name is exposed outside of here. -class CrostiniManager : public chromeos::ConciergeClient::Observer, +class CrostiniManager : public KeyedService, + public chromeos::ConciergeClient::Observer, public chromeos::CiceroneClient::Observer { public: using ConciergeClientCallback = @@ -145,10 +152,17 @@ virtual void OnConciergeStarted(ConciergeClientResult result) = 0; virtual void OnDiskImageCreated(ConciergeClientResult result) = 0; virtual void OnVmStarted(ConciergeClientResult result) = 0; + virtual void OnContainerDownloading(int32_t download_percent) = 0; + virtual void OnContainerCreated(ConciergeClientResult result) = 0; virtual void OnContainerStarted(ConciergeClientResult result) = 0; virtual void OnSshKeysFetched(ConciergeClientResult result) = 0; }; + static CrostiniManager* GetForProfile(Profile* profile); + + explicit CrostiniManager(Profile* profile); + ~CrostiniManager() override; + // Checks if the cros-termina component is installed. bool IsCrosTerminaInstalled() const; @@ -163,7 +177,7 @@ static AppLaunchParams GenerateTerminalAppLaunchParams(Profile* profile); // Upgrades cros-termina component if the current version is not compatible. - void MaybeUpgradeCrostini(Profile* profile); + void MaybeUpgradeCrostini(); // Installs the current version of cros-termina component. Attempts to apply // pending upgrades if a MaybeUpgradeCrostini failed. @@ -182,8 +196,6 @@ // |callback| is called if the arguments are bad, or after the method call // finishes. void CreateDiskImage( - // The cryptohome id for the user's encrypted storage. - const std::string& cryptohome_id, // The path to the disk image, including the name of // the image itself. The image name should match the // name of the VM that it will be used for. @@ -197,8 +209,6 @@ // |callback| is called if the arguments are bad, or after the method call // finishes. void DestroyDiskImage( - // The cryptohome id for the user's encrypted storage. - const std::string& cryptohome_id, // The path to the disk image, including the name of // the image itself. const base::FilePath& disk_path, @@ -206,32 +216,28 @@ vm_tools::concierge::StorageLocation storage_location, DestroyDiskImageCallback callback); - void ListVmDisks( - // The cryptohome id for the user's encrypted storage. - const std::string& cryptohome_id, - ListVmDisksCallback callback); + void ListVmDisks(ListVmDisksCallback callback); // Checks the arguments for starting a Termina VM. Starts a Termina VM via // ConciergeClient::StartTerminaVm. |callback| is called if the arguments // are bad, or after the method call finishes. - void StartTerminaVm(std::string owner_id, - // The human-readable name to be assigned to this VM. - std::string name, - // Path to the disk image on the host. - const base::FilePath& disk_path, - StartTerminaVmCallback callback); + void StartTerminaVm( + // The human-readable name to be assigned to this VM. + std::string name, + // Path to the disk image on the host. + const base::FilePath& disk_path, + StartTerminaVmCallback callback); // Checks the arguments for stopping a Termina VM. Stops the Termina VM via // ConciergeClient::StopVm. |callback| is called if the arguments are bad, // or after the method call finishes. - void StopVm(Profile* profile, std::string name, StopVmCallback callback); + void StopVm(std::string name, StopVmCallback callback); // Checks the arguments for creating an Lxd container via // CiceroneClient::CreateLxdContainer. |callback| is called immediately if the // arguments are bad, or once the container has been created. void CreateLxdContainer(std::string vm_name, std::string container_name, - std::string owner_id, CrostiniResultCallback callback); // Checks the arguments for starting an Lxd container via @@ -239,7 +245,6 @@ // arguments are bad, or once the container has been created. void StartLxdContainer(std::string vm_name, std::string container_name, - std::string owner_id, CrostiniResultCallback callback); // Checks the arguments for setting up an Lxd container user via @@ -247,15 +252,13 @@ // the arguments are bad, or once garcon has been started. void SetUpLxdContainerUser(std::string vm_name, std::string container_name, - std::string owner_id, std::string container_username, CrostiniResultCallback callback); // Asynchronously launches an app as specified by its desktop file id. // |callback| is called with SUCCESS when the relevant process is started // or LAUNCH_CONTAINER_APPLICATION_FAILED if there was an error somewhere. - void LaunchContainerApplication(Profile* profile, - std::string vm_name, + void LaunchContainerApplication(std::string vm_name, std::string container_name, std::string desktop_file_id, const std::vector<std::string>& files, @@ -263,8 +266,7 @@ // Asynchronously gets app icons as specified by their desktop file ids. // |callback| is called after the method call finishes. - void GetContainerAppIcons(Profile* profile, - std::string vm_name, + void GetContainerAppIcons(std::string vm_name, std::string container_name, std::vector<std::string> desktop_file_ids, int icon_size, @@ -274,8 +276,7 @@ // Begin installation of a Linux Package inside the container. If the // installation is successfully started, further updates will be sent to // added InstallLinuxPackageProgressObservers. - void InstallLinuxPackage(Profile* profile, - std::string vm_name, + void InstallLinuxPackage(std::string vm_name, std::string container_name, std::string package_path, InstallLinuxPackageCallback callback); @@ -285,24 +286,22 @@ // |callback| is called after the method call finishes. void GetContainerSshKeys(std::string vm_name, std::string container_name, - std::string cryptohome_id, GetContainerSshKeysCallback callback); // Create the crosh-in-a-window that displays a shell in an container on a VM. - Browser* CreateContainerTerminal(const AppLaunchParams& launch_params, - const GURL& vsh_in_crosh_url); + static Browser* CreateContainerTerminal(const AppLaunchParams& launch_params, + const GURL& vsh_in_crosh_url); // Shows the already created crosh-in-a-window that displays a shell in an // already running container on a VM. - void ShowContainerTerminal(const AppLaunchParams& launch_params, - const GURL& vsh_in_crosh_url, - Browser* browser); + static void ShowContainerTerminal(const AppLaunchParams& launch_params, + const GURL& vsh_in_crosh_url, + Browser* browser); // Launches the crosh-in-a-window that displays a shell in an already running // container on a VM and passes |terminal_args| as parameters to that shell // which will cause them to be executed as program inside that shell. - void LaunchContainerTerminal(Profile* profile, - const std::string& vm_name, + void LaunchContainerTerminal(const std::string& vm_name, const std::string& container_name, const std::vector<std::string>& terminal_args); @@ -310,27 +309,25 @@ static const RestartId kUninitializedRestartId = -1; // Runs all the steps required to restart the given crostini vm and container. // The optional |observer| tracks progress. - RestartId RestartCrostini(Profile* profile, - std::string vm_name, + RestartId RestartCrostini(std::string vm_name, std::string container_name, RestartCrostiniCallback callback, RestartObserver* observer = nullptr); - void AbortRestartCrostini(Profile* profile, RestartId id); + // Aborts a restart. A "next" restarter with the same <vm_name, + // container_name> will run, if there is one. + void AbortRestartCrostini(RestartId id); // Adds a callback to receive notification of container shutdown. void AddShutdownContainerCallback( - Profile* profile, std::string vm_name, std::string container_name, ShutdownContainerCallback shutdown_callback); // Add/remove observers for package install progress. void AddInstallLinuxPackageProgressObserver( - Profile* profile, InstallLinuxPackageProgressObserver* observer); void RemoveInstallLinuxPackageProgressObserver( - Profile* profile, InstallLinuxPackageProgressObserver* observer); // ConciergeClient::Observer: @@ -352,38 +349,26 @@ void OnTremplinStarted( const vm_tools::cicerone::TremplinStartedSignal& signal) override; - void RemoveCrostini(Profile* profile, - std::string vm_name, + void RemoveCrostini(std::string vm_name, std::string container_name, RemoveCrostiniCallback callback); - // Returns the singleton instance of CrostiniManager. - static CrostiniManager* GetInstance(); + void SetVmState(std::string vm_name, VmState vm_state); + bool IsVmRunning(std::string vm_name); - bool IsVmRunning(Profile* profile, std::string vm_name); // Returns null if VM is not running. - base::Optional<vm_tools::concierge::VmInfo> GetVmInfo(Profile* profile, - std::string vm_name); - void AddRunningVmForTesting(std::string owner_id, - std::string vm_name, + base::Optional<vm_tools::concierge::VmInfo> GetVmInfo(std::string vm_name); + void AddRunningVmForTesting(std::string vm_name, vm_tools::concierge::VmInfo vm_info); - bool IsContainerRunning(Profile* profile, - std::string vm_name, - std::string container_name); + bool IsContainerRunning(std::string vm_name, std::string container_name); // Clear the lists of running VMs and containers. - // TODO(timloh): This is fragile. We should make the CrostiniManager a keyed - // service so that separate tests get new instances of it. - void ResetForTesting(); // Can be called for testing to skip restart. void set_skip_restart_for_testing() { skip_restart_for_testing_ = true; } bool skip_restart_for_testing() { return skip_restart_for_testing_; } private: - friend struct base::DefaultSingletonTraits<CrostiniManager>; - - CrostiniManager(); - ~CrostiniManager() override; + class CrostiniRestarter; // Callback for ConciergeClient::CreateDiskImage. Called after the Concierge // service method finishes. @@ -404,10 +389,10 @@ base::Optional<vm_tools::concierge::ListVmDisksResponse> reply); // Callback for ConciergeClient::StartTerminaVm. Called after the Concierge - // service method finishes. |callback| is called if the container has already - // been started, otherwise it is passed to OnStartTremplin. + // service method finishes. Updates running containers list then calls the + // |callback| if the container has already been started, otherwise passes the + // callback to OnStartTremplin. void OnStartTerminaVm( - std::string owner_id, std::string vm_name, StartTerminaVmCallback callback, base::Optional<vm_tools::concierge::StartVmResponse> reply); @@ -415,15 +400,13 @@ // Callback for ConciergeClient::TremplinStartedSignal. Called after the // Tremplin service starts. Updates running containers list and then calls the // |callback|. - void OnStartTremplin(std::pair<std::string, std::string> key, - vm_tools::concierge::VmInfo vm_info, + void OnStartTremplin(std::string vm_name, StartTerminaVmCallback callback, ConciergeClientResult result); // Callback for ConciergeClient::StopVm. Called after the Concierge // service method finishes. - void OnStopVm(std::string owner_id, - std::string vm_name, + void OnStopVm(std::string vm_name, StopVmCallback callback, base::Optional<vm_tools::concierge::StopVmResponse> reply); @@ -447,7 +430,6 @@ // is still being created, in which case we will wait for an // OnLxdContainerCreated event. void OnCreateLxdContainer( - std::string owner_id, std::string vm_name, std::string container_name, CrostiniResultCallback callback, @@ -455,7 +437,6 @@ // Callback for CiceroneClient::StartLxdContainer. void OnStartLxdContainer( - std::string owner_id, std::string vm_name, std::string container_name, CrostiniResultCallback callback, @@ -463,7 +444,6 @@ // Callback for CiceroneClient::SetUpLxdContainerUser. void OnSetUpLxdContainerUser( - std::string owner_id, std::string vm_name, std::string container_name, CrostiniResultCallback callback, @@ -503,51 +483,55 @@ CreateDiskImageCallback callback, int64_t free_disk_size); - bool IsContainerRunning(std::string owner_id, - std::string vm_name, - std::string container_name); + void FinishRestart(CrostiniRestarter* restarter, + ConciergeClientResult result); + + Profile* profile_; + std::string owner_id_; bool skip_restart_for_testing_ = false; bool is_cros_termina_registered_ = false; bool termina_update_check_needed_ = false; - // Pending container started callbacks are keyed by <owner_id, vm_name, - // container_name> string tuples. - std::multimap<std::tuple<std::string, std::string, std::string>, - StartContainerCallback> + // Pending container started callbacks are keyed by <vm_name, container_name> + // string pairs. + std::multimap<std::pair<std::string, std::string>, StartContainerCallback> start_container_callbacks_; - // Pending ShutdownContainer callbacks are keyed by <owner_id, vm_name, - // container_name> string tuples. - std::multimap<std::tuple<std::string, std::string, std::string>, - ShutdownContainerCallback> + // Pending ShutdownContainer callbacks are keyed by <vm_name, container_name> + // string pairs. + std::multimap<std::pair<std::string, std::string>, ShutdownContainerCallback> shutdown_container_callbacks_; - // Pending CreateLxdContainer callbacks are keyed by <owner_id, vm_name, - // container_name> string tuples. These are used if CreateLxdContainer - // indicates we need to wait for an LxdContainerCreate signal. - std::multimap<std::tuple<std::string, std::string, std::string>, - CrostiniResultCallback> + // Pending CreateLxdContainer callbacks are keyed by <vm_name, container_name> + // string pairs. These are used if CreateLxdContainer indicates we need to + // wait for an LxdContainerCreate signal. + std::multimap<std::pair<std::string, std::string>, CrostiniResultCallback> create_lxd_container_callbacks_; - // Callbacks to run after Tremplin is started, keyed by <owner_id, vm_name> - // pairs. These are used if StartTerminaVm completes but we need to wait from - // Tremplin to start. - std::multimap<std::pair<std::string, std::string>, base::OnceClosure> - tremplin_started_callbacks_; + // Callbacks to run after Tremplin is started, keyed by vm_name. These are + // used if StartTerminaVm completes but we need to wait from Tremplin to + // start. + std::multimap<std::string, base::OnceClosure> tremplin_started_callbacks_; - // Running vms as <owner_id, vm_name> pairs. - std::map<std::pair<std::string, std::string>, vm_tools::concierge::VmInfo> + std::map<std::string, std::pair<VmState, vm_tools::concierge::VmInfo>> running_vms_; - // Running containers as keyed by <owner_id, vm_name> string pairs. - std::multimap<std::pair<std::string, std::string>, std::string> - running_containers_; + // Running containers as keyed by vm name. + std::multimap<std::string, std::string> running_containers_; - // Keyed by owner_id. - std::multimap<std::string, InstallLinuxPackageProgressObserver*> + base::ObserverList<InstallLinuxPackageProgressObserver>::Unchecked install_linux_package_progress_observers_; + // Restarts by <vm_name, container_name>. Only one restarter flow is actually + // running for a given container, other restarters will just have their + // callback called when the running restarter completes. + std::multimap<std::pair<std::string, std::string>, CrostiniManager::RestartId> + restarters_by_container_; + + std::map<CrostiniManager::RestartId, scoped_refptr<CrostiniRestarter>> + restarters_by_id_; + // Note: This should remain the last member so it'll be destroyed and // invalidate its weak pointers before any other members are destroyed. base::WeakPtrFactory<CrostiniManager> weak_ptr_factory_;
diff --git a/chrome/browser/chromeos/crostini/crostini_manager_factory.cc b/chrome/browser/chromeos/crostini/crostini_manager_factory.cc new file mode 100644 index 0000000..238acf9 --- /dev/null +++ b/chrome/browser/chromeos/crostini/crostini_manager_factory.cc
@@ -0,0 +1,38 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/crostini/crostini_manager_factory.h" + +#include "chrome/browser/chromeos/crostini/crostini_manager.h" +#include "chrome/browser/profiles/profile.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" + +namespace crostini { + +// static +CrostiniManager* CrostiniManagerFactory::GetForProfile(Profile* profile) { + return static_cast<CrostiniManager*>( + GetInstance()->GetServiceForBrowserContext(profile, true)); +} + +// static +CrostiniManagerFactory* CrostiniManagerFactory::GetInstance() { + static base::NoDestructor<CrostiniManagerFactory> factory; + return factory.get(); +} + +CrostiniManagerFactory::CrostiniManagerFactory() + : BrowserContextKeyedServiceFactory( + "CrostiniManager", + BrowserContextDependencyManager::GetInstance()) {} + +CrostiniManagerFactory::~CrostiniManagerFactory() = default; + +KeyedService* CrostiniManagerFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + Profile* profile = Profile::FromBrowserContext(context); + return new CrostiniManager(profile); +} + +} // namespace crostini
diff --git a/chrome/browser/chromeos/crostini/crostini_manager_factory.h b/chrome/browser/chromeos/crostini/crostini_manager_factory.h new file mode 100644 index 0000000..533b3a7 --- /dev/null +++ b/chrome/browser/chromeos/crostini/crostini_manager_factory.h
@@ -0,0 +1,38 @@ +// 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 CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_MANAGER_FACTORY_H_ +#define CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_MANAGER_FACTORY_H_ + +#include "base/macros.h" +#include "base/no_destructor.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +class Profile; + +namespace crostini { + +class CrostiniManager; + +class CrostiniManagerFactory : public BrowserContextKeyedServiceFactory { + public: + static CrostiniManager* GetForProfile(Profile* profile); + static CrostiniManagerFactory* GetInstance(); + + private: + friend class base::NoDestructor<CrostiniManagerFactory>; + + CrostiniManagerFactory(); + ~CrostiniManagerFactory() override; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + + DISALLOW_COPY_AND_ASSIGN(CrostiniManagerFactory); +}; + +} // namespace crostini + +#endif // CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_MANAGER_FACTORY_H_
diff --git a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc index 3b758c1..fc6b2ebe 100644 --- a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc +++ b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
@@ -22,7 +22,6 @@ namespace crostini { namespace { -const char kOwnerId[] = "owner_id"; const char kVmName[] = "vm_name"; const char kContainerName[] = "container_name"; } // namespace @@ -95,8 +94,7 @@ void OnStartTremplinRecordsRunningVmCallback(base::OnceClosure closure, ConciergeClientResult result) { // Check that running_vms_ contains the running vm. - EXPECT_TRUE( - CrostiniManager::GetInstance()->IsVmRunning(profile(), kVmName)); + EXPECT_TRUE(crostini_manager()->IsVmRunning(kVmName)); std::move(closure).Run(); } @@ -117,25 +115,15 @@ } CrostiniManagerTest() - : fake_cicerone_client_(new chromeos::FakeCiceroneClient()), - fake_concierge_client_(new chromeos::FakeConciergeClient()), - scoped_task_environment_( + : scoped_task_environment_( base::test::ScopedTaskEnvironment::MainThreadType::UI), test_browser_thread_bundle_( content::TestBrowserThreadBundle::REAL_IO_THREAD) { - // Initialization between D-Bus, fake clients, and the singleton - // CrostiniManager is tricky since CrostiniManager is a global singleton and - // doesn't add itself as an observer for the new Concierge/Cicerone clients - // created for each test. We must first get a handle on the D-Bus setter - // and initialize it, then reset CrostiniManager and add it as an observer - // for the clients in this test, then set the fake clients into D-Bus. - auto dbus_setter = chromeos::DBusThreadManager::GetSetterForTesting(); chromeos::DBusThreadManager::Initialize(); - CrostiniManager::GetInstance()->ResetForTesting(); - fake_concierge_client_->AddObserver(CrostiniManager::GetInstance()); - fake_cicerone_client_->AddObserver(CrostiniManager::GetInstance()); - dbus_setter->SetConciergeClient(base::WrapUnique(fake_concierge_client_)); - dbus_setter->SetCiceroneClient(base::WrapUnique(fake_cicerone_client_)); + fake_cicerone_client_ = static_cast<chromeos::FakeCiceroneClient*>( + chromeos::DBusThreadManager::Get()->GetCiceroneClient()); + fake_concierge_client_ = static_cast<chromeos::FakeConciergeClient*>( + chromeos::DBusThreadManager::Get()->GetConciergeClient()); } ~CrostiniManagerTest() override { chromeos::DBusThreadManager::Shutdown(); } @@ -143,16 +131,19 @@ void SetUp() override { run_loop_ = std::make_unique<base::RunLoop>(); profile_ = std::make_unique<TestingProfile>(); + crostini_manager_ = std::make_unique<CrostiniManager>(profile_.get()); } void TearDown() override { - run_loop_.reset(); + crostini_manager_.reset(); profile_.reset(); + run_loop_.reset(); } protected: base::RunLoop* run_loop() { return run_loop_.get(); } Profile* profile() { return profile_.get(); } + CrostiniManager* crostini_manager() { return crostini_manager_.get(); } // Owned by chromeos::DBusThreadManager chromeos::FakeCiceroneClient* fake_cicerone_client_; @@ -161,6 +152,7 @@ std::unique_ptr<base::RunLoop> run_loop_; // run_loop_ must be created on the UI thread. std::unique_ptr<TestingProfile> profile_; + std::unique_ptr<CrostiniManager> crostini_manager_; private: base::test::ScopedTaskEnvironment scoped_task_environment_; @@ -171,19 +163,8 @@ TEST_F(CrostiniManagerTest, CreateDiskImageNameError) { const base::FilePath& disk_path = base::FilePath(""); - CrostiniManager::GetInstance()->CreateDiskImage( - "a_cryptohome_id", disk_path, - vm_tools::concierge::STORAGE_CRYPTOHOME_ROOT, - base::BindOnce(&CrostiniManagerTest::CreateDiskImageClientErrorCallback, - base::Unretained(this), run_loop()->QuitClosure())); - run_loop()->Run(); -} - -TEST_F(CrostiniManagerTest, CreateDiskImageCryptohomeError) { - const base::FilePath& disk_path = base::FilePath(kVmName); - - CrostiniManager::GetInstance()->CreateDiskImage( - "", disk_path, vm_tools::concierge::STORAGE_CRYPTOHOME_ROOT, + crostini_manager()->CreateDiskImage( + disk_path, vm_tools::concierge::STORAGE_CRYPTOHOME_ROOT, base::BindOnce(&CrostiniManagerTest::CreateDiskImageClientErrorCallback, base::Unretained(this), run_loop()->QuitClosure())); run_loop()->Run(); @@ -192,8 +173,8 @@ TEST_F(CrostiniManagerTest, CreateDiskImageStorageLocationError) { const base::FilePath& disk_path = base::FilePath(kVmName); - CrostiniManager::GetInstance()->CreateDiskImage( - "a_cryptohome_id", disk_path, + crostini_manager()->CreateDiskImage( + disk_path, vm_tools::concierge::StorageLocation_INT_MIN_SENTINEL_DO_NOT_USE_, base::BindOnce(&CrostiniManagerTest::CreateDiskImageClientErrorCallback, base::Unretained(this), run_loop()->QuitClosure())); @@ -203,9 +184,8 @@ TEST_F(CrostiniManagerTest, CreateDiskImageSuccess) { const base::FilePath& disk_path = base::FilePath(kVmName); - CrostiniManager::GetInstance()->CreateDiskImage( - "a_cryptohome_id", disk_path, - vm_tools::concierge::STORAGE_CRYPTOHOME_DOWNLOADS, + crostini_manager()->CreateDiskImage( + disk_path, vm_tools::concierge::STORAGE_CRYPTOHOME_DOWNLOADS, base::BindOnce(&CrostiniManagerTest::CreateDiskImageSuccessCallback, base::Unretained(this), run_loop()->QuitClosure())); run_loop()->Run(); @@ -214,19 +194,8 @@ TEST_F(CrostiniManagerTest, DestroyDiskImageNameError) { const base::FilePath& disk_path = base::FilePath(""); - CrostiniManager::GetInstance()->DestroyDiskImage( - "a_cryptohome_id", disk_path, - vm_tools::concierge::STORAGE_CRYPTOHOME_ROOT, - base::BindOnce(&CrostiniManagerTest::DestroyDiskImageClientErrorCallback, - base::Unretained(this), run_loop()->QuitClosure())); - run_loop()->Run(); -} - -TEST_F(CrostiniManagerTest, DestroyDiskImageCryptohomeError) { - const base::FilePath& disk_path = base::FilePath(kVmName); - - CrostiniManager::GetInstance()->DestroyDiskImage( - "", disk_path, vm_tools::concierge::STORAGE_CRYPTOHOME_ROOT, + crostini_manager()->DestroyDiskImage( + disk_path, vm_tools::concierge::STORAGE_CRYPTOHOME_ROOT, base::BindOnce(&CrostiniManagerTest::DestroyDiskImageClientErrorCallback, base::Unretained(this), run_loop()->QuitClosure())); run_loop()->Run(); @@ -235,8 +204,8 @@ TEST_F(CrostiniManagerTest, DestroyDiskImageStorageLocationError) { const base::FilePath& disk_path = base::FilePath(kVmName); - CrostiniManager::GetInstance()->DestroyDiskImage( - "a_cryptohome_id", disk_path, + crostini_manager()->DestroyDiskImage( + disk_path, vm_tools::concierge::StorageLocation_INT_MIN_SENTINEL_DO_NOT_USE_, base::BindOnce(&CrostiniManagerTest::DestroyDiskImageClientErrorCallback, base::Unretained(this), run_loop()->QuitClosure())); @@ -246,44 +215,25 @@ TEST_F(CrostiniManagerTest, DestroyDiskImageSuccess) { const base::FilePath& disk_path = base::FilePath(kVmName); - CrostiniManager::GetInstance()->DestroyDiskImage( - "a_cryptohome_id", disk_path, - vm_tools::concierge::STORAGE_CRYPTOHOME_DOWNLOADS, + crostini_manager()->DestroyDiskImage( + disk_path, vm_tools::concierge::STORAGE_CRYPTOHOME_DOWNLOADS, base::BindOnce(&CrostiniManagerTest::DestroyDiskImageSuccessCallback, base::Unretained(this), run_loop()->QuitClosure())); run_loop()->Run(); } -TEST_F(CrostiniManagerTest, ListVmDisksCryptohomeError) { - CrostiniManager::GetInstance()->ListVmDisks( - "", base::BindOnce(&CrostiniManagerTest::ListVmDisksClientErrorCallback, - base::Unretained(this), run_loop()->QuitClosure())); - run_loop()->Run(); -} - TEST_F(CrostiniManagerTest, ListVmDisksSuccess) { - CrostiniManager::GetInstance()->ListVmDisks( - "a_cryptohome_id", + crostini_manager()->ListVmDisks( base::BindOnce(&CrostiniManagerTest::ListVmDisksSuccessCallback, base::Unretained(this), run_loop()->QuitClosure())); run_loop()->Run(); } -TEST_F(CrostiniManagerTest, StartTerminaVmOwnerIdError) { - const base::FilePath& disk_path = base::FilePath(kVmName); - - CrostiniManager::GetInstance()->StartTerminaVm( - "", kVmName, disk_path, - base::BindOnce(&CrostiniManagerTest::StartTerminaVmClientErrorCallback, - base::Unretained(this), run_loop()->QuitClosure())); - run_loop()->Run(); -} - TEST_F(CrostiniManagerTest, StartTerminaVmNameError) { const base::FilePath& disk_path = base::FilePath(kVmName); - CrostiniManager::GetInstance()->StartTerminaVm( - kOwnerId, "", disk_path, + crostini_manager()->StartTerminaVm( + "", disk_path, base::BindOnce(&CrostiniManagerTest::StartTerminaVmClientErrorCallback, base::Unretained(this), run_loop()->QuitClosure())); run_loop()->Run(); @@ -292,8 +242,8 @@ TEST_F(CrostiniManagerTest, StartTerminaVmDiskPathError) { const base::FilePath& disk_path = base::FilePath(); - CrostiniManager::GetInstance()->StartTerminaVm( - kOwnerId, kVmName, disk_path, + crostini_manager()->StartTerminaVm( + kVmName, disk_path, base::BindOnce(&CrostiniManagerTest::StartTerminaVmClientErrorCallback, base::Unretained(this), run_loop()->QuitClosure())); run_loop()->Run(); @@ -302,8 +252,8 @@ TEST_F(CrostiniManagerTest, StartTerminaVmSuccess) { const base::FilePath& disk_path = base::FilePath(kVmName); - CrostiniManager::GetInstance()->StartTerminaVm( - kOwnerId, kVmName, disk_path, + crostini_manager()->StartTerminaVm( + kVmName, disk_path, base::BindOnce(&CrostiniManagerTest::StartTerminaVmSuccessCallback, base::Unretained(this), run_loop()->QuitClosure())); run_loop()->Run(); @@ -314,29 +264,28 @@ const std::string owner_id = CryptohomeIdForProfile(profile()); // Start the Vm. - CrostiniManager::GetInstance()->StartTerminaVm( - owner_id, kVmName, disk_path, + crostini_manager()->StartTerminaVm( + kVmName, disk_path, base::BindOnce( &CrostiniManagerTest::OnStartTremplinRecordsRunningVmCallback, base::Unretained(this), run_loop()->QuitClosure())); // Check that the Vm start is not recorded (without tremplin start). - EXPECT_FALSE(CrostiniManager::GetInstance()->IsVmRunning(profile(), kVmName)); + EXPECT_FALSE(crostini_manager()->IsVmRunning(kVmName)); run_loop()->Run(); } TEST_F(CrostiniManagerTest, StopVmNameError) { - CrostiniManager::GetInstance()->StopVm( - profile(), "", - base::BindOnce(&CrostiniManagerTest::StopVmClientErrorCallback, - base::Unretained(this), run_loop()->QuitClosure())); + crostini_manager()->StopVm( + "", base::BindOnce(&CrostiniManagerTest::StopVmClientErrorCallback, + base::Unretained(this), run_loop()->QuitClosure())); run_loop()->Run(); } TEST_F(CrostiniManagerTest, StopVmSuccess) { - CrostiniManager::GetInstance()->StopVm( - profile(), kVmName, + crostini_manager()->StopVm( + kVmName, base::BindOnce(&CrostiniManagerTest::StopVmSuccessCallback, base::Unretained(this), run_loop()->QuitClosure())); run_loop()->Run(); @@ -345,8 +294,8 @@ TEST_F(CrostiniManagerTest, InstallLinuxPackageSignalNotConnectedError) { fake_cicerone_client_->set_install_linux_package_progress_signal_connected( false); - CrostiniManager::GetInstance()->InstallLinuxPackage( - profile(), kVmName, kContainerName, "/tmp/package.deb", + crostini_manager()->InstallLinuxPackage( + kVmName, kContainerName, "/tmp/package.deb", base::BindOnce(&CrostiniManagerTest::InstallLinuxPackageCallback, base::Unretained(this), run_loop()->QuitClosure(), ConciergeClientResult::INSTALL_LINUX_PACKAGE_FAILED, @@ -358,8 +307,8 @@ vm_tools::cicerone::InstallLinuxPackageResponse response; response.set_status(vm_tools::cicerone::InstallLinuxPackageResponse::STARTED); fake_cicerone_client_->set_install_linux_package_response(response); - CrostiniManager::GetInstance()->InstallLinuxPackage( - profile(), kVmName, kContainerName, "/tmp/package.deb", + crostini_manager()->InstallLinuxPackage( + kVmName, kContainerName, "/tmp/package.deb", base::BindOnce(&CrostiniManagerTest::InstallLinuxPackageCallback, base::Unretained(this), run_loop()->QuitClosure(), ConciergeClientResult::SUCCESS, std::string())); @@ -372,8 +321,8 @@ response.set_status(vm_tools::cicerone::InstallLinuxPackageResponse::FAILED); response.set_failure_reason(failure_reason); fake_cicerone_client_->set_install_linux_package_response(response); - CrostiniManager::GetInstance()->InstallLinuxPackage( - profile(), kVmName, kContainerName, "/tmp/package.deb", + crostini_manager()->InstallLinuxPackage( + kVmName, kContainerName, "/tmp/package.deb", base::BindOnce(&CrostiniManagerTest::InstallLinuxPackageCallback, base::Unretained(this), run_loop()->QuitClosure(), ConciergeClientResult::INSTALL_LINUX_PACKAGE_FAILED, @@ -386,7 +335,6 @@ public: void SetUp() override { CrostiniManagerTest::SetUp(); - owner_id_ = CryptohomeIdForProfile(profile()); } void RestartCrostiniCallback(base::OnceClosure closure, @@ -420,6 +368,14 @@ } } + void OnContainerDownloading(int32_t download_percent) override {} + + void OnContainerCreated(ConciergeClientResult result) override { + if (abort_on_container_created_) { + Abort(); + } + } + void OnContainerStarted(ConciergeClientResult result) override { if (abort_on_container_started_) { Abort(); @@ -434,8 +390,7 @@ protected: void Abort() { - CrostiniManager::GetInstance()->AbortRestartCrostini(profile(), - restart_id_); + crostini_manager()->AbortRestartCrostini(restart_id_); run_loop()->Quit(); } @@ -456,11 +411,11 @@ CrostiniManager::RestartId restart_id_ = CrostiniManager::kUninitializedRestartId; - std::string owner_id_; bool abort_on_component_loaded_ = false; bool abort_on_concierge_started_ = false; bool abort_on_disk_image_created_ = false; bool abort_on_vm_started_ = false; + bool abort_on_container_created_ = false; bool abort_on_container_started_ = false; bool abort_on_ssh_keys_fetched_ = false; int restart_crostini_callback_count_ = 0; @@ -468,8 +423,8 @@ }; TEST_F(CrostiniManagerRestartTest, RestartSuccess) { - restart_id_ = CrostiniManager::GetInstance()->RestartCrostini( - profile(), kVmName, kContainerName, + restart_id_ = crostini_manager()->RestartCrostini( + kVmName, kContainerName, base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback, base::Unretained(this), run_loop()->QuitClosure()), this); @@ -483,8 +438,8 @@ TEST_F(CrostiniManagerRestartTest, AbortOnComponentLoaded) { abort_on_component_loaded_ = true; - restart_id_ = CrostiniManager::GetInstance()->RestartCrostini( - profile(), kVmName, kContainerName, + restart_id_ = crostini_manager()->RestartCrostini( + kVmName, kContainerName, base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback, base::Unretained(this), run_loop()->QuitClosure()), this); @@ -497,8 +452,8 @@ TEST_F(CrostiniManagerRestartTest, AbortOnConciergeStarted) { abort_on_concierge_started_ = true; - restart_id_ = CrostiniManager::GetInstance()->RestartCrostini( - profile(), kVmName, kContainerName, + restart_id_ = crostini_manager()->RestartCrostini( + kVmName, kContainerName, base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback, base::Unretained(this), run_loop()->QuitClosure()), this); @@ -511,8 +466,8 @@ TEST_F(CrostiniManagerRestartTest, AbortOnDiskImageCreated) { abort_on_disk_image_created_ = true; - restart_id_ = CrostiniManager::GetInstance()->RestartCrostini( - profile(), kVmName, kContainerName, + restart_id_ = crostini_manager()->RestartCrostini( + kVmName, kContainerName, base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback, base::Unretained(this), run_loop()->QuitClosure()), this); @@ -525,8 +480,23 @@ TEST_F(CrostiniManagerRestartTest, AbortOnVmStarted) { abort_on_vm_started_ = true; - restart_id_ = CrostiniManager::GetInstance()->RestartCrostini( - profile(), kVmName, kContainerName, + restart_id_ = crostini_manager()->RestartCrostini( + kVmName, kContainerName, + base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback, + base::Unretained(this), run_loop()->QuitClosure()), + this); + run_loop()->Run(); + EXPECT_TRUE(fake_concierge_client_->create_disk_image_called()); + EXPECT_TRUE(fake_concierge_client_->start_termina_vm_called()); + EXPECT_FALSE(fake_concierge_client_->get_container_ssh_keys_called()); + EXPECT_EQ(0, restart_crostini_callback_count_); +} + +TEST_F(CrostiniManagerRestartTest, AbortOnContainerCreated) { + abort_on_container_created_ = true; + // Use termina/penguin names to allow fetch ssh keys. + restart_id_ = crostini_manager()->RestartCrostini( + kCrostiniDefaultVmName, kCrostiniDefaultContainerName, base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback, base::Unretained(this), run_loop()->QuitClosure()), this); @@ -540,8 +510,8 @@ TEST_F(CrostiniManagerRestartTest, AbortOnContainerStarted) { abort_on_container_started_ = true; // Use termina/penguin names to allow fetch ssh keys. - restart_id_ = CrostiniManager::GetInstance()->RestartCrostini( - profile(), kCrostiniDefaultVmName, kCrostiniDefaultContainerName, + restart_id_ = crostini_manager()->RestartCrostini( + kCrostiniDefaultVmName, kCrostiniDefaultContainerName, base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback, base::Unretained(this), run_loop()->QuitClosure()), this); @@ -554,8 +524,8 @@ TEST_F(CrostiniManagerRestartTest, OnlyMountTerminaPenguin) { // Use names other than termina/penguin. Will not mount sshfs. - restart_id_ = CrostiniManager::GetInstance()->RestartCrostini( - profile(), kVmName, kContainerName, + restart_id_ = crostini_manager()->RestartCrostini( + kVmName, kContainerName, base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback, base::Unretained(this), run_loop()->QuitClosure()), this); @@ -567,16 +537,16 @@ } TEST_F(CrostiniManagerRestartTest, MultiRestartAllowed) { - CrostiniManager::GetInstance()->RestartCrostini( - profile(), kVmName, kContainerName, + crostini_manager()->RestartCrostini( + kVmName, kContainerName, base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback, base::Unretained(this), run_loop()->QuitClosure())); - CrostiniManager::GetInstance()->RestartCrostini( - profile(), kVmName, kContainerName, + crostini_manager()->RestartCrostini( + kVmName, kContainerName, base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback, base::Unretained(this), run_loop()->QuitClosure())); - CrostiniManager::GetInstance()->RestartCrostini( - profile(), kVmName, kContainerName, + crostini_manager()->RestartCrostini( + kVmName, kContainerName, base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback, base::Unretained(this), run_loop()->QuitClosure())); run_loop()->Run(); @@ -609,8 +579,8 @@ &CrostiniManagerRestartTest_MountForTerminaPenguin_Test::SshfsMount)); // Use termina/penguin to perform mount. - restart_id_ = CrostiniManager::GetInstance()->RestartCrostini( - profile(), kCrostiniDefaultVmName, kCrostiniDefaultContainerName, + restart_id_ = crostini_manager()->RestartCrostini( + kCrostiniDefaultVmName, kCrostiniDefaultContainerName, base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback, base::Unretained(this), run_loop()->QuitClosure())); run_loop()->Run();
diff --git a/chrome/browser/chromeos/crostini/crostini_package_installer_service.cc b/chrome/browser/chromeos/crostini/crostini_package_installer_service.cc index 9af3055..47f505e6 100644 --- a/chrome/browser/chromeos/crostini/crostini_package_installer_service.cc +++ b/chrome/browser/chromeos/crostini/crostini_package_installer_service.cc
@@ -7,6 +7,7 @@ #include "base/bind.h" #include "base/files/file_path.h" #include "base/no_destructor.h" +#include "chrome/browser/chromeos/crostini/crostini_manager_factory.h" #include "chrome/browser/profiles/profile.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" #include "components/keyed_service/content/browser_context_keyed_service_factory.h" @@ -34,7 +35,10 @@ CrostiniPackageInstallerServiceFactory() : BrowserContextKeyedServiceFactory( "CrostiniPackageInstallerService", - BrowserContextDependencyManager::GetInstance()) {} + BrowserContextDependencyManager::GetInstance()) { + DependsOn(CrostiniManagerFactory::GetInstance()); + } + ~CrostiniPackageInstallerServiceFactory() override = default; // BrowserContextKeyedServiceFactory: @@ -55,13 +59,15 @@ CrostiniPackageInstallerService::CrostiniPackageInstallerService( Profile* profile) : profile_(profile), weak_ptr_factory_(this) { - CrostiniManager::GetInstance()->AddInstallLinuxPackageProgressObserver( - profile, this); + CrostiniManager::GetForProfile(profile) + ->AddInstallLinuxPackageProgressObserver(this); } -CrostiniPackageInstallerService::~CrostiniPackageInstallerService() { - CrostiniManager::GetInstance()->RemoveInstallLinuxPackageProgressObserver( - profile_, this); +CrostiniPackageInstallerService::~CrostiniPackageInstallerService() = default; + +void CrostiniPackageInstallerService::Shutdown() { + CrostiniManager::GetForProfile(profile_) + ->RemoveInstallLinuxPackageProgressObserver(this); } void CrostiniPackageInstallerService::NotificationClosed( @@ -90,8 +96,8 @@ const std::string& container_name, const std::string& package_path, CrostiniManager::InstallLinuxPackageCallback callback) { - CrostiniManager::GetInstance()->InstallLinuxPackage( - profile_, vm_name, container_name, package_path, + CrostiniManager::GetForProfile(profile_)->InstallLinuxPackage( + vm_name, container_name, package_path, base::BindOnce(&CrostiniPackageInstallerService::OnInstallLinuxPackage, weak_ptr_factory_.GetWeakPtr(), vm_name, container_name, std::move(callback)));
diff --git a/chrome/browser/chromeos/crostini/crostini_package_installer_service.h b/chrome/browser/chromeos/crostini/crostini_package_installer_service.h index ff5b487..474ce0d 100644 --- a/chrome/browser/chromeos/crostini/crostini_package_installer_service.h +++ b/chrome/browser/chromeos/crostini/crostini_package_installer_service.h
@@ -25,6 +25,9 @@ explicit CrostiniPackageInstallerService(Profile* profile); ~CrostiniPackageInstallerService() override; + // KeyedService: + void Shutdown() override; + void NotificationClosed(CrostiniPackageInstallerNotification* notification); // Install a Linux package. If successfully started, a system notification
diff --git a/chrome/browser/chromeos/crostini/crostini_registry_service.cc b/chrome/browser/chromeos/crostini/crostini_registry_service.cc index edc952d..c27bfd2 100644 --- a/chrome/browser/chromeos/crostini/crostini_registry_service.cc +++ b/chrome/browser/chromeos/crostini/crostini_registry_service.cc
@@ -791,9 +791,8 @@ break; } - crostini::CrostiniManager::GetInstance()->GetContainerAppIcons( - profile_, registration->VmName(), registration->ContainerName(), - desktop_file_ids, + crostini::CrostiniManager::GetForProfile(profile_)->GetContainerAppIcons( + registration->VmName(), registration->ContainerName(), desktop_file_ids, app_list::AppListConfig::instance().grid_icon_dimension(), icon_scale, base::BindOnce(&CrostiniRegistryService::OnContainerAppIcon, weak_ptr_factory_.GetWeakPtr(), app_id, scale_factor));
diff --git a/chrome/browser/chromeos/crostini/crostini_remover.cc b/chrome/browser/chromeos/crostini/crostini_remover.cc index 16d2f8b..c0e66792 100644 --- a/chrome/browser/chromeos/crostini/crostini_remover.cc +++ b/chrome/browser/chromeos/crostini/crostini_remover.cc
@@ -35,7 +35,7 @@ void CrostiniRemover::RemoveCrostini() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - CrostiniManager::GetInstance()->InstallTerminaComponent( + CrostiniManager::GetForProfile(profile_)->InstallTerminaComponent( base::BindOnce(&CrostiniRemover::OnComponentLoaded, this)); } @@ -44,7 +44,7 @@ std::move(callback_).Run(ConciergeClientResult::UNKNOWN_ERROR); return; } - CrostiniManager::GetInstance()->StartConcierge( + CrostiniManager::GetForProfile(profile_)->StartConcierge( base::BindOnce(&CrostiniRemover::OnConciergeStarted, this)); } @@ -53,9 +53,8 @@ std::move(callback_).Run(ConciergeClientResult::UNKNOWN_ERROR); return; } - CrostiniManager::GetInstance()->StopVm( - profile_, vm_name_, - base::BindOnce(&CrostiniRemover::StopVmFinished, this)); + CrostiniManager::GetForProfile(profile_)->StopVm( + vm_name_, base::BindOnce(&CrostiniRemover::StopVmFinished, this)); } void CrostiniRemover::StopVmFinished(ConciergeClientResult result) { @@ -68,8 +67,8 @@ vm_name_, container_name_); CrostiniMimeTypesServiceFactory::GetForProfile(profile_)->ClearMimeTypes( vm_name_, container_name_); - CrostiniManager::GetInstance()->DestroyDiskImage( - CryptohomeIdForProfile(profile_), base::FilePath(vm_name_), + CrostiniManager::GetForProfile(profile_)->DestroyDiskImage( + base::FilePath(vm_name_), vm_tools::concierge::StorageLocation::STORAGE_CRYPTOHOME_ROOT, base::BindOnce(&CrostiniRemover::DestroyDiskImageFinished, this)); } @@ -81,7 +80,7 @@ return; } // Only set kCrostiniEnabled to false once cleanup is completely finished. - CrostiniManager::GetInstance()->StopConcierge( + CrostiniManager::GetForProfile(profile_)->StopConcierge( base::BindOnce(&CrostiniRemover::StopConciergeFinished, this)); }
diff --git a/chrome/browser/chromeos/crostini/crostini_share_path.cc b/chrome/browser/chromeos/crostini/crostini_share_path.cc index eaea49e..d63b948 100644 --- a/chrome/browser/chromeos/crostini/crostini_share_path.cc +++ b/chrome/browser/chromeos/crostini/crostini_share_path.cc
@@ -29,8 +29,8 @@ std::string path, base::OnceCallback<void(bool, std::string)> callback) { base::Optional<vm_tools::concierge::VmInfo> vm_info = - crostini::CrostiniManager::GetInstance()->GetVmInfo(profile, - std::move(vm_name)); + crostini::CrostiniManager::GetForProfile(profile)->GetVmInfo( + std::move(vm_name)); if (!vm_info) { std::move(callback).Run(false, "Cannot share, VM not running"); return;
diff --git a/chrome/browser/chromeos/crostini/crostini_share_path_unittest.cc b/chrome/browser/chromeos/crostini/crostini_share_path_unittest.cc index 9366008..5e1a2bff 100644 --- a/chrome/browser/chromeos/crostini/crostini_share_path_unittest.cc +++ b/chrome/browser/chromeos/crostini/crostini_share_path_unittest.cc
@@ -67,27 +67,15 @@ } CrostiniSharePathTest() - : fake_seneschal_client_(new chromeos::FakeSeneschalClient()), - fake_concierge_client_(new chromeos::FakeConciergeClient()), - fake_cicerone_client_(new chromeos::FakeCiceroneClient()), - scoped_task_environment_( + : scoped_task_environment_( base::test::ScopedTaskEnvironment::MainThreadType::UI), test_browser_thread_bundle_( content::TestBrowserThreadBundle::REAL_IO_THREAD) { - // Initialization between D-Bus, fake clients, and the singleton - // CrostiniManager is tricky since CrostiniManager is a global singleton and - // doesn't add itself as an observer for the new Concierge/Cicerone clients - // created for each test. We must first get a handle on the D-Bus setter - // and initialize it, then reset CrostiniManager and add it as an observer - // for the clients in this test, then set the fake clients into D-Bus. - auto dbus_setter = chromeos::DBusThreadManager::GetSetterForTesting(); chromeos::DBusThreadManager::Initialize(); - CrostiniManager::GetInstance()->ResetForTesting(); - fake_concierge_client_->AddObserver(CrostiniManager::GetInstance()); - fake_cicerone_client_->AddObserver(CrostiniManager::GetInstance()); - dbus_setter->SetConciergeClient(base::WrapUnique(fake_concierge_client_)); - dbus_setter->SetCiceroneClient(base::WrapUnique(fake_cicerone_client_)); - dbus_setter->SetSeneschalClient(base::WrapUnique(fake_seneschal_client_)); + fake_concierge_client_ = static_cast<chromeos::FakeConciergeClient*>( + chromeos::DBusThreadManager::Get()->GetConciergeClient()); + fake_seneschal_client_ = static_cast<chromeos::FakeSeneschalClient*>( + chromeos::DBusThreadManager::Get()->GetSeneschalClient()); } ~CrostiniSharePathTest() override { chromeos::DBusThreadManager::Shutdown(); } @@ -109,7 +97,6 @@ // Owned by chromeos::DBusThreadManager chromeos::FakeSeneschalClient* fake_seneschal_client_; chromeos::FakeConciergeClient* fake_concierge_client_; - chromeos::FakeCiceroneClient* fake_cicerone_client_; std::unique_ptr<base::RunLoop> run_loop_; // run_loop_ must be created on the UI thread. @@ -127,8 +114,8 @@ start_vm_response.mutable_vm_info()->set_seneschal_server_handle(123); fake_concierge_client_->set_start_vm_response(start_vm_response); - CrostiniManager::GetInstance()->StartTerminaVm( - "test", "vm-running-success", base::FilePath("path"), + CrostiniManager::GetForProfile(profile())->StartTerminaVm( + "vm-running-success", base::FilePath("path"), base::BindOnce( &CrostiniSharePathTest::SharePathSuccessStartTerminaVmCallback, base::Unretained(this))); @@ -146,8 +133,8 @@ share_path_response.set_failure_reason("test failure"); fake_seneschal_client_->set_share_path_response(share_path_response); - CrostiniManager::GetInstance()->StartTerminaVm( - "test", "vm-running-error", base::FilePath("path"), + CrostiniManager::GetForProfile(profile())->StartTerminaVm( + "vm-running-error", base::FilePath("path"), base::BindOnce( &CrostiniSharePathTest::SharePathErrorStartTerminaVmCallback, base::Unretained(this)));
diff --git a/chrome/browser/chromeos/crostini/crostini_util.cc b/chrome/browser/chromeos/crostini/crostini_util.cc index 659f95a..be7814ae 100644 --- a/chrome/browser/chromeos/crostini/crostini_util.cc +++ b/chrome/browser/chromeos/crostini/crostini_util.cc
@@ -92,15 +92,15 @@ Browser* CreateTerminal(const AppLaunchParams& launch_params, const GURL& vsh_in_crosh_url) { - return crostini::CrostiniManager::GetInstance()->CreateContainerTerminal( - launch_params, vsh_in_crosh_url); + return crostini::CrostiniManager::CreateContainerTerminal(launch_params, + vsh_in_crosh_url); } void ShowTerminal(const AppLaunchParams& launch_params, const GURL& vsh_in_crosh_url, Browser* browser) { - crostini::CrostiniManager::GetInstance()->ShowContainerTerminal( - launch_params, vsh_in_crosh_url, browser); + crostini::CrostiniManager::ShowContainerTerminal(launch_params, + vsh_in_crosh_url, browser); browser->window()->GetNativeWindow()->SetProperty( kOverrideWindowIconResourceIdKey, IDR_LOGO_CROSTINI_TERMINAL); } @@ -118,8 +118,8 @@ chrome_launcher_controller->crostini_app_window_shelf_controller(); DCHECK_NE(observer, nullptr); observer->OnAppLaunchRequested(app_id, display_id); - crostini::CrostiniManager::GetInstance()->LaunchContainerApplication( - profile, registration.VmName(), registration.ContainerName(), + crostini::CrostiniManager::GetForProfile(profile)->LaunchContainerApplication( + registration.VmName(), registration.ContainerName(), registration.DesktopFileId(), files, base::BindOnce(OnContainerApplicationLaunched, app_id)); } @@ -238,8 +238,8 @@ } bool IsCrostiniRunning(Profile* profile) { - return crostini::CrostiniManager::GetInstance()->IsVmRunning( - profile, kCrostiniDefaultVmName); + return crostini::CrostiniManager::GetForProfile(profile)->IsVmRunning( + kCrostiniDefaultVmName); } void LaunchCrostiniApp(Profile* profile, @@ -255,8 +255,8 @@ ChromeLauncherController* chrome_controller = ChromeLauncherController::instance(); if (chrome_controller && - !crostini::CrostiniManager::GetInstance()->IsContainerRunning( - profile, vm_name, container_name)) { + !crostini::CrostiniManager::GetForProfile(profile)->IsContainerRunning( + vm_name, container_name)) { chrome_controller->GetShelfSpinnerController()->AddSpinnerToShelf( app_id, std::make_unique<ShelfSpinnerItemController>(app_id)); } @@ -266,7 +266,11 @@ const std::string& app_id, int64_t display_id, const std::vector<std::string>& files) { - auto* crostini_manager = crostini::CrostiniManager::GetInstance(); + // Policies can change under us, and crostini may now be forbidden. + if (!IsCrostiniUIAllowedForProfile(profile)) { + return; + } + auto* crostini_manager = crostini::CrostiniManager::GetForProfile(profile); crostini::CrostiniRegistryService* registry_service = crostini::CrostiniRegistryServiceFactory::GetForProfile(profile); base::Optional<crostini::CrostiniRegistryService::Registration> registration = @@ -318,7 +322,7 @@ base::BindOnce(&AddSpinner, app_id, profile, vm_name, container_name), base::TimeDelta::FromMilliseconds(kDelayBeforeSpinnerMs)); crostini_manager->RestartCrostini( - profile, vm_name, container_name, + vm_name, container_name, base::BindOnce(OnCrostiniRestarted, app_id, browser, std::move(launch_closure))); }
diff --git a/chrome/browser/chromeos/dbus/vm_applications_service_provider.cc b/chrome/browser/chromeos/dbus/vm_applications_service_provider.cc index 21965ea..57fbf16 100644 --- a/chrome/browser/chromeos/dbus/vm_applications_service_provider.cc +++ b/chrome/browser/chromeos/dbus/vm_applications_service_provider.cc
@@ -106,8 +106,8 @@ Profile* profile = ProfileManager::GetPrimaryUserProfile(); if (IsCrostiniEnabled(profile) && request.owner_id() == CryptohomeIdForProfile(profile)) { - crostini::CrostiniManager::GetInstance()->LaunchContainerTerminal( - profile, request.vm_name(), request.container_name(), + crostini::CrostiniManager::GetForProfile(profile)->LaunchContainerTerminal( + request.vm_name(), request.container_name(), std::vector<std::string>(request.params().begin(), request.params().end())); }
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc index 4c50be9..bfa4065bb 100644 --- a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc +++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
@@ -540,11 +540,12 @@ EnableCrostiniForProfile(&scoped_feature_list); // Setup CrostiniManager for testing. - crostini::CrostiniManager::GetInstance()->set_skip_restart_for_testing(); + crostini::CrostiniManager* crostini_manager = + crostini::CrostiniManager::GetForProfile(browser()->profile()); + crostini_manager->set_skip_restart_for_testing(); vm_tools::concierge::VmInfo vm_info; - crostini::CrostiniManager::GetInstance()->AddRunningVmForTesting( - CryptohomeIdForProfile(browser()->profile()), kCrostiniDefaultVmName, - std::move(vm_info)); + crostini_manager->AddRunningVmForTesting(kCrostiniDefaultVmName, + std::move(vm_info)); ExpectCrostiniMount(); @@ -565,7 +566,8 @@ IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest, CrostiniIncognito) { base::test::ScopedFeatureList scoped_feature_list; EnableCrostiniForProfile(&scoped_feature_list); - crostini::CrostiniManager::GetInstance()->set_skip_restart_for_testing(); + crostini::CrostiniManager::GetForProfile(browser()->profile()) + ->set_skip_restart_for_testing(); ExpectCrostiniMount(); scoped_refptr<extensions::FileManagerPrivateMountCrostiniContainerFunction>
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc index 9fab10e44..a7b752c9 100644 --- a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc +++ b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
@@ -661,8 +661,8 @@ Profile* profile = Profile::FromBrowserContext(browser_context())->GetOriginalProfile(); DCHECK(IsCrostiniEnabled(profile)); - crostini::CrostiniManager::GetInstance()->RestartCrostini( - profile, kCrostiniDefaultVmName, kCrostiniDefaultContainerName, + crostini::CrostiniManager::GetForProfile(profile)->RestartCrostini( + kCrostiniDefaultVmName, kCrostiniDefaultContainerName, base::BindOnce( &FileManagerPrivateMountCrostiniContainerFunction::RestartCallback, this));
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc index 00382c2..175a9eb9 100644 --- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc +++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -1027,7 +1027,8 @@ // crostini interface allowed for testing without such tight coupling. crostini_volume_ = std::make_unique<CrostiniTestVolume>(); profile()->GetPrefs()->SetBoolean(crostini::prefs::kCrostiniEnabled, true); - crostini::CrostiniManager::GetInstance()->set_skip_restart_for_testing(); + crostini::CrostiniManager::GetForProfile(profile()->GetOriginalProfile()) + ->set_skip_restart_for_testing(); chromeos::DBusThreadManager* dbus_thread_manager = chromeos::DBusThreadManager::Get(); static_cast<chromeos::FakeCrosDisksClient*>(
diff --git a/chrome/browser/chromeos/file_manager/file_manager_jstest.cc b/chrome/browser/chromeos/file_manager/file_manager_jstest.cc index cd77754..815bfe6 100644 --- a/chrome/browser/chromeos/file_manager/file_manager_jstest.cc +++ b/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
@@ -121,8 +121,7 @@ } IN_PROC_BROWSER_TEST_F(FileManagerJsTest, ThumbnailLoader) { - RunTest(base::FilePath( - FILE_PATH_LITERAL("foreground/js/thumbnail_loader_unittest.html"))); + RunGeneratedTest("/foreground/js/thumbnail_loader_unittest.html"); } IN_PROC_BROWSER_TEST_F(FileManagerJsTest, MetadataCacheItem) {
diff --git a/chrome/browser/chromeos/file_manager/image_loader_jstest.cc b/chrome/browser/chromeos/file_manager/image_loader_jstest.cc index 2de91a72..a2ef3b55 100644 --- a/chrome/browser/chromeos/file_manager/image_loader_jstest.cc +++ b/chrome/browser/chromeos/file_manager/image_loader_jstest.cc
@@ -11,18 +11,17 @@ }; IN_PROC_BROWSER_TEST_F(ImageLoaderJsTest, ImageLoaderClientTest) { - RunTest(base::FilePath(FILE_PATH_LITERAL( - "image_loader_client_unittest.html"))); + RunGeneratedTest("/image_loader_client_unittest.html"); } IN_PROC_BROWSER_TEST_F(ImageLoaderJsTest, CacheTest) { - RunTest(base::FilePath(FILE_PATH_LITERAL("cache_unittest.html"))); + RunGeneratedTest("/cache_unittest.html"); } IN_PROC_BROWSER_TEST_F(ImageLoaderJsTest, ImageLoaderTest) { - RunTest(base::FilePath(FILE_PATH_LITERAL("image_loader_unittest.html"))); + RunGeneratedTest("/image_loader_unittest.html"); } IN_PROC_BROWSER_TEST_F(ImageLoaderJsTest, PiexLoaderTest) { - RunTest(base::FilePath(FILE_PATH_LITERAL("piex_loader_unittest.html"))); + RunGeneratedTest("/piex_loader_unittest.html"); }
diff --git a/chrome/browser/chromeos/file_manager/volume_manager.cc b/chrome/browser/chromeos/file_manager/volume_manager.cc index d894f62f..c4b5444 100644 --- a/chrome/browser/chromeos/file_manager/volume_manager.cc +++ b/chrome/browser/chromeos/file_manager/volume_manager.cc
@@ -553,10 +553,11 @@ DoMountEvent(chromeos::MOUNT_ERROR_NONE, std::move(volume)); // Listen for crostini container shutdown and remove volume. - crostini::CrostiniManager::GetInstance()->AddShutdownContainerCallback( - profile_, kCrostiniDefaultVmName, kCrostiniDefaultContainerName, - base::BindOnce(&VolumeManager::RemoveSshfsCrostiniVolume, - weak_ptr_factory_.GetWeakPtr(), sshfs_mount_path)); + crostini::CrostiniManager::GetForProfile(profile_) + ->AddShutdownContainerCallback( + kCrostiniDefaultVmName, kCrostiniDefaultContainerName, + base::BindOnce(&VolumeManager::RemoveSshfsCrostiniVolume, + weak_ptr_factory_.GetWeakPtr(), sshfs_mount_path)); } void VolumeManager::RemoveSshfsCrostiniVolume(
diff --git a/chrome/browser/chromeos/login/session/chrome_session_manager.cc b/chrome/browser/chromeos/login/session/chrome_session_manager.cc index 5b8afb0..6c9e7fe 100644 --- a/chrome/browser/chromeos/login/session/chrome_session_manager.cc +++ b/chrome/browser/chromeos/login/session/chrome_session_manager.cc
@@ -144,8 +144,11 @@ policy::AppInstallEventLogManagerWrapper::CreateForProfile(user_profile); } arc::ArcServiceLauncher::Get()->OnPrimaryUserProfilePrepared(user_profile); - crostini::CrostiniManager::GetInstance()->MaybeUpgradeCrostini( - user_profile); + + crostini::CrostiniManager* crostini_manager = + crostini::CrostiniManager::GetForProfile(user_profile); + if (crostini_manager) + crostini_manager->MaybeUpgradeCrostini(); if (user->GetType() == user_manager::USER_TYPE_CHILD) { ScreenTimeControllerFactory::GetForBrowserContext(user_profile);
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc index f4469d8..8a01995 100644 --- a/chrome/browser/chromeos/login/session/user_session_manager.cc +++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -1422,7 +1422,11 @@ policy::AppInstallEventLogManagerWrapper::CreateForProfile(profile); } arc::ArcServiceLauncher::Get()->OnPrimaryUserProfilePrepared(profile); - crostini::CrostiniManager::GetInstance()->MaybeUpgradeCrostini(profile); + + crostini::CrostiniManager* crostini_manager = + crostini::CrostiniManager::GetForProfile(profile); + if (crostini_manager) + crostini_manager->MaybeUpgradeCrostini(); TetherService* tether_service = TetherService::Get(profile); if (tether_service)
diff --git a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc index 5719461..b78a02e 100644 --- a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc +++ b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
@@ -26,6 +26,7 @@ #include "components/guest_view/browser/guest_view_base.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" #include "extensions/browser/extension_host.h" #include "extensions/browser/extension_registry.h" @@ -129,11 +130,12 @@ return extension_name; } -bool ChromeDevToolsManagerDelegate::AllowInspectingWebContents( - content::WebContents* web_contents) { - return AllowInspection( - Profile::FromBrowserContext(web_contents->GetBrowserContext()), - web_contents); +bool ChromeDevToolsManagerDelegate::AllowInspectingRenderFrameHost( + content::RenderFrameHost* rfh) { + Profile* profile = + Profile::FromBrowserContext(rfh->GetProcess()->GetBrowserContext()); + return AllowInspection(profile, extensions::ProcessManager::Get(profile) + ->GetExtensionForRenderFrameHost(rfh)); } // static
diff --git a/chrome/browser/devtools/chrome_devtools_manager_delegate.h b/chrome/browser/devtools/chrome_devtools_manager_delegate.h index 7be478c..f5ac9d7b 100644 --- a/chrome/browser/devtools/chrome_devtools_manager_delegate.h +++ b/chrome/browser/devtools/chrome_devtools_manager_delegate.h
@@ -65,7 +65,7 @@ void DisposeBrowserContext(content::BrowserContext*, DisposeCallback callback) override; - bool AllowInspectingWebContents(content::WebContents* web_contents) override; + bool AllowInspectingRenderFrameHost(content::RenderFrameHost* rfh) override; void ClientAttached(content::DevToolsAgentHost* agent_host, content::DevToolsAgentHostClient* client) override; void ClientDetached(content::DevToolsAgentHost* agent_host,
diff --git a/chrome/browser/devtools/device/adb/mock_adb_server.cc b/chrome/browser/devtools/device/adb/mock_adb_server.cc index f4bd58f..c844f515 100644 --- a/chrome/browser/devtools/device/adb/mock_adb_server.cc +++ b/chrome/browser/devtools/device/adb/mock_adb_server.cc
@@ -623,17 +623,18 @@ } void StartMockAdbServer(FlushMode flush_mode) { + base::RunLoop run_loop; BrowserThread::PostTaskAndReply( BrowserThread::IO, FROM_HERE, base::BindOnce(&StartMockAdbServerOnIOThread, flush_mode), - base::RunLoop::QuitCurrentWhenIdleClosureDeprecated()); - content::RunMessageLoop(); + run_loop.QuitClosure()); + run_loop.Run(); } void StopMockAdbServer() { - BrowserThread::PostTaskAndReply( - BrowserThread::IO, FROM_HERE, - base::BindOnce(&StopMockAdbServerOnIOThread), - base::RunLoop::QuitCurrentWhenIdleClosureDeprecated()); - content::RunMessageLoop(); + base::RunLoop run_loop; + BrowserThread::PostTaskAndReply(BrowserThread::IO, FROM_HERE, + base::BindOnce(&StopMockAdbServerOnIOThread), + run_loop.QuitClosure()); + run_loop.Run(); }
diff --git a/chrome/browser/devtools/devtools_sanity_browsertest.cc b/chrome/browser/devtools/devtools_sanity_browsertest.cc index d5defb18..7641feb3 100644 --- a/chrome/browser/devtools/devtools_sanity_browsertest.cc +++ b/chrome/browser/devtools/devtools_sanity_browsertest.cc
@@ -1898,9 +1898,8 @@ // Installs an extensions, emulating that it has been force-installed by // policy. // Contains assertions - callers should wrap calls of this method in - // |ASSERT_NO_FATAL_FAILURE|. Fills |*out_web_contents| with a |WebContents| - // that belongs to the force-installed extension. - void ForceInstallExtension(content::WebContents** out_web_contents) { + // |ASSERT_NO_FATAL_FAILURE|. + void ForceInstallExtension(std::string* extension_id) { base::FilePath crx_path; base::PathService::Get(chrome::DIR_TEST_DATA, &crx_path); crx_path = crx_path.AppendASCII("devtools") @@ -1909,8 +1908,15 @@ const Extension* extension = InstallExtension( crx_path, 1, extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD); ASSERT_TRUE(extension); + *extension_id = extension->id(); + } - GURL url("chrome-extension://" + extension->id() + "/options.html"); + // Same as above, but also fills |*out_web_contents| with a |WebContents| + // that has been navigated to the force-installed extension. + void ForceInstallExtensionAndOpen(content::WebContents** out_web_contents) { + std::string extension_id; + ForceInstallExtension(&extension_id); + GURL url("chrome-extension://" + extension_id + "/options.html"); ui_test_utils::NavigateToURL(browser(), url); content::WebContents* web_contents = browser()->tab_strip_model()->GetWebContentsAt(0); @@ -1926,13 +1932,38 @@ kDisallowedForForceInstalledExtensions)); content::WebContents* web_contents = nullptr; - ASSERT_NO_FATAL_FAILURE(ForceInstallExtension(&web_contents)); + ASSERT_NO_FATAL_FAILURE(ForceInstallExtensionAndOpen(&web_contents)); DevToolsWindow::OpenDevToolsWindow(web_contents); auto agent_host = content::DevToolsAgentHost::GetOrCreateFor(web_contents); ASSERT_FALSE(DevToolsWindow::FindDevToolsWindow(agent_host.get())); } +IN_PROC_BROWSER_TEST_F( + DevToolsSanityExtensionTest, + PolicyDisallowedForForceInstalledExtensionsAfterNavigation) { + browser()->profile()->GetPrefs()->SetInteger( + prefs::kDevToolsAvailability, + static_cast<int>(policy::DeveloperToolsPolicyHandler::Availability:: + kDisallowedForForceInstalledExtensions)); + + std::string extension_id; + ASSERT_NO_FATAL_FAILURE(ForceInstallExtension(&extension_id)); + content::WebContents* web_contents = + browser()->tab_strip_model()->GetWebContentsAt(0); + + // It's possible to open DevTools for about:blank. + ui_test_utils::NavigateToURL(browser(), GURL("about:blank")); + DevToolsWindow::OpenDevToolsWindow(web_contents); + auto agent_host = content::DevToolsAgentHost::GetOrCreateFor(web_contents); + ASSERT_TRUE(DevToolsWindow::FindDevToolsWindow(agent_host.get())); + + // Navigating to extension page should close DevTools. + ui_test_utils::NavigateToURL( + browser(), GURL("chrome-extension://" + extension_id + "/options.html")); + ASSERT_FALSE(DevToolsWindow::FindDevToolsWindow(agent_host.get())); +} + class DevToolsAllowedByCommandLineSwitch : public DevToolsSanityExtensionTest { public: void SetUpCommandLine(base::CommandLine* command_line) override { @@ -1957,7 +1988,7 @@ kDisallowedForForceInstalledExtensions)); content::WebContents* web_contents = nullptr; - ASSERT_NO_FATAL_FAILURE(ForceInstallExtension(&web_contents)); + ASSERT_NO_FATAL_FAILURE(ForceInstallExtensionAndOpen(&web_contents)); DevToolsWindow::OpenDevToolsWindow(web_contents); auto agent_host = content::DevToolsAgentHost::GetOrCreateFor(web_contents);
diff --git a/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc b/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc index c862c0f..1ceefb33 100644 --- a/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc +++ b/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc
@@ -754,8 +754,8 @@ Profile* profile = Profile::FromBrowserContext(browser_context()); CrostiniInstallerView::Show(profile); CrostiniInstallerView::GetActiveViewForTesting()->Accept(); - crostini::CrostiniManager::GetInstance()->RestartCrostini( - profile, kCrostiniDefaultVmName, kCrostiniDefaultContainerName, + crostini::CrostiniManager::GetForProfile(profile)->RestartCrostini( + kCrostiniDefaultVmName, kCrostiniDefaultContainerName, base::BindOnce( &AutotestPrivateRunCrostiniInstallerFunction::CrostiniRestarted, this));
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 2f6a37b6..eb561be8 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -1535,13 +1535,6 @@ "Remove a navigation entry when the corresponding history entry has been " "deleted."; -const char kRemoveUsageOfDeprecatedGaiaSigninEndpointName[] = - "Remove usage of the deprecated GAIA sign-in endpoint"; -const char kRemoveUsageOfDeprecatedGaiaSigninEndpointDescription[] = - "The Gaia sign-in endpoint used for full-tab sign-in page is deprecated. " - "This flags controls wheter it should no longer be used during a sign-in " - " flow."; - const char kRendererSideResourceSchedulerName[] = "Renderer side ResourceScheduler"; const char kRendererSideResourceSchedulerDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index ca1bf03..e10c5cef 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -940,9 +940,6 @@ extern const char kRemoveNavigationHistoryName[]; extern const char kRemoveNavigationHistoryDescription[]; -extern const char kRemoveUsageOfDeprecatedGaiaSigninEndpointName[]; -extern const char kRemoveUsageOfDeprecatedGaiaSigninEndpointDescription[]; - extern const char kRendererSideResourceSchedulerName[]; extern const char kRendererSideResourceSchedulerDescription[];
diff --git a/chrome/browser/importer/in_process_importer_bridge.cc b/chrome/browser/importer/in_process_importer_bridge.cc index 5832fa6..b6e2661 100644 --- a/chrome/browser/importer/in_process_importer_bridge.cc +++ b/chrome/browser/importer/in_process_importer_bridge.cc
@@ -174,13 +174,8 @@ #if defined(OS_WIN) void InProcessImporterBridge::AddIE7PasswordInfo( - const importer::ImporterIE7PasswordInfo& password_info) { - IE7PasswordInfo ie7_password_info; - ie7_password_info.url_hash = password_info.url_hash; - ie7_password_info.encrypted_data = password_info.encrypted_data; - ie7_password_info.date_created = password_info.date_created; - - writer_->AddIE7PasswordInfo(ie7_password_info); + const importer::ImporterIE7PasswordInfo&) { + // TODO(crbug.com/456119): delete AddIE7PasswordInfo } #endif // OS_WIN
diff --git a/chrome/browser/importer/profile_writer.cc b/chrome/browser/importer/profile_writer.cc index 2c47e3a..dab027aa 100644 --- a/chrome/browser/importer/profile_writer.cc +++ b/chrome/browser/importer/profile_writer.cc
@@ -14,7 +14,6 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/thread.h" -#include "build/build_config.h" #include "chrome/browser/bookmarks/bookmark_model_factory.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/favicon/favicon_service_factory.h" @@ -36,10 +35,6 @@ #include "components/search_engines/template_url.h" #include "components/search_engines/template_url_service.h" -#if defined(OS_WIN) -#include "components/password_manager/core/browser/webdata/password_web_data_service_win.h" -#endif - using bookmarks::BookmarkModel; using bookmarks::BookmarkNode; @@ -96,13 +91,6 @@ profile_, ServiceAccessType::EXPLICIT_ACCESS)->AddLogin(form); } -#if defined(OS_WIN) -void ProfileWriter::AddIE7PasswordInfo(const IE7PasswordInfo& info) { - WebDataServiceFactory::GetPasswordWebDataForProfile( - profile_, ServiceAccessType::EXPLICIT_ACCESS)->AddIE7Login(info); -} -#endif - void ProfileWriter::AddHistoryPage(const history::URLRows& page, history::VisitSource visit_source) { if (!page.empty())
diff --git a/chrome/browser/importer/profile_writer.h b/chrome/browser/importer/profile_writer.h index 3913e09..f4d0fe0 100644 --- a/chrome/browser/importer/profile_writer.h +++ b/chrome/browser/importer/profile_writer.h
@@ -25,10 +25,6 @@ class AutofillEntry; } -#if defined(OS_WIN) -struct IE7PasswordInfo; -#endif - // ProfileWriter encapsulates profile for writing entries into it. // This object must be invoked on UI thread. class ProfileWriter : public base::RefCountedThreadSafe<ProfileWriter> { @@ -44,10 +40,6 @@ // Helper methods for adding data to local stores. virtual void AddPasswordForm(const autofill::PasswordForm& form); -#if defined(OS_WIN) - virtual void AddIE7PasswordInfo(const IE7PasswordInfo& info); -#endif - virtual void AddHistoryPage(const history::URLRows& page, history::VisitSource visit_source);
diff --git a/chrome/browser/media/autoplay_metrics_browsertest.cc b/chrome/browser/media/autoplay_metrics_browsertest.cc index 028912f..5d909f34 100644 --- a/chrome/browser/media/autoplay_metrics_browsertest.cc +++ b/chrome/browser/media/autoplay_metrics_browsertest.cc
@@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/task/post_task.h" #include "chrome/browser/ui/browser.h" #include "chrome/test/base/in_process_browser_test.h" #include "components/ukm/test_ukm_recorder.h" @@ -19,25 +18,15 @@ class AutoplayMetricsBrowserTest : public InProcessBrowserTest { public: - using Entry = ukm::builders::Media_Autoplay_Attempt; - using CreatedEntry = ukm::builders::DocumentCreated; - void SetUpOnMainThread() override { host_resolver()->AddRule("*", "127.0.0.1"); content::SetupCrossSiteRedirector(embedded_test_server()); ASSERT_TRUE(embedded_test_server()->Start()); } - void TryAutoplay(ukm::TestUkmRecorder& ukm_recorder, - const content::ToRenderFrameHost& adapter) { - base::RunLoop run_loop; - base::PostDelayedTask(FROM_HERE, run_loop.QuitClosure(), - base::TimeDelta::FromSeconds(10)); - ukm_recorder.SetOnAddEntryCallback(Entry::kEntryName, - run_loop.QuitClosure()); + void TryAutoplay(const content::ToRenderFrameHost& adapter) { EXPECT_TRUE(ExecuteScriptWithoutUserGesture(adapter.render_frame_host(), "tryPlayback();")); - run_loop.Run(); } void NavigateFrameAndWait(content::RenderFrameHost* rfh, const GURL& url) { @@ -81,6 +70,9 @@ IN_PROC_BROWSER_TEST_F(AutoplayMetricsBrowserTest, RecordAutoplayAttemptUkm) { ukm::TestAutoSetUkmRecorder test_ukm_recorder; + using Entry = ukm::builders::Media_Autoplay_Attempt; + using CreatedEntry = ukm::builders::DocumentCreated; + GURL main_url(embedded_test_server()->GetURL("example.com", "/media/autoplay_iframe.html")); GURL foo_url( @@ -90,25 +82,25 @@ // Navigate main frame, try play. NavigateFrameAndWait(web_contents()->GetMainFrame(), main_url); - TryAutoplay(test_ukm_recorder, web_contents()); + TryAutoplay(web_contents()); // Check that we recorded a UKM event using the main frame URL. { auto ukm_entries = test_ukm_recorder.GetEntriesByName(Entry::kEntryName); - ASSERT_EQ(1u, ukm_entries.size()); + EXPECT_EQ(1u, ukm_entries.size()); test_ukm_recorder.ExpectEntrySourceHasUrl(ukm_entries[0], main_url); } // Navigate sub frame, try play. NavigateFrameAndWait(first_child(), foo_url); - TryAutoplay(test_ukm_recorder, first_child()); + TryAutoplay(first_child()); // Check that we recorded a UKM event that is not keyed to any URL. { auto ukm_entries = test_ukm_recorder.GetEntriesByName(Entry::kEntryName); - ASSERT_EQ(2u, ukm_entries.size()); + EXPECT_EQ(2u, ukm_entries.size()); EXPECT_FALSE( test_ukm_recorder.GetSourceForSourceId(ukm_entries[1]->source_id)); @@ -130,13 +122,13 @@ // Navigate sub sub frame, try play. NavigateFrameAndWait(second_child(), bar_url); - TryAutoplay(test_ukm_recorder, second_child()); + TryAutoplay(second_child()); // Check that we recorded a UKM event that is not keyed to any url. { auto ukm_entries = test_ukm_recorder.GetEntriesByName(Entry::kEntryName); - ASSERT_EQ(3u, ukm_entries.size()); + EXPECT_EQ(3u, ukm_entries.size()); EXPECT_FALSE( test_ukm_recorder.GetSourceForSourceId(ukm_entries[2]->source_id)); @@ -158,13 +150,13 @@ // Navigate top frame, try play. NavigateFrameAndWait(web_contents()->GetMainFrame(), foo_url); - TryAutoplay(test_ukm_recorder, web_contents()); + TryAutoplay(web_contents()); // Check that we recorded a UKM event using the main frame URL. { auto ukm_entries = test_ukm_recorder.GetEntriesByName(Entry::kEntryName); - ASSERT_EQ(4u, ukm_entries.size()); + EXPECT_EQ(4u, ukm_entries.size()); test_ukm_recorder.ExpectEntrySourceHasUrl(ukm_entries[3], foo_url); } }
diff --git a/chrome/browser/offline_pages/offline_page_utils.cc b/chrome/browser/offline_pages/offline_page_utils.cc index f351c53c..a23e41f 100644 --- a/chrome/browser/offline_pages/offline_page_utils.cc +++ b/chrome/browser/offline_pages/offline_page_utils.cc
@@ -59,15 +59,17 @@ void OnGetPagesByURLDone( const GURL& url, int tab_id, - const std::vector<std::string>& namespaces_to_show_in_original_tab, + const std::vector<std::string>& namespaces_restricted_to_tab_from_client_id, base::OnceCallback<void(const std::vector<OfflinePageItem>&)> callback, const MultipleOfflinePageItemResult& pages) { std::vector<OfflinePageItem> selected_pages; std::string tab_id_str = base::IntToString(tab_id); // Exclude pages whose tab id does not match. + // Note: For this restriction to work offline pages saved to tab-bound + // namespaces must have the assigned tab id set to their ClientId::id field. for (const auto& page : pages) { - if (base::ContainsValue(namespaces_to_show_in_original_tab, + if (base::ContainsValue(namespaces_restricted_to_tab_from_client_id, page.client_id.name_space) && page.client_id.id != tab_id_str) { continue; @@ -215,7 +217,7 @@ offline_page_model->GetPagesByURL( url, base::BindOnce(&OnGetPagesByURLDone, url, tab_id, offline_page_model->GetPolicyController() - ->GetNamespacesRestrictedToOriginalTab(), + ->GetNamespacesRestrictedToTabFromClientId(), std::move(callback))); }
diff --git a/chrome/browser/password_manager/password_store_factory.cc b/chrome/browser/password_manager/password_store_factory.cc index 0669f55..0452fd49 100644 --- a/chrome/browser/password_manager/password_store_factory.cc +++ b/chrome/browser/password_manager/password_store_factory.cc
@@ -42,7 +42,6 @@ #if defined(OS_WIN) #include "chrome/browser/password_manager/password_manager_util_win.h" -#include "components/password_manager/core/browser/webdata/password_web_data_service_win.h" #elif defined(OS_MACOSX) #include "chrome/browser/password_manager/password_store_mac.h" #elif defined(OS_CHROMEOS) || defined(OS_ANDROID)
diff --git a/chrome/browser/profiles/off_the_record_profile_impl.cc b/chrome/browser/profiles/off_the_record_profile_impl.cc index 9426898..c99922a7 100644 --- a/chrome/browser/profiles/off_the_record_profile_impl.cc +++ b/chrome/browser/profiles/off_the_record_profile_impl.cc
@@ -479,10 +479,16 @@ // occurs later upon first VideoDecodePerfHistory API request that requires DB // access. DB operations will not block the UI thread. if (!decode_history) { - // TODO(https://crbug/865321): For non guest sessions, this should instead - // be a pointer to GetOriginalProfile()->GetVideoDecodePerfHistory(). - // Passing nullptr while we sort out lifetime issues. - media::VideoDecodeStatsDBProvider* seed_db_provider = nullptr; + // Use the original profile's DB to seed the OTR VideoDeocdePerfHisotry. The + // original DB is treated as read-only, while OTR playbacks will write stats + // to the InMemory version (cleared on profile destruction). Guest profiles + // don't have a root profile like incognito, meaning they don't have a seed + // DB to call on and we can just pass null. + media::VideoDecodeStatsDBProvider* seed_db_provider = + IsGuestSession() ? nullptr + // Safely passing raw pointer to VideoDecodePerfHistory + // because original profile will outlive this profile. + : GetOriginalProfile()->GetVideoDecodePerfHistory(); auto db_factory = std::make_unique<media::InMemoryVideoDecodeStatsDBFactory>(
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc index 04a5df8..0214dc50 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc
@@ -1533,7 +1533,8 @@ chromeos::multidevice_setup::AuthTokenValidatorFactory::GetForProfile( this), std::make_unique< - chromeos::multidevice_setup::AndroidSmsAppHelperDelegateImpl>(this)); + chromeos::multidevice_setup::AndroidSmsAppHelperDelegateImpl>(this), + chromeos::GcmDeviceInfoProviderImpl::GetInstance()); } #endif // defined(OS_CHROMEOS)
diff --git a/chrome/browser/resources/accessibility/accessibility.html b/chrome/browser/resources/accessibility/accessibility.html index 57e6405a..2371586 100644 --- a/chrome/browser/resources/accessibility/accessibility.html +++ b/chrome/browser/resources/accessibility/accessibility.html
@@ -1,5 +1,5 @@ <!doctype html> -<html> +<html lang="en"> <!-- Copyright (c) 2013 The Chromium Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be @@ -147,6 +147,5 @@ <h2>Pages:</h2> <div id="pages" class="list"></div> - <script src="chrome://resources/js/i18n_template.js"></script> </body> </html>
diff --git a/chrome/browser/resources/md_bookmarks/app.html b/chrome/browser/resources/md_bookmarks/app.html index 45d7dab..348cd0a 100644 --- a/chrome/browser/resources/md_bookmarks/app.html +++ b/chrome/browser/resources/md_bookmarks/app.html
@@ -78,7 +78,7 @@ </div> <bookmarks-router></bookmarks-router> <bookmarks-command-manager></bookmarks-command-manager> - <bookmarks-toast-manager duration="5000"></bookmarks-toast-manager> + <bookmarks-toast-manager duration="10000"></bookmarks-toast-manager> </template> <script src="chrome://bookmarks/app.js"></script> </dom-module>
diff --git a/chrome/browser/resources/settings/people_page/sync_page.html b/chrome/browser/resources/settings/people_page/sync_page.html index 6007ce8f..d1e99ea 100644 --- a/chrome/browser/resources/settings/people_page/sync_page.html +++ b/chrome/browser/resources/settings/people_page/sync_page.html
@@ -410,8 +410,7 @@ $i18n{personalizeGoogleServicesTitle} </div> <paper-icon-button-light actionable class="icon-external"> - <button aria-label="$i18n{personalizeGoogleServicesTitle}"> - </button> + <button></button> </paper-icon-button-light> </a> @@ -422,7 +421,7 @@ $i18n{manageSyncedDataTitle} </div> <paper-icon-button-light actionable class="icon-external"> - <button aria-label="$i18n{manageSyncedDataTitle}"></button> + <button></button> </paper-icon-button-light> </a> @@ -452,20 +451,29 @@ on-paper-radio-group-changed= "onEncryptionRadioSelectionChanged_"> <cr-radio-button name="encrypt-with-google" - class="list-item" disabled="[[syncPrefs.encryptAllData]]"> + class="list-item" disabled="[[syncPrefs.encryptAllData]]" + aria-label="$i18n{encryptWithGoogleCredentialsLabel}"> $i18n{encryptWithGoogleCredentialsLabel} </cr-radio-button> - <cr-radio-button name="encrypt-with-passphrase" - class="list-item" disabled="[[syncPrefs.encryptAllData]]"> - <template is="dom-if" if="[[syncPrefs.fullEncryptionBody]]"> - <span>[[syncPrefs.fullEncryptionBody]]</span> - </template> - <template is="dom-if" if="[[!syncPrefs.fullEncryptionBody]]"> - <span on-click="onLearnMoreTap_"> + <template is="dom-if" if="[[syncPrefs.fullEncryptionBody]]"> + <cr-radio-button name="encrypt-with-passphrase" + class="list-item" disabled="[[syncPrefs.encryptAllData]]" + aria-labelledby="fullEncryptionBody"> + <span id="fullEncryptionBody"> + [[syncPrefs.fullEncryptionBody]] + </span> + </cr-radio-button> + </template> + <template is="dom-if" if="[[!syncPrefs.fullEncryptionBody]]"> + <cr-radio-button name="encrypt-with-passphrase" + class="list-item" disabled="[[syncPrefs.encryptAllData]]" + aria-labelledby="encryptWithSyncPassphraseLabel"> + <span id="encryptWithSyncPassphraseLabel" + on-click="onLearnMoreTap_"> $i18nRaw{encryptWithSyncPassphraseLabel} </span> - </template> - </cr-radio-button> + </cr-radio-button> + </template> </paper-radio-group> </div>
diff --git a/chrome/browser/signin/dice_browsertest.cc b/chrome/browser/signin/dice_browsertest.cc index 1240265..06d0fab 100644 --- a/chrome/browser/signin/dice_browsertest.cc +++ b/chrome/browser/signin/dice_browsertest.cc
@@ -277,10 +277,11 @@ // Handler for ServiceLogin on the embedded test server. // Calls the callback with the dice request header, or kNoDiceRequestHeader if // there is no Dice header. -std::unique_ptr<HttpResponse> HandleServiceLoginURL( +std::unique_ptr<HttpResponse> HandleChromeSigninEmbeddedURL( const base::RepeatingCallback<void(const std::string&)>& callback, const HttpRequest& request) { - if (!net::test_server::ShouldHandle(request, "/ServiceLogin")) + if (!net::test_server::ShouldHandle(request, + "/embedded/setup/chrome/usermenu")) return nullptr; std::string dice_request_header(kNoDiceRequestHeader); @@ -336,8 +337,8 @@ base::BindRepeating(&DiceBrowserTestBase::OnTokenRevocationRequest, base::Unretained(this)))); https_server_.RegisterDefaultHandler(base::BindRepeating( - &FakeGaia::HandleServiceLoginURL, - base::BindRepeating(&DiceBrowserTestBase::OnServiceLoginRequest, + &FakeGaia::HandleChromeSigninEmbeddedURL, + base::BindRepeating(&DiceBrowserTestBase::OnChromeSigninEmbeddedRequest, base::Unretained(this)))); signin::SetDiceAccountReconcilorBlockDelayForTesting( kAccountReconcilorDelayMs); @@ -496,9 +497,9 @@ dice_request_header_ = dice_request_header; } - void OnServiceLoginRequest(const std::string& dice_request_header) { + void OnChromeSigninEmbeddedRequest(const std::string& dice_request_header) { dice_request_header_ = dice_request_header; - RunClosureIfValid(std::move(service_login_quit_closure_)); + RunClosureIfValid(std::move(chrome_signin_embedded_quit_closure_)); } void OnEnableSyncRequest(base::OnceClosure unblock_response_closure) { @@ -617,7 +618,7 @@ base::OnceClosure token_requested_quit_closure_; base::OnceClosure token_revoked_quit_closure_; base::OnceClosure refresh_token_available_quit_closure_; - base::OnceClosure service_login_quit_closure_; + base::OnceClosure chrome_signin_embedded_quit_closure_; base::OnceClosure unblock_count_quit_closure_; base::OnceClosure tokens_loaded_quit_closure_; base::OnceClosure google_signin_succeeded_quit_closure_; @@ -782,13 +783,12 @@ // Checks that Dice request header is not set from request from WebUI. // See https://crbug.com/428396 IN_PROC_BROWSER_TEST_F(DiceBrowserTest, NoDiceFromWebUI) { - // Navigate to Gaia and from the native tab, which uses an extension. ui_test_utils::NavigateToURL(browser(), GURL("chrome:chrome-signin")); // Check that the request had no Dice request header. if (dice_request_header_.empty()) - WaitForClosure(&service_login_quit_closure_); + WaitForClosure(&chrome_signin_embedded_quit_closure_); EXPECT_EQ(kNoDiceRequestHeader, dice_request_header_); EXPECT_EQ(0, reconcilor_blocked_count_); WaitForReconcilorUnblockedCount(0);
diff --git a/chrome/browser/signin/signin_promo.cc b/chrome/browser/signin/signin_promo.cc index 1117b72..fdd2349 100644 --- a/chrome/browser/signin/signin_promo.cc +++ b/chrome/browser/signin/signin_promo.cc
@@ -81,12 +81,9 @@ // |access_point| indicates where the sign in is being initiated. // |reason| indicates the purpose of using this URL. // |auto_close| whether to close the sign in promo automatically when done. -// |is_constrained| whether to load the URL in a constrained window, false -// by default. GURL GetPromoURL(signin_metrics::AccessPoint access_point, signin_metrics::Reason reason, - bool auto_close, - bool is_constrained) { + bool auto_close) { CHECK_LT(static_cast<int>(access_point), static_cast<int>(signin_metrics::AccessPoint::ACCESS_POINT_MAX)); CHECK_NE(static_cast<int>(access_point), @@ -106,20 +103,14 @@ url = net::AppendQueryParameter(url, signin::kSignInPromoQueryKeyAutoClose, "1"); } - if (is_constrained) { - url = net::AppendQueryParameter( - url, signin::kSignInPromoQueryKeyConstrained, "1"); - } - return url; } GURL GetReauthURL(signin_metrics::AccessPoint access_point, signin_metrics::Reason reason, const std::string& email, - bool auto_close, - bool is_constrained) { - GURL url = GetPromoURL(access_point, reason, auto_close, is_constrained); + bool auto_close) { + GURL url = GetPromoURL(access_point, reason, auto_close); url = net::AppendQueryParameter(url, "email", email); url = net::AppendQueryParameter(url, "validateEmail", "1"); return net::AppendQueryParameter(url, "readOnlyEmail", "1"); @@ -135,7 +126,6 @@ const char kSignInPromoQueryKeyForceKeepData[] = "force_keep_data"; const char kSignInPromoQueryKeyReason[] = "reason"; const char kSignInPromoQueryKeySource[] = "source"; -const char kSignInPromoQueryKeyConstrained[] = "constrained"; const char kSigninPromoLandingURLSuccessPage[] = "success.html"; bool ShouldShowPromoAtStartup(Profile* profile, bool is_new_profile) { @@ -228,23 +218,13 @@ GURL GetPromoURLForTab(signin_metrics::AccessPoint access_point, signin_metrics::Reason reason, bool auto_close) { - if (base::FeatureList::IsEnabled( - features::kRemoveUsageOfDeprecatedGaiaSigninEndpoint)) { - // The full-tab sign-in endpoint is deprecated. Use the constrained page for - // the full-tab URL as well. - return GetPromoURL(access_point, reason, auto_close, - true /* is_constrained */); - } - - return GetPromoURL(access_point, reason, auto_close, - false /* is_constrained */); + return GetPromoURL(access_point, reason, auto_close); } GURL GetPromoURLForDialog(signin_metrics::AccessPoint access_point, signin_metrics::Reason reason, bool auto_close) { - return GetPromoURL(access_point, reason, auto_close, - true /* is_constrained */); + return GetPromoURL(access_point, reason, auto_close); } GURL GetReauthURLForDialog(signin_metrics::AccessPoint access_point, @@ -253,8 +233,7 @@ const std::string& account_id) { AccountInfo info = AccountTrackerServiceFactory::GetForProfile(profile) ->GetAccountInfo(account_id); - return GetReauthURL(access_point, reason, info.email, true /* auto_close */, - true /* is_constrained */); + return GetReauthURL(access_point, reason, info.email, true /* auto_close */); } GURL GetReauthURLForTab(signin_metrics::AccessPoint access_point, @@ -264,24 +243,13 @@ AccountInfo info = AccountTrackerServiceFactory::GetForProfile(profile)->GetAccountInfo( account_id); - - if (base::FeatureList::IsEnabled( - features::kRemoveUsageOfDeprecatedGaiaSigninEndpoint)) { - // The full-tab sign-in endpoint is deprecated. Use the constrained page for - // the full-tab URL as well. - return GetReauthURL(access_point, reason, info.email, true /* auto_close */, - true /* is_constrained */); - } - - return GetReauthURL(access_point, reason, info.email, true /* auto_close */, - false /* is_constrained */); + return GetReauthURL(access_point, reason, info.email, true /* auto_close */); } GURL GetReauthURLWithEmailForDialog(signin_metrics::AccessPoint access_point, signin_metrics::Reason reason, const std::string& email) { - return GetReauthURL(access_point, reason, email, true /* auto_close */, - true /* is_constrained */); + return GetReauthURL(access_point, reason, email, true /* auto_close */); } GURL GetSigninURLForDice(Profile* profile, const std::string& email) {
diff --git a/chrome/browser/signin/signin_promo.h b/chrome/browser/signin/signin_promo.h index 06a342a..f1f5a78 100644 --- a/chrome/browser/signin/signin_promo.h +++ b/chrome/browser/signin/signin_promo.h
@@ -26,7 +26,6 @@ extern const char kSignInPromoQueryKeyForceKeepData[]; extern const char kSignInPromoQueryKeyReason[]; extern const char kSignInPromoQueryKeySource[]; -extern const char kSignInPromoQueryKeyConstrained[]; extern const char kSigninPromoLandingURLSuccessPage[]; // Returns true if we should show the sign in promo at startup.
diff --git a/chrome/browser/signin/signin_promo_unittest.cc b/chrome/browser/signin/signin_promo_unittest.cc index 91f387f..abb794f 100644 --- a/chrome/browser/signin/signin_promo_unittest.cc +++ b/chrome/browser/signin/signin_promo_unittest.cc
@@ -13,14 +13,12 @@ TEST_F(SigninPromoTest, TestPromoURL) { GURL expected_url_1( - "chrome://chrome-signin/" - "?access_point=0&reason=0&auto_close=1&constrained=1"); + "chrome://chrome-signin/?access_point=0&reason=0&auto_close=1"); EXPECT_EQ(expected_url_1, GetPromoURLForDialog( signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE, signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT, true)); - GURL expected_url_2( - "chrome://chrome-signin/?access_point=15&reason=3&constrained=1"); + GURL expected_url_2("chrome://chrome-signin/?access_point=15&reason=3"); EXPECT_EQ(expected_url_2, GetPromoURLForDialog( signin_metrics::AccessPoint::ACCESS_POINT_SIGNIN_PROMO, @@ -30,8 +28,8 @@ TEST_F(SigninPromoTest, TestReauthURL) { GURL expected_url_1( "chrome://chrome-signin/" - "?access_point=0&reason=0&auto_close=1&constrained=1&email=example%" - "40domain.com&validateEmail=1&readOnlyEmail=1"); + "?access_point=0&reason=0&auto_close=1&email=example%40domain.com" + "&validateEmail=1&readOnlyEmail=1"); EXPECT_EQ(expected_url_1, GetReauthURLWithEmailForDialog( signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE,
diff --git a/chrome/browser/ui/app_list/arc/arc_default_app_list.cc b/chrome/browser/ui/app_list/arc/arc_default_app_list.cc index 2f630c8..891a0aa5 100644 --- a/chrome/browser/ui/app_list/arc/arc_default_app_list.cc +++ b/chrome/browser/ui/app_list/arc/arc_default_app_list.cc
@@ -1,10 +1,11 @@ -// 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 "chrome/browser/ui/app_list/arc/arc_default_app_list.h" +#include "base/barrier_closure.h" #include "base/files/file_enumerator.h" +#include "base/files/file_util.h" #include "base/json/json_file_value_serializer.h" #include "base/path_service.h" #include "base/task/post_task.h" @@ -34,87 +35,78 @@ const base::FilePath::CharType kArcDirectory[] = FILE_PATH_LITERAL("arc"); const base::FilePath::CharType kArcTestDirectory[] = FILE_PATH_LITERAL("arc_default_apps"); +const base::FilePath::CharType kArcTestBoardDirectory[] = + FILE_PATH_LITERAL("arc_board_default_apps"); +const base::FilePath::CharType kBoardDirectory[] = + FILE_PATH_LITERAL("/var/cache/arc_default_apps"); bool use_test_apps_directory = false; -std::unique_ptr<ArcDefaultAppList::AppInfoMap> -ReadAppsFromFileThread() { +std::unique_ptr<ArcDefaultAppList::AppInfoMap> ReadAppsFromFileThread( + const base::FilePath& base_path) { + base::FilePath root_dir; + // FileEnumerator does not work with a symbolic link dir. So map link + // to real folder in case |base_path| specifies a symbolic link. + if (!base::ReadSymbolicLink(base_path, &root_dir)) + root_dir = base_path; + std::unique_ptr<ArcDefaultAppList::AppInfoMap> apps = std::make_unique<ArcDefaultAppList::AppInfoMap>(); - base::FilePath base_path; - if (!use_test_apps_directory) { - if (!base::PathService::Get(chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS, - &base_path)) - return apps; - base_path = base_path.Append(kArcDirectory); - } else { - if (!base::PathService::Get(chrome::DIR_TEST_DATA, &base_path)) - return apps; - base_path = base_path.AppendASCII(kArcTestDirectory); - } - base::FilePath::StringType extension(".json"); - base::FileEnumerator json_files( - base_path, - false, // Recursive. - base::FileEnumerator::FILES); + base::FileEnumerator json_files(root_dir, + false, // Recursive. + base::FileEnumerator::FILES); for (base::FilePath file = json_files.Next(); !file.empty(); file = json_files.Next()) { - - if (file.MatchesExtension(extension)) { - JSONFileValueDeserializer deserializer(file); - std::string error_msg; - std::unique_ptr<base::Value> app_info = - deserializer.Deserialize(nullptr, &error_msg); - if (!app_info) { - VLOG(2) << "Unable to deserialize json data: " << error_msg - << " in file " << file.value() << "."; - continue; - } - - std::unique_ptr<base::DictionaryValue> app_info_dictionary = - base::DictionaryValue::From(std::move(app_info)); - CHECK(app_info_dictionary); - - std::string name; - std::string package_name; - std::string activity; - std::string app_path; - bool oem = false; - - app_info_dictionary->GetString(kName, &name); - app_info_dictionary->GetString(kPackageName, &package_name); - app_info_dictionary->GetString(kActivity, &activity); - app_info_dictionary->GetString(kAppPath, &app_path); - app_info_dictionary->GetBoolean(kOem, &oem); - - if (name.empty() || - package_name.empty() || - activity.empty() || - app_path.empty()) { - VLOG(2) << "ARC app declaration is incomplete in file " << file.value() - << "."; - continue; - } - - const std::string app_id = ArcAppListPrefs::GetAppId( - package_name, activity); - std::unique_ptr<ArcDefaultAppList::AppInfo> app = - std::make_unique<ArcDefaultAppList::AppInfo>(name, - package_name, - activity, - oem, - base_path.Append(app_path)); - apps.get()->insert( - std::pair<std::string, - std::unique_ptr<ArcDefaultAppList::AppInfo>>( - app_id, std::move(app))); - } else { + if (!file.MatchesExtension(extension)) { DVLOG(1) << "Not considering: " << file.LossyDisplayName() << " (does not have a .json extension)"; + continue; } + + JSONFileValueDeserializer deserializer(file); + std::string error_msg; + std::unique_ptr<base::Value> app_info = + deserializer.Deserialize(nullptr, &error_msg); + if (!app_info) { + VLOG(2) << "Unable to deserialize json data: " << error_msg << " in file " + << file.value() << "."; + continue; + } + + std::unique_ptr<base::DictionaryValue> app_info_dictionary = + base::DictionaryValue::From(std::move(app_info)); + CHECK(app_info_dictionary); + + std::string name; + std::string package_name; + std::string activity; + std::string app_path; + bool oem = false; + + app_info_dictionary->GetString(kName, &name); + app_info_dictionary->GetString(kPackageName, &package_name); + app_info_dictionary->GetString(kActivity, &activity); + app_info_dictionary->GetString(kAppPath, &app_path); + app_info_dictionary->GetBoolean(kOem, &oem); + + if (name.empty() || package_name.empty() || activity.empty() || + app_path.empty()) { + VLOG(2) << "ARC app declaration is incomplete in file " << file.value() + << "."; + continue; + } + + const std::string app_id = + ArcAppListPrefs::GetAppId(package_name, activity); + std::unique_ptr<ArcDefaultAppList::AppInfo> app = + std::make_unique<ArcDefaultAppList::AppInfo>( + name, package_name, activity, oem, root_dir.Append(app_path)); + apps.get()->insert( + std::pair<std::string, std::unique_ptr<ArcDefaultAppList::AppInfo>>( + app_id, std::move(app))); } return apps; @@ -150,17 +142,48 @@ weak_ptr_factory_(this) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + // Load default apps from two sources. + // /usr/share/google-chrome/extensions/arc - contains default apps for all + // boards that share the same image. + // /var/cache/arc_default_apps that is link to + // /usr/share/google-chrome/extensions/arc/BOARD_NAME - contains default + // apps for particular current board. + // + std::vector<base::FilePath> sources; + + base::FilePath base_path; + if (!use_test_apps_directory) { + const bool valid_path = base::PathService::Get( + chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS, &base_path); + DCHECK(valid_path); + sources.push_back(base_path.Append(kArcDirectory)); + sources.push_back(base::FilePath(kBoardDirectory)); + } else { + const bool valid_path = + base::PathService::Get(chrome::DIR_TEST_DATA, &base_path); + DCHECK(valid_path); + sources.push_back(base_path.Append(kArcTestDirectory)); + sources.push_back(base_path.Append(kArcTestBoardDirectory)); + } + + // Using base::Unretained(this) here is safe since we own barrier_closure_. + barrier_closure_ = base::BarrierClosure( + sources.size(), + base::BindOnce(&ArcDefaultAppList::OnAppsReady, base::Unretained(this))); + // Once ready OnAppsReady is called. - base::PostTaskWithTraitsAndReplyWithResult( - FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT}, - base::Bind(&ReadAppsFromFileThread), - base::Bind(&ArcDefaultAppList::OnAppsReady, - weak_ptr_factory_.GetWeakPtr())); + for (const auto& source : sources) { + base::PostTaskWithTraitsAndReplyWithResult( + FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT}, + base::BindOnce(&ReadAppsFromFileThread, source), + base::BindOnce(&ArcDefaultAppList::OnAppsRead, + weak_ptr_factory_.GetWeakPtr())); + } } ArcDefaultAppList::~ArcDefaultAppList() = default; -void ArcDefaultAppList::OnAppsReady(std::unique_ptr<AppInfoMap> apps) { +void ArcDefaultAppList::OnAppsRead(std::unique_ptr<AppInfoMap> apps) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); const PrefService* const prefs = profile_->GetPrefs(); @@ -170,6 +193,11 @@ app_map[entry.first] = std::move(entry.second); } + barrier_closure_.Run(); +} + +void ArcDefaultAppList::OnAppsReady() { + const PrefService* const prefs = profile_->GetPrefs(); // Register Play Store as default app. Some services and ArcSupportHost may // not be available in tests. extensions::ExtensionService* service =
diff --git a/chrome/browser/ui/app_list/arc/arc_default_app_list.h b/chrome/browser/ui/app_list/arc/arc_default_app_list.h index c1d8929..4b70c53 100644 --- a/chrome/browser/ui/app_list/arc/arc_default_app_list.h +++ b/chrome/browser/ui/app_list/arc/arc_default_app_list.h
@@ -86,8 +86,10 @@ } private: - // Called when default apps are ready. - void OnAppsReady(std::unique_ptr<AppInfoMap> apps); + // Called when default apps are read from the provided source. + void OnAppsRead(std::unique_ptr<AppInfoMap> apps); + // Called when default apps from all sources are read. + void OnAppsReady(); // Unowned pointer. Profile* profile_; @@ -100,6 +102,8 @@ AppInfoMap visible_apps_; // Keeps hidden apps. AppInfoMap hidden_apps_; + // To wait until all sources with apps are loaded. + base::RepeatingClosure barrier_closure_; base::WeakPtrFactory<ArcDefaultAppList> weak_ptr_factory_;
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_context_menu.cc b/chrome/browser/ui/app_list/crostini/crostini_app_context_menu.cc index 4e0a278..359e479 100644 --- a/chrome/browser/ui/app_list/crostini/crostini_app_context_menu.cc +++ b/chrome/browser/ui/app_list/crostini/crostini_app_context_menu.cc
@@ -58,8 +58,8 @@ case ash::MENU_CLOSE: if (app_id() == kCrostiniTerminalId) { - crostini::CrostiniManager::GetInstance()->StopVm( - profile(), kCrostiniDefaultVmName, base::DoNothing()); + crostini::CrostiniManager::GetForProfile(profile())->StopVm( + kCrostiniDefaultVmName, base::DoNothing()); return; } break;
diff --git a/chrome/browser/ui/cocoa/browser_dialogs_views_mac.cc b/chrome/browser/ui/cocoa/browser_dialogs_views_mac.cc index c18ea3f9..5685b99d 100644 --- a/chrome/browser/ui/cocoa/browser_dialogs_views_mac.cc +++ b/chrome/browser/ui/cocoa/browser_dialogs_views_mac.cc
@@ -57,17 +57,19 @@ return; } - views::View* anchor_view = - bubble_anchor_util::GetPageInfoAnchorView(browser, anchor); + bubble_anchor_util::AnchorConfiguration configuration = + bubble_anchor_util::GetPageInfoAnchorConfiguration(browser, anchor); gfx::Rect anchor_rect = - anchor_view ? gfx::Rect() - : bubble_anchor_util::GetPageInfoAnchorRect(browser); + configuration.anchor_view + ? gfx::Rect() + : bubble_anchor_util::GetPageInfoAnchorRect(browser); gfx::NativeWindow parent_window = browser->window()->GetNativeWindow(); views::BubbleDialogDelegateView* bubble = PageInfoBubbleView::CreatePageInfoBubble( - anchor_view, anchor_rect, parent_window, browser->profile(), - web_contents, virtual_url, security_info); + configuration.anchor_view, anchor_rect, parent_window, + browser->profile(), web_contents, virtual_url, security_info); bubble->GetWidget()->Show(); + bubble->set_arrow(configuration.bubble_arrow); KeepBubbleAnchored(bubble, GetPageInfoDecoration(parent_window)); }
diff --git a/chrome/browser/ui/extensions/OWNERS b/chrome/browser/ui/extensions/OWNERS index 4116bbb..b8cca04d 100644 --- a/chrome/browser/ui/extensions/OWNERS +++ b/chrome/browser/ui/extensions/OWNERS
@@ -1,5 +1,6 @@ # App-y stuff benwells@chromium.org +per-file hosted_app_*=alancutter@chromium.org per-file hosted_app_*=mgiuca@chromium.org per-file hosted_app_*=ortuno@chromium.org
diff --git a/chrome/browser/ui/views/bubble_anchor_util_views.cc b/chrome/browser/ui/views/bubble_anchor_util_views.cc index 63c87f88..14398ef 100644 --- a/chrome/browser/ui/views/bubble_anchor_util_views.cc +++ b/chrome/browser/ui/views/bubble_anchor_util_views.cc
@@ -16,22 +16,24 @@ namespace bubble_anchor_util { -views::View* GetPageInfoAnchorView(Browser* browser, Anchor anchor) { +AnchorConfiguration GetPageInfoAnchorConfiguration(Browser* browser, + Anchor anchor) { #if defined(OS_MACOSX) if (views_mode_controller::IsViewsBrowserCocoa()) - return nullptr; + return {}; #endif BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser); if (anchor == kLocationBar && browser_view->GetLocationBarView()->IsDrawn()) - return browser_view->GetLocationBarView()->GetSecurityBubbleAnchorView(); + return {browser_view->GetLocationBarView()->GetSecurityBubbleAnchorView(), + views::BubbleBorder::TOP_LEFT}; // Fall back to menu button if no location bar present. views::View* app_menu_button = browser_view->toolbar_button_provider()->GetAppMenuButton(); if (app_menu_button && app_menu_button->IsDrawn()) - return app_menu_button; - return nullptr; + return {app_menu_button, views::BubbleBorder::TOP_RIGHT}; + return {}; } gfx::Rect GetPageInfoAnchorRect(Browser* browser) { @@ -39,8 +41,9 @@ if (views_mode_controller::IsViewsBrowserCocoa()) return GetPageInfoAnchorRectCocoa(browser); #endif - // GetPageInfoAnchorView() should be preferred if available. - DCHECK_EQ(GetPageInfoAnchorView(browser), nullptr); + // GetPageInfoAnchorConfiguration()'s anchor_view should be preferred if + // available. + DCHECK_EQ(GetPageInfoAnchorConfiguration(browser).anchor_view, nullptr); BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser); // Get position in view (taking RTL UI into account).
diff --git a/chrome/browser/ui/views/bubble_anchor_util_views.h b/chrome/browser/ui/views/bubble_anchor_util_views.h index 80a9828..f8671630 100644 --- a/chrome/browser/ui/views/bubble_anchor_util_views.h +++ b/chrome/browser/ui/views/bubble_anchor_util_views.h
@@ -6,6 +6,7 @@ #define CHROME_BROWSER_UI_VIEWS_BUBBLE_ANCHOR_UTIL_VIEWS_H_ #include "chrome/browser/ui/bubble_anchor_util.h" +#include "ui/views/bubble/bubble_border.h" namespace views { class View; @@ -15,9 +16,17 @@ namespace bubble_anchor_util { -// Returns the PageInfo |anchor| View for |browser|, or null if it should not be -// used. -views::View* GetPageInfoAnchorView(Browser* browser, Anchor = kLocationBar); +struct AnchorConfiguration { + views::View* anchor_view = nullptr; + views::BubbleBorder::Arrow bubble_arrow = views::BubbleBorder::TOP_LEFT; +}; + +// Returns: +// - The PageInfo |anchor| View for |browser|, or null if it should not be +// used. +// - The arrow position for the PageInfo bubble. +AnchorConfiguration GetPageInfoAnchorConfiguration(Browser* browser, + Anchor = kLocationBar); } // namespace bubble_anchor_util
diff --git a/chrome/browser/ui/views/crostini/crostini_installer_view.cc b/chrome/browser/ui/views/crostini/crostini_installer_view.cc index 50d4304..b582c41a 100644 --- a/chrome/browser/ui/views/crostini/crostini_installer_view.cc +++ b/chrome/browser/ui/views/crostini/crostini_installer_view.cc
@@ -9,6 +9,7 @@ #include "ash/public/cpp/ash_typography.h" #include "base/metrics/histogram_functions.h" +#include "base/numerics/ranges.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/task/post_task.h" @@ -137,7 +138,7 @@ // Retry. DCHECK(state_ == State::PROMPT || state_ == State::ERROR); - state_ = State::INSTALL_START; + UpdateState(State::INSTALL_START); profile_->GetPrefs()->SetBoolean(crostini::prefs::kCrostiniEnabled, true); progress_bar_->SetVisible(true); @@ -159,11 +160,13 @@ } // Kick off the Crostini Restart sequence. We will be added as an observer. - restart_id_ = crostini::CrostiniManager::GetInstance()->RestartCrostini( - profile_, kCrostiniDefaultVmName, kCrostiniDefaultContainerName, - base::BindOnce(&CrostiniInstallerView::MountContainerFinished, - weak_ptr_factory_.GetWeakPtr()), - this); + restart_id_ = + crostini::CrostiniManager::GetForProfile(profile_)->RestartCrostini( + kCrostiniDefaultVmName, kCrostiniDefaultContainerName, + base::BindOnce(&CrostiniInstallerView::MountContainerFinished, + weak_ptr_factory_.GetWeakPtr()), + this); + UpdateState(State::INSTALL_IMAGE_LOADER); return false; } @@ -172,8 +175,8 @@ restart_id_ != crostini::CrostiniManager::kUninitializedRestartId) { // Abort the long-running flow, and prevent our RestartObserver methods // being called after "this" has been destroyed. - crostini::CrostiniManager::GetInstance()->AbortRestartCrostini(profile_, - restart_id_); + crostini::CrostiniManager::GetForProfile(profile_)->AbortRestartCrostini( + restart_id_); RecordSetupResultHistogram(SetupResult::kUserCancelled); } else { RecordSetupResultHistogram(SetupResult::kNotStarted); @@ -195,8 +198,8 @@ } void CrostiniInstallerView::OnComponentLoaded(ConciergeClientResult result) { - DCHECK_EQ(state_, State::INSTALL_START); - state_ = State::INSTALL_IMAGE_LOADER; + DCHECK_EQ(state_, State::INSTALL_IMAGE_LOADER); + if (result != ConciergeClientResult::SUCCESS) { LOG(ERROR) << "Failed to install the cros-termina component"; HandleError( @@ -205,12 +208,12 @@ return; } VLOG(1) << "cros-termina install success"; + UpdateState(State::START_CONCIERGE); StepProgress(); } void CrostiniInstallerView::OnConciergeStarted(ConciergeClientResult result) { - DCHECK_EQ(state_, State::INSTALL_IMAGE_LOADER); - state_ = State::START_CONCIERGE; + DCHECK_EQ(state_, State::START_CONCIERGE); if (result != ConciergeClientResult::SUCCESS) { LOG(ERROR) << "Failed to install start Concierge with error code: " << static_cast<int>(result); @@ -220,12 +223,12 @@ return; } VLOG(1) << "Concierge service started"; + UpdateState(State::CREATE_DISK_IMAGE); StepProgress(); } void CrostiniInstallerView::OnDiskImageCreated(ConciergeClientResult result) { - DCHECK_EQ(state_, State::START_CONCIERGE); - state_ = State::CREATE_DISK_IMAGE; + DCHECK_EQ(state_, State::CREATE_DISK_IMAGE); if (result != ConciergeClientResult::SUCCESS) { LOG(ERROR) << "Failed to create disk imagewith error code: " << static_cast<int>(result); @@ -235,12 +238,12 @@ return; } VLOG(1) << "Created crostini disk image"; + UpdateState(State::START_TERMINA_VM); StepProgress(); } void CrostiniInstallerView::OnVmStarted(ConciergeClientResult result) { - DCHECK_EQ(state_, State::CREATE_DISK_IMAGE); - state_ = State::START_TERMINA_VM; + DCHECK_EQ(state_, State::START_TERMINA_VM); if (result != ConciergeClientResult::SUCCESS) { LOG(ERROR) << "Failed to start Termina VM with error code: " << static_cast<int>(result); @@ -250,12 +253,28 @@ return; } VLOG(1) << "Started Termina VM successfully"; + UpdateState(State::CREATE_CONTAINER); + StepProgress(); +} + +void CrostiniInstallerView::OnContainerDownloading(int32_t download_percent) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + DCHECK_EQ(state_, State::CREATE_CONTAINER); + container_download_percent_ = base::ClampToRange(download_percent, 0, 100); + StepProgress(); +} + +void CrostiniInstallerView::OnContainerCreated(ConciergeClientResult result) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + DCHECK_EQ(state_, State::CREATE_CONTAINER); + UpdateState(State::START_CONTAINER); StepProgress(); } void CrostiniInstallerView::OnContainerStarted(ConciergeClientResult result) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - state_ = State::START_CONTAINER; + DCHECK_EQ(state_, State::START_CONTAINER); + if (result != ConciergeClientResult::SUCCESS) { LOG(ERROR) << "Failed to start container with error code: " << static_cast<int>(result); @@ -265,12 +284,14 @@ return; } VLOG(1) << "Started container successfully"; + UpdateState(State::FETCH_SSH_KEYS); StepProgress(); } void CrostiniInstallerView::OnSshKeysFetched(ConciergeClientResult result) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - state_ = State::FETCH_SSH_KEYS; + DCHECK_EQ(state_, State::FETCH_SSH_KEYS); + if (result != ConciergeClientResult::SUCCESS) { LOG(ERROR) << "Failed to fetch ssh keys with error code: " << static_cast<int>(result); @@ -280,6 +301,7 @@ return; } VLOG(1) << "Fetched ssh keys successfully"; + UpdateState(State::MOUNT_CONTAINER); StepProgress(); } @@ -390,7 +412,7 @@ RecordSetupResultHistogram(result); restart_id_ = crostini::CrostiniManager::kUninitializedRestartId; - state_ = State::ERROR; + UpdateState(State::ERROR); message_label_->SetVisible(true); message_label_->SetText(error_message); SetBigMessageLabel(); @@ -408,7 +430,6 @@ void CrostiniInstallerView::MountContainerFinished( ConciergeClientResult result) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - state_ = State::MOUNT_CONTAINER; if (result != ConciergeClientResult::SUCCESS) { LOG(ERROR) << "Failed to mount container with error code: " << static_cast<int>(result); @@ -423,10 +444,10 @@ void CrostiniInstallerView::ShowLoginShell() { DCHECK_EQ(state_, State::MOUNT_CONTAINER); - state_ = State::SHOW_LOGIN_SHELL; + UpdateState(State::SHOW_LOGIN_SHELL); - crostini::CrostiniManager::GetInstance()->LaunchContainerTerminal( - profile_, kCrostiniDefaultVmName, kCrostiniDefaultContainerName, + crostini::CrostiniManager::GetForProfile(profile_)->LaunchContainerTerminal( + kCrostiniDefaultVmName, kCrostiniDefaultContainerName, std::vector<std::string>()); StepProgress(); @@ -436,11 +457,73 @@ } void CrostiniInstallerView::StepProgress() { + base::TimeDelta time_in_state = base::Time::Now() - state_start_time_; + + VLOG(1) << "state_ = " << static_cast<int>(state_); + + double state_start_mark = 0; + double state_end_mark = 0; + int state_max_seconds = 1; + + switch (state_) { + case State::INSTALL_START: + state_start_mark = 0; + state_end_mark = 0; + break; + case State::INSTALL_IMAGE_LOADER: + state_start_mark = 0.0; + state_end_mark = 0.25; + state_max_seconds = 30; + break; + case State::START_CONCIERGE: + state_start_mark = 0.25; + state_end_mark = 0.26; + break; + case State::CREATE_DISK_IMAGE: + state_start_mark = 0.26; + state_end_mark = 0.27; + break; + case State::START_TERMINA_VM: + state_start_mark = 0.27; + state_end_mark = 0.35; + state_max_seconds = 8; + break; + case State::CREATE_CONTAINER: + state_start_mark = 0.35; + state_end_mark = 0.95; + state_max_seconds = 180; + break; + case State::START_CONTAINER: + state_start_mark = 0.95; + state_end_mark = 0.99; + state_max_seconds = 8; + break; + case State::FETCH_SSH_KEYS: + state_start_mark = 0.99; + state_end_mark = 1; + break; + + default: + break; + } + if (State::INSTALL_START <= state_ && state_ < State::INSTALL_END) { - // Setting value to -1 makes the progress bar play the - // "indeterminate animation". - progress_bar_->SetValue(-1); + double state_fraction = time_in_state.InSecondsF() / state_max_seconds; + + if (state_ == State::CREATE_CONTAINER) { + // In CREATE_CONTAINER, consume half the progress bar with downloading, + // the rest with time. + state_fraction = + 0.5 * (state_fraction + 0.01 * container_download_percent_); + } + VLOG(1) << "start = " << state_start_mark << ", end = " << state_end_mark + << ", fraction = " << state_fraction; + progress_bar_->SetValue(state_start_mark + + base::ClampToRange(state_fraction, 0.0, 1.0) * + (state_end_mark - state_start_mark)); progress_bar_->SetVisible(true); + } else { + progress_bar_->SetVisible(false); } SetMessageLabel(); SetBigMessageLabel(); @@ -448,31 +531,63 @@ GetWidget()->GetRootView()->Layout(); } +void CrostiniInstallerView::UpdateState(State new_state) { + state_start_time_ = base::Time::Now(); + state_ = new_state; + if (state_ == State::INSTALL_START) { + state_progress_timer_ = std::make_unique<base::RepeatingTimer>(); + state_progress_timer_->Start( + FROM_HERE, base::TimeDelta::FromMilliseconds(500), + base::BindRepeating(&CrostiniInstallerView::StepProgress, + weak_ptr_factory_.GetWeakPtr())); + } else if (state_ < State::INSTALL_START || state_ >= State::INSTALL_END) { + if (state_progress_timer_) { + VLOG(1) << "Killing timer, state_ = " << static_cast<int>(state_); + state_progress_timer_->AbandonAndStop(); + } + } +} + void CrostiniInstallerView::SetMessageLabel() { int message_id = 0; // The States below refer to stages that have completed. // The messages selected refer to the next stage, now underway. - if (state_ == State::INSTALL_START) { - message_id = IDS_CROSTINI_INSTALLER_LOAD_TERMINA_MESSAGE; - } else if (state_ == State::INSTALL_IMAGE_LOADER) { - message_id = IDS_CROSTINI_INSTALLER_START_CONCIERGE_MESSAGE; - } else if (state_ == State::START_CONCIERGE) { - message_id = IDS_CROSTINI_INSTALLER_CREATE_DISK_IMAGE_MESSAGE; - } else if (state_ == State::CREATE_DISK_IMAGE) { - message_id = IDS_CROSTINI_INSTALLER_START_TERMINA_VM_MESSAGE; - } else if (state_ == State::START_TERMINA_VM) { - message_id = IDS_CROSTINI_INSTALLER_START_CONTAINER_MESSAGE; - } else if (state_ == State::START_CONTAINER) { - message_id = IDS_CROSTINI_INSTALLER_FETCH_SSH_KEYS_MESSAGE; - } else if (state_ == State::FETCH_SSH_KEYS) { - message_id = IDS_CROSTINI_INSTALLER_MOUNT_CONTAINER_MESSAGE; + switch (state_) { + case State::INSTALL_IMAGE_LOADER: + message_id = IDS_CROSTINI_INSTALLER_LOAD_TERMINA_MESSAGE; + break; + case State::START_CONCIERGE: + message_id = IDS_CROSTINI_INSTALLER_START_CONCIERGE_MESSAGE; + break; + case State::CREATE_DISK_IMAGE: + message_id = IDS_CROSTINI_INSTALLER_CREATE_DISK_IMAGE_MESSAGE; + break; + case State::START_TERMINA_VM: + message_id = IDS_CROSTINI_INSTALLER_START_TERMINA_VM_MESSAGE; + break; + case State::CREATE_CONTAINER: + message_id = IDS_CROSTINI_INSTALLER_START_CONTAINER_MESSAGE; + break; + case State::START_CONTAINER: + message_id = IDS_CROSTINI_INSTALLER_START_CONTAINER_MESSAGE; + break; + case State::FETCH_SSH_KEYS: + message_id = IDS_CROSTINI_INSTALLER_FETCH_SSH_KEYS_MESSAGE; + break; + case State::MOUNT_CONTAINER: + message_id = IDS_CROSTINI_INSTALLER_MOUNT_CONTAINER_MESSAGE; + break; + default: + break; } - if (message_id != 0) { - message_label_->SetText(l10n_util::GetStringUTF16(message_id)); - message_label_->SetVisible(true); - } else { + + if (message_id == 0) { message_label_->SetVisible(false); + return; } + + message_label_->SetText(l10n_util::GetStringUTF16(message_id)); + message_label_->SetVisible(true); } void CrostiniInstallerView::SetBigMessageLabel() {
diff --git a/chrome/browser/ui/views/crostini/crostini_installer_view.h b/chrome/browser/ui/views/crostini/crostini_installer_view.h index c674c4a..a50a53df 100644 --- a/chrome/browser/ui/views/crostini/crostini_installer_view.h +++ b/chrome/browser/ui/views/crostini/crostini_installer_view.h
@@ -7,6 +7,8 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" +#include "base/time/time.h" +#include "base/timer/timer.h" #include "chrome/browser/chromeos/crostini/crostini_manager.h" #include "chrome/browser/component_updater/cros_component_installer_chromeos.h" #include "ui/views/controls/link_listener.h" @@ -68,6 +70,8 @@ void OnConciergeStarted(crostini::ConciergeClientResult result) override; void OnDiskImageCreated(crostini::ConciergeClientResult result) override; void OnVmStarted(crostini::ConciergeClientResult result) override; + void OnContainerDownloading(int32_t download_percent) override; + void OnContainerCreated(crostini::ConciergeClientResult result) override; void OnContainerStarted(crostini::ConciergeClientResult result) override; void OnSshKeysFetched(crostini::ConciergeClientResult result) override; @@ -83,6 +87,7 @@ START_CONCIERGE, // Starting the Concierge D-Bus client. CREATE_DISK_IMAGE, // Creating the image for the Termina VM. START_TERMINA_VM, // Starting the Termina VM. + CREATE_CONTAINER, // Creating the container inside the Termina VM. START_CONTAINER, // Starting the container inside the Termina VM. FETCH_SSH_KEYS, // Fetch ssh keys from concierge. MOUNT_CONTAINER, // Do sshfs mount of container. @@ -97,6 +102,7 @@ void MountContainerFinished(crostini::ConciergeClientResult result); void ShowLoginShell(); void StepProgress(); + void UpdateState(State new_state); void SetMessageLabel(); void SetBigMessageLabel(); @@ -112,6 +118,9 @@ Profile* profile_; crostini::CrostiniManager::RestartId restart_id_ = crostini::CrostiniManager::kUninitializedRestartId; + int32_t container_download_percent_ = 0; + base::Time state_start_time_; + std::unique_ptr<base::RepeatingTimer> state_progress_timer_; // Whether the result has been logged or not is stored to prevent multiple // results being logged for a given setup flow. This can happen due to
diff --git a/chrome/browser/ui/views/crostini/crostini_installer_view_browsertest.cc b/chrome/browser/ui/views/crostini/crostini_installer_view_browsertest.cc index ce03308..ec51430 100644 --- a/chrome/browser/ui/views/crostini/crostini_installer_view_browsertest.cc +++ b/chrome/browser/ui/views/crostini/crostini_installer_view_browsertest.cc
@@ -7,7 +7,6 @@ #include "base/metrics/histogram_base.h" #include "base/run_loop.h" #include "base/test/metrics/histogram_tester.h" -#include "chrome/browser/chromeos/crostini/crostini_manager.h" #include "chrome/browser/chromeos/crostini/crostini_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/app_list/app_list_client_impl.h" @@ -19,7 +18,6 @@ #include "chrome/test/base/in_process_browser_test.h" #include "chromeos/dbus/cros_disks_client.h" #include "chromeos/dbus/dbus_thread_manager.h" -#include "chromeos/dbus/fake_cicerone_client.h" #include "chromeos/dbus/fake_concierge_client.h" #include "chromeos/dbus/fake_cros_disks_client.h" #include "chromeos/disks/disk_mount_manager.h" @@ -38,16 +36,8 @@ const vm_tools::concierge::StartVmRequest& request, chromeos::DBusMethodCallback<vm_tools::concierge::StartVmResponse> callback) override { - signal_.set_owner_id(request.owner_id()); - signal_.set_vm_name(request.name()); - chromeos::FakeConciergeClient::StartTerminaVm(request, std::move(callback)); - - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&WaitingFakeConciergeClient::OnTremplinStarted, - base::Unretained(this))); if (closure_) { base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(closure_)); @@ -62,87 +52,9 @@ } private: - void OnTremplinStarted() { - crostini::CrostiniManager::GetInstance()->OnTremplinStarted(signal_); - } - - vm_tools::cicerone::TremplinStartedSignal signal_; base::OnceClosure closure_; }; - // TODO(timloh): Consider moving this logic into the fake itself. - class SignalingFakeCiceroneClient : public chromeos::FakeCiceroneClient { - public: - void CreateLxdContainer( - const vm_tools::cicerone::CreateLxdContainerRequest& request, - chromeos::DBusMethodCallback< - vm_tools::cicerone::CreateLxdContainerResponse> callback) override { - lxd_container_created_signal_.set_owner_id(request.owner_id()); - lxd_container_created_signal_.set_vm_name(request.vm_name()); - lxd_container_created_signal_.set_container_name( - request.container_name()); - - chromeos::FakeCiceroneClient::CreateLxdContainer( - request, - base::BindOnce(&SignalingFakeCiceroneClient::OnLxdContainerCreated, - base::Unretained(this), std::move(callback))); - } - - void SetUpLxdContainerUser( - const vm_tools::cicerone::SetUpLxdContainerUserRequest& request, - chromeos::DBusMethodCallback< - vm_tools::cicerone::SetUpLxdContainerUserResponse> callback) - override { - container_started_signal_.set_owner_id(request.owner_id()); - container_started_signal_.set_vm_name(request.vm_name()); - container_started_signal_.set_container_name(request.container_name()); - - chromeos::FakeCiceroneClient::SetUpLxdContainerUser( - request, - base::BindOnce(&SignalingFakeCiceroneClient::OnSetUpLxdContainerUser, - base::Unretained(this), std::move(callback))); - } - - private: - void OnLxdContainerCreated( - chromeos::DBusMethodCallback< - vm_tools::cicerone::CreateLxdContainerResponse> callback, - base::Optional<vm_tools::cicerone::CreateLxdContainerResponse> reply) { - std::move(callback).Run(reply); - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce( - &SignalingFakeCiceroneClient::SendLxdContainerCreatedSignal, - base::Unretained(this))); - } - - void OnSetUpLxdContainerUser( - chromeos::DBusMethodCallback< - vm_tools::cicerone::SetUpLxdContainerUserResponse> callback, - base::Optional<vm_tools::cicerone::SetUpLxdContainerUserResponse> - reply) { - std::move(callback).Run(reply); - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce( - &SignalingFakeCiceroneClient::SendContainerStartedSignal, - base::Unretained(this))); - } - - void SendLxdContainerCreatedSignal() { - crostini::CrostiniManager::GetInstance()->OnLxdContainerCreated( - lxd_container_created_signal_); - } - - void SendContainerStartedSignal() { - crostini::CrostiniManager::GetInstance()->OnContainerStarted( - container_started_signal_); - } - - vm_tools::cicerone::LxdContainerCreatedSignal lxd_container_created_signal_; - vm_tools::cicerone::ContainerStartedSignal container_started_signal_; - }; - class WaitingDiskMountManagerObserver : public chromeos::disks::DiskMountManager::Observer { public: @@ -165,13 +77,10 @@ CrostiniInstallerViewBrowserTest() : waiting_fake_concierge_client_(new WaitingFakeConciergeClient()), - signaling_fake_cicerone_client_(new SignalingFakeCiceroneClient()), waiting_disk_mount_manager_observer_( new WaitingDiskMountManagerObserver) { chromeos::DBusThreadManager::GetSetterForTesting()->SetConciergeClient( base::WrapUnique(waiting_fake_concierge_client_)); - chromeos::DBusThreadManager::GetSetterForTesting()->SetCiceroneClient( - base::WrapUnique(signaling_fake_cicerone_client_)); static_cast<chromeos::FakeCrosDisksClient*>( chromeos::DBusThreadManager::Get()->GetCrosDisksClient()) ->AddCustomMountPointCallback(base::BindRepeating( @@ -179,12 +88,16 @@ base::Unretained(this))); } - // DialogBrowserTest: + // CrostiniDialogBrowserTest: void ShowUi(const std::string& name) override { ShowCrostiniInstallerView(browser()->profile(), CrostiniUISurface::kSettings); } + void SetUpOnMainThread() override { + CrostiniDialogBrowserTest::SetUpOnMainThread(); + } + CrostiniInstallerView* ActiveView() { return CrostiniInstallerView::GetActiveViewForTesting(); } @@ -200,7 +113,6 @@ protected: // Owned by chromeos::DBusThreadManager WaitingFakeConciergeClient* waiting_fake_concierge_client_ = nullptr; - SignalingFakeCiceroneClient* signaling_fake_cicerone_client_ = nullptr; WaitingDiskMountManagerObserver* waiting_disk_mount_manager_observer_ = nullptr;
diff --git a/chrome/browser/ui/views/crostini/crostini_uninstaller_view.cc b/chrome/browser/ui/views/crostini/crostini_uninstaller_view.cc index 2caa4ef..031dc5c8 100644 --- a/chrome/browser/ui/views/crostini/crostini_uninstaller_view.cc +++ b/chrome/browser/ui/views/crostini/crostini_uninstaller_view.cc
@@ -84,8 +84,8 @@ l10n_util::GetStringUTF16(IDS_CROSTINI_UNINSTALLER_UNINSTALLING_MESSAGE)); // Kick off the Crostini Remove sequence. - crostini::CrostiniManager::GetInstance()->RemoveCrostini( - profile_, kCrostiniDefaultVmName, kCrostiniDefaultContainerName, + crostini::CrostiniManager::GetForProfile(profile_)->RemoveCrostini( + kCrostiniDefaultVmName, kCrostiniDefaultContainerName, base::BindOnce(&CrostiniUninstallerView::UninstallCrostiniFinished, weak_ptr_factory_.GetWeakPtr()));
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc index cb9e7258..8a0c10e52 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
@@ -57,6 +57,7 @@ #include "chrome/browser/ui/views/location_bar/content_setting_image_view.h" #include "chrome/browser/ui/views/location_bar/zoom_bubble_view.h" #include "chrome/browser/ui/views/page_action/page_action_icon_container_view.h" +#include "chrome/browser/ui/views/page_info/page_info_bubble_view_base.h" #include "chrome/browser/ui/views/profiles/profile_indicator_icon.h" #include "chrome/browser/ui/views/tabs/tab.h" #include "chrome/browser/ui/views/tabs/tab_strip.h" @@ -835,6 +836,32 @@ } // namespace +// Tests that the page info dialog doesn't anchor in a way that puts it outside +// of hosted app windows. This is important as some platforms don't support +// bubble anchor adjustment (see |BubbleDialogDelegateView::CreateBubble()|). +IN_PROC_BROWSER_TEST_P(HostedAppNonClientFrameViewAshTest, + PageInfoBubblePosition) { + // Resize app window to only take up the left half of the screen. + views::Widget* widget = browser_view_->GetWidget(); + gfx::Size screen_size = + display::Screen::GetScreen() + ->GetDisplayNearestWindow(widget->GetNativeWindow()) + .work_area_size(); + widget->SetBounds( + gfx::Rect(0, 0, screen_size.width() / 2, screen_size.height())); + + // Show page info dialog (currently PWAs use page info in place of an actual + // app info dialog). + chrome::ExecuteCommand(app_browser_, IDC_HOSTED_APP_MENU_APP_INFO); + + // Check the bubble anchors inside the main app window even if there's space + // available outside the main app window. + gfx::Rect page_info_bounds = PageInfoBubbleViewBase::GetPageInfoBubble() + ->GetWidget() + ->GetWindowBoundsInScreen(); + EXPECT_TRUE(widget->GetWindowBoundsInScreen().Contains(page_info_bounds)); +} + IN_PROC_BROWSER_TEST_P(HostedAppNonClientFrameViewAshTest, FocusableViews) { EXPECT_TRUE(browser_view_->contents_web_view()->HasFocus()); browser_view_->GetFocusManager()->AdvanceFocus(false);
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc index 78b6397..d109053 100644 --- a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc +++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
@@ -77,8 +77,9 @@ #include "chrome/browser/safe_browsing/chrome_password_protection_service.h" #endif +using bubble_anchor_util::AnchorConfiguration; using bubble_anchor_util::GetPageInfoAnchorRect; -using bubble_anchor_util::GetPageInfoAnchorView; +using bubble_anchor_util::GetPageInfoAnchorConfiguration; namespace { @@ -922,14 +923,16 @@ security_info, anchor); } #endif - views::View* anchor_view = GetPageInfoAnchorView(browser, anchor); + AnchorConfiguration configuration = + GetPageInfoAnchorConfiguration(browser, anchor); gfx::Rect anchor_rect = - anchor_view ? gfx::Rect() : GetPageInfoAnchorRect(browser); + configuration.anchor_view ? gfx::Rect() : GetPageInfoAnchorRect(browser); gfx::NativeWindow parent_window = browser->window()->GetNativeWindow(); views::BubbleDialogDelegateView* bubble = PageInfoBubbleView::CreatePageInfoBubble( - anchor_view, anchor_rect, parent_window, browser->profile(), - web_contents, virtual_url, security_info); + configuration.anchor_view, anchor_rect, parent_window, + browser->profile(), web_contents, virtual_url, security_info); + bubble->set_arrow(configuration.bubble_arrow); BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser); auto* location_bar = browser_view->GetLocationBarView(); if (location_bar)
diff --git a/chrome/browser/ui/views/permission_bubble/chooser_bubble_ui.cc b/chrome/browser/ui/views/permission_bubble/chooser_bubble_ui.cc index a30a079..b9bea6a5 100644 --- a/chrome/browser/ui/views/permission_bubble/chooser_bubble_ui.cc +++ b/chrome/browser/ui/views/permission_bubble/chooser_bubble_ui.cc
@@ -17,13 +17,12 @@ #include "ui/views/controls/table/table_view_observer.h" #include "ui/views/window/dialog_client_view.h" +using bubble_anchor_util::AnchorConfiguration; + namespace { -constexpr views::BubbleBorder::Arrow kChooserAnchorArrow = - views::BubbleBorder::TOP_LEFT; - -views::View* GetChooserAnchorView(Browser* browser) { - return bubble_anchor_util::GetPageInfoAnchorView(browser); +AnchorConfiguration GetChooserAnchorConfiguration(Browser* browser) { + return bubble_anchor_util::GetPageInfoAnchorConfiguration(browser); } gfx::Rect GetChooserAnchorRect(Browser* browser) { @@ -79,9 +78,7 @@ ChooserBubbleUiViewDelegate::ChooserBubbleUiViewDelegate( Browser* browser, std::unique_ptr<ChooserController> chooser_controller) - : views::BubbleDialogDelegateView(GetChooserAnchorView(browser), - kChooserAnchorArrow), - device_chooser_content_view_(nullptr) { + : device_chooser_content_view_(nullptr) { // ------------------------------------ // | Chooser bubble title | // | -------------------------------- | @@ -99,8 +96,7 @@ device_chooser_content_view_ = new DeviceChooserContentView(this, std::move(chooser_controller)); - if (!GetAnchorView()) - SetAnchorRect(GetChooserAnchorRect(browser)); + UpdateAnchor(browser); chrome::RecordDialogCreation(chrome::DialogIdentifier::CHOOSER_UI); } @@ -166,10 +162,11 @@ } void ChooserBubbleUiViewDelegate::UpdateAnchor(Browser* browser) { - views::View* anchor_view = GetChooserAnchorView(browser); - SetAnchorView(anchor_view); - if (!anchor_view) + AnchorConfiguration configuration = GetChooserAnchorConfiguration(browser); + SetAnchorView(configuration.anchor_view); + if (!configuration.anchor_view) SetAnchorRect(GetChooserAnchorRect(browser)); + set_arrow(configuration.bubble_arrow); } void ChooserBubbleUiViewDelegate::set_bubble_reference(
diff --git a/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc b/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc index 55a49cb..d8b0c84 100644 --- a/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc +++ b/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc
@@ -37,18 +37,17 @@ #include "chrome/common/chrome_features.h" #endif +using bubble_anchor_util::AnchorConfiguration; + namespace { // (Square) pixel size of icon. constexpr int kPermissionIconSize = 18; -// The type of arrow to display on the permission bubble. -constexpr views::BubbleBorder::Arrow kPermissionAnchorArrow = - views::BubbleBorder::TOP_LEFT; - -// Returns the view to anchor the permission bubble to. May be null. -views::View* GetPermissionAnchorView(Browser* browser) { - return bubble_anchor_util::GetPageInfoAnchorView(browser); +// Returns the view to anchor the permission bubble to (may be null) and the +// arrow position of the bubble. +AnchorConfiguration GetPermissionAnchorConfiguration(Browser* browser) { + return bubble_anchor_util::GetPageInfoAnchorConfiguration(browser); } // Returns the anchor rect to anchor the permission bubble to, as a fallback. @@ -104,7 +103,6 @@ DCHECK(!requests.empty()); set_close_on_deactivate(false); - set_arrow(kPermissionAnchorArrow); #if defined(OS_MACOSX) // On Mac, the browser UI flips depending on a runtime feature. TODO(tapted): @@ -237,10 +235,12 @@ } void PermissionsBubbleDialogDelegateView::UpdateAnchor() { - views::View* anchor_view = GetPermissionAnchorView(owner_->browser()); - SetAnchorView(anchor_view); - if (!anchor_view) + AnchorConfiguration configuration = + GetPermissionAnchorConfiguration(owner_->browser()); + SetAnchorView(configuration.anchor_view); + if (!configuration.anchor_view) SetAnchorRect(GetPermissionAnchorRect(owner_->browser())); + set_arrow(configuration.bubble_arrow); } //////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc b/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc index cf29c54..9900d621 100644 --- a/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc +++ b/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc
@@ -336,8 +336,7 @@ return; updating_crostini_size_ = true; - crostini::CrostiniManager::GetInstance()->ListVmDisks( - CryptohomeIdForProfile(profile_), + crostini::CrostiniManager::GetForProfile(profile_)->ListVmDisks( base::BindOnce(&StorageHandler::OnGetCrostiniSize, weak_ptr_factory_.GetWeakPtr())); }
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler.cc b/chrome/browser/ui/webui/signin/inline_login_handler.cc index 0738564..0af8570 100644 --- a/chrome/browser/ui/webui/signin/inline_login_handler.cc +++ b/chrome/browser/ui/webui/signin/inline_login_handler.cc
@@ -137,11 +137,10 @@ if (!default_email.empty()) params.SetString("email", default_email); - std::string is_constrained; - net::GetValueForKeyInQuery( - current_url, signin::kSignInPromoQueryKeyConstrained, &is_constrained); - if (!is_constrained.empty()) - params.SetString(signin::kSignInPromoQueryKeyConstrained, is_constrained); + // The legacy full-tab Chrome sign-in page is no longer used as it was relying + // on exchanging cookies for refresh tokens and that endpoint is no longer + // supported. + params.SetString("constrained", "1"); // TODO(rogerta): this needs to be passed on to gaia somehow. std::string read_only_email; @@ -180,14 +179,6 @@ main_frame_url, kSignInPromoQueryKeyShowAccountManagement, "1"); main_frame_url = net::AppendOrReplaceQueryParameter( main_frame_url, signin::kSignInPromoQueryKeyForceKeepData, "1"); - if (base::FeatureList::IsEnabled( - features::kRemoveUsageOfDeprecatedGaiaSigninEndpoint)) { - main_frame_url = net::AppendOrReplaceQueryParameter( - main_frame_url, signin::kSignInPromoQueryKeyConstrained, "1"); - } else { - main_frame_url = net::AppendOrReplaceQueryParameter( - main_frame_url, signin::kSignInPromoQueryKeyConstrained, "0"); - } NavigateParams params(profile, main_frame_url, ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc b/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc index e332323..a741418 100644 --- a/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc +++ b/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
@@ -166,7 +166,6 @@ const std::string& email, const std::string& gaia_id, const std::string& password, - const std::string& session_index, const std::string& auth_code, const std::string& signin_scoped_device_id, bool choose_what_to_sync, @@ -182,22 +181,16 @@ email_(email), gaia_id_(gaia_id), password_(password), - session_index_(session_index), auth_code_(auth_code), choose_what_to_sync_(choose_what_to_sync), confirm_untrusted_signin_(confirm_untrusted_signin), is_force_sign_in_with_usermanager_(is_force_sign_in_with_usermanager) { DCHECK(profile_); DCHECK(!email_.empty()); - if (!auth_code_.empty()) { - gaia_auth_fetcher_.StartAuthCodeForOAuth2TokenExchangeWithDeviceId( - auth_code, signin_scoped_device_id); - } else { - DCHECK(!session_index_.empty()); - gaia_auth_fetcher_ - .DeprecatedStartCookieForOAuthLoginTokenExchangeWithDeviceId( - session_index_, signin_scoped_device_id); - } + DCHECK(!auth_code_.empty()); + + gaia_auth_fetcher_.StartAuthCodeForOAuth2TokenExchangeWithDeviceId( + auth_code_, signin_scoped_device_id); } InlineSigninHelper::~InlineSigninHelper() {} @@ -463,11 +456,6 @@ signin_metrics::Reason reason = signin::GetSigninReasonForPromoURL(current_url); - std::string is_constrained; - net::GetValueForKeyInQuery(current_url, "constrained", &is_constrained); - - // Use new embedded flow if in constrained window. - if (is_constrained == "1") { const GURL& url = GaiaUrls::GetInstance()->embedded_signin_url(); params.SetBoolean("isNewGaiaFlow", true); params.SetString("clientId", @@ -491,12 +479,9 @@ break; } params.SetString("flow", flow); - } content::WebContentsObserver::Observe(contents); LogHistogramValue(signin_metrics::HISTOGRAM_SHOWN); - UMA_HISTOGRAM_BOOLEAN("Signin.UseDeprecatedGaiaSigninEndpoint", - is_constrained == "1"); } void InlineLoginHandlerImpl::CompleteLogin(const base::ListValue* args) { @@ -533,19 +518,10 @@ DCHECK(!gaia_id_string16.empty()); std::string gaia_id = base::UTF16ToASCII(gaia_id_string16); - std::string is_constrained; - net::GetValueForKeyInQuery(current_url, "constrained", &is_constrained); - const bool is_password_separated_signin_flow = is_constrained == "1"; - - base::string16 session_index_string16; - dict->GetString("sessionIndex", &session_index_string16); - std::string session_index = base::UTF16ToASCII(session_index_string16); - DCHECK(is_password_separated_signin_flow || !session_index.empty()); - base::string16 auth_code_string16; dict->GetString("authCode", &auth_code_string16); std::string auth_code = base::UTF16ToASCII(auth_code_string16); - DCHECK(!is_password_separated_signin_flow || !auth_code.empty()); + DCHECK(!auth_code.empty()); bool choose_what_to_sync = false; dict->GetBoolean("chooseWhatToSync", &choose_what_to_sync); @@ -573,8 +549,8 @@ if (reason == signin_metrics::Reason::REASON_REAUTHENTICATION) { FinishCompleteLoginParams params( this, partition, current_url, base::FilePath(), - confirm_untrusted_signin_, email, gaia_id, password, session_index, - auth_code, choose_what_to_sync, false); + confirm_untrusted_signin_, email, gaia_id, password, auth_code, + choose_what_to_sync, false); ProfileManager::CreateCallback callback = base::Bind(&InlineLoginHandlerImpl::FinishCompleteLogin, params); profiles::LoadProfileAsync(path, callback); @@ -588,8 +564,8 @@ handler = this; FinishCompleteLoginParams params( handler, partition, current_url, path, confirm_untrusted_signin_, - email, gaia_id, password, session_index, auth_code, - choose_what_to_sync, is_force_signin_enabled); + email, gaia_id, password, auth_code, choose_what_to_sync, + is_force_signin_enabled); ProfileManager::CreateCallback callback = base::Bind(&InlineLoginHandlerImpl::FinishCompleteLogin, params); if (is_force_signin_enabled) { @@ -602,12 +578,11 @@ } } } else { - FinishCompleteLogin( - FinishCompleteLoginParams(this, partition, current_url, - base::FilePath(), confirm_untrusted_signin_, - email, gaia_id, password, session_index, - auth_code, choose_what_to_sync, false), - profile, Profile::CREATE_STATUS_CREATED); + FinishCompleteLogin(FinishCompleteLoginParams( + this, partition, current_url, base::FilePath(), + confirm_untrusted_signin_, email, gaia_id, password, + auth_code, choose_what_to_sync, false), + profile, Profile::CREATE_STATUS_CREATED); } } @@ -620,7 +595,6 @@ const std::string& email, const std::string& gaia_id, const std::string& password, - const std::string& session_index, const std::string& auth_code, bool choose_what_to_sync, bool is_force_sign_in_with_usermanager) @@ -632,7 +606,6 @@ email(email), gaia_id(gaia_id), password(password), - session_index(session_index), auth_code(auth_code), choose_what_to_sync(choose_what_to_sync), is_force_sign_in_with_usermanager(is_force_sign_in_with_usermanager) {} @@ -727,8 +700,8 @@ handler_weak_ptr, params.partition->GetURLLoaderFactoryForBrowserProcess(), profile, status, params.url, params.email, params.gaia_id, params.password, - params.session_index, params.auth_code, signin_scoped_device_id, - params.choose_what_to_sync, params.confirm_untrusted_signin, + params.auth_code, signin_scoped_device_id, params.choose_what_to_sync, + params.confirm_untrusted_signin, params.is_force_sign_in_with_usermanager); // If opened from user manager to unlock a profile, make sure the user manager
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_impl.h b/chrome/browser/ui/webui/signin/inline_login_handler_impl.h index fb2d86f..dd143eb 100644 --- a/chrome/browser/ui/webui/signin/inline_login_handler_impl.h +++ b/chrome/browser/ui/webui/signin/inline_login_handler_impl.h
@@ -64,7 +64,6 @@ const std::string& email, const std::string& gaia_id, const std::string& password, - const std::string& session_index, const std::string& auth_code, bool choose_what_to_sync, bool is_force_sign_in_with_usermanager); @@ -89,9 +88,6 @@ std::string gaia_id; // Password of the account used to sign in. std::string password; - // Index within gaia cookie of the account used to sign in. Used only - // with password combined signin flow. - std::string session_index; // Authentication code used to exchange for a login scoped refresh token // for the account used to sign in. Used only with password separated // signin flow. @@ -136,7 +132,6 @@ const std::string& email, const std::string& gaia_id, const std::string& password, - const std::string& session_index, const std::string& auth_code, const std::string& signin_scoped_device_id, bool choose_what_to_sync, @@ -192,7 +187,6 @@ std::string email_; std::string gaia_id_; std::string password_; - std::string session_index_; std::string auth_code_; bool choose_what_to_sync_; bool confirm_untrusted_signin_;
diff --git a/chrome/browser/ui/webui/signin/inline_login_ui_browsertest.cc b/chrome/browser/ui/webui/signin/inline_login_ui_browsertest.cc index 0fd4b89..2be52a36 100644 --- a/chrome/browser/ui/webui/signin/inline_login_ui_browsertest.cc +++ b/chrome/browser/ui/webui/signin/inline_login_ui_browsertest.cc
@@ -161,7 +161,6 @@ const std::string& email, const std::string& gaia_id, const std::string& password, - const std::string& session_index, const std::string& auth_code, const std::string& signin_scoped_device_id, bool choose_what_to_sync, @@ -191,7 +190,6 @@ const std::string& email, const std::string& gaia_id, const std::string& password, - const std::string& session_index, const std::string& auth_code, const std::string& signin_scoped_device_id, bool choose_what_to_sync, @@ -204,7 +202,6 @@ email, gaia_id, password, - session_index, auth_code, signin_scoped_device_id, choose_what_to_sync, @@ -223,7 +220,6 @@ const std::string& email, const std::string& gaia_id, const std::string& password, - const std::string& session_index, const std::string& auth_code, const std::string& signin_scoped_device_id, bool choose_what_to_sync, @@ -250,7 +246,6 @@ const std::string& email, const std::string& gaia_id, const std::string& password, - const std::string& session_index, const std::string& auth_code, const std::string& signin_scoped_device_id, bool choose_what_to_sync, @@ -264,7 +259,6 @@ email, gaia_id, password, - session_index, auth_code, signin_scoped_device_id, choose_what_to_sync, @@ -500,13 +494,6 @@ host_resolver()->AddRule("*", "127.0.0.1"); - deprecated_client_login_to_oauth2_response_ = - std::make_unique<net::test_server::ControllableHttpResponse>( - embedded_test_server(), - GaiaUrls::GetInstance() - ->deprecated_client_login_to_oauth2_url() - .path(), - /*relative_url_is_prefix=*/true); oauth2_token_exchange_success_ = std::make_unique<net::test_server::ControllableHttpResponse>( embedded_test_server(), @@ -526,14 +513,6 @@ ASSERT_TRUE(token_service_); } - void SimulateStartCookieForOAuthLoginTokenExchangeSuccess( - const std::string& cookie_string) { - deprecated_client_login_to_oauth2_response_->WaitForRequest(); - deprecated_client_login_to_oauth2_response_->Send( - net::HTTP_OK, "text/html; charset=utf-8", "", {cookie_string}); - deprecated_client_login_to_oauth2_response_->Done(); - } - void SimulateStartAuthCodeForOAuth2TokenExchangeSuccess( const std::string& json_response) { oauth2_token_exchange_success_->WaitForRequest(); @@ -559,8 +538,6 @@ protected: std::unique_ptr<net::test_server::ControllableHttpResponse> - deprecated_client_login_to_oauth2_response_; - std::unique_ptr<net::test_server::ControllableHttpResponse> oauth2_token_exchange_success_; private: @@ -573,34 +550,6 @@ DISALLOW_COPY_AND_ASSIGN(InlineLoginHelperBrowserTest); }; -// Test signin helper calls correct fetcher methods when called with a session -// index. -IN_PROC_BROWSER_TEST_F(InlineLoginHelperBrowserTest, WithSessionIndex) { - base::WeakPtr<InlineLoginHandlerImpl> handler; - MockInlineSigninHelper helper(handler, test_shared_loader_factory(), - browser()->profile(), GURL(), "foo@gmail.com", - "gaiaid-12345", "password", - "0", // session index from above - std::string(), // auth code - std::string(), - false, // choose what to sync - false); // confirm untrusted signin - base::RunLoop run_loop; - EXPECT_CALL(helper, OnClientOAuthSuccess(_)) - .WillOnce(testing::InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); })); - - SimulateStartCookieForOAuthLoginTokenExchangeSuccess( - "oauth_code=code; secure; httponly"); - - SimulateStartAuthCodeForOAuth2TokenExchangeSuccess( - R"({ - "access_token": "access_token", - "expires_in": 1234567890, - "refresh_token": "refresh_token" - })"); - run_loop.Run(); -} - // Test signin helper calls correct fetcher methods when called with an // auth code. IN_PROC_BROWSER_TEST_F(InlineLoginHelperBrowserTest, WithAuthCode) { @@ -608,7 +557,6 @@ MockInlineSigninHelper helper(handler, test_shared_loader_factory(), browser()->profile(), GURL(), "foo@gmail.com", "gaiaid-12345", "password", - "", // session index "auth_code", // auth code std::string(), false, // choose what to sync @@ -645,7 +593,6 @@ ->GetURLLoaderFactoryForBrowserProcess(), browser()->profile(), url, "foo@gmail.com", "gaiaid-12345", "password", - "", // session index "auth_code", // auth code std::string(), false, // choose what to sync @@ -689,7 +636,6 @@ new MockSyncStarterInlineSigninHelper( handler, test_shared_loader_factory(), browser()->profile(), url, "foo@gmail.com", "gaiaid-12345", "password", - "", // session index "auth_code", // auth code std::string(), true, // choose what to sync @@ -720,7 +666,6 @@ new MockSyncStarterInlineSigninHelper( handler, test_shared_loader_factory(), browser()->profile(), url, "foo@gmail.com", "gaiaid-12345", "password", - "", // session index "auth_code", // auth code std::string(), false, // choose what to sync @@ -752,7 +697,6 @@ new MockSyncStarterInlineSigninHelper( handler, test_shared_loader_factory(), browser()->profile(), url, "foo@gmail.com", "gaiaid-12345", "password", - "", // session index "auth_code", // auth code std::string(), false, // choose what to sync @@ -784,7 +728,6 @@ browser()->profile(), Profile::CreateStatus::CREATE_STATUS_INITIALIZED, url, "foo@gmail.com", "gaiaid-12345", "password", - "", // session index "auth_code", // auth code std::string(), false, // choose what to sync @@ -809,7 +752,6 @@ browser()->profile(), Profile::CreateStatus::CREATE_STATUS_INITIALIZED, url, "foo@gmail.com", "gaiaid-12345", "password", - "", // session index "auth_code", // auth code std::string(), false, // choose what to sync @@ -830,7 +772,7 @@ MockSyncStarterInlineSigninHelper* helper = new MockSyncStarterInlineSigninHelper( handler, test_shared_loader_factory(), browser()->profile(), url, - "foo@gmail.com", "gaiaid-12345", "password", "", "auth_code", + "foo@gmail.com", "gaiaid-12345", "password", "auth_code", std::string(), false, false, true); EXPECT_CALL( *helper,
diff --git a/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc index 0f6c2a75..0b4befc 100644 --- a/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc +++ b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc
@@ -92,29 +92,20 @@ web_app::WebAppProvider::Get(profile())->Reset(); } - // TODO(nigeltao): replace the N SimulateEtc methods with 1 method, that - // derives the extensions::Manifest::Location from app_info.install_source. - void SimulatePreviouslyInstalledApp(PendingAppManager::AppInfo app_info, - extensions::Manifest::Location location = - extensions::Manifest::INTERNAL) { + void SimulatePreviouslyInstalledApp(GURL url, + extensions::Manifest::Location location) { scoped_refptr<extensions::Extension> extension = extensions::ExtensionBuilder("Dummy Name") .SetLocation(location) - .SetID(crx_file::id_util::GenerateId("fake_app_id_for:" + - app_info.url.spec())) + .SetID( + crx_file::id_util::GenerateId("fake_app_id_for:" + url.spec())) .Build(); extensions::ExtensionRegistry* registry = extensions::ExtensionRegistry::Get(profile()); registry->AddEnabled(extension); ExtensionIdsMap extension_ids_map(profile()->GetPrefs()); - extension_ids_map.Insert(app_info.url, extension->id()); - } - - void SimulatePreviouslyInstalledPolicyApp( - PendingAppManager::AppInfo app_info) { - SimulatePreviouslyInstalledApp( - std::move(app_info), extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD); + extension_ids_map.Insert(url, extension->id()); } private: @@ -216,9 +207,12 @@ TEST_F(WebAppPolicyManagerTest, UninstallAppInstalledInPreviousSession) { // Simulate two policy apps and a regular app that were installed in the // previous session. - SimulatePreviouslyInstalledPolicyApp(GetWindowedAppInfo()); - SimulatePreviouslyInstalledPolicyApp(GetTabbedAppInfo()); - SimulatePreviouslyInstalledApp(GetDefaultContainerAppInfo()); + SimulatePreviouslyInstalledApp( + GURL(kWindowedUrl), extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD); + SimulatePreviouslyInstalledApp( + GURL(kTabbedUrl), extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD); + SimulatePreviouslyInstalledApp(GURL(kDefaultContainerUrl), + extensions::Manifest::INTERNAL); // Push a policy with only one of the apps. base::Value first_list(base::Value::Type::LIST);
diff --git a/chrome/browser/web_data_service_factory.cc b/chrome/browser/web_data_service_factory.cc index 7681a62..282cf9d 100644 --- a/chrome/browser/web_data_service_factory.cc +++ b/chrome/browser/web_data_service_factory.cc
@@ -22,10 +22,6 @@ #include "components/webdata_services/web_data_service_wrapper.h" #include "content/public/browser/browser_thread.h" -#if defined(OS_WIN) -#include "components/password_manager/core/browser/webdata/password_web_data_service_win.h" -#endif - using content::BrowserThread; namespace { @@ -151,20 +147,6 @@ : scoped_refptr<TokenWebData>(nullptr); } -#if defined(OS_WIN) -// static -scoped_refptr<PasswordWebDataService> -WebDataServiceFactory::GetPasswordWebDataForProfile( - Profile* profile, - ServiceAccessType access_type) { - WebDataServiceWrapper* wrapper = - WebDataServiceFactory::GetForProfile(profile, access_type); - // |wrapper| can be null in Incognito mode. - return wrapper ? wrapper->GetPasswordWebData() - : scoped_refptr<PasswordWebDataService>(nullptr); -} -#endif - // static scoped_refptr<payments::PaymentManifestWebDataService> WebDataServiceFactory::GetPaymentManifestWebDataForProfile(
diff --git a/chrome/browser/web_data_service_factory.h b/chrome/browser/web_data_service_factory.h index 013bc9b..c13c076fc 100644 --- a/chrome/browser/web_data_service_factory.h +++ b/chrome/browser/web_data_service_factory.h
@@ -21,10 +21,6 @@ class TokenWebData; class WebDataServiceWrapper; -#if defined(OS_WIN) -class PasswordWebDataService; -#endif - namespace payments { class PaymentManifestWebDataService; } @@ -64,13 +60,6 @@ Profile* profile, ServiceAccessType access_type); -#if defined(OS_WIN) - // Returns the PasswordWebDataService associated with the |profile|. - static scoped_refptr<PasswordWebDataService> GetPasswordWebDataForProfile( - Profile* profile, - ServiceAccessType access_type); -#endif - static scoped_refptr<payments::PaymentManifestWebDataService> GetPaymentManifestWebDataForProfile(Profile* profile, ServiceAccessType access_type);
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc index 12073395..eeeef47 100644 --- a/chrome/common/chrome_features.cc +++ b/chrome/common/chrome_features.cc
@@ -495,12 +495,6 @@ const base::Feature kPushMessagingBackgroundMode{ "PushMessagingBackgroundMode", base::FEATURE_DISABLED_BY_DEFAULT}; -#if !defined(OS_ANDROID) -const base::Feature kRemoveUsageOfDeprecatedGaiaSigninEndpoint{ - "RemoveUsageOfDeprecatedGaiaSigninEndpoint", - base::FEATURE_ENABLED_BY_DEFAULT}; -#endif - const base::Feature kSafeSearchUrlReporting{"SafeSearchUrlReporting", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h index 3cae097..84505520 100644 --- a/chrome/common/chrome_features.h +++ b/chrome/common/chrome_features.h
@@ -324,11 +324,6 @@ COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kPushMessagingBackgroundMode; -#if !defined(OS_ANDROID) -COMPONENT_EXPORT(CHROME_FEATURES) -extern const base::Feature kRemoveUsageOfDeprecatedGaiaSigninEndpoint; -#endif - COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kSafeSearchUrlReporting;
diff --git a/chrome/test/chromedriver/chrome/chrome_finder.cc b/chrome/test/chromedriver/chrome/chrome_finder.cc index 9115579..4194f510e 100644 --- a/chrome/test/chromedriver/chrome/chrome_finder.cc +++ b/chrome/test/chromedriver/chrome/chrome_finder.cc
@@ -12,10 +12,13 @@ #include "base/base_paths.h" #include "base/bind.h" #include "base/callback.h" +#include "base/environment.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/macros.h" #include "base/path_service.h" +#include "base/strings/string_split.h" +#include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #if defined(OS_WIN) @@ -64,6 +67,40 @@ } #endif +void GetPathsFromEnvironment(std::vector<base::FilePath>* paths) { + base::FilePath::StringType delimiter; + base::FilePath::StringType commonPath; + std::string path; + std::unique_ptr<base::Environment> env(base::Environment::Create()); + + if (!env->GetVar("PATH", &path)) { + return; + } + +#if defined(OS_WIN) + commonPath = base::UTF8ToWide(path); + delimiter = L";"; +#else + commonPath = path; + delimiter = ":"; +#endif + + std::vector<base::FilePath::StringType> path_entries = base::SplitString( + commonPath, delimiter, base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); + + for (auto& path_entry : path_entries) { +#if defined(OS_WIN) + size_t size = path_entry.size(); + if (size >= 2 && path_entry[0] == '"' && path_entry[size - 1] == '"') { + path_entry.erase(0, 1); + path_entry.erase(size - 2, 1); + } +#endif + if (path_entry.size() > 0) + paths->emplace_back(path_entry); + } +} + } // namespace namespace internal { @@ -124,6 +161,7 @@ std::vector<base::FilePath> locations; GetApplicationDirs(&locations); + GetPathsFromEnvironment(&locations); return internal::FindExe( base::Bind(&base::PathExists), browser_exes,
diff --git a/chrome/test/data/arc_default_apps/test_app2.json b/chrome/test/data/arc_board_default_apps/test_app2.json similarity index 100% rename from chrome/test/data/arc_default_apps/test_app2.json rename to chrome/test/data/arc_board_default_apps/test_app2.json
diff --git a/chrome/test/data/arc_default_apps/test_app2/icon_100p_48.png b/chrome/test/data/arc_board_default_apps/test_app2/icon_100p_48.png similarity index 100% rename from chrome/test/data/arc_default_apps/test_app2/icon_100p_48.png rename to chrome/test/data/arc_board_default_apps/test_app2/icon_100p_48.png Binary files differ
diff --git a/chrome/test/data/arc_default_apps/test_app2/icon_100p_64.png b/chrome/test/data/arc_board_default_apps/test_app2/icon_100p_64.png similarity index 100% rename from chrome/test/data/arc_default_apps/test_app2/icon_100p_64.png rename to chrome/test/data/arc_board_default_apps/test_app2/icon_100p_64.png Binary files differ
diff --git a/chrome/test/data/arc_default_apps/test_app2/icon_200p_48.png b/chrome/test/data/arc_board_default_apps/test_app2/icon_200p_48.png similarity index 100% rename from chrome/test/data/arc_default_apps/test_app2/icon_200p_48.png rename to chrome/test/data/arc_board_default_apps/test_app2/icon_200p_48.png Binary files differ
diff --git a/chrome/test/data/arc_default_apps/test_app2/icon_200p_64.png b/chrome/test/data/arc_board_default_apps/test_app2/icon_200p_64.png similarity index 100% rename from chrome/test/data/arc_default_apps/test_app2/icon_200p_64.png rename to chrome/test/data/arc_board_default_apps/test_app2/icon_200p_64.png Binary files differ
diff --git a/chromecast/media/cma/backend/media_pipeline_backend_for_mixer.cc b/chromecast/media/cma/backend/media_pipeline_backend_for_mixer.cc index dbafa0a..d852198 100644 --- a/chromecast/media/cma/backend/media_pipeline_backend_for_mixer.cc +++ b/chromecast/media/cma/backend/media_pipeline_backend_for_mixer.cc
@@ -79,6 +79,7 @@ audio_ready_to_play_ = !audio_decoder_; first_resume_processed_ = false; + start_playback_timestamp_us_ = INT64_MIN; start_playback_pts_us_ = start_pts; int64_t effective_start_pts = @@ -92,7 +93,11 @@ if (video_decoder_ && !video_decoder_->Start(start_playback_pts_us_, true)) return false; - state_ = kStatePlaying; + if (av_sync_) { + state_ = kStatePaused; + } else { + state_ = kStatePlaying; + } return true; } @@ -111,14 +116,19 @@ } bool MediaPipelineBackendForMixer::Pause() { - DCHECK_EQ(kStatePlaying, state_); - if (!first_resume_processed_) { + if (av_sync_ && !first_resume_processed_) { + DCHECK_EQ(kStatePaused, state_); return true; } - if (audio_decoder_ && !audio_decoder_->Pause()) + + DCHECK_EQ(kStatePlaying, state_); + + if (audio_decoder_ && !audio_decoder_->Pause()) { return false; - if (video_decoder_ && !video_decoder_->Pause()) + } + if (video_decoder_ && !video_decoder_->Pause()) { return false; + } if (av_sync_) { av_sync_->NotifyPause(); } @@ -128,8 +138,8 @@ } bool MediaPipelineBackendForMixer::Resume() { - if (!first_resume_processed_) { - DCHECK_EQ(kStatePlaying, state_); + DCHECK_EQ(kStatePaused, state_); + if (av_sync_ && !first_resume_processed_) { LOG(INFO) << "First resume received."; first_resume_processed_ = true; @@ -137,7 +147,6 @@ return true; } - DCHECK_EQ(kStatePaused, state_); if (av_sync_) { av_sync_->NotifyResume(); } @@ -238,54 +247,52 @@ void MediaPipelineBackendForMixer::OnVideoReadyToPlay() { DCHECK(GetTaskRunner()->RunsTasksInCurrentSequence()); DCHECK(!video_ready_to_play_); + DCHECK(video_decoder_); LOG(INFO) << "Video ready to play"; - video_ready_to_play_ = true; - TryStartPlayback(); -} + if (av_sync_) { + TryStartPlayback(); + } else if (!IsIgnorePtsMode()) { + start_playback_timestamp_us_ = MonotonicClockNow(); + LOG(INFO) << "Starting playback at=" << start_playback_timestamp_us_; -void MediaPipelineBackendForMixer::OnAudioReadyForPlayback() { - LOG(INFO) << "The audio is ready for playback."; - DCHECK(!audio_ready_to_play_); - - audio_ready_to_play_ = true; - - TryStartPlayback(); -} - -void MediaPipelineBackendForMixer::TryStartPlayback() { - DCHECK(state_ == kStatePlaying || state_ == kStatePaused); - - if (av_sync_ && !(audio_ready_to_play_ && first_resume_processed_ && - video_ready_to_play_)) { - return; - } - - if (IsIgnorePtsMode()) { - start_playback_timestamp_us_ = INT64_MIN; - } else { - start_playback_timestamp_us_ = - MonotonicClockNow() + kSyncedPlaybackStartDelayUs; - } - - LOG(INFO) << "Starting playback at=" << start_playback_timestamp_us_; - - // Note that SetPts needs to be called even if the playback is not AV sync'd - // (for example we only have a video stream). - if (video_decoder_ && !IsIgnorePtsMode()) { video_decoder_->SetPts(start_playback_timestamp_us_, start_playback_pts_us_); } +} - if (audio_decoder_ && av_sync_) { - audio_decoder_->StartPlaybackAt(start_playback_timestamp_us_); - } +void MediaPipelineBackendForMixer::OnAudioReadyForPlayback() { + DCHECK(!audio_ready_to_play_); + + LOG(INFO) << "Audio ready to play"; + audio_ready_to_play_ = true; if (av_sync_) { - av_sync_->NotifyStart(start_playback_timestamp_us_, start_playback_pts_us_); + TryStartPlayback(); } +} + +void MediaPipelineBackendForMixer::TryStartPlayback() { + DCHECK(av_sync_); + DCHECK(state_ == kStatePaused); + DCHECK(!IsIgnorePtsMode()); + DCHECK(video_decoder_); + DCHECK(audio_decoder_); + + if (!audio_ready_to_play_ || !video_ready_to_play_ || + !first_resume_processed_) { + return; + } + + start_playback_timestamp_us_ = + MonotonicClockNow() + kSyncedPlaybackStartDelayUs; + LOG(INFO) << "Starting playback at=" << start_playback_timestamp_us_; + + video_decoder_->SetPts(start_playback_timestamp_us_, start_playback_pts_us_); + audio_decoder_->StartPlaybackAt(start_playback_timestamp_us_); + av_sync_->NotifyStart(start_playback_timestamp_us_, start_playback_pts_us_); state_ = kStatePlaying; }
diff --git a/chromeos/dbus/smb_provider_client.cc b/chromeos/dbus/smb_provider_client.cc index f087056..0e34f72 100644 --- a/chromeos/dbus/smb_provider_client.cc +++ b/chromeos/dbus/smb_provider_client.cc
@@ -76,7 +76,7 @@ smbprovider::kMountMethod); dbus::MessageWriter writer(&method_call); writer.AppendProtoAsArrayOfBytes(options); - writer.AppendFileDescriptor(password_fd.release()); + writer.AppendFileDescriptor(password_fd.get()); CallMethod(&method_call, &SmbProviderClientImpl::HandleMountCallback, &callback); } @@ -97,7 +97,7 @@ smbprovider::kRemountMethod); dbus::MessageWriter writer(&method_call); writer.AppendProtoAsArrayOfBytes(options); - writer.AppendFileDescriptor(password_fd.release()); + writer.AppendFileDescriptor(password_fd.get()); CallDefaultMethod(&method_call, &callback); } @@ -214,7 +214,7 @@ smbprovider::kWriteFileMethod); dbus::MessageWriter writer(&method_call); writer.AppendProtoAsArrayOfBytes(options); - writer.AppendFileDescriptor(temp_fd.release()); + writer.AppendFileDescriptor(temp_fd.get()); CallDefaultMethod(&method_call, &callback); }
diff --git a/chromeos/services/device_sync/public/cpp/fake_device_sync_client.cc b/chromeos/services/device_sync/public/cpp/fake_device_sync_client.cc index 33bbe4ad..df114d9 100644 --- a/chromeos/services/device_sync/public/cpp/fake_device_sync_client.cc +++ b/chromeos/services/device_sync/public/cpp/fake_device_sync_client.cc
@@ -17,12 +17,12 @@ void FakeDeviceSyncClient::ForceEnrollmentNow( mojom::DeviceSync::ForceEnrollmentNowCallback callback) { - std::move(callback).Run(force_enrollment_now_success_); + force_enrollment_now_callback_queue_.push(std::move(callback)); } void FakeDeviceSyncClient::ForceSyncNow( mojom::DeviceSync::ForceSyncNowCallback callback) { - std::move(callback).Run(force_sync_now_success_); + force_sync_now_callback_queue_.push(std::move(callback)); } cryptauth::RemoteDeviceRefList FakeDeviceSyncClient::GetSyncedDevices() { @@ -54,6 +54,27 @@ get_debug_info_callback_queue_.push(std::move(callback)); } +int FakeDeviceSyncClient::GetForceEnrollmentNowCallbackQueueSize() { + return force_enrollment_now_callback_queue_.size(); +} + +int FakeDeviceSyncClient::GetForceSyncNowCallbackQueueSize() { + return force_sync_now_callback_queue_.size(); +} + +void FakeDeviceSyncClient::InvokePendingForceEnrollmentNowCallback( + bool success) { + DCHECK(force_enrollment_now_callback_queue_.size() > 0); + std::move(force_enrollment_now_callback_queue_.front()).Run(success); + force_enrollment_now_callback_queue_.pop(); +} + +void FakeDeviceSyncClient::InvokePendingForceSyncNowCallback(bool success) { + DCHECK(force_sync_now_callback_queue_.size() > 0); + std::move(force_sync_now_callback_queue_.front()).Run(success); + force_sync_now_callback_queue_.pop(); +} + void FakeDeviceSyncClient::InvokePendingSetSoftwareFeatureStateCallback( mojom::NetworkRequestResult result_code) { std::move(set_software_feature_state_callback_queue_.front())
diff --git a/chromeos/services/device_sync/public/cpp/fake_device_sync_client.h b/chromeos/services/device_sync/public/cpp/fake_device_sync_client.h index 1bd60e84..7257604 100644 --- a/chromeos/services/device_sync/public/cpp/fake_device_sync_client.h +++ b/chromeos/services/device_sync/public/cpp/fake_device_sync_client.h
@@ -27,6 +27,10 @@ FakeDeviceSyncClient(); ~FakeDeviceSyncClient() override; + int GetForceEnrollmentNowCallbackQueueSize(); + int GetForceSyncNowCallbackQueueSize(); + void InvokePendingForceEnrollmentNowCallback(bool success); + void InvokePendingForceSyncNowCallback(bool success); void InvokePendingSetSoftwareFeatureStateCallback( mojom::NetworkRequestResult result_code); void InvokePendingFindEligibleDevicesCallback( @@ -35,14 +39,6 @@ cryptauth::RemoteDeviceRefList ineligible_devices); void InvokePendingGetDebugInfoCallback(mojom::DebugInfoPtr debug_info_ptr); - void set_force_enrollment_now_success(bool force_enrollment_now_success) { - force_enrollment_now_success_ = force_enrollment_now_success; - } - - void set_force_sync_now_success(bool force_sync_now_success) { - force_sync_now_success_ = force_sync_now_success; - } - void set_synced_devices(cryptauth::RemoteDeviceRefList synced_devices) { synced_devices_ = synced_devices; } @@ -52,9 +48,9 @@ local_device_metadata_ = local_device_metadata; } - using DeviceSyncClient::NotifyReady; using DeviceSyncClient::NotifyEnrollmentFinished; using DeviceSyncClient::NotifyNewDevicesSynced; + using DeviceSyncClient::NotifyReady; private: // DeviceSyncClient: @@ -73,11 +69,13 @@ FindEligibleDevicesCallback callback) override; void GetDebugInfo(mojom::DeviceSync::GetDebugInfoCallback callback) override; - bool force_enrollment_now_success_; - bool force_sync_now_success_; cryptauth::RemoteDeviceRefList synced_devices_; base::Optional<cryptauth::RemoteDeviceRef> local_device_metadata_; + std::queue<mojom::DeviceSync::ForceEnrollmentNowCallback> + force_enrollment_now_callback_queue_; + std::queue<mojom::DeviceSync::ForceSyncNowCallback> + force_sync_now_callback_queue_; std::queue<mojom::DeviceSync::SetSoftwareFeatureStateCallback> set_software_feature_state_callback_queue_; std::queue<FindEligibleDevicesCallback> find_eligible_devices_callback_queue_;
diff --git a/chromeos/services/multidevice_setup/BUILD.gn b/chromeos/services/multidevice_setup/BUILD.gn index b4c229a1..acf403c2 100644 --- a/chromeos/services/multidevice_setup/BUILD.gn +++ b/chromeos/services/multidevice_setup/BUILD.gn
@@ -14,6 +14,8 @@ "account_status_change_delegate_notifier.h", "account_status_change_delegate_notifier_impl.cc", "account_status_change_delegate_notifier_impl.h", + "device_reenroller.cc", + "device_reenroller.h", "eligible_host_devices_provider.h", "eligible_host_devices_provider_impl.cc", "eligible_host_devices_provider_impl.h", @@ -110,6 +112,7 @@ sources = [ "account_status_change_delegate_notifier_impl_unittest.cc", + "device_reenroller_unittest.cc", "eligible_host_devices_provider_impl_unittest.cc", "feature_state_manager_impl_unittest.cc", "host_backend_delegate_impl_unittest.cc",
diff --git a/chromeos/services/multidevice_setup/device_reenroller.cc b/chromeos/services/multidevice_setup/device_reenroller.cc new file mode 100644 index 0000000..30fe9c2 --- /dev/null +++ b/chromeos/services/multidevice_setup/device_reenroller.cc
@@ -0,0 +1,166 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/services/multidevice_setup/device_reenroller.h" + +#include "base/containers/flat_set.h" +#include "base/no_destructor.h" +#include "chromeos/components/proximity_auth/logging/logging.h" +#include "chromeos/services/device_sync/public/cpp/device_sync_client.h" +#include "components/cryptauth/gcm_device_info_provider.h" + +namespace chromeos { + +namespace multidevice_setup { + +namespace { + +// The number of minutes to wait before retrying a failed re-enrollment or +// device sync attempt. +const int kNumMinutesBetweenRetries = 5; + +std::vector<cryptauth::SoftwareFeature> +ComputeSupportedSoftwareFeaturesSortedDedupedListFromGcmDeviceInfo( + const cryptauth::GcmDeviceInfo& gcm_device_info) { + base::flat_set<cryptauth::SoftwareFeature> sorted_and_deduped_set; + for (int i = 0; i < gcm_device_info.supported_software_features_size(); ++i) { + sorted_and_deduped_set.insert( + gcm_device_info.supported_software_features(i)); + } + return std::vector<cryptauth::SoftwareFeature>(sorted_and_deduped_set.begin(), + sorted_and_deduped_set.end()); +} + +std::vector<cryptauth::SoftwareFeature> +ComputeSupportedSoftwareFeaturesSortedDedupedListFromLocalDeviceMetadata( + const cryptauth::RemoteDeviceRef& local_device_metadata) { + base::flat_set<cryptauth::SoftwareFeature> sorted_and_deduped_set; + for (int i = cryptauth::SoftwareFeature_MIN; + i <= cryptauth::SoftwareFeature_MAX; ++i) { + cryptauth::SoftwareFeature feature = + static_cast<cryptauth::SoftwareFeature>(i); + if (local_device_metadata.GetSoftwareFeatureState(feature) != + cryptauth::SoftwareFeatureState::kNotSupported) { + sorted_and_deduped_set.insert(feature); + } + } + return std::vector<cryptauth::SoftwareFeature>(sorted_and_deduped_set.begin(), + sorted_and_deduped_set.end()); +} + +} // namespace + +// static +DeviceReenroller::Factory* DeviceReenroller::Factory::test_factory_ = nullptr; + +// static +DeviceReenroller::Factory* DeviceReenroller::Factory::Get() { + if (test_factory_) + return test_factory_; + + static base::NoDestructor<Factory> factory; + return factory.get(); +} + +// static +void DeviceReenroller::Factory::SetFactoryForTesting(Factory* test_factory) { + test_factory_ = test_factory; +} + +DeviceReenroller::Factory::~Factory() = default; + +std::unique_ptr<DeviceReenroller> DeviceReenroller::Factory::BuildInstance( + device_sync::DeviceSyncClient* device_sync_client, + const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider, + std::unique_ptr<base::OneShotTimer> timer) { + return base::WrapUnique(new DeviceReenroller( + device_sync_client, gcm_device_info_provider, std::move(timer))); +} + +DeviceReenroller::~DeviceReenroller() = default; + +DeviceReenroller::DeviceReenroller( + device_sync::DeviceSyncClient* device_sync_client, + const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider, + std::unique_ptr<base::OneShotTimer> timer) + : device_sync_client_(device_sync_client), + gcm_supported_software_features_( + ComputeSupportedSoftwareFeaturesSortedDedupedListFromGcmDeviceInfo( + gcm_device_info_provider->GetGcmDeviceInfo())), + timer_(std::move(timer)) { + // If the current set of supported software features from GcmDeviceInfo + // differs from that of the local device metadata on the CryptAuth server, + // attempt re-enrollment. Note: Both lists here are sorted and duplicate-free. + if (gcm_supported_software_features_ != + ComputeSupportedSoftwareFeaturesSortedDedupedListFromLocalDeviceMetadata( + *device_sync_client_->GetLocalDeviceMetadata())) { + AttemptReenrollment(); + } +} + +void DeviceReenroller::AttemptReenrollment() { + DCHECK(!timer_->IsRunning()); + device_sync_client_->ForceEnrollmentNow(base::BindOnce( + &DeviceReenroller::OnForceEnrollmentNowComplete, base::Unretained(this))); +} + +void DeviceReenroller::AttemptDeviceSync() { + DCHECK(!timer_->IsRunning()); + device_sync_client_->ForceSyncNow(base::BindOnce( + &DeviceReenroller::OnForceSyncNowComplete, base::Unretained(this))); +} + +void DeviceReenroller::OnForceEnrollmentNowComplete(bool success) { + if (success) { + PA_LOG(INFO) << "DeviceReenroller::OnForceEnrollmentNowComplete(): " + << "Forced enrollment attempt was successful. " + << "Syncing devices now."; + AttemptDeviceSync(); + return; + } + PA_LOG(WARNING) << "DeviceReenroller::OnForceEnrollmentNowComplete(): " + << "Forced enrollment attempt was unsuccessful. Retrying in " + << kNumMinutesBetweenRetries << " minutes."; + timer_->Start(FROM_HERE, + base::TimeDelta::FromMinutes(kNumMinutesBetweenRetries), + base::BindOnce(&DeviceReenroller::AttemptReenrollment, + base::Unretained(this))); +} + +void DeviceReenroller::OnForceSyncNowComplete(bool success) { + // This is used to track if the device sync properly updated the local device + // metadata to reflect the supported software features from GcmDeviceInfo. + bool local_device_metadata_agrees = + device_sync_client_->GetLocalDeviceMetadata() && + ComputeSupportedSoftwareFeaturesSortedDedupedListFromLocalDeviceMetadata( + *device_sync_client_->GetLocalDeviceMetadata()) == + gcm_supported_software_features_; + + if (success && local_device_metadata_agrees) { + PA_LOG(INFO) << "DeviceReenroller::OnForceSyncNowComplete(): " + << "Forced device sync attempt was successful."; + return; + } + if (!success) { + PA_LOG(WARNING) << "DeviceReenroller::OnForceSyncNowComplete(): " + << "Forced device sync attempt was unsuccessful. " + << "Retrying in " << kNumMinutesBetweenRetries + << " minutes."; + } else { + DCHECK(!local_device_metadata_agrees); + PA_LOG(WARNING) << "DeviceReenroller::OnForceSyncNowComplete(): " + << "The local device metadata's supported software " + << "features do not agree with the set extracted from GCM " + << "device info. Retrying in " << kNumMinutesBetweenRetries + << " minutes."; + } + timer_->Start(FROM_HERE, + base::TimeDelta::FromMinutes(kNumMinutesBetweenRetries), + base::BindOnce(&DeviceReenroller::AttemptDeviceSync, + base::Unretained(this))); +} + +} // namespace multidevice_setup + +} // namespace chromeos
diff --git a/chromeos/services/multidevice_setup/device_reenroller.h b/chromeos/services/multidevice_setup/device_reenroller.h new file mode 100644 index 0000000..96bc9e88 --- /dev/null +++ b/chromeos/services/multidevice_setup/device_reenroller.h
@@ -0,0 +1,82 @@ +// 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 CHROMEOS_SERVICES_MULTIDEVICE_SETUP_DEVICE_REENROLLER_H_ +#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_DEVICE_REENROLLER_H_ + +#include <memory> + +#include "base/macros.h" +#include "components/cryptauth/proto/cryptauth_api.pb.h" + +namespace base { +class OneShotTimer; +} // namespace base + +namespace cryptauth { +class GcmDeviceInfoProvider; +} // namespace cryptauth + +namespace chromeos { + +namespace device_sync { +class DeviceSyncClient; +} // namespace device_sync + +namespace multidevice_setup { + +// The DeviceReenroller constructor re-enrolls and syncs the device if the set +// of supported SoftwareFeatures in the current GCM device info differs from +// that of the local device metadata on the CryptAuth server. +class DeviceReenroller { + public: + class Factory { + public: + static Factory* Get(); + static void SetFactoryForTesting(Factory* test_factory); + virtual ~Factory(); + virtual std::unique_ptr<DeviceReenroller> BuildInstance( + device_sync::DeviceSyncClient* device_sync_client, + const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider, + std::unique_ptr<base::OneShotTimer> timer = + std::make_unique<base::OneShotTimer>()); + + private: + static Factory* test_factory_; + }; + + virtual ~DeviceReenroller(); + + private: + DeviceReenroller( + device_sync::DeviceSyncClient* device_sync_client, + const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider, + std::unique_ptr<base::OneShotTimer> timer); + + void AttemptReenrollment(); + void AttemptDeviceSync(); + + // If the re-enrollment was successful, force a device sync; otherwise, retry + // re-enrollment every 5 minutes or until success. + void OnForceEnrollmentNowComplete(bool success); + // If the device sync was successful and the list of supported software + // features on the CryptAuth server now agrees with the list of supported + // software features in GcmDeviceInfo, log the success; otherwise, retry + // device sync every 5 minutes or until success. + void OnForceSyncNowComplete(bool success); + + device_sync::DeviceSyncClient* device_sync_client_; + // The sorted and deduped list of supported software features extracted from + // GcmDeviceInfo. + std::vector<cryptauth::SoftwareFeature> gcm_supported_software_features_; + std::unique_ptr<base::OneShotTimer> timer_; + + DISALLOW_COPY_AND_ASSIGN(DeviceReenroller); +}; + +} // namespace multidevice_setup + +} // namespace chromeos + +#endif // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_DEVICE_REENROLLER_H_
diff --git a/chromeos/services/multidevice_setup/device_reenroller_unittest.cc b/chromeos/services/multidevice_setup/device_reenroller_unittest.cc new file mode 100644 index 0000000..70f6e0a --- /dev/null +++ b/chromeos/services/multidevice_setup/device_reenroller_unittest.cc
@@ -0,0 +1,377 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/services/multidevice_setup/device_reenroller.h" + +#include "base/macros.h" +#include "base/timer/mock_timer.h" +#include "chromeos/services/device_sync/public/cpp/fake_device_sync_client.h" +#include "components/cryptauth/fake_gcm_device_info_provider.h" +#include "components/cryptauth/remote_device_test_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { + +namespace multidevice_setup { + +class MultiDeviceSetupDeviceReenrollerTest : public testing::Test { + protected: + MultiDeviceSetupDeviceReenrollerTest() + : test_local_device_(cryptauth::CreateRemoteDeviceRefForTest()) {} + ~MultiDeviceSetupDeviceReenrollerTest() override = default; + + // testing::Test: + void SetUp() override { + fake_device_sync_client_ = + std::make_unique<device_sync::FakeDeviceSyncClient>(); + + fake_gcm_device_info_provider_ = + std::make_unique<cryptauth::FakeGcmDeviceInfoProvider>( + cryptauth::GcmDeviceInfo()); + } + + void SetLocalDeviceMetadataSoftwareFeaturesMap( + const std::map<cryptauth::SoftwareFeature, + cryptauth::SoftwareFeatureState>& map) { + cryptauth::GetMutableRemoteDevice(test_local_device_)->software_features = + map; + fake_device_sync_client_->set_local_device_metadata(test_local_device_); + } + + void SetFakeGcmDeviceInfoProviderWithSupportedSoftwareFeatures( + const std::vector<cryptauth::SoftwareFeature>& + supported_software_features) { + cryptauth::GcmDeviceInfo gcm_device_info; + gcm_device_info.clear_supported_software_features(); + for (cryptauth::SoftwareFeature feature : supported_software_features) { + gcm_device_info.add_supported_software_features(feature); + } + fake_gcm_device_info_provider_ = + std::make_unique<cryptauth::FakeGcmDeviceInfoProvider>(gcm_device_info); + } + + device_sync::FakeDeviceSyncClient* fake_device_sync_client() { + return fake_device_sync_client_.get(); + } + + base::MockOneShotTimer* timer() { return mock_timer_; } + + void CreateDeviceReenroller() { + auto mock_timer = std::make_unique<base::MockOneShotTimer>(); + mock_timer_ = mock_timer.get(); + + device_reenroller_ = DeviceReenroller::Factory::Get()->BuildInstance( + fake_device_sync_client_.get(), fake_gcm_device_info_provider_.get(), + std::move(mock_timer)); + } + + private: + std::unique_ptr<DeviceReenroller> device_reenroller_; + + std::unique_ptr<device_sync::FakeDeviceSyncClient> fake_device_sync_client_; + std::unique_ptr<cryptauth::FakeGcmDeviceInfoProvider> + fake_gcm_device_info_provider_; + base::MockOneShotTimer* mock_timer_; + + cryptauth::RemoteDeviceRef test_local_device_; + + DISALLOW_COPY_AND_ASSIGN(MultiDeviceSetupDeviceReenrollerTest); +}; + +TEST_F(MultiDeviceSetupDeviceReenrollerTest, + IfGmcDeviceInfoAndLocalDeviceMetadataMatchThenNoReenrollment) { + // Set the current local device metadata to contain a sample of supported + // software features. + SetLocalDeviceMetadataSoftwareFeaturesMap( + std::map<cryptauth::SoftwareFeature, cryptauth::SoftwareFeatureState>{ + {cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}, + {cryptauth::SoftwareFeature::EASY_UNLOCK_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}}); + // Set the current GcmDeviceInfo supported software features to contain the + // same set. + SetFakeGcmDeviceInfoProviderWithSupportedSoftwareFeatures( + std::vector<cryptauth::SoftwareFeature>{ + cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeature::EASY_UNLOCK_CLIENT}); + + CreateDeviceReenroller(); + + // No enrollment or device sync attempts should have taken place nor should + // any be scheduled. + EXPECT_EQ( + 0, fake_device_sync_client()->GetForceEnrollmentNowCallbackQueueSize()); + EXPECT_EQ(0, fake_device_sync_client()->GetForceSyncNowCallbackQueueSize()); + EXPECT_FALSE(timer()->IsRunning()); +} + +TEST_F(MultiDeviceSetupDeviceReenrollerTest, + IfFeaturesBecomeUnsupportedThenUpdateAndReenroll) { + // Set the current local device metadata to contain a sample of supported + // software features. + SetLocalDeviceMetadataSoftwareFeaturesMap( + std::map<cryptauth::SoftwareFeature, cryptauth::SoftwareFeatureState>{ + {cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}, + {cryptauth::SoftwareFeature::EASY_UNLOCK_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}}); + // Remove one supported software feature in the GcmDeviceInfo. + SetFakeGcmDeviceInfoProviderWithSupportedSoftwareFeatures( + std::vector<cryptauth::SoftwareFeature>{ + cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT}); + + CreateDeviceReenroller(); + + // Assume successful enrollment, sync, and local device metadata update. + EXPECT_EQ( + 1, fake_device_sync_client()->GetForceEnrollmentNowCallbackQueueSize()); + fake_device_sync_client()->InvokePendingForceEnrollmentNowCallback( + true /* success */); + EXPECT_EQ(1, fake_device_sync_client()->GetForceSyncNowCallbackQueueSize()); + SetLocalDeviceMetadataSoftwareFeaturesMap( + std::map<cryptauth::SoftwareFeature, cryptauth::SoftwareFeatureState>{ + {cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}}); + fake_device_sync_client()->InvokePendingForceSyncNowCallback( + true /* success */); + // No other attempts should be scheduled. + EXPECT_FALSE(timer()->IsRunning()); +} + +TEST_F(MultiDeviceSetupDeviceReenrollerTest, + IfFeaturesBecomeSupportedThenUpdateAndReenroll) { + // Set the current local device metadata to contain a sample of supported + // software features. + SetLocalDeviceMetadataSoftwareFeaturesMap( + std::map<cryptauth::SoftwareFeature, cryptauth::SoftwareFeatureState>{ + {cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}, + {cryptauth::SoftwareFeature::EASY_UNLOCK_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}}); + // Add one more supported software feature in the GcmDeviceInfo. + SetFakeGcmDeviceInfoProviderWithSupportedSoftwareFeatures( + std::vector<cryptauth::SoftwareFeature>{ + cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeature::EASY_UNLOCK_CLIENT, + cryptauth::SoftwareFeature::MAGIC_TETHER_CLIENT}); + + CreateDeviceReenroller(); + + // Assume successful enrollment, sync, and local device metadata update. + EXPECT_EQ( + 1, fake_device_sync_client()->GetForceEnrollmentNowCallbackQueueSize()); + fake_device_sync_client()->InvokePendingForceEnrollmentNowCallback( + true /* success */); + EXPECT_EQ(1, fake_device_sync_client()->GetForceSyncNowCallbackQueueSize()); + SetLocalDeviceMetadataSoftwareFeaturesMap( + std::map<cryptauth::SoftwareFeature, cryptauth::SoftwareFeatureState>{ + {cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}, + {cryptauth::SoftwareFeature::EASY_UNLOCK_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}, + {cryptauth::SoftwareFeature::MAGIC_TETHER_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}}); + fake_device_sync_client()->InvokePendingForceSyncNowCallback( + true /* success */); + // No other attempts should be scheduled. + EXPECT_FALSE(timer()->IsRunning()); +} + +TEST_F(MultiDeviceSetupDeviceReenrollerTest, + IfReenrollmentFailsThenScheduleAnotherAttempt) { + // Set the current local device metadata to contain a sample of supported + // software features. + SetLocalDeviceMetadataSoftwareFeaturesMap( + std::map<cryptauth::SoftwareFeature, cryptauth::SoftwareFeatureState>{ + {cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}, + {cryptauth::SoftwareFeature::EASY_UNLOCK_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}}); + // Add one more supported software feature in the GcmDeviceInfo to trigger a + // re-enrollment attempt. + SetFakeGcmDeviceInfoProviderWithSupportedSoftwareFeatures( + std::vector<cryptauth::SoftwareFeature>{ + cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeature::EASY_UNLOCK_CLIENT, + cryptauth::SoftwareFeature::MAGIC_TETHER_CLIENT}); + + CreateDeviceReenroller(); + + EXPECT_EQ( + 1, fake_device_sync_client()->GetForceEnrollmentNowCallbackQueueSize()); + // Assume unsuccessful re-enrollment attempt. + fake_device_sync_client()->InvokePendingForceEnrollmentNowCallback( + false /* success */); + // No device sync call should have been made. + EXPECT_EQ(0, fake_device_sync_client()->GetForceSyncNowCallbackQueueSize()); + // Another re-enrollment attempt should be scheduled. + EXPECT_TRUE(timer()->IsRunning()); + // This should trigger another enrollment attempt. + timer()->Fire(); + EXPECT_EQ( + 1, fake_device_sync_client()->GetForceEnrollmentNowCallbackQueueSize()); + EXPECT_EQ(0, fake_device_sync_client()->GetForceSyncNowCallbackQueueSize()); +} + +TEST_F(MultiDeviceSetupDeviceReenrollerTest, + IfDeviceSyncFailsThenScheduleAnotherAttempt) { + // Set the current local device metadata to contain a sample of supported + // software features. + SetLocalDeviceMetadataSoftwareFeaturesMap( + std::map<cryptauth::SoftwareFeature, cryptauth::SoftwareFeatureState>{ + {cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}, + {cryptauth::SoftwareFeature::EASY_UNLOCK_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}}); + // Add one more supported software feature in the GcmDeviceInfo to trigger a + // re-enrollment attempt. + SetFakeGcmDeviceInfoProviderWithSupportedSoftwareFeatures( + std::vector<cryptauth::SoftwareFeature>{ + cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeature::EASY_UNLOCK_CLIENT, + cryptauth::SoftwareFeature::MAGIC_TETHER_CLIENT}); + + CreateDeviceReenroller(); + + // Assume successful re-enrollment attempt. + EXPECT_EQ( + 1, fake_device_sync_client()->GetForceEnrollmentNowCallbackQueueSize()); + fake_device_sync_client()->InvokePendingForceEnrollmentNowCallback( + true /* success */); + EXPECT_EQ(1, fake_device_sync_client()->GetForceSyncNowCallbackQueueSize()); + // Assume unsuccessful device sync attempt. + fake_device_sync_client()->InvokePendingForceSyncNowCallback( + false /* success */); + // Another device sync attempt should be scheduled. + EXPECT_TRUE(timer()->IsRunning()); + // This should trigger another device sync attempt. + timer()->Fire(); + EXPECT_EQ( + 0, fake_device_sync_client()->GetForceEnrollmentNowCallbackQueueSize()); + EXPECT_EQ(1, fake_device_sync_client()->GetForceSyncNowCallbackQueueSize()); +} + +TEST_F(MultiDeviceSetupDeviceReenrollerTest, + IfLocalDeviceMetadataNotUpdatedCorrectlyThenScheduleAnotherSyncAttempt) { + // Set the current local device metadata to contain a sample of supported + // software features. + SetLocalDeviceMetadataSoftwareFeaturesMap( + std::map<cryptauth::SoftwareFeature, cryptauth::SoftwareFeatureState>{ + {cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}, + {cryptauth::SoftwareFeature::EASY_UNLOCK_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}}); + // Add one more supported software feature in the GcmDeviceInfo to trigger a + // re-enrollment attempt. + SetFakeGcmDeviceInfoProviderWithSupportedSoftwareFeatures( + std::vector<cryptauth::SoftwareFeature>{ + cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeature::EASY_UNLOCK_CLIENT, + cryptauth::SoftwareFeature::MAGIC_TETHER_CLIENT}); + + CreateDeviceReenroller(); + // Assume successful enrollment and device sync. + EXPECT_EQ( + 1, fake_device_sync_client()->GetForceEnrollmentNowCallbackQueueSize()); + fake_device_sync_client()->InvokePendingForceEnrollmentNowCallback( + true /* success */); + EXPECT_EQ(1, fake_device_sync_client()->GetForceSyncNowCallbackQueueSize()); + // Assume local device metadata was not updated correctly. + SetLocalDeviceMetadataSoftwareFeaturesMap( + std::map<cryptauth::SoftwareFeature, cryptauth::SoftwareFeatureState>{ + {cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}, + {cryptauth::SoftwareFeature::EASY_UNLOCK_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}}); + fake_device_sync_client()->InvokePendingForceSyncNowCallback( + true /* success */); + // Another device sync attempt should be scheduled. + EXPECT_TRUE(timer()->IsRunning()); + // This should trigger another device sync attempt. + timer()->Fire(); + EXPECT_EQ( + 0, fake_device_sync_client()->GetForceEnrollmentNowCallbackQueueSize()); + EXPECT_EQ(1, fake_device_sync_client()->GetForceSyncNowCallbackQueueSize()); +} + +TEST_F(MultiDeviceSetupDeviceReenrollerTest, + GcmDeviceInfoFeatureListOrderingAndDuplicatesAreIrrelevantForReenroll) { + // Set the current local device metadata to contain a sample of supported + // software features. + SetLocalDeviceMetadataSoftwareFeaturesMap( + std::map<cryptauth::SoftwareFeature, cryptauth::SoftwareFeatureState>{ + {cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}, + {cryptauth::SoftwareFeature::EASY_UNLOCK_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}, + {cryptauth::SoftwareFeature::MAGIC_TETHER_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}}); + // Add one more supported software feature in the GcmDeviceInfo. + SetFakeGcmDeviceInfoProviderWithSupportedSoftwareFeatures( + std::vector<cryptauth::SoftwareFeature>{ + cryptauth::SoftwareFeature::SMS_CONNECT_CLIENT, + cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeature::EASY_UNLOCK_CLIENT, + cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeature::SMS_CONNECT_CLIENT, + cryptauth::SoftwareFeature::MAGIC_TETHER_CLIENT}); + + CreateDeviceReenroller(); + + // Assume successful enrollment, sync, and local device metadata update. + EXPECT_EQ( + 1, fake_device_sync_client()->GetForceEnrollmentNowCallbackQueueSize()); + fake_device_sync_client()->InvokePendingForceEnrollmentNowCallback( + true /* success */); + EXPECT_EQ(1, fake_device_sync_client()->GetForceSyncNowCallbackQueueSize()); + SetLocalDeviceMetadataSoftwareFeaturesMap( + std::map<cryptauth::SoftwareFeature, cryptauth::SoftwareFeatureState>{ + {cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}, + {cryptauth::SoftwareFeature::EASY_UNLOCK_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}, + {cryptauth::SoftwareFeature::MAGIC_TETHER_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}, + {cryptauth::SoftwareFeature::SMS_CONNECT_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}}); + fake_device_sync_client()->InvokePendingForceSyncNowCallback( + true /* success */); + // No other attempts should be scheduled. + EXPECT_FALSE(timer()->IsRunning()); +} + +TEST_F( + MultiDeviceSetupDeviceReenrollerTest, + GcmDeviceInfoFeatureListOrderingAndDuplicatesAreIrrelevantForNoReenroll) { + // Set the current local device metadata to contain a sample of supported + // software features. + SetLocalDeviceMetadataSoftwareFeaturesMap( + std::map<cryptauth::SoftwareFeature, cryptauth::SoftwareFeatureState>{ + {cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}, + {cryptauth::SoftwareFeature::EASY_UNLOCK_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}, + {cryptauth::SoftwareFeature::MAGIC_TETHER_CLIENT, + cryptauth::SoftwareFeatureState::kSupported}}); + // Add one more supported software feature in the GcmDeviceInfo. + SetFakeGcmDeviceInfoProviderWithSupportedSoftwareFeatures( + std::vector<cryptauth::SoftwareFeature>{ + cryptauth::SoftwareFeature::EASY_UNLOCK_CLIENT, + cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeature::EASY_UNLOCK_CLIENT, + cryptauth::SoftwareFeature::BETTER_TOGETHER_CLIENT, + cryptauth::SoftwareFeature::MAGIC_TETHER_CLIENT}); + + CreateDeviceReenroller(); + + EXPECT_EQ( + 0, fake_device_sync_client()->GetForceEnrollmentNowCallbackQueueSize()); + EXPECT_EQ(0, fake_device_sync_client()->GetForceSyncNowCallbackQueueSize()); + // No other attempts should be scheduled. + EXPECT_FALSE(timer()->IsRunning()); +} + +} // namespace multidevice_setup + +} // namespace chromeos
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_impl.cc b/chromeos/services/multidevice_setup/multidevice_setup_impl.cc index cf24736..cd51893 100644 --- a/chromeos/services/multidevice_setup/multidevice_setup_impl.cc +++ b/chromeos/services/multidevice_setup/multidevice_setup_impl.cc
@@ -9,6 +9,7 @@ #include "base/time/default_clock.h" #include "chromeos/components/proximity_auth/logging/logging.h" #include "chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.h" +#include "chromeos/services/multidevice_setup/device_reenroller.h" #include "chromeos/services/multidevice_setup/eligible_host_devices_provider_impl.h" #include "chromeos/services/multidevice_setup/feature_state_manager_impl.h" #include "chromeos/services/multidevice_setup/host_backend_delegate_impl.h" @@ -54,10 +55,12 @@ secure_channel::SecureChannelClient* secure_channel_client, AuthTokenValidator* auth_token_validator, std::unique_ptr<AndroidSmsAppHelperDelegate> - android_sms_app_helper_delegate) { + android_sms_app_helper_delegate, + const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider) { return base::WrapUnique(new MultiDeviceSetupImpl( pref_service, device_sync_client, secure_channel_client, - auth_token_validator, std::move(android_sms_app_helper_delegate))); + auth_token_validator, std::move(android_sms_app_helper_delegate), + gcm_device_info_provider)); } MultiDeviceSetupImpl::MultiDeviceSetupImpl( @@ -66,7 +69,8 @@ secure_channel::SecureChannelClient* secure_channel_client, AuthTokenValidator* auth_token_validator, std::unique_ptr<AndroidSmsAppHelperDelegate> - android_sms_app_helper_delegate) + android_sms_app_helper_delegate, + const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider) : android_sms_app_helper_delegate_( std::move(android_sms_app_helper_delegate)), eligible_host_devices_provider_( @@ -102,6 +106,9 @@ pref_service, setup_flow_completion_recorder_.get(), base::DefaultClock::GetInstance())), + device_reenroller_(DeviceReenroller::Factory::Get()->BuildInstance( + device_sync_client, + gcm_device_info_provider)), auth_token_validator_(auth_token_validator) { host_status_provider_->AddObserver(this); feature_state_manager_->AddObserver(this);
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_impl.h b/chromeos/services/multidevice_setup/multidevice_setup_impl.h index 46ec211..038c3381 100644 --- a/chromeos/services/multidevice_setup/multidevice_setup_impl.h +++ b/chromeos/services/multidevice_setup/multidevice_setup_impl.h
@@ -17,6 +17,10 @@ class PrefService; +namespace cryptauth { +class GcmDeviceInfoProvider; +} // namespace cryptauth + namespace chromeos { namespace device_sync { @@ -32,6 +36,7 @@ class AccountStatusChangeDelegateNotifier; class AndroidSmsAppHelperDelegate; class AuthTokenValidator; +class DeviceReenroller; class HostBackendDelegate; class HostStatusProvider; class HostVerifier; @@ -54,7 +59,8 @@ secure_channel::SecureChannelClient* secure_channel_client, AuthTokenValidator* auth_token_validator, std::unique_ptr<AndroidSmsAppHelperDelegate> - android_sms_app_helper_delegate); + android_sms_app_helper_delegate, + const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider); private: static Factory* test_factory_; @@ -72,7 +78,8 @@ secure_channel::SecureChannelClient* secure_channel_client, AuthTokenValidator* auth_token_validator, std::unique_ptr<AndroidSmsAppHelperDelegate> - android_sms_app_helper_delegate); + android_sms_app_helper_delegate, + const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider); // mojom::MultiDeviceSetup: void SetAccountStatusChangeDelegate( @@ -114,6 +121,7 @@ std::unique_ptr<FeatureStateManager> feature_state_manager_; std::unique_ptr<SetupFlowCompletionRecorder> setup_flow_completion_recorder_; std::unique_ptr<AccountStatusChangeDelegateNotifier> delegate_notifier_; + std::unique_ptr<DeviceReenroller> device_reenroller_; AuthTokenValidator* auth_token_validator_; mojo::InterfacePtrSet<mojom::HostStatusObserver> host_status_observers_;
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_impl_unittest.cc b/chromeos/services/multidevice_setup/multidevice_setup_impl_unittest.cc index 33ce0a5..8a18c141 100644 --- a/chromeos/services/multidevice_setup/multidevice_setup_impl_unittest.cc +++ b/chromeos/services/multidevice_setup/multidevice_setup_impl_unittest.cc
@@ -10,6 +10,7 @@ #include "base/test/scoped_task_environment.h" #include "chromeos/services/device_sync/public/cpp/fake_device_sync_client.h" #include "chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.h" +#include "chromeos/services/multidevice_setup/device_reenroller.h" #include "chromeos/services/multidevice_setup/eligible_host_devices_provider_impl.h" #include "chromeos/services/multidevice_setup/fake_account_status_change_delegate.h" #include "chromeos/services/multidevice_setup/fake_account_status_change_delegate_notifier.h" @@ -31,6 +32,7 @@ #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" #include "chromeos/services/multidevice_setup/setup_flow_completion_recorder_impl.h" #include "chromeos/services/secure_channel/public/cpp/client/fake_secure_channel_client.h" +#include "components/cryptauth/fake_gcm_device_info_provider.h" #include "components/cryptauth/remote_device_test_util.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gtest/include/gtest/gtest.h" @@ -350,6 +352,36 @@ DISALLOW_COPY_AND_ASSIGN(FakeAccountStatusChangeDelegateNotifierFactory); }; +class FakeDeviceReenrollerFactory : public DeviceReenroller::Factory { + public: + FakeDeviceReenrollerFactory( + device_sync::FakeDeviceSyncClient* expected_device_sync_client, + const cryptauth::FakeGcmDeviceInfoProvider* + expected_gcm_device_info_provider) + : expected_device_sync_client_(expected_device_sync_client), + expected_gcm_device_info_provider_(expected_gcm_device_info_provider) {} + + ~FakeDeviceReenrollerFactory() override = default; + + private: + // DeviceReenroller::Factory: + std::unique_ptr<DeviceReenroller> BuildInstance( + device_sync::DeviceSyncClient* device_sync_client, + const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider, + std::unique_ptr<base::OneShotTimer> timer) override { + EXPECT_EQ(expected_device_sync_client_, device_sync_client); + EXPECT_EQ(expected_gcm_device_info_provider_, gcm_device_info_provider); + // Only check inputs and return nullptr. We do not want to trigger the + // DeviceReenroller logic in these unit tests. + return nullptr; + } + + device_sync::FakeDeviceSyncClient* expected_device_sync_client_; + const cryptauth::GcmDeviceInfoProvider* expected_gcm_device_info_provider_; + + DISALLOW_COPY_AND_ASSIGN(FakeDeviceReenrollerFactory); +}; + } // namespace class MultiDeviceSetupImplTest : public testing::Test { @@ -375,6 +407,10 @@ fake_android_sms_app_helper_delegate_ = fake_android_sms_app_helper_delegate.get(); + fake_gcm_device_info_provider_ = + std::make_unique<cryptauth::FakeGcmDeviceInfoProvider>( + cryptauth::GcmDeviceInfo()); + fake_eligible_host_devices_provider_factory_ = std::make_unique<FakeEligibleHostDevicesProviderFactory>( fake_device_sync_client_.get()); @@ -422,10 +458,18 @@ AccountStatusChangeDelegateNotifierImpl::Factory::SetFactoryForTesting( fake_account_status_change_delegate_notifier_factory_.get()); + fake_device_reenroller_factory_ = + std::make_unique<FakeDeviceReenrollerFactory>( + fake_device_sync_client_.get(), + fake_gcm_device_info_provider_.get()); + DeviceReenroller::Factory::SetFactoryForTesting( + fake_device_reenroller_factory_.get()); + multidevice_setup_ = MultiDeviceSetupImpl::Factory::Get()->BuildInstance( test_pref_service_.get(), fake_device_sync_client_.get(), fake_secure_channel_client_.get(), fake_auth_token_validator_.get(), - std::move(fake_android_sms_app_helper_delegate)); + std::move(fake_android_sms_app_helper_delegate), + fake_gcm_device_info_provider_.get()); } void TearDown() override { @@ -437,6 +481,7 @@ SetupFlowCompletionRecorderImpl::Factory::SetFactoryForTesting(nullptr); AccountStatusChangeDelegateNotifierImpl::Factory::SetFactoryForTesting( nullptr); + DeviceReenroller::Factory::SetFactoryForTesting(nullptr); } void CallSetAccountStatusChangeDelegate() { @@ -685,6 +730,8 @@ std::unique_ptr<secure_channel::FakeSecureChannelClient> fake_secure_channel_client_; std::unique_ptr<FakeAuthTokenValidator> fake_auth_token_validator_; + std::unique_ptr<cryptauth::FakeGcmDeviceInfoProvider> + fake_gcm_device_info_provider_; std::unique_ptr<FakeEligibleHostDevicesProviderFactory> fake_eligible_host_devices_provider_factory_; @@ -699,6 +746,7 @@ fake_setup_flow_completion_recorder_factory_; std::unique_ptr<FakeAccountStatusChangeDelegateNotifierFactory> fake_account_status_change_delegate_notifier_factory_; + std::unique_ptr<FakeDeviceReenrollerFactory> fake_device_reenroller_factory_; FakeAndroidSmsAppHelperDelegate* fake_android_sms_app_helper_delegate_; std::unique_ptr<FakeAccountStatusChangeDelegate>
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_initializer.cc b/chromeos/services/multidevice_setup/multidevice_setup_initializer.cc index f1ad70d..9457b14d 100644 --- a/chromeos/services/multidevice_setup/multidevice_setup_initializer.cc +++ b/chromeos/services/multidevice_setup/multidevice_setup_initializer.cc
@@ -44,10 +44,12 @@ secure_channel::SecureChannelClient* secure_channel_client, AuthTokenValidator* auth_token_validator, std::unique_ptr<AndroidSmsAppHelperDelegate> - android_sms_app_helper_delegate) { + android_sms_app_helper_delegate, + const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider) { return base::WrapUnique(new MultiDeviceSetupInitializer( pref_service, device_sync_client, secure_channel_client, - auth_token_validator, std::move(android_sms_app_helper_delegate))); + auth_token_validator, std::move(android_sms_app_helper_delegate), + gcm_device_info_provider)); } MultiDeviceSetupInitializer::MultiDeviceSetupInitializer( @@ -56,13 +58,15 @@ secure_channel::SecureChannelClient* secure_channel_client, AuthTokenValidator* auth_token_validator, std::unique_ptr<AndroidSmsAppHelperDelegate> - android_sms_app_helper_delegate) + android_sms_app_helper_delegate, + const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider) : pref_service_(pref_service), device_sync_client_(device_sync_client), secure_channel_client_(secure_channel_client), auth_token_validator_(auth_token_validator), android_sms_app_helper_delegate_( - std::move(android_sms_app_helper_delegate)) { + std::move(android_sms_app_helper_delegate)), + gcm_device_info_provider_(gcm_device_info_provider) { if (device_sync_client_->is_ready()) { InitializeImplementation(); return; @@ -221,7 +225,8 @@ multidevice_setup_impl_ = MultiDeviceSetupImpl::Factory::Get()->BuildInstance( pref_service_, device_sync_client_, secure_channel_client_, - auth_token_validator_, std::move(android_sms_app_helper_delegate_)); + auth_token_validator_, std::move(android_sms_app_helper_delegate_), + gcm_device_info_provider_); if (pending_delegate_) { multidevice_setup_impl_->SetAccountStatusChangeDelegate(
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_initializer.h b/chromeos/services/multidevice_setup/multidevice_setup_initializer.h index 0bc80f79..fabccff 100644 --- a/chromeos/services/multidevice_setup/multidevice_setup_initializer.h +++ b/chromeos/services/multidevice_setup/multidevice_setup_initializer.h
@@ -16,6 +16,10 @@ class PrefService; +namespace cryptauth { +class GcmDeviceInfoProvider; +} // namespace cryptauth + namespace chromeos { namespace secure_channel { @@ -45,7 +49,8 @@ secure_channel::SecureChannelClient* secure_channel_client, AuthTokenValidator* auth_token_validator, std::unique_ptr<AndroidSmsAppHelperDelegate> - android_sms_app_helper_delegate); + android_sms_app_helper_delegate, + const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider); private: static Factory* test_factory_; @@ -60,7 +65,8 @@ secure_channel::SecureChannelClient* secure_channel_client, AuthTokenValidator* auth_token_validator, std::unique_ptr<AndroidSmsAppHelperDelegate> - android_sms_app_helper_delegate); + android_sms_app_helper_delegate, + const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider); // mojom::MultiDeviceSetup: void SetAccountStatusChangeDelegate( @@ -94,6 +100,7 @@ secure_channel::SecureChannelClient* secure_channel_client_; AuthTokenValidator* auth_token_validator_; std::unique_ptr<AndroidSmsAppHelperDelegate> android_sms_app_helper_delegate_; + const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider_; std::unique_ptr<mojom::MultiDeviceSetup> multidevice_setup_impl_;
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_service.cc b/chromeos/services/multidevice_setup/multidevice_setup_service.cc index c77a8f8..6cfce7e 100644 --- a/chromeos/services/multidevice_setup/multidevice_setup_service.cc +++ b/chromeos/services/multidevice_setup/multidevice_setup_service.cc
@@ -6,6 +6,7 @@ #include "chromeos/components/proximity_auth/logging/logging.h" #include "chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.h" +#include "chromeos/services/multidevice_setup/device_reenroller.h" #include "chromeos/services/multidevice_setup/host_backend_delegate_impl.h" #include "chromeos/services/multidevice_setup/host_verifier_impl.h" #include "chromeos/services/multidevice_setup/multidevice_setup_base.h" @@ -32,14 +33,16 @@ secure_channel::SecureChannelClient* secure_channel_client, AuthTokenValidator* auth_token_validator, std::unique_ptr<AndroidSmsAppHelperDelegate> - android_sms_app_helper_delegate) + android_sms_app_helper_delegate, + const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider) : multidevice_setup_( MultiDeviceSetupInitializer::Factory::Get()->BuildInstance( pref_service, device_sync_client, secure_channel_client, auth_token_validator, - std::move(android_sms_app_helper_delegate))) {} + std::move(android_sms_app_helper_delegate), + gcm_device_info_provider)) {} MultiDeviceSetupService::~MultiDeviceSetupService() = default;
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_service.h b/chromeos/services/multidevice_setup/multidevice_setup_service.h index 63b0e839..50bdffb 100644 --- a/chromeos/services/multidevice_setup/multidevice_setup_service.h +++ b/chromeos/services/multidevice_setup/multidevice_setup_service.h
@@ -14,6 +14,10 @@ class PrefService; class PrefRegistrySimple; +namespace cryptauth { +class GcmDeviceInfoProvider; +} // namespace cryptauth + namespace chromeos { namespace device_sync { @@ -41,7 +45,8 @@ secure_channel::SecureChannelClient* secure_channel_client, AuthTokenValidator* auth_token_validator, std::unique_ptr<AndroidSmsAppHelperDelegate> - android_sms_app_helper_delegate); + android_sms_app_helper_delegate, + const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider); ~MultiDeviceSetupService() override; static void RegisterProfilePrefs(PrefRegistrySimple* registry);
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc b/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc index 146cfd0aa..e480443 100644 --- a/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc +++ b/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc
@@ -18,6 +18,7 @@ #include "chromeos/services/multidevice_setup/public/mojom/constants.mojom.h" #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" #include "chromeos/services/secure_channel/public/cpp/client/fake_secure_channel_client.h" +#include "components/cryptauth/fake_gcm_device_info_provider.h" #include "components/cryptauth/remote_device_test_util.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "services/service_manager/public/cpp/test/test_connector_factory.h" @@ -39,13 +40,16 @@ device_sync::FakeDeviceSyncClient* expected_device_sync_client, secure_channel::FakeSecureChannelClient* expected_secure_channel_client, FakeAuthTokenValidator* expected_auth_token_validator, - FakeAndroidSmsAppHelperDelegate* expected_android_sms_app_helper_delegate) + FakeAndroidSmsAppHelperDelegate* expected_android_sms_app_helper_delegate, + const cryptauth::FakeGcmDeviceInfoProvider* + expected_gcm_device_info_provider) : expected_testing_pref_service_(expected_testing_pref_service), expected_device_sync_client_(expected_device_sync_client), expected_secure_channel_client_(expected_secure_channel_client), expected_auth_token_validator_(expected_auth_token_validator), expected_android_sms_app_helper_delegate_( - expected_android_sms_app_helper_delegate) {} + expected_android_sms_app_helper_delegate), + expected_gcm_device_info_provider_(expected_gcm_device_info_provider) {} ~FakeMultiDeviceSetupFactory() override = default; @@ -58,7 +62,9 @@ secure_channel::SecureChannelClient* secure_channel_client, AuthTokenValidator* auth_token_validator, std::unique_ptr<AndroidSmsAppHelperDelegate> - android_sms_app_helper_delegate) override { + android_sms_app_helper_delegate, + const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider) + override { EXPECT_FALSE(instance_); EXPECT_EQ(expected_testing_pref_service_, pref_service); EXPECT_EQ(expected_device_sync_client_, device_sync_client); @@ -66,6 +72,7 @@ EXPECT_EQ(expected_auth_token_validator_, auth_token_validator); EXPECT_EQ(expected_android_sms_app_helper_delegate_, android_sms_app_helper_delegate.get()); + EXPECT_EQ(expected_gcm_device_info_provider_, gcm_device_info_provider); auto instance = std::make_unique<FakeMultiDeviceSetup>(); instance_ = instance.get(); @@ -77,6 +84,8 @@ secure_channel::FakeSecureChannelClient* expected_secure_channel_client_; FakeAuthTokenValidator* expected_auth_token_validator_; FakeAndroidSmsAppHelperDelegate* expected_android_sms_app_helper_delegate_; + const cryptauth::FakeGcmDeviceInfoProvider* + expected_gcm_device_info_provider_; FakeMultiDeviceSetup* instance_ = nullptr; @@ -105,12 +114,16 @@ std::make_unique<FakeAndroidSmsAppHelperDelegate>(); fake_android_sms_app_helper_delegate_ = fake_android_sms_app_helper_delegate.get(); + fake_gcm_device_info_provider_ = + std::make_unique<cryptauth::FakeGcmDeviceInfoProvider>( + cryptauth::GcmDeviceInfo()); fake_multidevice_setup_factory_ = std::make_unique<FakeMultiDeviceSetupFactory>( test_pref_service_.get(), fake_device_sync_client_.get(), fake_secure_channel_client_.get(), fake_auth_token_validator_.get(), - fake_android_sms_app_helper_delegate_); + fake_android_sms_app_helper_delegate_, + fake_gcm_device_info_provider_.get()); MultiDeviceSetupImpl::Factory::SetFactoryForTesting( fake_multidevice_setup_factory_.get()); @@ -120,7 +133,8 @@ test_pref_service_.get(), fake_device_sync_client_.get(), fake_secure_channel_client_.get(), fake_auth_token_validator_.get(), - std::move(fake_android_sms_app_helper_delegate))); + std::move(fake_android_sms_app_helper_delegate), + fake_gcm_device_info_provider_.get())); auto connector = connector_factory_->CreateConnector(); connector->BindInterface(mojom::kServiceName, &multidevice_setup_ptr_); @@ -178,6 +192,8 @@ fake_secure_channel_client_; std::unique_ptr<FakeAuthTokenValidator> fake_auth_token_validator_; FakeAndroidSmsAppHelperDelegate* fake_android_sms_app_helper_delegate_; + std::unique_ptr<cryptauth::FakeGcmDeviceInfoProvider> + fake_gcm_device_info_provider_; std::unique_ptr<FakeMultiDeviceSetupFactory> fake_multidevice_setup_factory_;
diff --git a/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc b/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc index 053642b..9e0bae0 100644 --- a/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc +++ b/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc
@@ -47,7 +47,9 @@ secure_channel::SecureChannelClient* secure_channel_client, AuthTokenValidator* auth_token_validator, std::unique_ptr<AndroidSmsAppHelperDelegate> - android_sms_app_helper_delegate) override { + android_sms_app_helper_delegate, + const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider) + override { EXPECT_TRUE(fake_multidevice_setup_); return std::move(fake_multidevice_setup_); } @@ -122,7 +124,8 @@ auto multidevice_setup_service = std::make_unique<MultiDeviceSetupService>( nullptr /* pref_service */, nullptr /* device_sync_client */, nullptr /* secure_channel_client */, nullptr /* auth_token_validator */, - nullptr /* android_sms_app_helper_delegate */); + nullptr /* android_sms_app_helper_delegate */, + nullptr /* gcm_device_info_provider */); connector_factory_ = service_manager::TestConnectorFactory::CreateForUniqueService(
diff --git a/chromeos/services/secure_channel/BUILD.gn b/chromeos/services/secure_channel/BUILD.gn index 1fea8ae..27039ef7 100644 --- a/chromeos/services/secure_channel/BUILD.gn +++ b/chromeos/services/secure_channel/BUILD.gn
@@ -78,6 +78,7 @@ "multiplexed_channel.h", "multiplexed_channel_impl.cc", "multiplexed_channel_impl.h", + "pending_ble_connection_request_base.h", "pending_ble_initiator_connection_request.cc", "pending_ble_initiator_connection_request.h", "pending_ble_listener_connection_request.cc", @@ -210,6 +211,7 @@ "connection_attempt_base_unittest.cc", "error_tolerant_ble_advertisement_impl_unittest.cc", "multiplexed_channel_impl_unittest.cc", + "pending_ble_connection_request_base_unittest.cc", "pending_ble_initiator_connection_request_unittest.cc", "pending_ble_listener_connection_request_unittest.cc", "pending_connection_manager_impl_unittest.cc",
diff --git a/chromeos/services/secure_channel/pending_ble_connection_request_base.h b/chromeos/services/secure_channel/pending_ble_connection_request_base.h new file mode 100644 index 0000000..10ebbb3 --- /dev/null +++ b/chromeos/services/secure_channel/pending_ble_connection_request_base.h
@@ -0,0 +1,78 @@ +// 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 CHROMEOS_SERVICES_SECURE_CHANNEL_PENDING_BLE_CONNECTION_REQUEST_BASE_H_ +#define CHROMEOS_SERVICES_SECURE_CHANNEL_PENDING_BLE_CONNECTION_REQUEST_BASE_H_ + +#include <memory> +#include <string> +#include <utility> + +#include "base/macros.h" +#include "chromeos/services/secure_channel/pending_connection_request_base.h" +#include "device/bluetooth/bluetooth_adapter.h" + +namespace chromeos { + +namespace secure_channel { + +template <typename BleFailureDetailType> +class PendingBleConnectionRequestBase + : public PendingConnectionRequestBase<BleFailureDetailType>, + public device::BluetoothAdapter::Observer { + public: + ~PendingBleConnectionRequestBase() override { + bluetooth_adapter_->RemoveObserver(this); + } + + protected: + PendingBleConnectionRequestBase( + std::unique_ptr<ClientConnectionParameters> client_connection_parameters, + ConnectionPriority connection_priority, + const std::string& readable_request_type_for_logging, + PendingConnectionRequestDelegate* delegate, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) + : PendingConnectionRequestBase<BleFailureDetailType>( + std::move(client_connection_parameters), + connection_priority, + readable_request_type_for_logging, + delegate), + bluetooth_adapter_(std::move(bluetooth_adapter)) { + bluetooth_adapter_->AddObserver(this); + } + + private: + friend class SecureChannelPendingBleConnectionRequestBaseTest; + + // device::BluetoothAdapter::Observer: + void AdapterPoweredChanged(device::BluetoothAdapter* adapter, + bool powered) override { + DCHECK_EQ(bluetooth_adapter_, adapter); + if (powered) + return; + + this->StopRequestDueToConnectionFailures( + mojom::ConnectionAttemptFailureReason::ADAPTER_DISABLED); + } + + void AdapterPresentChanged(device::BluetoothAdapter* adapter, + bool present) override { + DCHECK_EQ(bluetooth_adapter_, adapter); + if (present) + return; + + this->StopRequestDueToConnectionFailures( + mojom::ConnectionAttemptFailureReason::ADAPTER_NOT_PRESENT); + } + + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter_; + + DISALLOW_COPY_AND_ASSIGN(PendingBleConnectionRequestBase); +}; + +} // namespace secure_channel + +} // namespace chromeos + +#endif // CHROMEOS_SERVICES_SECURE_CHANNEL_PENDING_BLE_CONNECTION_REQUEST_BASE_H_
diff --git a/chromeos/services/secure_channel/pending_ble_connection_request_base_unittest.cc b/chromeos/services/secure_channel/pending_ble_connection_request_base_unittest.cc new file mode 100644 index 0000000..230215d --- /dev/null +++ b/chromeos/services/secure_channel/pending_ble_connection_request_base_unittest.cc
@@ -0,0 +1,148 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/services/secure_channel/pending_ble_connection_request_base.h" + +#include <memory> + +#include "base/run_loop.h" +#include "base/test/scoped_task_environment.h" +#include "chromeos/services/secure_channel/fake_client_connection_parameters.h" +#include "chromeos/services/secure_channel/fake_connection_delegate.h" +#include "chromeos/services/secure_channel/fake_pending_connection_request_delegate.h" +#include "chromeos/services/secure_channel/public/cpp/shared/connection_priority.h" +#include "device/bluetooth/test/mock_bluetooth_adapter.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { + +namespace secure_channel { + +namespace { + +const char kTestReadableRequestTypeForLogging[] = "Test Request Type"; +const char kTestFeature[] = "testFeature"; +enum class TestFailureDetail { kTestFailureReason }; + +// Since PendingBleConnectionRequestBase is templatized, a concrete +// implementation is needed for its test. +class TestPendingBleConnectionRequestBase + : public PendingBleConnectionRequestBase<TestFailureDetail> { + public: + TestPendingBleConnectionRequestBase( + std::unique_ptr<ClientConnectionParameters> client_connection_parameters, + ConnectionPriority connection_priority, + PendingConnectionRequestDelegate* delegate, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) + : PendingBleConnectionRequestBase<TestFailureDetail>( + std::move(client_connection_parameters), + connection_priority, + kTestReadableRequestTypeForLogging, + delegate, + std::move(bluetooth_adapter)) {} + ~TestPendingBleConnectionRequestBase() override = default; + + // PendingConnectionRequest<TestFailureDetailType>: + void HandleConnectionFailure(TestFailureDetail failure_detail) override {} +}; + +} // namespace + +class SecureChannelPendingBleConnectionRequestBaseTest : public testing::Test { + protected: + SecureChannelPendingBleConnectionRequestBaseTest() = default; + ~SecureChannelPendingBleConnectionRequestBaseTest() override = default; + + void SetUp() override { + auto fake_client_connection_parameters = + std::make_unique<FakeClientConnectionParameters>(kTestFeature); + fake_client_connection_parameters_ = + fake_client_connection_parameters.get(); + + mock_adapter_ = + base::MakeRefCounted<testing::NiceMock<device::MockBluetoothAdapter>>(); + + fake_pending_connection_request_delegate_ = + std::make_unique<FakePendingConnectionRequestDelegate>(); + + test_pending_ble_connection_request_ = + std::make_unique<TestPendingBleConnectionRequestBase>( + std::move(fake_client_connection_parameters), + ConnectionPriority::kLow, + fake_pending_connection_request_delegate_.get(), mock_adapter_); + + EXPECT_TRUE(mock_adapter_->GetObservers().HasObserver( + test_pending_ble_connection_request_.get())); + } + + const base::Optional< + PendingConnectionRequestDelegate::FailedConnectionReason>& + GetFailedConnectionReason() { + return fake_pending_connection_request_delegate_ + ->GetFailedConnectionReasonForId( + test_pending_ble_connection_request_->GetRequestId()); + } + + const base::Optional<mojom::ConnectionAttemptFailureReason>& + GetConnectionAttemptFailureReason() const { + return fake_client_connection_parameters_->failure_reason(); + } + + void SimulateAdapterPoweredChanged(bool powered) { + test_pending_ble_connection_request_->AdapterPoweredChanged( + mock_adapter_.get(), powered); + } + + void SimulateAdapterPresentChanged(bool present) { + test_pending_ble_connection_request_->AdapterPresentChanged( + mock_adapter_.get(), present); + } + + private: + FakeClientConnectionParameters* fake_client_connection_parameters_; + std::unique_ptr<FakePendingConnectionRequestDelegate> + fake_pending_connection_request_delegate_; + scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>> mock_adapter_; + + std::unique_ptr<TestPendingBleConnectionRequestBase> + test_pending_ble_connection_request_; + + DISALLOW_COPY_AND_ASSIGN(SecureChannelPendingBleConnectionRequestBaseTest); +}; + +TEST_F(SecureChannelPendingBleConnectionRequestBaseTest, + HandleAdapterPoweredChanged) { + // Turning the adapter on should do nothing. + SimulateAdapterPoweredChanged(true /* powered */); + EXPECT_FALSE(GetFailedConnectionReason()); + EXPECT_FALSE(GetConnectionAttemptFailureReason()); + + // Turning the adapter off should trigger a failure. + SimulateAdapterPoweredChanged(false /* powered */); + EXPECT_EQ( + PendingConnectionRequestDelegate::FailedConnectionReason::kRequestFailed, + *GetFailedConnectionReason()); + EXPECT_EQ(mojom::ConnectionAttemptFailureReason::ADAPTER_DISABLED, + *GetConnectionAttemptFailureReason()); +} + +TEST_F(SecureChannelPendingBleConnectionRequestBaseTest, + HandleAdapterPresentChanged) { + // The adapter appearing should do nothing. + SimulateAdapterPresentChanged(true /* present */); + EXPECT_FALSE(GetFailedConnectionReason()); + EXPECT_FALSE(GetConnectionAttemptFailureReason()); + + // The adapter disappearing should trigger a failure. + SimulateAdapterPresentChanged(false /* present */); + EXPECT_EQ( + PendingConnectionRequestDelegate::FailedConnectionReason::kRequestFailed, + *GetFailedConnectionReason()); + EXPECT_EQ(mojom::ConnectionAttemptFailureReason::ADAPTER_NOT_PRESENT, + *GetConnectionAttemptFailureReason()); +} + +} // namespace secure_channel + +} // namespace chromeos
diff --git a/chromeos/services/secure_channel/pending_ble_initiator_connection_request.cc b/chromeos/services/secure_channel/pending_ble_initiator_connection_request.cc index a3a5e757..4b8ca286 100644 --- a/chromeos/services/secure_channel/pending_ble_initiator_connection_request.cc +++ b/chromeos/services/secure_channel/pending_ble_initiator_connection_request.cc
@@ -4,6 +4,8 @@ #include "chromeos/services/secure_channel/pending_ble_initiator_connection_request.h" +#include <utility> + #include "base/memory/ptr_util.h" #include "base/no_destructor.h" #include "chromeos/components/proximity_auth/logging/logging.h" @@ -13,7 +15,9 @@ namespace secure_channel { namespace { + const char kBleInitiatorReadableRequestTypeForLogging[] = "BLE Initiator"; + } // namespace // The number of times to attempt to connect to a device without receiving any @@ -60,20 +64,24 @@ PendingBleInitiatorConnectionRequest::Factory::BuildInstance( std::unique_ptr<ClientConnectionParameters> client_connection_parameters, ConnectionPriority connection_priority, - PendingConnectionRequestDelegate* delegate) { + PendingConnectionRequestDelegate* delegate, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) { return base::WrapUnique(new PendingBleInitiatorConnectionRequest( - std::move(client_connection_parameters), connection_priority, delegate)); + std::move(client_connection_parameters), connection_priority, delegate, + bluetooth_adapter)); } PendingBleInitiatorConnectionRequest::PendingBleInitiatorConnectionRequest( std::unique_ptr<ClientConnectionParameters> client_connection_parameters, ConnectionPriority connection_priority, - PendingConnectionRequestDelegate* delegate) - : PendingConnectionRequestBase<BleInitiatorFailureType>( + PendingConnectionRequestDelegate* delegate, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) + : PendingBleConnectionRequestBase<BleInitiatorFailureType>( std::move(client_connection_parameters), connection_priority, kBleInitiatorReadableRequestTypeForLogging, - delegate) {} + delegate, + std::move(bluetooth_adapter)) {} PendingBleInitiatorConnectionRequest::~PendingBleInitiatorConnectionRequest() = default;
diff --git a/chromeos/services/secure_channel/pending_ble_initiator_connection_request.h b/chromeos/services/secure_channel/pending_ble_initiator_connection_request.h index b2a2a0a0..05c7722 100644 --- a/chromeos/services/secure_channel/pending_ble_initiator_connection_request.h +++ b/chromeos/services/secure_channel/pending_ble_initiator_connection_request.h
@@ -5,10 +5,12 @@ #ifndef CHROMEOS_SERVICES_SECURE_CHANNEL_PENDING_BLE_INITIATOR_CONNECTION_REQUEST_H_ #define CHROMEOS_SERVICES_SECURE_CHANNEL_PENDING_BLE_INITIATOR_CONNECTION_REQUEST_H_ +#include <memory> + #include "base/macros.h" #include "chromeos/services/secure_channel/ble_initiator_failure_type.h" #include "chromeos/services/secure_channel/client_connection_parameters.h" -#include "chromeos/services/secure_channel/pending_connection_request_base.h" +#include "chromeos/services/secure_channel/pending_ble_connection_request_base.h" #include "chromeos/services/secure_channel/public/cpp/shared/connection_priority.h" namespace chromeos { @@ -17,7 +19,7 @@ // ConnectionRequest corresponding to BLE connections in the initiator role. class PendingBleInitiatorConnectionRequest - : public PendingConnectionRequestBase<BleInitiatorFailureType> { + : public PendingBleConnectionRequestBase<BleInitiatorFailureType> { public: class Factory { public: @@ -28,7 +30,8 @@ BuildInstance(std::unique_ptr<ClientConnectionParameters> client_connection_parameters, ConnectionPriority connection_priority, - PendingConnectionRequestDelegate* delegate); + PendingConnectionRequestDelegate* delegate, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter); private: static Factory* test_factory_; @@ -43,7 +46,8 @@ PendingBleInitiatorConnectionRequest( std::unique_ptr<ClientConnectionParameters> client_connection_parameters, ConnectionPriority connection_priority, - PendingConnectionRequestDelegate* delegate); + PendingConnectionRequestDelegate* delegate, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter); // PendingConnectionRequest<BleInitiatorFailureType>: void HandleConnectionFailure(BleInitiatorFailureType failure_detail) override;
diff --git a/chromeos/services/secure_channel/pending_ble_initiator_connection_request_unittest.cc b/chromeos/services/secure_channel/pending_ble_initiator_connection_request_unittest.cc index 34285a2..d73f9c8 100644 --- a/chromeos/services/secure_channel/pending_ble_initiator_connection_request_unittest.cc +++ b/chromeos/services/secure_channel/pending_ble_initiator_connection_request_unittest.cc
@@ -5,6 +5,7 @@ #include "chromeos/services/secure_channel/pending_ble_initiator_connection_request.h" #include <memory> +#include <utility> #include "base/run_loop.h" #include "base/test/scoped_task_environment.h" @@ -12,6 +13,7 @@ #include "chromeos/services/secure_channel/fake_client_connection_parameters.h" #include "chromeos/services/secure_channel/fake_pending_connection_request_delegate.h" #include "chromeos/services/secure_channel/public/cpp/shared/connection_priority.h" +#include "device/bluetooth/test/mock_bluetooth_adapter.h" #include "testing/gtest/include/gtest/gtest.h" namespace chromeos { @@ -36,12 +38,14 @@ std::make_unique<FakeClientConnectionParameters>(kTestFeature); fake_client_connection_parameters_ = fake_client_connection_parameters.get(); + mock_adapter_ = + base::MakeRefCounted<testing::NiceMock<device::MockBluetoothAdapter>>(); pending_ble_initiator_request_ = PendingBleInitiatorConnectionRequest::Factory::Get()->BuildInstance( std::move(fake_client_connection_parameters), ConnectionPriority::kLow, - fake_pending_connection_request_delegate_.get()); + fake_pending_connection_request_delegate_.get(), mock_adapter_); } const base::Optional< @@ -67,6 +71,7 @@ std::unique_ptr<FakePendingConnectionRequestDelegate> fake_pending_connection_request_delegate_; FakeClientConnectionParameters* fake_client_connection_parameters_; + scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>> mock_adapter_; std::unique_ptr<PendingConnectionRequest<BleInitiatorFailureType>> pending_ble_initiator_request_;
diff --git a/chromeos/services/secure_channel/pending_ble_listener_connection_request.cc b/chromeos/services/secure_channel/pending_ble_listener_connection_request.cc index 4de6e57..b3b4a3f 100644 --- a/chromeos/services/secure_channel/pending_ble_listener_connection_request.cc +++ b/chromeos/services/secure_channel/pending_ble_listener_connection_request.cc
@@ -4,6 +4,8 @@ #include "chromeos/services/secure_channel/pending_ble_listener_connection_request.h" +#include <utility> + #include "base/memory/ptr_util.h" #include "base/no_destructor.h" #include "chromeos/components/proximity_auth/logging/logging.h" @@ -43,20 +45,24 @@ PendingBleListenerConnectionRequest::Factory::BuildInstance( std::unique_ptr<ClientConnectionParameters> client_connection_parameters, ConnectionPriority connection_priority, - PendingConnectionRequestDelegate* delegate) { + PendingConnectionRequestDelegate* delegate, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) { return base::WrapUnique(new PendingBleListenerConnectionRequest( - std::move(client_connection_parameters), connection_priority, delegate)); + std::move(client_connection_parameters), connection_priority, delegate, + bluetooth_adapter)); } PendingBleListenerConnectionRequest::PendingBleListenerConnectionRequest( std::unique_ptr<ClientConnectionParameters> client_connection_parameters, ConnectionPriority connection_priority, - PendingConnectionRequestDelegate* delegate) - : PendingConnectionRequestBase<BleListenerFailureType>( + PendingConnectionRequestDelegate* delegate, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) + : PendingBleConnectionRequestBase<BleListenerFailureType>( std::move(client_connection_parameters), connection_priority, kBleListenerReadableRequestTypeForLogging, - delegate) {} + delegate, + std::move(bluetooth_adapter)) {} PendingBleListenerConnectionRequest::~PendingBleListenerConnectionRequest() = default;
diff --git a/chromeos/services/secure_channel/pending_ble_listener_connection_request.h b/chromeos/services/secure_channel/pending_ble_listener_connection_request.h index 7d6edea..e6fb7685 100644 --- a/chromeos/services/secure_channel/pending_ble_listener_connection_request.h +++ b/chromeos/services/secure_channel/pending_ble_listener_connection_request.h
@@ -5,10 +5,12 @@ #ifndef CHROMEOS_SERVICES_SECURE_CHANNEL_PENDING_BLE_LISTENER_CONNECTION_REQUEST_H_ #define CHROMEOS_SERVICES_SECURE_CHANNEL_PENDING_BLE_LISTENER_CONNECTION_REQUEST_H_ +#include <memory> + #include "base/macros.h" #include "chromeos/services/secure_channel/ble_listener_failure_type.h" #include "chromeos/services/secure_channel/client_connection_parameters.h" -#include "chromeos/services/secure_channel/pending_connection_request_base.h" +#include "chromeos/services/secure_channel/pending_ble_connection_request_base.h" #include "chromeos/services/secure_channel/public/cpp/shared/connection_priority.h" namespace chromeos { @@ -17,7 +19,7 @@ // ConnectionRequest corresponding to BLE connections in the listener role. class PendingBleListenerConnectionRequest - : public PendingConnectionRequestBase<BleListenerFailureType> { + : public PendingBleConnectionRequestBase<BleListenerFailureType> { public: class Factory { public: @@ -28,7 +30,8 @@ BuildInstance(std::unique_ptr<ClientConnectionParameters> client_connection_parameters, ConnectionPriority connection_priority, - PendingConnectionRequestDelegate* delegate); + PendingConnectionRequestDelegate* delegate, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter); private: static Factory* test_factory_; @@ -40,7 +43,8 @@ PendingBleListenerConnectionRequest( std::unique_ptr<ClientConnectionParameters> client_connection_parameters, ConnectionPriority connection_priority, - PendingConnectionRequestDelegate* delegate); + PendingConnectionRequestDelegate* delegate, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter); // PendingConnectionRequest<BleListenerFailureType>: void HandleConnectionFailure(BleListenerFailureType failure_detail) override;
diff --git a/chromeos/services/secure_channel/pending_ble_listener_connection_request_unittest.cc b/chromeos/services/secure_channel/pending_ble_listener_connection_request_unittest.cc index 12872f0..879fcf7 100644 --- a/chromeos/services/secure_channel/pending_ble_listener_connection_request_unittest.cc +++ b/chromeos/services/secure_channel/pending_ble_listener_connection_request_unittest.cc
@@ -5,6 +5,7 @@ #include "chromeos/services/secure_channel/pending_ble_listener_connection_request.h" #include <memory> +#include <utility> #include "base/run_loop.h" #include "base/test/scoped_task_environment.h" @@ -12,6 +13,7 @@ #include "chromeos/services/secure_channel/fake_client_connection_parameters.h" #include "chromeos/services/secure_channel/fake_pending_connection_request_delegate.h" #include "chromeos/services/secure_channel/public/cpp/shared/connection_priority.h" +#include "device/bluetooth/test/mock_bluetooth_adapter.h" #include "testing/gtest/include/gtest/gtest.h" namespace chromeos { @@ -36,12 +38,14 @@ std::make_unique<FakeClientConnectionParameters>(kTestFeature); fake_client_connection_parameters_ = fake_client_connection_parameters.get(); + mock_adapter_ = + base::MakeRefCounted<testing::NiceMock<device::MockBluetoothAdapter>>(); pending_ble_listener_request_ = PendingBleListenerConnectionRequest::Factory::Get()->BuildInstance( std::move(fake_client_connection_parameters), ConnectionPriority::kLow, - fake_pending_connection_request_delegate_.get()); + fake_pending_connection_request_delegate_.get(), mock_adapter_); } const base::Optional< @@ -67,6 +71,7 @@ std::unique_ptr<FakePendingConnectionRequestDelegate> fake_pending_connection_request_delegate_; FakeClientConnectionParameters* fake_client_connection_parameters_; + scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>> mock_adapter_; std::unique_ptr<PendingConnectionRequest<BleListenerFailureType>> pending_ble_listener_request_;
diff --git a/chromeos/services/secure_channel/pending_connection_manager_impl.cc b/chromeos/services/secure_channel/pending_connection_manager_impl.cc index e9b5517..f372325 100644 --- a/chromeos/services/secure_channel/pending_connection_manager_impl.cc +++ b/chromeos/services/secure_channel/pending_connection_manager_impl.cc
@@ -43,16 +43,19 @@ std::unique_ptr<PendingConnectionManager> PendingConnectionManagerImpl::Factory::BuildInstance( Delegate* delegate, - BleConnectionManager* ble_connection_manager) { - return base::WrapUnique( - new PendingConnectionManagerImpl(delegate, ble_connection_manager)); + BleConnectionManager* ble_connection_manager, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) { + return base::WrapUnique(new PendingConnectionManagerImpl( + delegate, ble_connection_manager, bluetooth_adapter)); } PendingConnectionManagerImpl::PendingConnectionManagerImpl( Delegate* delegate, - BleConnectionManager* ble_connection_manager) + BleConnectionManager* ble_connection_manager, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) : PendingConnectionManager(delegate), - ble_connection_manager_(ble_connection_manager) {} + ble_connection_manager_(ble_connection_manager), + bluetooth_adapter_(bluetooth_adapter) {} PendingConnectionManagerImpl::~PendingConnectionManagerImpl() = default; @@ -174,7 +177,7 @@ bool success = connection_attempt->AddPendingConnectionRequest( PendingBleInitiatorConnectionRequest::Factory::Get()->BuildInstance( std::move(client_connection_parameters), connection_priority, - connection_attempt.get() /* delegate */)); + connection_attempt.get() /* delegate */, bluetooth_adapter_)); if (!success) { PA_LOG(ERROR) << "PendingConnectionManagerImpl::" @@ -207,7 +210,7 @@ bool success = connection_attempt->AddPendingConnectionRequest( PendingBleListenerConnectionRequest::Factory::Get()->BuildInstance( std::move(client_connection_parameters), connection_priority, - connection_attempt.get() /* delegate */)); + connection_attempt.get() /* delegate */, bluetooth_adapter_)); if (!success) { PA_LOG(ERROR) << "PendingConnectionManagerImpl::"
diff --git a/chromeos/services/secure_channel/pending_connection_manager_impl.h b/chromeos/services/secure_channel/pending_connection_manager_impl.h index 8bc0984d..fb1d82d 100644 --- a/chromeos/services/secure_channel/pending_connection_manager_impl.h +++ b/chromeos/services/secure_channel/pending_connection_manager_impl.h
@@ -21,6 +21,7 @@ #include "chromeos/services/secure_channel/device_id_pair.h" #include "chromeos/services/secure_channel/pending_connection_manager.h" #include "chromeos/services/secure_channel/public/cpp/shared/connection_priority.h" +#include "device/bluetooth/bluetooth_adapter.h" namespace chromeos { @@ -46,7 +47,8 @@ virtual ~Factory(); virtual std::unique_ptr<PendingConnectionManager> BuildInstance( Delegate* delegate, - BleConnectionManager* ble_connection_manager); + BleConnectionManager* ble_connection_manager, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter); private: static Factory* test_factory_; @@ -55,8 +57,10 @@ ~PendingConnectionManagerImpl() override; private: - PendingConnectionManagerImpl(Delegate* delegate, - BleConnectionManager* ble_connection_manager); + PendingConnectionManagerImpl( + Delegate* delegate, + BleConnectionManager* ble_connection_manager, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter); // PendingConnectionManager: void HandleConnectionRequest( @@ -95,6 +99,7 @@ details_to_attempt_details_map_; BleConnectionManager* ble_connection_manager_; + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter_; DISALLOW_COPY_AND_ASSIGN(PendingConnectionManagerImpl); };
diff --git a/chromeos/services/secure_channel/pending_connection_manager_impl_unittest.cc b/chromeos/services/secure_channel/pending_connection_manager_impl_unittest.cc index 9224a48..a3da3fa 100644 --- a/chromeos/services/secure_channel/pending_connection_manager_impl_unittest.cc +++ b/chromeos/services/secure_channel/pending_connection_manager_impl_unittest.cc
@@ -22,6 +22,7 @@ #include "chromeos/services/secure_channel/fake_pending_connection_request.h" #include "chromeos/services/secure_channel/pending_ble_initiator_connection_request.h" #include "chromeos/services/secure_channel/pending_ble_listener_connection_request.h" +#include "device/bluetooth/test/mock_bluetooth_adapter.h" #include "testing/gtest/include/gtest/gtest.h" namespace chromeos { @@ -211,7 +212,8 @@ BuildInstance( std::unique_ptr<ClientConnectionParameters> client_connection_parameters, ConnectionPriority connection_priority, - PendingConnectionRequestDelegate* delegate) override { + PendingConnectionRequestDelegate* delegate, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) override { EXPECT_EQ(expected_client_connection_parameters_, client_connection_parameters.get()); EXPECT_EQ(*expected_connection_priority_, connection_priority); @@ -257,7 +259,8 @@ BuildInstance( std::unique_ptr<ClientConnectionParameters> client_connection_parameters, ConnectionPriority connection_priority, - PendingConnectionRequestDelegate* delegate) override { + PendingConnectionRequestDelegate* delegate, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) override { EXPECT_EQ(expected_client_connection_parameters_, client_connection_parameters.get()); EXPECT_EQ(*expected_connection_priority_, connection_priority); @@ -337,8 +340,12 @@ PendingBleListenerConnectionRequest::Factory::SetFactoryForTesting( fake_pending_ble_listener_connection_request_factory_.get()); + mock_adapter_ = + base::MakeRefCounted<testing::NiceMock<device::MockBluetoothAdapter>>(); + manager_ = PendingConnectionManagerImpl::Factory::Get()->BuildInstance( - fake_delegate_.get(), fake_ble_connection_manager_.get()); + fake_delegate_.get(), fake_ble_connection_manager_.get(), + mock_adapter_); } void TearDown() override { @@ -592,6 +599,7 @@ fake_pending_ble_initiator_connection_request_factory_; std::unique_ptr<FakePendingBleListenerConnectionRequestFactory> fake_pending_ble_listener_connection_request_factory_; + scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>> mock_adapter_; std::unique_ptr<PendingConnectionManager> manager_;
diff --git a/chromeos/services/secure_channel/public/mojom/secure_channel.mojom b/chromeos/services/secure_channel/public/mojom/secure_channel.mojom index 9426169..f58a8c4 100644 --- a/chromeos/services/secure_channel/public/mojom/secure_channel.mojom +++ b/chromeos/services/secure_channel/public/mojom/secure_channel.mojom
@@ -34,7 +34,13 @@ REMOTE_DEVICE_INVALID_PSK, // Timeouts occurred trying to contact the remote device. - TIMEOUT_FINDING_DEVICE + TIMEOUT_FINDING_DEVICE, + + // The local Bluetooth adapter is disabled (turned off). + ADAPTER_DISABLED, + + // The local Bluetooth adapter is not present. + ADAPTER_NOT_PRESENT }; enum ConnectionCreationDetail {
diff --git a/chromeos/services/secure_channel/secure_channel_impl.cc b/chromeos/services/secure_channel/secure_channel_impl.cc index 1b103e50..0f973f20 100644 --- a/chromeos/services/secure_channel/secure_channel_impl.cc +++ b/chromeos/services/secure_channel/secure_channel_impl.cc
@@ -71,7 +71,8 @@ SecureChannelImpl::SecureChannelImpl( scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) - : timer_factory_(TimerFactoryImpl::Factory::Get()->BuildInstance()), + : bluetooth_adapter_(std::move(bluetooth_adapter)), + timer_factory_(TimerFactoryImpl::Factory::Get()->BuildInstance()), remote_device_cache_( cryptauth::RemoteDeviceCache::Factory::Get()->BuildInstance()), ble_service_data_helper_( @@ -79,13 +80,14 @@ remote_device_cache_.get())), ble_connection_manager_( BleConnectionManagerImpl::Factory::Get()->BuildInstance( - bluetooth_adapter, + bluetooth_adapter_, ble_service_data_helper_.get(), timer_factory_.get())), pending_connection_manager_( PendingConnectionManagerImpl::Factory::Get()->BuildInstance( this /* delegate */, - ble_connection_manager_.get())), + ble_connection_manager_.get(), + bluetooth_adapter_)), active_connection_manager_( ActiveConnectionManagerImpl::Factory::Get()->BuildInstance( this /* delegate */)) {} @@ -207,6 +209,17 @@ return; } + // Check 4: Medium-specific verification. + switch (connection_medium) { + case ConnectionMedium::kBluetoothLowEnergy: + // Is the local Bluetooth adapter disabled or not present? If either, + // notify client and return early. + if (CheckIfBluetoothAdapterDisabledOrNotPresent( + api_fn_name, client_connection_parameters.get())) + return; + break; + } + // At this point, the request has been deemed valid. ConnectionAttemptDetails connection_attempt_details( device_to_connect.GetDeviceId(), local_device.GetDeviceId(), @@ -320,6 +333,26 @@ return true; } +bool SecureChannelImpl::CheckIfBluetoothAdapterDisabledOrNotPresent( + ApiFunctionName api_fn_name, + ClientConnectionParameters* client_connection_parameters) { + if (!bluetooth_adapter_->IsPresent()) { + RejectRequestForReason( + api_fn_name, mojom::ConnectionAttemptFailureReason::ADAPTER_NOT_PRESENT, + client_connection_parameters); + return true; + } + + if (!bluetooth_adapter_->IsPowered()) { + RejectRequestForReason( + api_fn_name, mojom::ConnectionAttemptFailureReason::ADAPTER_DISABLED, + client_connection_parameters); + return true; + } + + return false; +} + base::Optional<SecureChannelImpl::InvalidRemoteDeviceReason> SecureChannelImpl::AddDeviceToCacheIfPossible( ApiFunctionName api_fn_name,
diff --git a/chromeos/services/secure_channel/secure_channel_impl.h b/chromeos/services/secure_channel/secure_channel_impl.h index 6bd6447..055322b 100644 --- a/chromeos/services/secure_channel/secure_channel_impl.h +++ b/chromeos/services/secure_channel/secure_channel_impl.h
@@ -56,7 +56,8 @@ ~SecureChannelImpl() override; private: - SecureChannelImpl(scoped_refptr<device::BluetoothAdapter> bluetooth_adapter); + explicit SecureChannelImpl( + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter); enum class InvalidRemoteDeviceReason { kInvalidPublicKey, kInvalidPsk }; @@ -136,6 +137,12 @@ ClientConnectionParameters* client_connection_parameters, bool is_local_device); + // Checks if |bluetooth_adapter_| is disabled or not present and rejects the + // connection request if so. Returns whether the request was rejected. + bool CheckIfBluetoothAdapterDisabledOrNotPresent( + ApiFunctionName api_fn_name, + ClientConnectionParameters* client_connection_parameters); + // Validates |device| and adds it to the |remote_device_cache_| if it is // valid. If it is not valid, the reason is provided as a return type, and the // device is not added to the cache. @@ -143,6 +150,7 @@ ApiFunctionName api_fn_name, const cryptauth::RemoteDevice& device); + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter_; std::unique_ptr<TimerFactory> timer_factory_; std::unique_ptr<cryptauth::RemoteDeviceCache> remote_device_cache_; std::unique_ptr<BleServiceDataHelper> ble_service_data_helper_;
diff --git a/chromeos/services/secure_channel/secure_channel_service_unittest.cc b/chromeos/services/secure_channel/secure_channel_service_unittest.cc index d528d23..e39e52b 100644 --- a/chromeos/services/secure_channel/secure_channel_service_unittest.cc +++ b/chromeos/services/secure_channel/secure_channel_service_unittest.cc
@@ -178,7 +178,8 @@ // PendingConnectionManagerImpl::Factory: std::unique_ptr<PendingConnectionManager> BuildInstance( PendingConnectionManager::Delegate* delegate, - BleConnectionManager* ble_connection_manager) override { + BleConnectionManager* ble_connection_manager, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) override { EXPECT_FALSE(instance_); EXPECT_EQ(fake_ble_connection_manager_factory_->instance(), ble_connection_manager); @@ -321,6 +322,14 @@ void SetUp() override { mock_adapter_ = base::MakeRefCounted<testing::NiceMock<device::MockBluetoothAdapter>>(); + is_adapter_powered_ = true; + is_adapter_present_ = true; + ON_CALL(*mock_adapter_, IsPresent()) + .WillByDefault( + Invoke(this, &SecureChannelServiceTest::is_adapter_present)); + ON_CALL(*mock_adapter_, IsPowered()) + .WillByDefault( + Invoke(this, &SecureChannelServiceTest::is_adapter_powered)); device::BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter_); test_task_runner_ = base::MakeRefCounted<base::TestSimpleTaskRunner>(); @@ -584,6 +593,12 @@ const cryptauth::RemoteDeviceList& test_devices() { return test_devices_; } + bool is_adapter_present() { return is_adapter_present_; } + void set_is_adapter_present(bool present) { is_adapter_present_ = present; } + + bool is_adapter_powered() { return is_adapter_powered_; } + void set_is_adapter_powered(bool powered) { is_adapter_powered_ = powered; } + private: void AttemptConnectionAndVerifyPendingConnection( const cryptauth::RemoteDevice& device_to_connect, @@ -853,6 +868,9 @@ std::unique_ptr<service_manager::TestConnectorFactory> connector_factory_; std::unique_ptr<service_manager::Connector> connector_; + bool is_adapter_powered_; + bool is_adapter_present_; + mojom::SecureChannelPtr secure_channel_ptr_; DISALLOW_COPY_AND_ASSIGN(SecureChannelServiceTest); @@ -948,6 +966,48 @@ mojom::ConnectionAttemptFailureReason::LOCAL_DEVICE_INVALID_PSK); } +TEST_F(SecureChannelServiceTest, + ListenForConnection_BluetoothAdapterNotPresent) { + FinishInitialization(); + + set_is_adapter_present(false); + + CallListenForConnectionFromDeviceAndVerifyRejection( + test_devices()[0], test_devices()[1], "feature", ConnectionPriority::kLow, + mojom::ConnectionAttemptFailureReason::ADAPTER_NOT_PRESENT); +} + +TEST_F(SecureChannelServiceTest, + InitiateConnection_BluetoothAdapterNotPresent) { + FinishInitialization(); + + set_is_adapter_present(false); + + CallInitiateConnectionToDeviceAndVerifyRejection( + test_devices()[0], test_devices()[1], "feature", ConnectionPriority::kLow, + mojom::ConnectionAttemptFailureReason::ADAPTER_NOT_PRESENT); +} + +TEST_F(SecureChannelServiceTest, ListenForConnection_BluetoothAdapterDisabled) { + FinishInitialization(); + + set_is_adapter_powered(false); + + CallListenForConnectionFromDeviceAndVerifyRejection( + test_devices()[0], test_devices()[1], "feature", ConnectionPriority::kLow, + mojom::ConnectionAttemptFailureReason::ADAPTER_DISABLED); +} + +TEST_F(SecureChannelServiceTest, InitiateConnection_BluetoothAdapterDisabled) { + FinishInitialization(); + + set_is_adapter_powered(false); + + CallInitiateConnectionToDeviceAndVerifyRejection( + test_devices()[0], test_devices()[1], "feature", ConnectionPriority::kLow, + mojom::ConnectionAttemptFailureReason::ADAPTER_DISABLED); +} + TEST_F(SecureChannelServiceTest, CallsQueuedBeforeInitializationComplete) { CallInitiateConnectionToDeviceAndVerifyInitializationNotComplete( test_devices()[4], test_devices()[5], "feature",
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn index c2ccf59..e8efb43 100644 --- a/components/arc/BUILD.gn +++ b/components/arc/BUILD.gn
@@ -39,7 +39,6 @@ "intent_helper/open_url_delegate.h", "lock_screen/arc_lock_screen_bridge.cc", "lock_screen/arc_lock_screen_bridge.h", - "metrics/arc_metrics_constants.h", "metrics/arc_metrics_service.cc", "metrics/arc_metrics_service.h", "midis/arc_midis_bridge.cc", @@ -71,6 +70,7 @@ public_deps = [ ":arc_base", + ":arc_metrics_constants", ":prefs", ] @@ -273,6 +273,12 @@ ] } +source_set("arc_metrics_constants") { + sources = [ + "metrics/arc_metrics_constants.h", + ] +} + source_set("unit_tests") { testonly = true sources = [
diff --git a/components/content_settings/core/browser/content_settings_registry.cc b/components/content_settings/core/browser/content_settings_registry.cc index abd4458..1cc5496 100644 --- a/components/content_settings/core/browser/content_settings_registry.cc +++ b/components/content_settings/core/browser/content_settings_registry.cc
@@ -21,8 +21,8 @@ namespace { -base::LazyInstance<ContentSettingsRegistry>::DestructorAtExit g_instance = - LAZY_INSTANCE_INITIALIZER; +base::LazyInstance<ContentSettingsRegistry>::DestructorAtExit + g_content_settings_registry_instance = LAZY_INSTANCE_INITIALIZER; // TODO(raymes): These overloaded functions make the registration code clearer. // When initializer lists are available they won't be needed. The initializer @@ -73,7 +73,7 @@ // static ContentSettingsRegistry* ContentSettingsRegistry::GetInstance() { - return g_instance.Pointer(); + return g_content_settings_registry_instance.Pointer(); } ContentSettingsRegistry::ContentSettingsRegistry()
diff --git a/components/content_settings/core/browser/website_settings_registry.cc b/components/content_settings/core/browser/website_settings_registry.cc index 8eee62f..8d22b75 100644 --- a/components/content_settings/core/browser/website_settings_registry.cc +++ b/components/content_settings/core/browser/website_settings_registry.cc
@@ -15,7 +15,7 @@ namespace { base::LazyInstance<content_settings::WebsiteSettingsRegistry>::DestructorAtExit - g_instance = LAZY_INSTANCE_INITIALIZER; + g_website_settings_registry_instance = LAZY_INSTANCE_INITIALIZER; } // namespace @@ -23,7 +23,7 @@ // static WebsiteSettingsRegistry* WebsiteSettingsRegistry::GetInstance() { - return g_instance.Pointer(); + return g_website_settings_registry_instance.Pointer(); } WebsiteSettingsRegistry::WebsiteSettingsRegistry() {
diff --git a/components/drive/chromeos/file_system.cc b/components/drive/chromeos/file_system.cc index 5fc38d20..e35a472 100644 --- a/components/drive/chromeos/file_system.cc +++ b/components/drive/chromeos/file_system.cc
@@ -935,13 +935,11 @@ for (const auto& change : entry.second.list()) { DCHECK(!change.team_drive_id().empty()); if (change.IsDelete()) { - const auto it = - team_drive_change_list_loaders_.find(change.team_drive_id()); - DCHECK(it != team_drive_change_list_loaders_.end()); - team_drive_change_list_loaders_.erase(it); - // If we were tracking the update status we can remove that as well. - last_update_metadata_.erase(change.team_drive_id()); - removed_team_drives.insert(change.team_drive_id()); + if (team_drive_change_list_loaders_.erase(change.team_drive_id()) > 0) { + // If we were tracking the update status we can remove that as well. + last_update_metadata_.erase(change.team_drive_id()); + removed_team_drives.insert(change.team_drive_id()); + } } else if (change.IsAddOrUpdate()) { // If this is an update (e.g. a renamed team drive), then just erase the // existing entry so we can re-add it with the new path.
diff --git a/components/drive/file_system_unittest.cc b/components/drive/file_system_unittest.cc index 1740cbf..6560b57 100644 --- a/components/drive/file_system_unittest.cc +++ b/components/drive/file_system_unittest.cc
@@ -1500,4 +1500,34 @@ EXPECT_LE(now, team_drive_metadata["td_id_2_2"].last_update_check_time); } +TEST_F(FileSystemTest, RemoveNonExistingTeamDrive) { + ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_SERVER_TIMESTAMP)); + ASSERT_TRUE(SetupTeamDrives()); + + // The first load will trigger the loading of team drives. + ReadDirectorySync(base::FilePath::FromUTF8Unsafe(".")); + + // Create a file change with a delete team drive, ensure file_system_ does not + // crash. + const base::FilePath path = + util::GetDriveTeamDrivesRootPath().Append("team_drive_2"); + std::unique_ptr<ResourceEntry> entry = GetResourceEntrySync(path); + ASSERT_TRUE(entry); + + drive::FileChange change; + change.Update(path, *entry, FileChange::CHANGE_TYPE_DELETE); + + // First time should be removed. + file_system_->OnTeamDrivesChanged(change); + std::set<std::string> expected_changes = {"td_id_2"}; + EXPECT_EQ(expected_changes, + mock_directory_observer_->removed_team_drive_ids()); + + // Second time should be no changes, and no crash. + file_system_->OnTeamDrivesChanged(change); + expected_changes = {}; + EXPECT_EQ(expected_changes, + mock_directory_observer_->removed_team_drive_ids()); +} + } // namespace drive
diff --git a/components/image_fetcher/OWNERS b/components/image_fetcher/OWNERS index 88fe2d2..b8b98892 100644 --- a/components/image_fetcher/OWNERS +++ b/components/image_fetcher/OWNERS
@@ -1,3 +1,4 @@ +fgorski@chromium.org markusheintz@chromium.org mathp@chromium.org treib@chromium.org
diff --git a/components/offline_pages/core/client_policy_controller.cc b/components/offline_pages/core/client_policy_controller.cc index 03eb030..9e3ea54 100644 --- a/components/offline_pages/core/client_policy_controller.cc +++ b/components/offline_pages/core/client_policy_controller.cc
@@ -27,7 +27,7 @@ kUnlimitedPages, kUnlimitedPages) .SetExpirePeriod(base::TimeDelta::FromDays(30)) .SetIsSupportedByRecentTabs(true) - .SetIsOnlyShownInOriginalTab(true) + .SetIsRestrictedToTabFromClientId(true) .Build())); policies_.insert(std::make_pair( kAsyncNamespace, @@ -89,7 +89,7 @@ kUnlimitedPages, 1) .SetIsRemovedOnCacheReset(true) .SetExpirePeriod(base::TimeDelta::FromHours(1)) - .SetIsOnlyShownInOriginalTab(true) + .SetIsRestrictedToTabFromClientId(true) .Build())); // Fallback policy. @@ -204,23 +204,25 @@ return *recent_tab_namespace_cache_; } -bool ClientPolicyController::IsRestrictedToOriginalTab( +bool ClientPolicyController::IsRestrictedToTabFromClientId( const std::string& name_space) const { - return GetPolicy(name_space).feature_policy.only_shown_in_original_tab; + return GetPolicy(name_space) + .feature_policy.is_restricted_to_tab_from_client_id; } const std::vector<std::string>& -ClientPolicyController::GetNamespacesRestrictedToOriginalTab() const { - if (show_in_original_tab_cache_) - return *show_in_original_tab_cache_; +ClientPolicyController::GetNamespacesRestrictedToTabFromClientId() const { + if (restricted_to_tab_from_client_id_cache_) + return *restricted_to_tab_from_client_id_cache_; - show_in_original_tab_cache_ = std::make_unique<std::vector<std::string>>(); + restricted_to_tab_from_client_id_cache_ = + std::make_unique<std::vector<std::string>>(); for (const auto& policy_item : policies_) { - if (policy_item.second.feature_policy.only_shown_in_original_tab) - show_in_original_tab_cache_->emplace_back(policy_item.first); + if (policy_item.second.feature_policy.is_restricted_to_tab_from_client_id) + restricted_to_tab_from_client_id_cache_->emplace_back(policy_item.first); } - return *show_in_original_tab_cache_; + return *restricted_to_tab_from_client_id_cache_; } bool ClientPolicyController::IsDisabledWhenPrefetchDisabled(
diff --git a/components/offline_pages/core/client_policy_controller.h b/components/offline_pages/core/client_policy_controller.h index 9084d9b..faef82cd 100644 --- a/components/offline_pages/core/client_policy_controller.h +++ b/components/offline_pages/core/client_policy_controller.h
@@ -57,10 +57,13 @@ const std::vector<std::string>& GetNamespacesShownAsRecentlyVisitedSite() const; - // Returns whether pages for |name_space| should never be shown outside the - // tab they were generated in. - bool IsRestrictedToOriginalTab(const std::string& name_space) const; - const std::vector<std::string>& GetNamespacesRestrictedToOriginalTab() const; + // Returns whether pages for |name_space| should only be opened in a + // specifically assigned tab. + // Note: For this restriction to work offline pages saved to this namespace + // must have the respective tab id set to their ClientId::id field. + bool IsRestrictedToTabFromClientId(const std::string& name_space) const; + const std::vector<std::string>& GetNamespacesRestrictedToTabFromClientId() + const; bool IsDisabledWhenPrefetchDisabled(const std::string& name_space) const; const std::vector<std::string>& GetNamespacesDisabledWhenPrefetchDisabled() @@ -89,7 +92,8 @@ mutable std::unique_ptr<std::vector<std::string>> user_requested_download_namespace_cache_; mutable std::unique_ptr<std::vector<std::string>> recent_tab_namespace_cache_; - mutable std::unique_ptr<std::vector<std::string>> show_in_original_tab_cache_; + mutable std::unique_ptr<std::vector<std::string>> + restricted_to_tab_from_client_id_cache_; mutable std::unique_ptr<std::vector<std::string>> disabled_when_prefetch_disabled_cache_;
diff --git a/components/offline_pages/core/client_policy_controller_unittest.cc b/components/offline_pages/core/client_policy_controller_unittest.cc index a88e0be..ff3e7bf 100644 --- a/components/offline_pages/core/client_policy_controller_unittest.cc +++ b/components/offline_pages/core/client_policy_controller_unittest.cc
@@ -35,7 +35,8 @@ void ExpectUserRequestedDownloadSupport(std::string name_space, bool expectation); void ExpectRecentTab(std::string name_space, bool expectation); - void ExpectOnlyOriginalTab(std::string name_space, bool expectation); + void ExpectRestrictedToTabFromClientId(std::string name_space, + bool expectation); void ExpectDisabledWhenPrefetchDisabled(std::string name_space, bool expectation); @@ -106,19 +107,21 @@ " a recently visited site."; } -void ClientPolicyControllerTest::ExpectOnlyOriginalTab(std::string name_space, - bool expectation) { +void ClientPolicyControllerTest::ExpectRestrictedToTabFromClientId( + std::string name_space, + bool expectation) { std::vector<std::string> cache = - controller()->GetNamespacesRestrictedToOriginalTab(); + controller()->GetNamespacesRestrictedToTabFromClientId(); auto result = std::find(cache.begin(), cache.end(), name_space); EXPECT_EQ(expectation, result != cache.end()) << "Namespace " << name_space << " had incorrect restriction when getting namespaces restricted to" - " the original tab"; - EXPECT_EQ(expectation, controller()->IsRestrictedToOriginalTab(name_space)) + " the tab from the client id field"; + EXPECT_EQ(expectation, + controller()->IsRestrictedToTabFromClientId(name_space)) << "Namespace " << name_space << " had incorrect restriction when directly checking if the namespace" - " is restricted to the original tab"; + " is restricted to the tab from the client id field"; } void ClientPolicyControllerTest::ExpectDisabledWhenPrefetchDisabled( @@ -147,7 +150,7 @@ ExpectDownloadSupport(kUndefinedNamespace, false); ExpectUserRequestedDownloadSupport(kUndefinedNamespace, false); ExpectRecentTab(kUndefinedNamespace, false); - ExpectOnlyOriginalTab(kUndefinedNamespace, false); + ExpectRestrictedToTabFromClientId(kUndefinedNamespace, false); ExpectDisabledWhenPrefetchDisabled(kUndefinedNamespace, false); } @@ -160,7 +163,7 @@ ExpectDownloadSupport(kBookmarkNamespace, false); ExpectUserRequestedDownloadSupport(kBookmarkNamespace, false); ExpectRecentTab(kBookmarkNamespace, false); - ExpectOnlyOriginalTab(kBookmarkNamespace, false); + ExpectRestrictedToTabFromClientId(kBookmarkNamespace, false); ExpectDisabledWhenPrefetchDisabled(kBookmarkNamespace, false); } @@ -173,7 +176,7 @@ ExpectDownloadSupport(kLastNNamespace, false); ExpectUserRequestedDownloadSupport(kLastNNamespace, false); ExpectRecentTab(kLastNNamespace, true); - ExpectOnlyOriginalTab(kLastNNamespace, true); + ExpectRestrictedToTabFromClientId(kLastNNamespace, true); ExpectDisabledWhenPrefetchDisabled(kLastNNamespace, false); } @@ -186,7 +189,7 @@ ExpectDownloadSupport(kAsyncNamespace, true); ExpectUserRequestedDownloadSupport(kAsyncNamespace, true); ExpectRecentTab(kAsyncNamespace, false); - ExpectOnlyOriginalTab(kAsyncNamespace, false); + ExpectRestrictedToTabFromClientId(kAsyncNamespace, false); ExpectDisabledWhenPrefetchDisabled(kAsyncNamespace, false); } @@ -199,7 +202,7 @@ ExpectDownloadSupport(kCCTNamespace, false); ExpectUserRequestedDownloadSupport(kCCTNamespace, false); ExpectRecentTab(kCCTNamespace, false); - ExpectOnlyOriginalTab(kCCTNamespace, false); + ExpectRestrictedToTabFromClientId(kCCTNamespace, false); ExpectDisabledWhenPrefetchDisabled(kCCTNamespace, true); } @@ -212,7 +215,7 @@ ExpectDownloadSupport(kDownloadNamespace, true); ExpectUserRequestedDownloadSupport(kDownloadNamespace, true); ExpectRecentTab(kDownloadNamespace, false); - ExpectOnlyOriginalTab(kDownloadNamespace, false); + ExpectRestrictedToTabFromClientId(kDownloadNamespace, false); ExpectDisabledWhenPrefetchDisabled(kDownloadNamespace, false); } @@ -226,7 +229,7 @@ ExpectDownloadSupport(kNTPSuggestionsNamespace, true); ExpectUserRequestedDownloadSupport(kNTPSuggestionsNamespace, true); ExpectRecentTab(kNTPSuggestionsNamespace, false); - ExpectOnlyOriginalTab(kNTPSuggestionsNamespace, false); + ExpectRestrictedToTabFromClientId(kNTPSuggestionsNamespace, false); ExpectDisabledWhenPrefetchDisabled(kNTPSuggestionsNamespace, false); } @@ -240,7 +243,7 @@ ExpectDownloadSupport(kSuggestedArticlesNamespace, false); ExpectUserRequestedDownloadSupport(kSuggestedArticlesNamespace, false); ExpectRecentTab(kSuggestedArticlesNamespace, false); - ExpectOnlyOriginalTab(kSuggestedArticlesNamespace, false); + ExpectRestrictedToTabFromClientId(kSuggestedArticlesNamespace, false); ExpectDisabledWhenPrefetchDisabled(kSuggestedArticlesNamespace, true); } @@ -254,7 +257,7 @@ ExpectDownloadSupport(kLivePageSharingNamespace, false); ExpectUserRequestedDownloadSupport(kLivePageSharingNamespace, false); ExpectRecentTab(kLivePageSharingNamespace, false); - ExpectOnlyOriginalTab(kLivePageSharingNamespace, true); + ExpectRestrictedToTabFromClientId(kLivePageSharingNamespace, true); ExpectDisabledWhenPrefetchDisabled(kLivePageSharingNamespace, false); }
diff --git a/components/offline_pages/core/offline_page_client_policy.h b/components/offline_pages/core/offline_page_client_policy.h index 33e8a23..de73ea2 100644 --- a/components/offline_pages/core/offline_page_client_policy.h +++ b/components/offline_pages/core/offline_page_client_policy.h
@@ -53,8 +53,10 @@ bool is_user_requested_download; // Whether pages are shown in recent tabs ui. bool is_supported_by_recent_tabs; - // Whether pages should only be viewed in the tab they were generated in. - bool only_shown_in_original_tab; + // Whether pages can only be viewed in a specific tab. Pages controlled by + // this policy must have their ClientId::id field set to their assigned tab's + // id. + bool is_restricted_to_tab_from_client_id; // Whether pages are removed on user-initiated cache reset. Defaults to true. bool is_removed_on_cache_reset; // Whether the namespace should be disabled if prefetching-related preferences @@ -69,7 +71,7 @@ : is_supported_by_download(false), is_user_requested_download(false), is_supported_by_recent_tabs(false), - only_shown_in_original_tab(false), + is_restricted_to_tab_from_client_id(false), is_removed_on_cache_reset(true), disabled_when_prefetch_disabled(false), is_suggested(false), @@ -157,10 +159,10 @@ return *this; } - OfflinePageClientPolicyBuilder& SetIsOnlyShownInOriginalTab( - const bool only_shown_in_original_tab) { - policy_.feature_policy.only_shown_in_original_tab = - only_shown_in_original_tab; + OfflinePageClientPolicyBuilder& SetIsRestrictedToTabFromClientId( + const bool is_restricted_to_tab_from_client_id) { + policy_.feature_policy.is_restricted_to_tab_from_client_id = + is_restricted_to_tab_from_client_id; return *this; }
diff --git a/components/omnibox/browser/bookmark_provider.cc b/components/omnibox/browser/bookmark_provider.cc index b9035d4..42802e63 100644 --- a/components/omnibox/browser/bookmark_provider.cc +++ b/components/omnibox/browser/bookmark_provider.cc
@@ -36,8 +36,6 @@ void BookmarkProvider::Start(const AutocompleteInput& input, bool minimal_changes) { TRACE_EVENT0("omnibox", "BookmarkProvider::Start"); - if (minimal_changes) - return; matches_.clear(); if (input.from_omnibox_focus() || input.text().empty())
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn index 943f5bac7..aea0cee5 100644 --- a/components/password_manager/core/browser/BUILD.gn +++ b/components/password_manager/core/browser/BUILD.gn
@@ -159,8 +159,6 @@ "webdata/logins_table.cc", "webdata/logins_table.h", "webdata/logins_table_win.cc", - "webdata/password_web_data_service_win.cc", - "webdata/password_web_data_service_win.h", ] if (password_reuse_detection_support) {
diff --git a/components/password_manager/core/browser/webdata/password_web_data_service_win.cc b/components/password_manager/core/browser/webdata/password_web_data_service_win.cc deleted file mode 100644 index efbc3916..0000000 --- a/components/password_manager/core/browser/webdata/password_web_data_service_win.cc +++ /dev/null
@@ -1,70 +0,0 @@ -// Copyright 2014 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/password_manager/core/browser/webdata/password_web_data_service_win.h" - -#include "base/bind.h" -#include "base/single_thread_task_runner.h" -#include "components/os_crypt/ie7_password_win.h" -#include "components/password_manager/core/browser/webdata/logins_table.h" -#include "components/webdata/common/web_database_service.h" - -PasswordWebDataService::PasswordWebDataService( - scoped_refptr<WebDatabaseService> wdbs, - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, - const ProfileErrorCallback& callback) - : WebDataServiceBase(wdbs, callback, ui_task_runner) {} - -void PasswordWebDataService::AddIE7Login(const IE7PasswordInfo& info) { - wdbs_->ScheduleDBTask( - FROM_HERE, - base::Bind(&PasswordWebDataService::AddIE7LoginImpl, this, info)); -} - -void PasswordWebDataService::RemoveIE7Login(const IE7PasswordInfo& info) { - wdbs_->ScheduleDBTask( - FROM_HERE, - base::Bind(&PasswordWebDataService::RemoveIE7LoginImpl, this, info)); -} - -PasswordWebDataService::Handle PasswordWebDataService::GetIE7Login( - const IE7PasswordInfo& info, - WebDataServiceConsumer* consumer) { - return wdbs_->ScheduleDBTaskWithResult( - FROM_HERE, - base::Bind(&PasswordWebDataService::GetIE7LoginImpl, this, info), - consumer); -} - -WebDatabase::State PasswordWebDataService::AddIE7LoginImpl( - const IE7PasswordInfo& info, - WebDatabase* db) { - return LoginsTable::FromWebDatabase(db)->AddIE7Login(info) ? - WebDatabase::COMMIT_NEEDED : WebDatabase::COMMIT_NOT_NEEDED; -} - -WebDatabase::State PasswordWebDataService::RemoveIE7LoginImpl( - const IE7PasswordInfo& info, - WebDatabase* db) { - return LoginsTable::FromWebDatabase(db)->RemoveIE7Login(info) ? - WebDatabase::COMMIT_NEEDED : WebDatabase::COMMIT_NOT_NEEDED; -} - -std::unique_ptr<WDTypedResult> PasswordWebDataService::GetIE7LoginImpl( - const IE7PasswordInfo& info, - WebDatabase* db) { - IE7PasswordInfo result; - LoginsTable::FromWebDatabase(db)->GetIE7Login(info, &result); - return std::unique_ptr<WDTypedResult>( - new WDResult<IE7PasswordInfo>(PASSWORD_IE7_RESULT, result)); -} - -//////////////////////////////////////////////////////////////////////////////// - -PasswordWebDataService::PasswordWebDataService( - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) - : WebDataServiceBase(nullptr, ProfileErrorCallback(), ui_task_runner) {} - -PasswordWebDataService::~PasswordWebDataService() { -}
diff --git a/components/password_manager/core/browser/webdata/password_web_data_service_win.h b/components/password_manager/core/browser/webdata/password_web_data_service_win.h deleted file mode 100644 index 08d0e535..0000000 --- a/components/password_manager/core/browser/webdata/password_web_data_service_win.h +++ /dev/null
@@ -1,78 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_WEBDATA_PASSWORD_WEB_DATA_SERVICE_WIN_H_ -#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_WEBDATA_PASSWORD_WEB_DATA_SERVICE_WIN_H_ - -#include <vector> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/sequenced_task_runner_helpers.h" -#include "components/webdata/common/web_data_results.h" -#include "components/webdata/common/web_data_service_base.h" -#include "components/webdata/common/web_data_service_consumer.h" -#include "components/webdata/common/web_database.h" - -struct IE7PasswordInfo; -class WebDatabaseService; - -namespace base { -class SingleThreadTaskRunner; -} - -namespace content { -class BrowserContext; -} - -// PasswordWebDataService is used to access IE7/8 Password data stored in the -// web database. All data is retrieved and archived in an asynchronous way. - -class WebDataServiceConsumer; - -class PasswordWebDataService : public WebDataServiceBase { - public: - // Retrieves a WebDataService for the given context. - static scoped_refptr<PasswordWebDataService> FromBrowserContext( - content::BrowserContext* context); - - PasswordWebDataService( - scoped_refptr<WebDatabaseService> wdbs, - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, - const ProfileErrorCallback& callback); - - // Adds |info| to the list of imported passwords from ie7/ie8. - void AddIE7Login(const IE7PasswordInfo& info); - - // Removes |info| from the list of imported passwords from ie7/ie8. - void RemoveIE7Login(const IE7PasswordInfo& info); - - // Gets the login matching the information in |info|. |consumer| will be - // notified when the request is done. The result is of type - // WDResult<IE7PasswordInfo>. - // If there is no match, the fields of the IE7PasswordInfo will be empty. - // All requests return a handle. The handle can be used to cancel the request. - Handle GetIE7Login(const IE7PasswordInfo& info, - WebDataServiceConsumer* consumer); - - protected: - // For unit tests, passes a null callback. - PasswordWebDataService( - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner); - - ~PasswordWebDataService() override; - - private: - // The following methods are only invoked on the DB sequence. - WebDatabase::State AddIE7LoginImpl(const IE7PasswordInfo& info, - WebDatabase* db); - WebDatabase::State RemoveIE7LoginImpl(const IE7PasswordInfo& info, - WebDatabase* db); - std::unique_ptr<WDTypedResult> GetIE7LoginImpl(const IE7PasswordInfo& info, - WebDatabase* db); - - DISALLOW_COPY_AND_ASSIGN(PasswordWebDataService); -}; - -#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_WEBDATA_PASSWORD_WEB_DATA_SERVICE_WIN_H_
diff --git a/components/translate/core/browser/BUILD.gn b/components/translate/core/browser/BUILD.gn index edcc98b..7032eb0 100644 --- a/components/translate/core/browser/BUILD.gn +++ b/components/translate/core/browser/BUILD.gn
@@ -59,6 +59,7 @@ "//components/strings", "//components/translate/core/common", "//components/variations", + "//components/variations/net", "//google_apis", "//net", "//services/metrics/public/cpp:metrics_cpp",
diff --git a/components/translate/core/browser/resources/translate.js b/components/translate/core/browser/resources/translate.js index fc78eaa8..595695d 100644 --- a/components/translate/core/browser/resources/translate.js +++ b/components/translate/core/browser/resources/translate.js
@@ -104,21 +104,29 @@ /** * Callback invoked when Translate Element's ready state is known. - * @type {Function} + * @type {function} */ var readyCallback; /** * Callback invoked when Translate Element's translation result is known. - * @type {Function} + * @type {function} */ var resultCallback; /** + * Callback invoked when Translate Element requests load of javascript files. + * Currently main.js and element_main.js are expected to be loaded. + * @type {function(string)} + */ + var loadJavascriptCallback; + + /** * Listens to security policy violations to set |errorCode|. */ document.addEventListener('securitypolicyviolation', function(event) { - if (securityOrigin.startsWith(event.blockedURI)) { + if (securityOrigin.startsWith(event.blockedURI) && + event.effectiveDirective == 'script-src') { errorCode = ERROR['BAD_ORIGIN']; invokeReadyCallback(); } @@ -175,7 +183,7 @@ return { /** * Setter for readyCallback. No op if already set. - * @param {Function} callback The function to be invoked. + * @param {function} callback The function to be invoked. */ set readyCallback(callback) { if (!readyCallback) { @@ -185,7 +193,7 @@ /** * Setter for resultCallback. No op if already set. - * @param {Function} callback The function to be invoked. + * @param {function} callback The function to be invoked. */ set resultCallback(callback) { if (!resultCallback) { @@ -194,6 +202,16 @@ }, /** + * Setter for loadJavascriptCallback. No op if already set. + * @param {function(string)} callback The function to be invoked. + */ + set loadJavascriptCallback(callback) { + if (!loadJavascriptCallback) { + loadJavascriptCallback = callback; + } + }, + + /** * Whether the library is ready. * The translate function should only be called when |libReady| is true. * @type {boolean} @@ -374,6 +392,12 @@ errorCode = ERROR['BAD_ORIGIN']; return; } + + if (loadJavascriptCallback) { + loadJavascriptCallback(url); + return; + } + var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.onreadystatechange = function() {
diff --git a/components/translate/core/browser/translate_language_list.cc b/components/translate/core/browser/translate_language_list.cc index 69618a4..1fdd0c4 100644 --- a/components/translate/core/browser/translate_language_list.cc +++ b/components/translate/core/browser/translate_language_list.cc
@@ -227,8 +227,12 @@ NotifyEvent(__LINE__, message); bool result = language_list_fetcher_->Request( - url, base::BindOnce(&TranslateLanguageList::OnLanguageListFetchComplete, - base::Unretained(this))); + url, + base::BindOnce(&TranslateLanguageList::OnLanguageListFetchComplete, + base::Unretained(this)), + // Use the strictest mode for request headers, since incognito state is + // not known. + /*is_incognito=*/true); if (!result) NotifyEvent(__LINE__, "Request is omitted due to retry limit"); }
diff --git a/components/translate/core/browser/translate_manager.cc b/components/translate/core/browser/translate_manager.cc index 74ea2d39..725d883 100644 --- a/components/translate/core/browser/translate_manager.cc +++ b/components/translate/core/browser/translate_manager.cc
@@ -369,7 +369,7 @@ base::Bind(&TranslateManager::OnTranslateScriptFetchComplete, GetWeakPtr(), source_lang, target_lang); - script->Request(callback); + script->Request(callback, translate_driver_->IsIncognito()); } void TranslateManager::RevertTranslation() {
diff --git a/components/translate/core/browser/translate_script.cc b/components/translate/core/browser/translate_script.cc index a87b45f3..1225ba6c 100644 --- a/components/translate/core/browser/translate_script.cc +++ b/components/translate/core/browser/translate_script.cc
@@ -59,7 +59,8 @@ TranslateScript::~TranslateScript() { } -void TranslateScript::Request(const RequestCallback& callback) { +void TranslateScript::Request(const RequestCallback& callback, + bool is_incognito) { script_fetch_start_time_ = base::Time::Now().ToJsTime(); DCHECK(data_.empty()) << "Do not fetch the script if it is already fetched"; @@ -73,11 +74,12 @@ GURL translate_script_url = GetTranslateScriptURL(); - fetcher_.reset(new TranslateURLFetcher); + fetcher_ = std::make_unique<TranslateURLFetcher>(); fetcher_->set_extra_request_header(kRequestHeader); fetcher_->Request(translate_script_url, base::BindOnce(&TranslateScript::OnScriptFetchComplete, - base::Unretained(this))); + base::Unretained(this)), + is_incognito); } // static @@ -111,8 +113,6 @@ translate_script_url, kAlwaysUseSslQueryName, kAlwaysUseSslQueryValue); -#if !defined(OS_IOS) - // iOS doesn't need to use specific loaders for the isolated world. translate_script_url = net::AppendQueryParameter( translate_script_url, kCssLoaderCallbackQueryName, @@ -121,7 +121,6 @@ translate_script_url, kJavascriptLoaderCallbackQueryName, kJavascriptLoaderCallbackQueryValue); -#endif // !defined(OS_IOS) translate_script_url = AddHostLocaleToUrl(translate_script_url); translate_script_url = AddApiKeyToUrl(translate_script_url);
diff --git a/components/translate/core/browser/translate_script.h b/components/translate/core/browser/translate_script.h index 44ebf6e..cc829128 100644 --- a/components/translate/core/browser/translate_script.h +++ b/components/translate/core/browser/translate_script.h
@@ -43,8 +43,9 @@ void Clear() { data_.clear(); } // Fetches the JS translate script (the script that is injected in the page - // to translate it). - void Request(const RequestCallback& callback); + // to translate it). |is_incognito| is used during the fetch to determine + // which variations headers to add. + void Request(const RequestCallback& callback, bool is_incognito); // Returns the URL to be used to load the translate script. static GURL GetTranslateScriptURL();
diff --git a/components/translate/core/browser/translate_script_unittest.cc b/components/translate/core/browser/translate_script_unittest.cc index 27192c3..605cc28 100644 --- a/components/translate/core/browser/translate_script_unittest.cc +++ b/components/translate/core/browser/translate_script_unittest.cc
@@ -46,8 +46,9 @@ } void Request() { - script_->Request( - base::Bind(&TranslateScriptTest::OnComplete, base::Unretained(this))); + script_->Request(base::BindRepeating(&TranslateScriptTest::OnComplete, + base::Unretained(this)), + /*is_incognito=*/false); } const std::string& GetData() { return script_->data(); }
diff --git a/components/translate/core/browser/translate_url_fetcher.cc b/components/translate/core/browser/translate_url_fetcher.cc index 364da62..9dae291 100644 --- a/components/translate/core/browser/translate_url_fetcher.cc +++ b/components/translate/core/browser/translate_url_fetcher.cc
@@ -7,6 +7,7 @@ #include "base/memory/ref_counted.h" #include "components/data_use_measurement/core/data_use_user_data.h" #include "components/translate/core/browser/translate_download_manager.h" +#include "components/variations/net/variations_http_headers.h" #include "net/base/load_flags.h" #include "net/http/http_status_code.h" #include "net/traffic_annotation/network_traffic_annotation.h" @@ -28,7 +29,8 @@ TranslateURLFetcher::~TranslateURLFetcher() {} bool TranslateURLFetcher::Request(const GURL& url, - TranslateURLFetcher::Callback callback) { + TranslateURLFetcher::Callback callback, + bool is_incognito) { // This function is not supposed to be called if the previous operation is not // finished. if (state_ == REQUESTING) { @@ -98,8 +100,12 @@ if (!extra_request_header_.empty()) resource_request->headers.AddHeadersFromString(extra_request_header_); - simple_loader_ = network::SimpleURLLoader::Create(std::move(resource_request), - traffic_annotation); + simple_loader_ = + variations::CreateSimpleURLLoaderWithVariationsHeadersUnknownSignedIn( + std::move(resource_request), + is_incognito ? variations::InIncognito::kYes + : variations::InIncognito::kNo, + traffic_annotation); // Set retry parameter for HTTP status code 5xx. This doesn't work against // 106 (net::ERR_INTERNET_DISCONNECTED) and so on. // TranslateLanguageList handles network status, and implements retry.
diff --git a/components/translate/core/browser/translate_url_fetcher.h b/components/translate/core/browser/translate_url_fetcher.h index 6bb37ae..58ac6b81 100644 --- a/components/translate/core/browser/translate_url_fetcher.h +++ b/components/translate/core/browser/translate_url_fetcher.h
@@ -52,8 +52,9 @@ // Requests to |url|. |callback| will be invoked when the function returns // true, and the request is finished asynchronously. // Returns false if the previous request is not finished, or the request - // is omitted due to retry limitation. - bool Request(const GURL& url, Callback callback); + // is omitted due to retry limitation. |is_incognito| is used during the fetch + // to determine which variations headers to add. + bool Request(const GURL& url, Callback callback, bool is_incognito); // Gets internal state. State state() { return state_; }
diff --git a/components/translate/ios/browser/resources/translate_ios.js b/components/translate/ios/browser/resources/translate_ios.js index 3af90141..077db2b3 100644 --- a/components/translate/ios/browser/resources/translate_ios.js +++ b/components/translate/ios/browser/resources/translate_ios.js
@@ -36,4 +36,13 @@ 'translationTime': cr.googleTranslate.translationTime}); } +/** + * Sets a callback to inform host to download javascript. + */ +cr.googleTranslate.loadJavascriptCallback = function(url) { + __gCrWeb.message.invokeOnHost({ + 'command': 'translate.loadjavascript', + 'url': url}); +} + } // installTranslateCallbacks
diff --git a/components/translate/ios/browser/translate_controller.h b/components/translate/ios/browser/translate_controller.h index 34333de..48c8ac2 100644 --- a/components/translate/ios/browser/translate_controller.h +++ b/components/translate/ios/browser/translate_controller.h
@@ -5,6 +5,7 @@ #ifndef COMPONENTS_TRANSLATE_IOS_BROWSER_TRANSLATE_CONTROLLER_H_ #define COMPONENTS_TRANSLATE_IOS_BROWSER_TRANSLATE_CONTROLLER_H_ +#include <memory> #include <string> #include "base/gtest_prod_util.h" @@ -13,17 +14,19 @@ #include "base/memory/weak_ptr.h" #include "components/translate/core/common/translate_errors.h" #include "ios/web/public/web_state/web_state_observer.h" +#include "services/network/public/cpp/simple_url_loader.h" @class JsTranslateManager; class GURL; namespace base { class DictionaryValue; -} +} // namespace base namespace web { +class NavigationContext; class WebState; -} +} // namespace web namespace translate { @@ -89,14 +92,25 @@ // Return false if the command is invalid. bool OnTranslateReady(const base::DictionaryValue& command); bool OnTranslateComplete(const base::DictionaryValue& command); + bool OnTranslateLoadJavaScript(const base::DictionaryValue& command); + + // Use to fetch additional scripts needed for translate. + void FetchScript(const std::string& url); + // The callback when the script is fetched or a server error occurred. + void OnScriptFetchComplete(std::unique_ptr<std::string> response_body); // web::WebStateObserver implementation: void WebStateDestroyed(web::WebState* web_state) override; + void DidStartNavigation(web::WebState* web_state, + web::NavigationContext* navigation_context) override; // The WebState this instance is observing. Will be null after // WebStateDestroyed has been called. web::WebState* web_state_ = nullptr; + // Used to fetch additional scripts needed for translate. + std::unique_ptr<network::SimpleURLLoader> script_fetcher_; + Observer* observer_; base::scoped_nsobject<JsTranslateManager> js_manager_; base::WeakPtrFactory<TranslateController> weak_method_factory_;
diff --git a/components/translate/ios/browser/translate_controller.mm b/components/translate/ios/browser/translate_controller.mm index bdfe588e..8819b78 100644 --- a/components/translate/ios/browser/translate_controller.mm +++ b/components/translate/ios/browser/translate_controller.mm
@@ -7,10 +7,18 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/logging.h" +#include "base/strings/string16.h" #include "base/strings/sys_string_conversions.h" +#include "base/strings/utf_string_conversions.h" #include "base/values.h" +#include "components/translate/core/common/translate_util.h" #import "components/translate/ios/browser/js_translate_manager.h" +#include "ios/web/public/browser_state.h" +#include "ios/web/public/web_state/navigation_context.h" #include "ios/web/public/web_state/web_state.h" +#include "net/traffic_annotation/network_traffic_annotation.h" +#include "services/network/public/cpp/resource_request.h" +#include "url/gurl.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." @@ -87,8 +95,9 @@ return OnTranslateReady(command); if (out_string == "translate.status") return OnTranslateComplete(command); + if (out_string == "translate.loadjavascript") + return OnTranslateLoadJavaScript(command); - NOTREACHED(); return false; } @@ -102,7 +111,6 @@ !command.GetDouble("errorCode", &error_code) || error_code < TranslateErrors::NONE || error_code >= TranslateErrors::TRANSLATE_ERROR_MAX) { - NOTREACHED(); return false; } @@ -110,7 +118,6 @@ static_cast<TranslateErrors::Type>(error_code); if (error_type == TranslateErrors::NONE) { if (!command.HasKey("loadTime") || !command.HasKey("readyTime")) { - NOTREACHED(); return false; } command.GetDouble("loadTime", &load_time); @@ -131,7 +138,6 @@ !command.GetDouble("errorCode", &error_code) || error_code < TranslateErrors::NONE || error_code >= TranslateErrors::TRANSLATE_ERROR_MAX) { - NOTREACHED(); return false; } @@ -140,7 +146,6 @@ if (error_type == TranslateErrors::NONE) { if (!command.HasKey("originalPageLanguage") || !command.HasKey("translationTime")) { - NOTREACHED(); return false; } command.GetString("originalPageLanguage", &original_language); @@ -153,6 +158,43 @@ return true; } +bool TranslateController::OnTranslateLoadJavaScript( + const base::DictionaryValue& command) { + std::string url; + if (!command.HasKey("url") || !command.GetString("url", &url)) { + return false; + } + + FetchScript(url); + + return true; +} + +void TranslateController::FetchScript(const std::string& url) { + GURL security_origin = translate::GetTranslateSecurityOrigin(); + if (url.find(security_origin.spec()) || script_fetcher_) { + return; + } + + auto resource_request = std::make_unique<network::ResourceRequest>(); + resource_request->url = GURL(url); + + script_fetcher_ = network::SimpleURLLoader::Create( + std::move(resource_request), NO_TRAFFIC_ANNOTATION_YET); + script_fetcher_->DownloadToStringOfUnboundedSizeUntilCrashAndDie( + web_state_->GetBrowserState()->GetURLLoaderFactory(), + base::BindOnce(&TranslateController::OnScriptFetchComplete, + base::Unretained(this))); +} + +void TranslateController::OnScriptFetchComplete( + std::unique_ptr<std::string> response_body) { + if (response_body) { + web_state_->ExecuteJavaScript(base::UTF8ToUTF16(*response_body)); + } + script_fetcher_.reset(); +} + // web::WebStateObserver implementation. void TranslateController::WebStateDestroyed(web::WebState* web_state) { @@ -160,6 +202,16 @@ web_state_->RemoveScriptCommandCallback(kCommandPrefix); web_state_->RemoveObserver(this); web_state_ = nullptr; + + script_fetcher_.reset(); +} + +void TranslateController::DidStartNavigation( + web::WebState* web_state, + web::NavigationContext* navigation_context) { + if (!navigation_context->IsSameDocument()) { + script_fetcher_.reset(); + } } } // namespace translate
diff --git a/components/ukm/test_ukm_recorder.cc b/components/ukm/test_ukm_recorder.cc index dbf810a..445348f9 100644 --- a/components/ukm/test_ukm_recorder.cc +++ b/components/ukm/test_ukm_recorder.cc
@@ -56,14 +56,6 @@ return false; } -void TestUkmRecorder::AddEntry(mojom::UkmEntryPtr entry) { - const bool should_run_callback = - entry && entry_hash_to_wait_for_ == entry->event_hash; - UkmRecorderImpl::AddEntry(std::move(entry)); - if (should_run_callback && on_add_entry_) - std::move(on_add_entry_).Run(); -} - const UkmSource* TestUkmRecorder::GetSourceForSourceId( SourceId source_id) const { const UkmSource* source = nullptr; @@ -76,12 +68,6 @@ return source; } -void TestUkmRecorder::SetOnAddEntryCallback(base::StringPiece entry_name, - base::OnceClosure on_add_entry) { - on_add_entry_ = std::move(on_add_entry); - entry_hash_to_wait_for_ = base::HashMetricName(entry_name); -} - std::vector<const mojom::UkmEntry*> TestUkmRecorder::GetEntriesByName( base::StringPiece entry_name) const { uint64_t hash = base::HashMetricName(entry_name);
diff --git a/components/ukm/test_ukm_recorder.h b/components/ukm/test_ukm_recorder.h index fab3c1ca..6950c3c 100644 --- a/components/ukm/test_ukm_recorder.h +++ b/components/ukm/test_ukm_recorder.h
@@ -31,8 +31,6 @@ bool ShouldRestrictToWhitelistedSourceIds() const override; bool ShouldRestrictToWhitelistedEntries() const override; - void AddEntry(mojom::UkmEntryPtr entry) override; - size_t sources_count() const { return sources().size(); } size_t entries_count() const { return entries().size(); } @@ -49,10 +47,6 @@ // Gets UkmSource data for a single SourceId. const UkmSource* GetSourceForSourceId(ukm::SourceId source_id) const; - // Sets a callback that will be called when recording an entry for entry name. - void SetOnAddEntryCallback(base::StringPiece entry_name, - base::OnceClosure on_add_entry); - // Gets all of the entries recorded for entry name. std::vector<const mojom::UkmEntry*> GetEntriesByName( base::StringPiece entry_name) const; @@ -81,9 +75,6 @@ base::StringPiece metric_name); private: - uint64_t entry_hash_to_wait_for_; - base::OnceClosure on_add_entry_; - DISALLOW_COPY_AND_ASSIGN(TestUkmRecorder); };
diff --git a/components/ukm/ukm_recorder_impl.h b/components/ukm/ukm_recorder_impl.h index 4acf6d88..7f3084f 100644 --- a/components/ukm/ukm_recorder_impl.h +++ b/components/ukm/ukm_recorder_impl.h
@@ -82,7 +82,6 @@ } // UkmRecorder: - void AddEntry(mojom::UkmEntryPtr entry) override; void UpdateSourceURL(SourceId source_id, const GURL& url) override; void UpdateAppURL(SourceId source_id, const GURL& url) override; void RecordNavigation( @@ -127,6 +126,8 @@ void RecordSource(std::unique_ptr<UkmSource> source); + void AddEntry(mojom::UkmEntryPtr entry) override; + // Load sampling configurations from field-trial information. void LoadExperimentSamplingInfo();
diff --git a/components/viz/host/gpu_host_impl.cc b/components/viz/host/gpu_host_impl.cc index 3b9b27a..10603fc 100644 --- a/components/viz/host/gpu_host_impl.cc +++ b/components/viz/host/gpu_host_impl.cc
@@ -368,9 +368,6 @@ #if defined(OS_ANDROID) std::string build_fp = base::android::BuildInfo::GetInstance()->android_build_fp(); - // TODO(ericrk): Remove this after it's up for a few days. - // https://crbug.com/699122. - CHECK(!build_fp.empty()); shader_prefix_key_ += "-" + build_fp; #endif } @@ -529,9 +526,9 @@ delegate_->DisableGpuCompositing(); } +#if defined(OS_WIN) void GpuHostImpl::SetChildSurface(gpu::SurfaceHandle parent, gpu::SurfaceHandle child) { -#if defined(OS_WIN) constexpr char kBadMessageError[] = "Bad parenting request from gpu process."; if (!params_.in_process) { DWORD parent_process_id = 0; @@ -555,10 +552,8 @@ child)) { LOG(ERROR) << kBadMessageError; } -#else - NOTREACHED(); -#endif } +#endif // defined(OS_WIN) void GpuHostImpl::StoreShaderToDisk(int32_t client_id, const std::string& key,
diff --git a/components/viz/host/gpu_host_impl.h b/components/viz/host/gpu_host_impl.h index c96d6bdf..3e9eb53 100644 --- a/components/viz/host/gpu_host_impl.h +++ b/components/viz/host/gpu_host_impl.h
@@ -105,14 +105,14 @@ bool in_process = false; // Whether caching GPU shader on disk is disabled or not. - bool disable_gpu_shader_disk_cache; + bool disable_gpu_shader_disk_cache = false; // A string representing the product name and version; used to build a // prefix for shader keys. std::string product; // Number of frames to CompositorFrame activation deadline. - base::Optional<uint32_t> deadline_to_synchronize_surfaces = base::nullopt; + base::Optional<uint32_t> deadline_to_synchronize_surfaces; // Task runner corresponding to the main thread. scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner; @@ -203,8 +203,10 @@ gpu::error::ContextLostReason reason, const GURL& active_url) override; void DisableGpuCompositing() override; +#if defined(OS_WIN) void SetChildSurface(gpu::SurfaceHandle parent, gpu::SurfaceHandle child) override; +#endif void StoreShaderToDisk(int32_t client_id, const std::string& key, const std::string& shader) override;
diff --git a/components/webdata_services/web_data_service_wrapper.cc b/components/webdata_services/web_data_service_wrapper.cc index 746398d..5bb235a 100644 --- a/components/webdata_services/web_data_service_wrapper.cc +++ b/components/webdata_services/web_data_service_wrapper.cc
@@ -32,10 +32,6 @@ #include "components/webdata/common/web_database_service.h" #include "components/webdata/common/webdata_constants.h" -#if defined(OS_WIN) -#include "components/password_manager/core/browser/webdata/password_web_data_service_win.h" -#endif - #if !defined(OS_IOS) #include "components/payments/content/payment_manifest_web_data_service.h" #include "components/payments/content/payment_method_manifest_table.h" @@ -160,13 +156,6 @@ base::Bind(show_error_callback, ERROR_LOADING_TOKEN)); token_web_data_->Init(); -#if defined(OS_WIN) - password_web_data_ = new PasswordWebDataService( - profile_database_, ui_task_runner, - base::Bind(show_error_callback, ERROR_LOADING_PASSWORD)); - password_web_data_->Init(); -#endif - #if !defined(OS_IOS) payment_manifest_web_data_ = new payments::PaymentManifestWebDataService( profile_database_, @@ -210,10 +199,6 @@ keyword_web_data_->ShutdownOnUISequence(); token_web_data_->ShutdownOnUISequence(); -#if defined(OS_WIN) - password_web_data_->ShutdownOnUISequence(); -#endif - #if !defined(OS_IOS) payment_manifest_web_data_->ShutdownOnUISequence(); #endif @@ -242,13 +227,6 @@ return token_web_data_.get(); } -#if defined(OS_WIN) -scoped_refptr<PasswordWebDataService> -WebDataServiceWrapper::GetPasswordWebData() { - return password_web_data_.get(); -} -#endif - #if !defined(OS_IOS) scoped_refptr<payments::PaymentManifestWebDataService> WebDataServiceWrapper::GetPaymentManifestWebData() {
diff --git a/components/webdata_services/web_data_service_wrapper.h b/components/webdata_services/web_data_service_wrapper.h index 76330da..cc430039 100644 --- a/components/webdata_services/web_data_service_wrapper.h +++ b/components/webdata_services/web_data_service_wrapper.h
@@ -19,10 +19,6 @@ class TokenWebData; class WebDatabaseService; -#if defined(OS_WIN) -class PasswordWebDataService; -#endif - #if !defined(OS_IOS) namespace payments { class PaymentManifestWebDataService; @@ -88,9 +84,6 @@ GetAccountAutofillWebData(); virtual scoped_refptr<KeywordWebDataService> GetKeywordWebData(); virtual scoped_refptr<TokenWebData> GetTokenWebData(); -#if defined(OS_WIN) - virtual scoped_refptr<PasswordWebDataService> GetPasswordWebData(); -#endif #if !defined(OS_IOS) virtual scoped_refptr<payments::PaymentManifestWebDataService> GetPaymentManifestWebData(); @@ -109,10 +102,6 @@ scoped_refptr<KeywordWebDataService> keyword_web_data_; scoped_refptr<TokenWebData> token_web_data_; -#if defined(OS_WIN) - scoped_refptr<PasswordWebDataService> password_web_data_; -#endif - #if !defined(OS_IOS) scoped_refptr<payments::PaymentManifestWebDataService> payment_manifest_web_data_;
diff --git a/content/browser/accessibility/browser_accessibility_manager_win.cc b/content/browser/accessibility/browser_accessibility_manager_win.cc index 7bc5c248..1a3eecf 100644 --- a/content/browser/accessibility/browser_accessibility_manager_win.cc +++ b/content/browser/accessibility/browser_accessibility_manager_win.cc
@@ -179,10 +179,18 @@ FireWinAccessibilityEvent(EVENT_OBJECT_REORDER, node); break; case Event::LIVE_REGION_CHANGED: - // NVDA and JAWS are inconsistent about speaking this event in content. - // Because of this, and because Firefox does not currently fire it, we - // are avoiding this event for now. - // FireWinAccessibilityEvent(EVENT_OBJECT_LIVEREGIONCHANGED, node); + // This event is redundant with the IA2_EVENT_TEXT_INSERTED events; + // however, JAWS 2018 and earlier do not process the text inserted + // events when "virtual cursor mode" is turned off (Insert+Z). + // Fortunately, firing the redudant event does not cause duplicate + // verbalizations in either screen reader. + // Future versions of JAWS may process the text inserted event when + // in focus mode, and so at some point the live region + // changed events may truly become redundant with the text inserted + // events. Note: Firefox does not fire this event, but JAWS processes + // Firefox live region events differently (utilizes MSAA's + // EVENT_OBJECT_SHOW). + FireWinAccessibilityEvent(EVENT_OBJECT_LIVEREGIONCHANGED, node); break; case Event::LOAD_COMPLETE: FireWinAccessibilityEvent(IA2_EVENT_DOCUMENT_LOAD_COMPLETE, node);
diff --git a/content/browser/android/gesture_listener_manager.cc b/content/browser/android/gesture_listener_manager.cc index 67632979..4d03f78e 100644 --- a/content/browser/android/gesture_listener_manager.cc +++ b/content/browser/android/gesture_listener_manager.cc
@@ -111,7 +111,7 @@ ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env); if (j_obj.is_null()) return; - Java_GestureListenerManagerImpl_onDestroy(env, j_obj); + Java_GestureListenerManagerImpl_onNativeDestroyed(env, j_obj); } void GestureListenerManager::ResetGestureDetection(
diff --git a/content/browser/android/ime_adapter_android.cc b/content/browser/android/ime_adapter_android.cc index 6bb9735..774cdd0 100644 --- a/content/browser/android/ime_adapter_android.cc +++ b/content/browser/android/ime_adapter_android.cc
@@ -150,7 +150,7 @@ JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jobject> obj = java_ime_adapter_.get(env); if (!obj.is_null()) - Java_ImeAdapterImpl_destroy(env, obj); + Java_ImeAdapterImpl_onNativeDestroyed(env, obj); } void ImeAdapterAndroid::UpdateRenderProcessConnection(
diff --git a/content/browser/android/select_popup.cc b/content/browser/android/select_popup.cc index 44d187ce..404e219 100644 --- a/content/browser/android/select_popup.cc +++ b/content/browser/android/select_popup.cc
@@ -53,7 +53,7 @@ ScopedJavaLocalRef<jobject> j_obj = java_obj_.get(env); if (j_obj.is_null()) return; - Java_SelectPopup_destroy(env, j_obj); + Java_SelectPopup_onNativeDestroyed(env, j_obj); } void SelectPopup::ShowMenu(RenderFrameHost* frame,
diff --git a/content/browser/android/text_suggestion_host_android.cc b/content/browser/android/text_suggestion_host_android.cc index ac308472..0f072fb 100644 --- a/content/browser/android/text_suggestion_host_android.cc +++ b/content/browser/android/text_suggestion_host_android.cc
@@ -64,7 +64,7 @@ JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jobject> obj = java_text_suggestion_host_.get(env); if (!obj.is_null()) - Java_TextSuggestionHost_destroy(env, obj); + Java_TextSuggestionHost_onNativeDestroyed(env, obj); } void TextSuggestionHostAndroid::UpdateRenderProcessConnection(
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc index 700c766..f80fb07 100644 --- a/content/browser/devtools/render_frame_devtools_agent_host.cc +++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -427,14 +427,7 @@ bool RenderFrameDevToolsAgentHost::AttachSession(DevToolsSession* session, TargetRegistry* registry) { - DevToolsManager* manager = DevToolsManager::GetInstance(); - if (manager->delegate() && web_contents()) { - if (!manager->delegate()->AllowInspectingWebContents(web_contents())) - return false; - } - const bool is_webui = - frame_host_ && (frame_host_->web_ui() || frame_host_->pending_web_ui()); - if (!session->client()->MayAttachToRenderer(frame_host_, is_webui)) + if (!ShouldAllowSession(session, frame_host_)) return false; session->SetRenderer(frame_host_ ? frame_host_->GetProcess()->GetID() @@ -599,14 +592,10 @@ agent_ptr_.reset(); std::vector<DevToolsSession*> restricted_sessions; - const bool is_webui = - frame_host && (frame_host->web_ui() || frame_host->pending_web_ui()); - for (DevToolsSession* session : sessions()) { - if (!session->client()->MayAttachToRenderer(frame_host, is_webui)) + if (!ShouldAllowSession(session, frame_host)) restricted_sessions.push_back(session); } - if (!restricted_sessions.empty()) ForceDetachRestrictedSessions(restricted_sessions); @@ -959,4 +948,19 @@ return frame_tree_node_ && frame_tree_node_->parent(); } +bool RenderFrameDevToolsAgentHost::ShouldAllowSession( + DevToolsSession* session, + RenderFrameHostImpl* frame_host) { + DevToolsManager* manager = DevToolsManager::GetInstance(); + if (manager->delegate() && frame_host) { + if (!manager->delegate()->AllowInspectingRenderFrameHost(frame_host)) + return false; + } + const bool is_webui = + frame_host && (frame_host->web_ui() || frame_host->pending_web_ui()); + if (!session->client()->MayAttachToRenderer(frame_host, is_webui)) + return false; + return true; +} + } // namespace content
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.h b/content/browser/devtools/render_frame_devtools_agent_host.h index a0867d1..37f0c8d1 100644 --- a/content/browser/devtools/render_frame_devtools_agent_host.h +++ b/content/browser/devtools/render_frame_devtools_agent_host.h
@@ -190,6 +190,9 @@ void RevokePolicy(); void SetFrameTreeNode(FrameTreeNode* frame_tree_node); + bool ShouldAllowSession(DevToolsSession* session, + RenderFrameHostImpl* frame_host); + #if defined(OS_ANDROID) device::mojom::WakeLock* GetWakeLock(); #endif
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index 1a58a87..94905e7 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -4075,7 +4075,7 @@ auto factory_request = mojo::MakeRequest(&factory_proxy_info); GetContentClient()->browser()->WillCreateURLLoaderFactory( browser_context, this, false /* is_navigation */, common_params.url, - &factory_request); + &factory_request, nullptr /* bypass_redirect_checks */); // Keep DevTools proxy lasy, i.e. closest to the network. RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory( this, false /* is_navigation */, false /* is_download */, @@ -4811,10 +4811,9 @@ bool bypass_redirect_checks = false; if (base::FeatureList::IsEnabled(network::features::kNetworkService)) { - bypass_redirect_checks = - GetContentClient()->browser()->WillCreateURLLoaderFactory( - context, this, false /* is_navigation */, url, - &default_factory_request); + GetContentClient()->browser()->WillCreateURLLoaderFactory( + context, this, false /* is_navigation */, url, &default_factory_request, + &bypass_redirect_checks); } // Keep DevTools proxy lasy, i.e. closest to the network.
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc index a135b69d..0097241 100644 --- a/content/browser/gpu/gpu_process_host.cc +++ b/content/browser/gpu/gpu_process_host.cc
@@ -872,13 +872,6 @@ } } -#if defined(OS_ANDROID) -void GpuProcessHost::OnDestroyingVideoSurfaceAck() { - TRACE_EVENT0("gpu", "GpuProcessHost::OnDestroyingVideoSurfaceAck"); - if (!send_destroying_video_surface_done_cb_.is_null()) - base::ResetAndReturn(&send_destroying_video_surface_done_cb_).Run(); -} -#endif void GpuProcessHost::OnProcessLaunched() { UMA_HISTOGRAM_TIMES("GPU.GPUProcessLaunchTime", @@ -952,14 +945,13 @@ #endif } -void GpuProcessHost::BlockDomainFrom3DAPIs(const GURL& url, - Delegate::DomainGuilt guilt) { +void GpuProcessHost::BlockDomainFrom3DAPIs(const GURL& url, DomainGuilt guilt) { GpuDataManagerImpl::DomainGuilt gpu_data_manager_guilt; switch (guilt) { - case Delegate::DomainGuilt::kKnown: + case DomainGuilt::kKnown: gpu_data_manager_guilt = GpuDataManagerImpl::DOMAIN_GUILT_KNOWN; break; - case Delegate::DomainGuilt::kUnknown: + case DomainGuilt::kUnknown: gpu_data_manager_guilt = GpuDataManagerImpl::DOMAIN_GUILT_UNKNOWN; } GpuDataManagerImpl::GetInstance()->BlockDomainFrom3DAPIs( @@ -1118,9 +1110,6 @@ if (gpu_host_) gpu_host_->SendOutstandingReplies(); - - if (!send_destroying_video_surface_done_cb_.is_null()) - base::ResetAndReturn(&send_destroying_video_surface_done_cb_).Run(); } void GpuProcessHost::RecordProcessCrash() {
diff --git a/content/browser/gpu/gpu_process_host.h b/content/browser/gpu/gpu_process_host.h index e56abaef..c9a47f6 100644 --- a/content/browser/gpu/gpu_process_host.h +++ b/content/browser/gpu/gpu_process_host.h
@@ -144,8 +144,7 @@ gpu_feature_info_for_hardware_gpu) override; void DidFailInitialize() override; void DidCreateContextSuccessfully() override; - void BlockDomainFrom3DAPIs(const GURL& url, - Delegate::DomainGuilt guilt) override; + void BlockDomainFrom3DAPIs(const GURL& url, DomainGuilt guilt) override; void DisableGpuCompositing() override; bool GpuAccessAllowed() const override; gpu::ShaderCacheFactory* GetShaderCacheFactory() override; @@ -162,10 +161,7 @@ void SendGpuProcessMessage(IPC::Message* message) override; #endif -// Message handlers. -#if defined(OS_ANDROID) - void OnDestroyingVideoSurfaceAck(); -#endif + // Message handlers. void OnFieldTrialActivated(const std::string& trial_name); bool LaunchGpuProcess(); @@ -183,9 +179,6 @@ // GPU process id in case GPU is not in-process. base::ProcessId process_id_ = base::kNullProcessId; - // A callback to signal the completion of a SendDestroyingVideoSurface call. - base::Closure send_destroying_video_surface_done_cb_; - // Qeueud messages to send when the process launches. base::queue<IPC::Message*> queued_messages_;
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc index d903495..91f6f32 100644 --- a/content/browser/loader/navigation_url_loader_impl.cc +++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -1511,8 +1511,8 @@ auto factory_request = mojo::MakeRequest(&factory_info); bool use_proxy = GetContentClient()->browser()->WillCreateURLLoaderFactory( partition->browser_context(), frame_tree_node->current_frame_host(), - true /* is_navigation */, new_request->url, &factory_request); - bypass_redirect_checks = use_proxy; + true /* is_navigation */, new_request->url, &factory_request, + &bypass_redirect_checks); if (RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory( frame_tree_node->current_frame_host(), true, false, &factory_request)) { @@ -1644,7 +1644,8 @@ auto* frame = frame_tree_node->current_frame_host(); GetContentClient()->browser()->WillCreateURLLoaderFactory( frame->GetSiteInstance()->GetBrowserContext(), frame, - true /* is_navigation */, url, &factory); + true /* is_navigation */, url, &factory, + nullptr /* bypass_redirect_checks */); it->second->Clone(std::move(factory)); }
diff --git a/content/browser/service_worker/service_worker_navigation_loader.cc b/content/browser/service_worker/service_worker_navigation_loader.cc index 3c47df54..803a4100b 100644 --- a/content/browser/service_worker/service_worker_navigation_loader.cc +++ b/content/browser/service_worker/service_worker_navigation_loader.cc
@@ -234,9 +234,10 @@ fetch_dispatcher_->MaybeStartNavigationPreloadWithURLLoader( resource_request_, url_loader_factory_getter_.get(), base::DoNothing(/* TODO(crbug/762357): metrics? */)); + + // Record worker start time here as |fetch_dispatcher_| will start a service + // worker if there is no running service worker. response_head_.service_worker_start_time = base::TimeTicks::Now(); - response_head_.load_timing.send_start = base::TimeTicks::Now(); - response_head_.load_timing.send_end = base::TimeTicks::Now(); fetch_dispatcher_->Run(); } @@ -276,7 +277,12 @@ "initial_worker_status", EmbeddedWorkerInstance::StatusToString(initial_worker_status)); - response_head_.service_worker_ready_time = base::TimeTicks::Now(); + // At this point a service worker is running and the fetch event is about + // to dispatch. Record some load timings. + base::TimeTicks now = base::TimeTicks::Now(); + response_head_.service_worker_ready_time = now; + response_head_.load_timing.send_start = now; + response_head_.load_timing.send_end = now; // Note that we don't record worker preparation time in S13nServiceWorker // path for now. If we want to measure worker preparation time we can
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc index 733a479..acec5df 100644 --- a/content/browser/storage_partition_impl.cc +++ b/content/browser/storage_partition_impl.cc
@@ -1309,7 +1309,7 @@ if (base::FeatureList::IsEnabled(network::features::kNetworkService)) { GetContentClient()->browser()->WillCreateURLLoaderFactory( browser_context(), nullptr, false /* is_navigation */, GURL(), - &request); + &request, nullptr /* bypass_redirect_checks */); } GetNetworkContext()->CreateURLLoaderFactory(std::move(request), std::move(params));
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn index 8135b93..55aed48 100644 --- a/content/public/android/BUILD.gn +++ b/content/public/android/BUILD.gn
@@ -219,7 +219,6 @@ "java/src/org/chromium/content/browser/selection/SmartSelectionProvider.java", "java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java", "java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java", - "java/src/org/chromium/content/browser/webcontents/WebContentsUserData.java", "java/src/org/chromium/content/common/ContentSwitchUtils.java", "java/src/org/chromium/content/common/ServiceManagerConnectionImpl.java", "java/src/org/chromium/content/common/SurfaceWrapper.java",
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentUiEventHandler.java b/content/public/android/java/src/org/chromium/content/browser/ContentUiEventHandler.java index 3d50e19..be063a1 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ContentUiEventHandler.java +++ b/content/public/android/java/src/org/chromium/content/browser/ContentUiEventHandler.java
@@ -9,12 +9,12 @@ import android.view.KeyEvent; import android.view.MotionEvent; +import org.chromium.base.UserData; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.content.browser.input.ImeAdapterImpl; import org.chromium.content.browser.webcontents.WebContentsImpl; import org.chromium.content.browser.webcontents.WebContentsImpl.UserDataFactory; -import org.chromium.content.browser.webcontents.WebContentsUserData; import org.chromium.content_public.browser.ViewEventSink.InternalAccessDelegate; import org.chromium.content_public.browser.WebContents; import org.chromium.device.gamepad.GamepadList; @@ -25,7 +25,7 @@ * content components. */ @JNINamespace("content") -public class ContentUiEventHandler { +public class ContentUiEventHandler implements UserData { private final WebContentsImpl mWebContents; private InternalAccessDelegate mEventDelegate; private long mNativeContentUiEventHandler; @@ -36,8 +36,8 @@ } public static ContentUiEventHandler fromWebContents(WebContents webContents) { - return WebContentsUserData.fromWebContents( - webContents, ContentUiEventHandler.class, UserDataFactoryLazyHolder.INSTANCE); + return ((WebContentsImpl) webContents) + .getOrSetUserData(ContentUiEventHandler.class, UserDataFactoryLazyHolder.INSTANCE); } public ContentUiEventHandler(WebContents webContents) {
diff --git a/content/public/android/java/src/org/chromium/content/browser/Gamepad.java b/content/public/android/java/src/org/chromium/content/browser/Gamepad.java index 0da266c..3aa5047 100644 --- a/content/public/android/java/src/org/chromium/content/browser/Gamepad.java +++ b/content/public/android/java/src/org/chromium/content/browser/Gamepad.java
@@ -6,9 +6,9 @@ import android.content.Context; +import org.chromium.base.UserData; import org.chromium.content.browser.webcontents.WebContentsImpl; import org.chromium.content.browser.webcontents.WebContentsImpl.UserDataFactory; -import org.chromium.content.browser.webcontents.WebContentsUserData; import org.chromium.content_public.browser.WebContents; import org.chromium.device.gamepad.GamepadList; @@ -16,7 +16,7 @@ * Encapsulates component class {@link GamepadList} for use in content, with regards * to its state according to content being attached to/detached from window. */ -class Gamepad implements WindowEventObserver { +class Gamepad implements WindowEventObserver, UserData { private final Context mContext; private static final class UserDataFactoryLazyHolder { @@ -24,8 +24,8 @@ } public static Gamepad from(WebContents webContents) { - return WebContentsUserData.fromWebContents( - webContents, Gamepad.class, UserDataFactoryLazyHolder.INSTANCE); + return ((WebContentsImpl) webContents) + .getOrSetUserData(Gamepad.class, UserDataFactoryLazyHolder.INSTANCE); } public Gamepad(WebContents webContents) {
diff --git a/content/public/android/java/src/org/chromium/content/browser/GestureListenerManagerImpl.java b/content/public/android/java/src/org/chromium/content/browser/GestureListenerManagerImpl.java index 69d72264..fb898d6 100644 --- a/content/public/android/java/src/org/chromium/content/browser/GestureListenerManagerImpl.java +++ b/content/public/android/java/src/org/chromium/content/browser/GestureListenerManagerImpl.java
@@ -10,6 +10,7 @@ import org.chromium.base.ObserverList; import org.chromium.base.ObserverList.RewindableIterator; import org.chromium.base.TraceEvent; +import org.chromium.base.UserData; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.blink_public.web.WebInputEventType; @@ -17,7 +18,6 @@ import org.chromium.content.browser.selection.SelectionPopupControllerImpl; import org.chromium.content.browser.webcontents.WebContentsImpl; import org.chromium.content.browser.webcontents.WebContentsImpl.UserDataFactory; -import org.chromium.content.browser.webcontents.WebContentsUserData; import org.chromium.content_public.browser.GestureListenerManager; import org.chromium.content_public.browser.GestureStateListener; import org.chromium.content_public.browser.ViewEventSink.InternalAccessDelegate; @@ -29,11 +29,11 @@ * Implementation of the interface {@link GestureListenerManager}. Manages * the {@link GestureStateListener} instances, and invokes them upon * notification of various events. - * Instantiated object is held inside {@link WebContentsUserData} that is - * managed by {@link WebContents}. + * Instantiated object is held inside {@link UserDataHost} that is managed by {@link WebContents}. */ @JNINamespace("content") -public class GestureListenerManagerImpl implements GestureListenerManager, WindowEventObserver { +public class GestureListenerManagerImpl + implements GestureListenerManager, WindowEventObserver, UserData { private static final class UserDataFactoryLazyHolder { private static final UserDataFactory<GestureListenerManagerImpl> INSTANCE = GestureListenerManagerImpl::new; @@ -67,8 +67,9 @@ * Creates one if not present. */ public static GestureListenerManagerImpl fromWebContents(WebContents webContents) { - return WebContentsUserData.fromWebContents( - webContents, GestureListenerManagerImpl.class, UserDataFactoryLazyHolder.INSTANCE); + return ((WebContentsImpl) webContents) + .getOrSetUserData( + GestureListenerManagerImpl.class, UserDataFactoryLazyHolder.INSTANCE); } public GestureListenerManagerImpl(WebContents webContents) { @@ -258,7 +259,7 @@ } @CalledByNative - private void onDestroy() { + private void onNativeDestroyed() { for (mIterator.rewind(); mIterator.hasNext();) mIterator.next().onDestroyed(); mListeners.clear(); mNativeGestureListenerManager = 0;
diff --git a/content/public/android/java/src/org/chromium/content/browser/JavascriptInjectorImpl.java b/content/public/android/java/src/org/chromium/content/browser/JavascriptInjectorImpl.java index 5fa36f0..c041621 100644 --- a/content/public/android/java/src/org/chromium/content/browser/JavascriptInjectorImpl.java +++ b/content/public/android/java/src/org/chromium/content/browser/JavascriptInjectorImpl.java
@@ -6,10 +6,11 @@ import android.util.Pair; +import org.chromium.base.UserData; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; +import org.chromium.content.browser.webcontents.WebContentsImpl; import org.chromium.content.browser.webcontents.WebContentsImpl.UserDataFactory; -import org.chromium.content.browser.webcontents.WebContentsUserData; import org.chromium.content_public.browser.JavascriptInjector; import org.chromium.content_public.browser.WebContents; @@ -23,7 +24,7 @@ * Implementation class of the interface {@link JavascriptInjector}. */ @JNINamespace("content") -public class JavascriptInjectorImpl implements JavascriptInjector { +public class JavascriptInjectorImpl implements JavascriptInjector, UserData { private static final class UserDataFactoryLazyHolder { private static final UserDataFactory<JavascriptInjectorImpl> INSTANCE = JavascriptInjectorImpl::new; @@ -39,8 +40,8 @@ * Creates one if not present. */ public static JavascriptInjector fromWebContents(WebContents webContents) { - return WebContentsUserData.fromWebContents( - webContents, JavascriptInjectorImpl.class, UserDataFactoryLazyHolder.INSTANCE); + return ((WebContentsImpl) webContents) + .getOrSetUserData(JavascriptInjectorImpl.class, UserDataFactoryLazyHolder.INSTANCE); } public JavascriptInjectorImpl(WebContents webContents) {
diff --git a/content/public/android/java/src/org/chromium/content/browser/JoystickHandler.java b/content/public/android/java/src/org/chromium/content/browser/JoystickHandler.java index 39695e9..e76a872 100644 --- a/content/public/android/java/src/org/chromium/content/browser/JoystickHandler.java +++ b/content/public/android/java/src/org/chromium/content/browser/JoystickHandler.java
@@ -7,9 +7,10 @@ import android.view.InputDevice; import android.view.MotionEvent; +import org.chromium.base.UserData; import org.chromium.content.browser.input.ImeAdapterImpl; +import org.chromium.content.browser.webcontents.WebContentsImpl; import org.chromium.content.browser.webcontents.WebContentsImpl.UserDataFactory; -import org.chromium.content.browser.webcontents.WebContentsUserData; import org.chromium.content_public.browser.ImeEventObserver; import org.chromium.content_public.browser.WebContents; import org.chromium.ui.base.EventForwarder; @@ -17,7 +18,7 @@ /** * Bridges content and joystick device event conversion and forwarding. */ -public class JoystickHandler implements ImeEventObserver { +public class JoystickHandler implements ImeEventObserver, UserData { private final EventForwarder mEventForwarder; // Whether joystick scroll is enabled. It's disabled when an editable field is focused. @@ -28,8 +29,8 @@ } public static JoystickHandler fromWebContents(WebContents webContents) { - return WebContentsUserData.fromWebContents( - webContents, JoystickHandler.class, UserDataFactoryLazyHolder.INSTANCE); + return ((WebContentsImpl) webContents) + .getOrSetUserData(JoystickHandler.class, UserDataFactoryLazyHolder.INSTANCE); } /**
diff --git a/content/public/android/java/src/org/chromium/content/browser/PopupController.java b/content/public/android/java/src/org/chromium/content/browser/PopupController.java index a905fa4..941a2d5 100644 --- a/content/public/android/java/src/org/chromium/content/browser/PopupController.java +++ b/content/public/android/java/src/org/chromium/content/browser/PopupController.java
@@ -4,9 +4,10 @@ package org.chromium.content.browser; +import org.chromium.base.UserData; import org.chromium.content.browser.selection.SelectionPopupControllerImpl; +import org.chromium.content.browser.webcontents.WebContentsImpl; import org.chromium.content.browser.webcontents.WebContentsImpl.UserDataFactory; -import org.chromium.content.browser.webcontents.WebContentsUserData; import org.chromium.content_public.browser.WebContents; import java.util.ArrayList; @@ -15,7 +16,7 @@ /** * Controls all the popup views on content view. */ -public class PopupController { +public class PopupController implements UserData { /** Interface for popup views that expose a method for hiding itself. */ public interface HideablePopup { /** @@ -31,8 +32,8 @@ private final List<HideablePopup> mHideablePopups = new ArrayList<>(); public static PopupController fromWebContents(WebContents webContents) { - return WebContentsUserData.fromWebContents( - webContents, PopupController.class, UserDataFactoryLazyHolder.INSTANCE); + return ((WebContentsImpl) webContents) + .getOrSetUserData(PopupController.class, UserDataFactoryLazyHolder.INSTANCE); } private PopupController(WebContents webContents) {}
diff --git a/content/public/android/java/src/org/chromium/content/browser/ViewEventSinkImpl.java b/content/public/android/java/src/org/chromium/content/browser/ViewEventSinkImpl.java index bc8b5b27..bd7050d 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ViewEventSinkImpl.java +++ b/content/public/android/java/src/org/chromium/content/browser/ViewEventSinkImpl.java
@@ -7,9 +7,9 @@ import android.content.res.Configuration; import org.chromium.base.TraceEvent; +import org.chromium.base.UserData; import org.chromium.content.browser.webcontents.WebContentsImpl; import org.chromium.content.browser.webcontents.WebContentsImpl.UserDataFactory; -import org.chromium.content.browser.webcontents.WebContentsUserData; import org.chromium.content_public.browser.ViewEventSink; import org.chromium.content_public.browser.WebContents; import org.chromium.ui.base.ViewAndroidDelegate; @@ -18,7 +18,7 @@ /** * Implementation of the interface {@link ViewEventSink}. */ -public final class ViewEventSinkImpl implements ViewEventSink, ActivityStateObserver { +public final class ViewEventSinkImpl implements ViewEventSink, ActivityStateObserver, UserData { private final WebContentsImpl mWebContents; // Whether the container view has view-level focus. @@ -40,8 +40,8 @@ } public static ViewEventSinkImpl from(WebContents webContents) { - return WebContentsUserData.fromWebContents( - webContents, ViewEventSinkImpl.class, UserDataFactoryLazyHolder.INSTANCE); + return ((WebContentsImpl) webContents) + .getOrSetUserData(ViewEventSinkImpl.class, UserDataFactoryLazyHolder.INSTANCE); } public ViewEventSinkImpl(WebContents webContents) {
diff --git a/content/public/android/java/src/org/chromium/content/browser/WindowEventObserverManager.java b/content/public/android/java/src/org/chromium/content/browser/WindowEventObserverManager.java index 1cd90d9..4eb55db 100644 --- a/content/public/android/java/src/org/chromium/content/browser/WindowEventObserverManager.java +++ b/content/public/android/java/src/org/chromium/content/browser/WindowEventObserverManager.java
@@ -8,9 +8,9 @@ import org.chromium.base.ActivityState; import org.chromium.base.ObserverList; +import org.chromium.base.UserData; import org.chromium.content.browser.webcontents.WebContentsImpl; import org.chromium.content.browser.webcontents.WebContentsImpl.UserDataFactory; -import org.chromium.content.browser.webcontents.WebContentsUserData; import org.chromium.content_public.browser.WebContents; import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.display.DisplayAndroid; @@ -19,7 +19,7 @@ /** * Manages {@link WindowEventObserver} instances used for WebContents. */ -public final class WindowEventObserverManager implements DisplayAndroidObserver { +public final class WindowEventObserverManager implements DisplayAndroidObserver, UserData { private final ObserverList<WindowEventObserver> mWindowEventObservers = new ObserverList<>(); private WindowAndroid mWindowAndroid; @@ -36,8 +36,9 @@ } public static WindowEventObserverManager from(WebContents webContents) { - return WebContentsUserData.fromWebContents( - webContents, WindowEventObserverManager.class, UserDataFactoryLazyHolder.INSTANCE); + return ((WebContentsImpl) webContents) + .getOrSetUserData( + WindowEventObserverManager.class, UserDataFactoryLazyHolder.INSTANCE); } private WindowEventObserverManager(WebContents webContents) {
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java index 0cf23c8c..70c745903 100644 --- a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java +++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
@@ -27,6 +27,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeProvider; +import org.chromium.base.UserData; import org.chromium.base.VisibleForTesting; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; @@ -36,7 +37,6 @@ import org.chromium.content.browser.accessibility.captioning.CaptioningController; import org.chromium.content.browser.webcontents.WebContentsImpl; import org.chromium.content.browser.webcontents.WebContentsImpl.UserDataFactory; -import org.chromium.content.browser.webcontents.WebContentsUserData; import org.chromium.content_public.browser.AccessibilitySnapshotCallback; import org.chromium.content_public.browser.AccessibilitySnapshotNode; import org.chromium.content_public.browser.WebContents; @@ -54,7 +54,8 @@ */ @JNINamespace("content") public class WebContentsAccessibilityImpl extends AccessibilityNodeProvider - implements AccessibilityStateChangeListener, WebContentsAccessibility, WindowEventObserver { + implements AccessibilityStateChangeListener, WebContentsAccessibility, WindowEventObserver, + UserData { // Constants from AccessibilityNodeInfo defined in the K SDK. private static final int ACTION_COLLAPSE = 0x00080000; private static final int ACTION_EXPAND = 0x00040000; @@ -140,8 +141,9 @@ } public static WebContentsAccessibilityImpl fromWebContents(WebContents webContents) { - return WebContentsUserData.fromWebContents(webContents, WebContentsAccessibilityImpl.class, - UserDataFactoryLazyHolder.INSTANCE); + return ((WebContentsImpl) webContents) + .getOrSetUserData( + WebContentsAccessibilityImpl.class, UserDataFactoryLazyHolder.INSTANCE); } protected WebContentsAccessibilityImpl(WebContents webContents) {
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java index a80191f..09ec4c7 100644 --- a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java +++ b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java
@@ -31,6 +31,7 @@ import org.chromium.base.Log; import org.chromium.base.TraceEvent; +import org.chromium.base.UserData; import org.chromium.base.VisibleForTesting; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; @@ -43,7 +44,6 @@ import org.chromium.content.browser.picker.InputDialogContainer; import org.chromium.content.browser.webcontents.WebContentsImpl; import org.chromium.content.browser.webcontents.WebContentsImpl.UserDataFactory; -import org.chromium.content.browser.webcontents.WebContentsUserData; import org.chromium.content_public.browser.ImeAdapter; import org.chromium.content_public.browser.ImeEventObserver; import org.chromium.content_public.browser.InputMethodManagerWrapper; @@ -81,7 +81,7 @@ * lifetime of the object. */ @JNINamespace("content") -public class ImeAdapterImpl implements ImeAdapter, WindowEventObserver { +public class ImeAdapterImpl implements ImeAdapter, WindowEventObserver, UserData { private static final String TAG = "cr_Ime"; private static final boolean DEBUG_LOGS = false; @@ -173,8 +173,8 @@ * @return {@link ImeAdapter} object. */ public static ImeAdapterImpl fromWebContents(WebContents webContents) { - return WebContentsUserData.fromWebContents( - webContents, ImeAdapterImpl.class, UserDataFactoryLazyHolder.INSTANCE); + return ((WebContentsImpl) webContents) + .getOrSetUserData(ImeAdapterImpl.class, UserDataFactoryLazyHolder.INSTANCE); } /** @@ -685,7 +685,7 @@ } @CalledByNative - private void destroy() { + private void onNativeDestroyed() { resetAndHideKeyboard(); mNativeImeAdapterAndroid = 0; mIsConnected = false;
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/SelectPopup.java b/content/public/android/java/src/org/chromium/content/browser/input/SelectPopup.java index 87d6545..e05f3abd 100644 --- a/content/public/android/java/src/org/chromium/content/browser/input/SelectPopup.java +++ b/content/public/android/java/src/org/chromium/content/browser/input/SelectPopup.java
@@ -8,6 +8,7 @@ import android.view.View; import android.view.ViewGroup; +import org.chromium.base.UserData; import org.chromium.base.VisibleForTesting; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; @@ -18,7 +19,6 @@ import org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl; import org.chromium.content.browser.webcontents.WebContentsImpl; import org.chromium.content.browser.webcontents.WebContentsImpl.UserDataFactory; -import org.chromium.content.browser.webcontents.WebContentsUserData; import org.chromium.content_public.browser.WebContents; import org.chromium.ui.base.DeviceFormFactor; import org.chromium.ui.base.ViewAndroidDelegate; @@ -31,8 +31,8 @@ * Handles the popup UI for the lt&;select> HTML tag support. */ @JNINamespace("content") -public class SelectPopup - implements HideablePopup, ViewAndroidDelegate.ContainerViewObserver, WindowEventObserver { +public class SelectPopup implements HideablePopup, ViewAndroidDelegate.ContainerViewObserver, + WindowEventObserver, UserData { /** UI for Select popup. */ public interface Ui { /** @@ -62,8 +62,8 @@ * @return {@link SelectPopup} object. */ public static SelectPopup fromWebContents(WebContents webContents) { - return WebContentsUserData.fromWebContents( - webContents, SelectPopup.class, UserDataFactoryLazyHolder.INSTANCE); + return ((WebContentsImpl) webContents) + .getOrSetUserData(SelectPopup.class, UserDataFactoryLazyHolder.INSTANCE); } @CalledByNative @@ -172,7 +172,7 @@ } @CalledByNative - private void destroy() { + private void onNativeDestroyed() { mNativeSelectPopup = 0; }
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/TextSuggestionHost.java b/content/public/android/java/src/org/chromium/content/browser/input/TextSuggestionHost.java index e5c42a5f..fb9ec20 100644 --- a/content/public/android/java/src/org/chromium/content/browser/input/TextSuggestionHost.java +++ b/content/public/android/java/src/org/chromium/content/browser/input/TextSuggestionHost.java
@@ -6,6 +6,7 @@ import android.content.Context; +import org.chromium.base.UserData; import org.chromium.base.VisibleForTesting; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; @@ -15,7 +16,6 @@ import org.chromium.content.browser.WindowEventObserverManager; import org.chromium.content.browser.webcontents.WebContentsImpl; import org.chromium.content.browser.webcontents.WebContentsImpl.UserDataFactory; -import org.chromium.content.browser.webcontents.WebContentsUserData; import org.chromium.content_public.browser.WebContents; import org.chromium.ui.base.ViewAndroidDelegate; import org.chromium.ui.base.WindowAndroid; @@ -26,7 +26,7 @@ * the commands in that menu (by calling back to the C++ class). */ @JNINamespace("content") -public class TextSuggestionHost implements WindowEventObserver, HideablePopup { +public class TextSuggestionHost implements WindowEventObserver, HideablePopup, UserData { private long mNativeTextSuggestionHost; private final WebContentsImpl mWebContents; private final Context mContext; @@ -49,8 +49,8 @@ * @return {@link TextSuggestionHost} object. */ public static TextSuggestionHost fromWebContents(WebContents webContents) { - return WebContentsUserData.fromWebContents( - webContents, TextSuggestionHost.class, UserDataFactoryLazyHolder.INSTANCE); + return ((WebContentsImpl) webContents) + .getOrSetUserData(TextSuggestionHost.class, UserDataFactoryLazyHolder.INSTANCE); } /** @@ -200,7 +200,7 @@ } @CalledByNative - private void destroy() { + private void onNativeDestroyed() { hidePopups(); mNativeTextSuggestionHost = 0; }
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 da52e15..5bcea30 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
@@ -36,6 +36,7 @@ import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.Log; +import org.chromium.base.UserData; import org.chromium.base.VisibleForTesting; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; @@ -51,7 +52,6 @@ import org.chromium.content.browser.input.ImeAdapterImpl; import org.chromium.content.browser.webcontents.WebContentsImpl; import org.chromium.content.browser.webcontents.WebContentsImpl.UserDataFactory; -import org.chromium.content.browser.webcontents.WebContentsUserData; import org.chromium.content_public.browser.ActionModeCallbackHelper; import org.chromium.content_public.browser.ImeEventObserver; import org.chromium.content_public.browser.SelectionClient; @@ -73,7 +73,7 @@ @TargetApi(Build.VERSION_CODES.M) public class SelectionPopupControllerImpl extends ActionModeCallbackHelper implements ImeEventObserver, SelectionPopupController, WindowEventObserver, HideablePopup, - ContainerViewObserver { + ContainerViewObserver, UserData { private static final String TAG = "SelectionPopupCtlr"; // 20 char limit /** @@ -199,8 +199,9 @@ * {@link #create()} is not called yet. */ public static SelectionPopupControllerImpl fromWebContents(WebContents webContents) { - return WebContentsUserData.fromWebContents(webContents, SelectionPopupControllerImpl.class, - UserDataFactoryLazyHolder.INSTANCE); + return ((WebContentsImpl) webContents) + .getOrSetUserData( + SelectionPopupControllerImpl.class, UserDataFactoryLazyHolder.INSTANCE); } /**
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java index 1c262fb0..25856a5 100644 --- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java +++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
@@ -20,6 +20,8 @@ import org.chromium.base.Callback; import org.chromium.base.Log; import org.chromium.base.ThreadUtils; +import org.chromium.base.UserData; +import org.chromium.base.UserDataHost; import org.chromium.base.VisibleForTesting; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; @@ -51,9 +53,7 @@ import org.chromium.ui.base.WindowAndroid; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.UUID; /** @@ -188,7 +188,7 @@ private boolean mInitialized; private static class WebContentsInternalsImpl implements WebContentsInternals { - public HashMap<Class<?>, WebContentsUserData> userDataMap; + public UserDataHost userDataHost; public ViewAndroidDelegate viewAndroidDelegate; } @@ -216,7 +216,7 @@ mInternalsHolder = internalsHolder; WebContentsInternalsImpl internals = new WebContentsInternalsImpl(); - internals.userDataMap = new HashMap<>(); + internals.userDataHost = new UserDataHost(); mInternalsHolder.set(internals); mRenderCoordinates = new RenderCoordinatesImpl(); @@ -319,9 +319,13 @@ @Override public void destroy() { + // Note that |WebContents.destroy| is not guaranteed to be invoked. + // Any resource release relying on this method will likely be leaked. + if (!ThreadUtils.runningOnUiThread()) { throw new IllegalStateException("Attempting to destroy WebContents on non-UI thread"); } + if (mNativeWebContentsAndroid != 0) nativeDestroyWebContents(mNativeWebContentsAndroid); } @@ -797,40 +801,42 @@ * not created yet, or {@code userDataFactory} is null, or the internal data * storage is already garbage-collected. */ - public <T> T getOrSetUserData(Class<T> key, UserDataFactory<T> userDataFactory) { + public <T extends UserData> T getOrSetUserData( + Class<T> key, UserDataFactory<T> userDataFactory) { // For tests that go without calling |initialize|. if (!mInitialized) return null; - Map<Class<?>, WebContentsUserData> userDataMap = getUserDataMap(); + UserDataHost userDataHost = getUserDataHost(); // Map can be null after WebView gets gc'ed on its way to destruction. - if (userDataMap == null) { - Log.e(TAG, "UserDataMap can't be found"); + if (userDataHost == null) { + Log.e(TAG, "UserDataHost can't be found"); return null; } - WebContentsUserData data = userDataMap.get(key); + T data = userDataHost.getUserData(key); if (data == null && userDataFactory != null) { - assert !userDataMap.containsKey(key); // Do not allow duplicated Data + assert userDataHost.getUserData(key) == null; // Do not allow overwriting T object = userDataFactory.create(this); assert key.isInstance(object); - userDataMap.put(key, new WebContentsUserData(object)); + // Retrieves from the map again to return null in case |setUserData| fails // to store the object. - data = userDataMap.get(key); + data = userDataHost.setUserData(key, object); } - return data != null ? key.cast(data.getObject()) : null; + return key.cast(data); } /** - * @return {@code UserDataMap} that contains internal user data. {@code null} if - * the map is already gc'ed. + * @return {@code UserDataHost} that contains internal user data. {@code null} if + * it is already gc'ed. */ - private Map<Class<?>, WebContentsUserData> getUserDataMap() { + private UserDataHost getUserDataHost() { + if (mInternalsHolder == null) return null; WebContentsInternals internals = mInternalsHolder.get(); if (internals == null) return null; - return ((WebContentsInternalsImpl) internals).userDataMap; + return ((WebContentsInternalsImpl) internals).userDataHost; } // WindowEventObserver
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsUserData.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsUserData.java deleted file mode 100644 index 0bf39a5..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsUserData.java +++ /dev/null
@@ -1,51 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.content.browser.webcontents; - -import org.chromium.content.browser.webcontents.WebContentsImpl.UserDataFactory; -import org.chromium.content_public.browser.WebContents; - -/** - * Holds an object to be stored in {@code userDataMap} in {@link WebContents} for those - * classes that have the lifetime of {@link WebContents} without hanging directly onto it. - * To create an object of a class {@code MyClass}, define a static method - * {@code fromWebContents()} where you call: - * <code> - * WebContentsUserData.fromWebContents(webContents, MyClass.class, MyClass::new); - * </code> - * - * {@code MyClass} should have a contstructor that accepts only one parameter: - * <code> - * public MyClass(WebContents webContents); - * </code> - */ -public final class WebContentsUserData { - private final Object mObject; - - WebContentsUserData(Object object) { - mObject = object; - } - - Object getObject() { - return mObject; - } - - /** - * Looks up the generic object of the given web contents. - * - * @param webContents The web contents for which to lookup the object. - * @param key Class instance of the object used as the key. - * @param userDataFactory Factory that creates an object of the generic class. Creates a new - * instance and returns it if not created yet. - * @return The object of the given web contents. Can be null if the object was not set, - * the user data map is already garbage-collected, or {@link WebContents#initialize()} - * is not called yet. - * - */ - public static <T> T fromWebContents( - WebContents webContents, Class<T> key, UserDataFactory<T> userDataFactory) { - return ((WebContentsImpl) webContents).getOrSetUserData(key, userDataFactory); - } -}
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 10dd92d..6039657 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
@@ -27,7 +27,6 @@ import org.chromium.content.browser.input.ChromiumBaseInputConnection; import org.chromium.content.browser.input.ImeTestUtils; import org.chromium.content.browser.selection.SelectionPopupControllerImpl; -import org.chromium.content_public.browser.ImeAdapter; import org.chromium.content_public.browser.SelectionClient; import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.test.ContentJUnit4ClassRunner; @@ -107,8 +106,7 @@ mActivityTestRule.waitForActiveShellToBeDoneLoading(); mWebContents = mActivityTestRule.getWebContents(); - mSelectionPopupController = - SelectionPopupControllerImpl.fromWebContents(mActivityTestRule.getWebContents()); + mSelectionPopupController = mActivityTestRule.getSelectionPopupController(); waitForSelectActionBarVisible(false); waitForPastePopupStatus(false); } @@ -635,8 +633,7 @@ private CharSequence getTextBeforeCursor(final int length, final int flags) { final ChromiumBaseInputConnection connection = - (ChromiumBaseInputConnection) ImeAdapter - .fromWebContents(mActivityTestRule.getWebContents()) + (ChromiumBaseInputConnection) mActivityTestRule.getImeAdapter() .getInputConnectionForTest(); return ImeTestUtils.runBlockingOnHandlerNoException( connection.getHandler(), new Callable<CharSequence>() {
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java index 442515f4..9bd3b1c 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java
@@ -26,6 +26,7 @@ import org.junit.Test; import org.junit.runner.RunWith; +import org.chromium.base.ThreadUtils; import org.chromium.base.test.BaseJUnit4ClassRunner; import org.chromium.base.test.util.MinAndroidSdkLevel; import org.chromium.base.test.util.UrlUtils; @@ -34,6 +35,7 @@ import org.chromium.content_shell_apk.ContentShellActivityTestRule; import java.lang.reflect.Method; +import java.util.concurrent.ExecutionException; /** * Tests for WebContentsAccessibility. Actually tests WebContentsAccessibilityImpl that @@ -64,8 +66,7 @@ * returns something not null. */ private AccessibilityNodeProvider enableAccessibilityAndWaitForNodeProvider() { - final WebContentsAccessibilityImpl wcax = - WebContentsAccessibilityImpl.fromWebContents(mActivityTestRule.getWebContents()); + final WebContentsAccessibilityImpl wcax = mActivityTestRule.getWebContentsAccessibility(); wcax.setState(true); wcax.setAccessibilityEnabledForTesting(); @@ -220,7 +221,8 @@ } }); - int virtualViewId = findNodeMatching(provider, View.NO_ID, matcher); + int virtualViewId = ThreadUtils.runOnUiThreadBlockingNoException( + () -> findNodeMatching(provider, View.NO_ID, matcher)); Assert.assertNotEquals(View.NO_ID, virtualViewId); return virtualViewId; } @@ -313,11 +315,11 @@ } }); - boolean result1 = provider.performAction( - editFieldVirtualViewId, AccessibilityNodeInfo.ACTION_FOCUS, null); - boolean result2 = provider.performAction( - editFieldVirtualViewId, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); - boolean result3 = provider.performAction(editFieldVirtualViewId, + boolean result1 = performActionOnUiThread( + provider, editFieldVirtualViewId, AccessibilityNodeInfo.ACTION_FOCUS, null); + boolean result2 = performActionOnUiThread(provider, editFieldVirtualViewId, + AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); + boolean result3 = performActionOnUiThread(provider, editFieldVirtualViewId, AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null); // Assert all actions are performed successfully. @@ -339,20 +341,26 @@ // Simulate swipe left. for (int i = 3; i >= 0; i--) { - boolean result = provider.performAction(editFieldVirtualViewId, + boolean result = performActionOnUiThread(provider, editFieldVirtualViewId, AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, args); // Assert that the index of the character traversed is correct. Assert.assertEquals(i, mostRecentCharIndex.value); } // Simulate swipe right. for (int i = 0; i <= 3; i++) { - boolean result = provider.performAction(editFieldVirtualViewId, + boolean result = performActionOnUiThread(provider, editFieldVirtualViewId, AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, args); // Assert that the index of the character traversed is correct. Assert.assertEquals(i, mostRecentCharIndex.value); } } + private static boolean performActionOnUiThread(AccessibilityNodeProvider provider, int viewId, + int action, Bundle args) throws ExecutionException { + return ThreadUtils.runOnUiThreadBlocking( + () -> provider.performAction(viewId, action, args)); + } + /** * Text fields should expose ACTION_SET_TEXT */
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeActivityTestRule.java b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeActivityTestRule.java index dfa0503..33601bd 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeActivityTestRule.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeActivityTestRule.java
@@ -60,7 +60,7 @@ public void setUpForUrl(String url) throws Exception { launchContentShellWithUrlSync(url); - mSelectionPopupController = SelectionPopupControllerImpl.fromWebContents(getWebContents()); + mSelectionPopupController = getSelectionPopupController(); final ImeAdapter imeAdapter = getImeAdapter(); InputConnectionProvider provider = @@ -126,10 +126,6 @@ resetAllStates(); } - SelectionPopupControllerImpl getSelectionPopupController() { - return mSelectionPopupController; - } - TestCallbackHelperContainer getTestCallBackHelperContainer() { return mCallbackContainer; } @@ -307,10 +303,6 @@ }); } - ImeAdapterImpl getImeAdapter() { - return ImeAdapterImpl.fromWebContents(getWebContents()); - } - ChromiumBaseInputConnection getInputConnection() { try { return ThreadUtils.runOnUiThreadBlocking(new Callable<ChromiumBaseInputConnection>() {
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/SelectPopupTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/SelectPopupTest.java index 827e1db87..effd882 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/input/SelectPopupTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/input/SelectPopupTest.java
@@ -24,7 +24,6 @@ import org.chromium.content_public.browser.test.util.DOMUtils; import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer; import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer.OnPageFinishedHelper; -import org.chromium.content_public.browser.test.util.WebContentsUtils; import org.chromium.content_shell_apk.ContentShellActivityTestRule; import org.chromium.content_shell_apk.ContentShellActivityTestRule.RerunWithUpdatedContainerView; @@ -61,7 +60,7 @@ @Override public boolean isSatisfied() { - return WebContentsUtils.isSelectPopupVisible(mActivityTestRule.getWebContents()); + return mActivityTestRule.getSelectPopup().isVisibleForTesting(); } } @@ -72,7 +71,7 @@ @Override public boolean isSatisfied() { - return !WebContentsUtils.isSelectPopupVisible(mActivityTestRule.getWebContents()); + return !mActivityTestRule.getSelectPopup().isVisibleForTesting(); } }
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/TextSuggestionMenuTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/TextSuggestionMenuTest.java index 03e7e8d..31ad458 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/input/TextSuggestionMenuTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/input/TextSuggestionMenuTest.java
@@ -282,7 +282,7 @@ ThreadUtils.runOnUiThreadBlocking(new Runnable() { @Override public void run() { - TextSuggestionHost.fromWebContents(webContents) + getTextSuggestionHost(webContents) .getTextSuggestionsPopupWindowForTesting() .dismiss(); } @@ -313,12 +313,11 @@ @Override public boolean isSatisfied() { SuggestionsPopupWindow suggestionsPopupWindow = - TextSuggestionHost.fromWebContents(webContents) + getTextSuggestionHost(webContents) .getTextSuggestionsPopupWindowForTesting(); SuggestionsPopupWindow spellCheckPopupWindow = - TextSuggestionHost.fromWebContents(webContents) - .getSpellCheckPopupWindowForTesting(); + getTextSuggestionHost(webContents).getSpellCheckPopupWindowForTesting(); return suggestionsPopupWindow == null && spellCheckPopupWindow == null; } @@ -327,16 +326,14 @@ private View getContentView(WebContents webContents) { SuggestionsPopupWindow suggestionsPopupWindow = - TextSuggestionHost.fromWebContents(webContents) - .getTextSuggestionsPopupWindowForTesting(); + getTextSuggestionHost(webContents).getTextSuggestionsPopupWindowForTesting(); if (suggestionsPopupWindow != null) { return suggestionsPopupWindow.getContentViewForTesting(); } SuggestionsPopupWindow spellCheckPopupWindow = - TextSuggestionHost.fromWebContents(webContents) - .getSpellCheckPopupWindowForTesting(); + getTextSuggestionHost(webContents).getSpellCheckPopupWindowForTesting(); if (spellCheckPopupWindow != null) { return spellCheckPopupWindow.getContentViewForTesting(); @@ -345,6 +342,11 @@ return null; } + private TextSuggestionHost getTextSuggestionHost(WebContents webContents) { + return ThreadUtils.runOnUiThreadBlockingNoException( + () -> TextSuggestionHost.fromWebContents(webContents)); + } + private ListView getSuggestionList(WebContents webContents) { View contentView = getContentView(webContents); return (ListView) contentView.findViewById(R.id.suggestionContainer);
diff --git a/content/public/app/mojo/content_browser_manifest.json b/content/public/app/mojo/content_browser_manifest.json index 1f3224a0..3648f50 100644 --- a/content/public/app/mojo/content_browser_manifest.json +++ b/content/public/app/mojo/content_browser_manifest.json
@@ -1,7 +1,10 @@ { "name": "content_browser", "display_name": "Content (browser process)", - "options": { "can_connect_to_other_services_as_any_user": true }, + "options": { + "can_connect_to_other_services_as_any_user": true, + "can_connect_to_other_services_with_any_instance_name": true + }, "interface_provider_specs": { "service_manager:connector": { "provides": { @@ -126,7 +129,6 @@ ], "service_manager": [ "service_manager:client_process", - "service_manager:instance_name", "service_manager:service_manager" ], "shape_detection": [
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc index 8e652e4b..cbd9987 100644 --- a/content/public/browser/content_browser_client.cc +++ b/content/public/browser/content_browser_client.cc
@@ -672,7 +672,8 @@ RenderFrameHost* frame, bool is_navigation, const GURL& url, - network::mojom::URLLoaderFactoryRequest* factory_request) { + network::mojom::URLLoaderFactoryRequest* factory_request, + bool* bypass_redirect_checks) { return false; }
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h index bc2b501..32fb2b5 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h
@@ -1138,6 +1138,9 @@ // requests for the URLLoaderFactory. Otherwise |*factory_request| is left // unmodified and this must return |false|. // + // |bypass_redirect_checks| will be set to true when the embedder will be + // handling redirect security checks. + // // Always called on the UI thread and only when the Network Service is // enabled. // @@ -1149,7 +1152,8 @@ RenderFrameHost* frame, bool is_navigation, const GURL& url, - network::mojom::URLLoaderFactoryRequest* factory_request); + network::mojom::URLLoaderFactoryRequest* factory_request, + bool* bypass_redirect_checks); // Allows the embedder to intercept a WebSocket connection. |*request| // is always valid upon entry and MUST be valid upon return. The embedder
diff --git a/content/public/browser/devtools_manager_delegate.cc b/content/public/browser/devtools_manager_delegate.cc index 5284bd8b..be5ffe2 100644 --- a/content/public/browser/devtools_manager_delegate.cc +++ b/content/public/browser/devtools_manager_delegate.cc
@@ -23,7 +23,8 @@ return std::string(); } -bool DevToolsManagerDelegate::AllowInspectingWebContents(WebContents* wc) { +bool DevToolsManagerDelegate::AllowInspectingRenderFrameHost( + RenderFrameHost* rfh) { return true; }
diff --git a/content/public/browser/devtools_manager_delegate.h b/content/public/browser/devtools_manager_delegate.h index 781af156..a31878d1 100644 --- a/content/public/browser/devtools_manager_delegate.h +++ b/content/public/browser/devtools_manager_delegate.h
@@ -20,6 +20,7 @@ namespace content { class DevToolsAgentHostClient; +class RenderFrameHost; class WebContents; class CONTENT_EXPORT DevToolsManagerDelegate { @@ -36,8 +37,8 @@ // Returns DevToolsAgentHost title to use for given |web_contents| target. virtual std::string GetTargetDescription(WebContents* web_contents); - // Returns whether embedder allows to inspect given |web_contents|. - virtual bool AllowInspectingWebContents(WebContents* web_contents); + // Returns whether embedder allows to inspect given |rfh|. + virtual bool AllowInspectingRenderFrameHost(RenderFrameHost* rfh); // Returns all targets embedder would like to report as debuggable // remotely.
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/WebContentsUtils.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/WebContentsUtils.java index 48a09372..9f39714 100644 --- a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/WebContentsUtils.java +++ b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/WebContentsUtils.java
@@ -8,9 +8,14 @@ import org.chromium.base.annotations.JNINamespace; import org.chromium.content.browser.input.SelectPopup; import org.chromium.content.browser.webcontents.WebContentsImpl; +import org.chromium.content_public.browser.GestureListenerManager; +import org.chromium.content_public.browser.ImeAdapter; import org.chromium.content_public.browser.RenderFrameHost; +import org.chromium.content_public.browser.ViewEventSink; import org.chromium.content_public.browser.WebContents; +import java.util.concurrent.ExecutionException; + /** * Collection of test-only WebContents utilities. */ @@ -55,6 +60,43 @@ ((WebContentsImpl) webContents).simulateRendererKilledForTesting(wasOomProtected)); } + /** + * Returns {@link ImeAdapter} instance associated with a given {@link WebContents}. + * @param webContents The WebContents in use. + */ + public static ImeAdapter getImeAdapter(WebContents webContents) { + try { + return ThreadUtils.runOnUiThreadBlocking(() -> ImeAdapter.fromWebContents(webContents)); + } catch (ExecutionException e) { + return null; + } + } + + /** + * Returns {@link GestureListenerManager} instance associated with a given {@link WebContents}. + * @param webContents The WebContents in use. + */ + public static GestureListenerManager getGestureListenerManager(WebContents webContents) { + try { + return ThreadUtils.runOnUiThreadBlocking( + () -> GestureListenerManager.fromWebContents(webContents)); + } catch (ExecutionException e) { + return null; + } + } + + /** + * Returns {@link ViewEventSink} instance associated with a given {@link WebContents}. + * @param webContents The WebContents in use. + */ + public static ViewEventSink getViewEventSink(WebContents webContents) { + try { + return ThreadUtils.runOnUiThreadBlocking(() -> ViewEventSink.from(webContents)); + } catch (ExecutionException e) { + return null; + } + } + private static native void nativeReportAllFrameSubmissions( WebContents webContents, boolean enabled); private static native RenderFrameHost nativeGetFocusedFrame(WebContents webContents);
diff --git a/content/public/test/fake_service_worker_context.cc b/content/public/test/fake_service_worker_context.cc index 14dec324..77df13c0 100644 --- a/content/public/test/fake_service_worker_context.cc +++ b/content/public/test/fake_service_worker_context.cc
@@ -89,8 +89,9 @@ void FakeServiceWorkerContext::StopAllServiceWorkersForOrigin( const GURL& origin) { - NOTREACHED(); + stop_all_service_workers_for_origin_calls_.push_back(origin); } + void FakeServiceWorkerContext::StopAllServiceWorkers(base::OnceClosure) { NOTREACHED(); }
diff --git a/content/public/test/fake_service_worker_context.h b/content/public/test/fake_service_worker_context.h index 4c9243d..00b6651 100644 --- a/content/public/test/fake_service_worker_context.h +++ b/content/public/test/fake_service_worker_context.h
@@ -79,12 +79,18 @@ return start_service_worker_and_dispatch_long_running_message_calls_; }; + const std::vector<GURL>& stop_all_service_workers_for_origin_calls() { + return stop_all_service_workers_for_origin_calls_; + } + private: bool start_service_worker_for_navigation_hint_called_ = false; std::vector<StartServiceWorkerAndDispatchLongRunningMessageArgs> start_service_worker_and_dispatch_long_running_message_calls_; + std::vector<GURL> stop_all_service_workers_for_origin_calls_; + base::ObserverList<ServiceWorkerContextObserver, true>::Unchecked observers_; DISALLOW_COPY_AND_ASSIGN(FakeServiceWorkerContext);
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index 7fd44e3..92563d7 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -3731,8 +3731,17 @@ std::unique_ptr<blink::WebServiceWorkerProvider> RenderFrameImpl::CreateServiceWorkerProvider() { + // Bail-out if we are about to be navigated away. + // We check that DocumentLoader is attached since: + // - This serves as the signal since the DocumentLoader is detached in + // FrameLoader::PrepareForCommit(). + // - Creating ServiceWorkerProvider in + // RenderFrameImpl::CreateServiceWorkerProvider() assumes that there is a + // DocumentLoader attached to the frame. + if (!frame_->GetDocumentLoader()) + return nullptr; + // At this point we should have non-null data source. - DCHECK(frame_->GetDocumentLoader()); if (!ChildThreadImpl::current()) return nullptr; // May be null in some tests. ServiceWorkerNetworkProvider* provider =
diff --git a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellActivityTestRule.java b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellActivityTestRule.java index b7674a7b..ca9390c 100644 --- a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellActivityTestRule.java +++ b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellActivityTestRule.java
@@ -25,6 +25,10 @@ import org.chromium.base.test.util.CallbackHelper; import org.chromium.base.test.util.UrlUtils; import org.chromium.content.browser.RenderCoordinatesImpl; +import org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl; +import org.chromium.content.browser.input.ImeAdapterImpl; +import org.chromium.content.browser.input.SelectPopup; +import org.chromium.content.browser.selection.SelectionPopupControllerImpl; import org.chromium.content.browser.webcontents.WebContentsImpl; import org.chromium.content_public.browser.JavascriptInjector; import org.chromium.content_public.browser.LoadUrlParams; @@ -157,12 +161,59 @@ } /** + * Returns the {@link SelectionPopupControllerImpl} of the WebContents. + */ + public SelectionPopupControllerImpl getSelectionPopupController() { + try { + return ThreadUtils.runOnUiThreadBlocking(() -> { + return SelectionPopupControllerImpl.fromWebContents( + getActivity().getActiveShell().getWebContents()); + }); + } catch (ExecutionException e) { + return null; + } + } + + /** + * Returns the {@link ImeAdapterImpl} of the WebContents. + */ + public ImeAdapterImpl getImeAdapter() { + try { + return ThreadUtils.runOnUiThreadBlocking( + () -> ImeAdapterImpl.fromWebContents(getWebContents())); + } catch (ExecutionException e) { + return null; + } + } + + /** + * Returns the {@link SelectPopup} of the WebContents. + */ + public SelectPopup getSelectPopup() { + try { + return ThreadUtils.runOnUiThreadBlocking( + () -> SelectPopup.fromWebContents(getWebContents())); + } catch (ExecutionException e) { + return null; + } + } + + public WebContentsAccessibilityImpl getWebContentsAccessibility() { + try { + return ThreadUtils.runOnUiThreadBlocking( + () -> WebContentsAccessibilityImpl.fromWebContents(getWebContents())); + } catch (ExecutionException e) { + return null; + } + } + + /** * Returns the RenderCoordinates of the WebContents. */ public RenderCoordinatesImpl getRenderCoordinates() { try { return ThreadUtils.runOnUiThreadBlocking( - () -> { return ((WebContentsImpl) getWebContents()).getRenderCoordinates(); }); + () -> ((WebContentsImpl) getWebContents()).getRenderCoordinates()); } catch (ExecutionException e) { return null; }
diff --git a/content/test/data/accessibility/event/live-region-add-expected-win.txt b/content/test/data/accessibility/event/live-region-add-expected-win.txt index a57cfad..2768682f 100644 --- a/content/test/data/accessibility/event/live-region-add-expected-win.txt +++ b/content/test/data/accessibility/event/live-region-add-expected-win.txt
@@ -1,3 +1,4 @@ +EVENT_OBJECT_LIVEREGIONCHANGED on <div#live> role=DIV EVENT_OBJECT_REORDER on <div#live> role=DIV EVENT_OBJECT_SHOW on <p> role=P IA2_EVENT_TEXT_INSERTED on <div#live> role=DIV new_text={'<obj>' start=6 end=7}
diff --git a/content/test/data/accessibility/event/live-region-change-expected-win.txt b/content/test/data/accessibility/event/live-region-change-expected-win.txt index aa60a845..d85c9ec 100644 --- a/content/test/data/accessibility/event/live-region-change-expected-win.txt +++ b/content/test/data/accessibility/event/live-region-change-expected-win.txt
@@ -1,2 +1,3 @@ +EVENT_OBJECT_LIVEREGIONCHANGED on <div#live> role=DIV IA2_EVENT_TEXT_INSERTED on <div#live> role=DIV new_text={'After' start=0 end=5} IA2_EVENT_TEXT_REMOVED on <div#live> role=DIV old_text={'Before' start=0 end=6}
diff --git a/extensions/shell/browser/shell_content_browser_client.cc b/extensions/shell/browser/shell_content_browser_client.cc index 2a32fe0..321a89e9 100644 --- a/extensions/shell/browser/shell_content_browser_client.cc +++ b/extensions/shell/browser/shell_content_browser_client.cc
@@ -291,12 +291,16 @@ content::RenderFrameHost* frame, bool is_navigation, const GURL& url, - network::mojom::URLLoaderFactoryRequest* factory_request) { + network::mojom::URLLoaderFactoryRequest* factory_request, + bool* bypass_redirect_checks) { auto* web_request_api = extensions::BrowserContextKeyedAPIFactory<extensions::WebRequestAPI>::Get( browser_context); - return web_request_api->MaybeProxyURLLoaderFactory(frame, is_navigation, - factory_request); + bool use_proxy = web_request_api->MaybeProxyURLLoaderFactory( + frame, is_navigation, factory_request); + if (bypass_redirect_checks) + *bypass_redirect_checks = use_proxy; + return use_proxy; } bool ShellContentBrowserClient::HandleExternalProtocol(
diff --git a/extensions/shell/browser/shell_content_browser_client.h b/extensions/shell/browser/shell_content_browser_client.h index 1176623..7b419386 100644 --- a/extensions/shell/browser/shell_content_browser_client.h +++ b/extensions/shell/browser/shell_content_browser_client.h
@@ -78,7 +78,8 @@ content::RenderFrameHost* frame_host, bool is_navigation, const GURL& url, - network::mojom::URLLoaderFactoryRequest* factory_request) override; + network::mojom::URLLoaderFactoryRequest* factory_request, + bool* bypass_redirect_checks) override; bool HandleExternalProtocol( const GURL& url, content::ResourceRequestInfo::WebContentsGetter web_contents_getter,
diff --git a/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm b/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm index cfc7bad..8fe1155 100644 --- a/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm +++ b/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm
@@ -67,7 +67,8 @@ @property(nonatomic, strong) GridLayout* defaultLayout; // The layout used while the grid is being reordered. @property(nonatomic, strong) UICollectionViewLayout* reorderingLayout; - +// YES if, when reordering is enabled, the order of the cells has changed. +@property(nonatomic, assign) BOOL hasChangedOrder; @end @implementation GridViewController @@ -88,6 +89,7 @@ @synthesize emptyStateAnimator = _emptyStateAnimator; @synthesize defaultLayout = _defaultLayout; @synthesize reorderingLayout = _reorderingLayout; +@synthesize hasChangedOrder = _hasChangedOrder; - (instancetype)init { if (self = [super init]) { @@ -286,7 +288,7 @@ GridItem* item = self.items[source]; [self.items removeObjectAtIndex:source]; [self.items insertObject:item atIndex:destination]; - + self.hasChangedOrder = YES; [self.delegate gridViewController:self didMoveItemWithID:item.identifier toIndex:destination]; @@ -587,6 +589,8 @@ if (!moving) { gesture.enabled = NO; } else { + base::RecordAction( + base::UserMetricsAction("MobileTabGridBeganReordering")); CGPoint cellCenter = [self.collectionView cellForItemAtIndexPath:path].center; self.itemReorderTouchPoint = @@ -624,12 +628,14 @@ animated:YES]; }]; [self.collectionView endInteractiveMovement]; + [self recordInteractiveReordering]; [CATransaction commit]; break; } case UIGestureRecognizerStateCancelled: self.itemReorderTouchPoint = CGPointZero; [self.collectionView cancelInteractiveMovement]; + [self recordInteractiveReordering]; [self.collectionView setCollectionViewLayout:self.defaultLayout animated:YES]; // Re-enable cancelled gesture. @@ -649,4 +655,14 @@ [self.collectionView updateInteractiveMovementTargetPosition:targetLocation]; } +- (void)recordInteractiveReordering { + if (self.hasChangedOrder) { + base::RecordAction(base::UserMetricsAction("MobileTabGridReordered")); + } else { + base::RecordAction( + base::UserMetricsAction("MobileTabGridEndedWithoutReordering")); + } + self.hasChangedOrder = NO; +} + @end
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm index 8b7deb17..974c3d5c 100644 --- a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm +++ b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
@@ -1245,14 +1245,20 @@ - (void)closeAllButtonTapped:(id)sender { switch (self.currentPage) { case TabGridPageIncognitoTabs: + base::RecordAction( + base::UserMetricsAction("MobileTabGridCloseAllIncognitoTabs")); [self.incognitoTabsDelegate closeAllItems]; break; case TabGridPageRegularTabs: DCHECK_EQ(self.undoCloseAllAvailable, self.regularTabsViewController.gridEmpty); if (self.undoCloseAllAvailable) { + base::RecordAction( + base::UserMetricsAction("MobileTabGridUndoCloseAllRegularTabs")); [self.regularTabsDelegate undoCloseAllItems]; } else { + base::RecordAction( + base::UserMetricsAction("MobileTabGridCloseAllRegularTabs")); [self.regularTabsDelegate saveAndCloseAllItems]; } self.undoCloseAllAvailable = !self.undoCloseAllAvailable;
diff --git a/ios/web/public/app/mojo/web_browser_manifest.json b/ios/web/public/app/mojo/web_browser_manifest.json index 1dc73c6..0cdaa6e 100644 --- a/ios/web/public/app/mojo/web_browser_manifest.json +++ b/ios/web/public/app/mojo/web_browser_manifest.json
@@ -1,7 +1,10 @@ { "name": "web_browser", "display_name": "Web", - "options": { "can_connect_to_other_services_as_any_user": true }, + "options": { + "can_connect_to_other_services_as_any_user": true, + "can_connect_to_other_services_with_any_instance_name": true + }, "interface_provider_specs": { "service_manager:connector": { "provides": { @@ -13,7 +16,6 @@ "*": [ "app" ], "service_manager": [ "service_manager:client_process", - "service_manager:instance_name", "service_manager:service_manager" ] }
diff --git a/ios/web/public/web_state/web_frame.h b/ios/web/public/web_state/web_frame.h index 7fe0be10..7907a9f 100644 --- a/ios/web/public/web_state/web_frame.h +++ b/ios/web/public/web_state/web_frame.h
@@ -9,10 +9,10 @@ #include "base/macros.h" #include "base/supports_user_data.h" +#include "base/time/time.h" #include "url/gurl.h" namespace base { -class TimeDelta; class Value; }
diff --git a/ios/web/web_state/web_frame_impl.h b/ios/web/web_state/web_frame_impl.h index d6474774..75f213d 100644 --- a/ios/web/web_state/web_frame_impl.h +++ b/ios/web/web_state/web_frame_impl.h
@@ -36,17 +36,12 @@ // The associated web state. WebState* GetWebState(); - // Detaches the receiver from the associated WebState. - void DetachFromWebState(); // WebFrame implementation std::string GetFrameId() const override; bool IsMainFrame() const override; GURL GetSecurityOrigin() const override; - // WebStateObserver implementation - void WebStateDestroyed(web::WebState* web_state) override; - bool CallJavaScriptFunction( const std::string& name, const std::vector<base::Value>& parameters) override; @@ -56,6 +51,9 @@ base::OnceCallback<void(const base::Value*)> callback, base::TimeDelta timeout) override; + // WebStateObserver implementation + void WebStateDestroyed(web::WebState* web_state) override; + private: // Calls the JavaScript function |name| in the frame context in the same // manner as the inherited CallJavaScriptFunction functions. If @@ -65,6 +63,11 @@ const std::vector<base::Value>& parameters, bool reply_with_result); + // Detaches the receiver from the associated WebState. + void DetachFromWebState(); + // Returns the script command name to use for this WebFrame. + const std::string GetScriptCommandPrefix(); + // A structure to store the callbacks associated with the // |CallJavaScriptFunction| requests. typedef base::CancelableOnceCallback<void(void)> TimeoutCallback;
diff --git a/ios/web/web_state/web_frame_impl.mm b/ios/web/web_state/web_frame_impl.mm index 12e9687..1ff3518 100644 --- a/ios/web/web_state/web_frame_impl.mm +++ b/ios/web/web_state/web_frame_impl.mm
@@ -43,11 +43,10 @@ DCHECK(web_state); web_state->AddObserver(this); - const std::string command_prefix = kJavaScriptReplyCommandPrefix + frame_id; web_state->AddScriptCommandCallback( base::BindRepeating(&WebFrameImpl::OnJavaScriptReply, base::Unretained(this), base::Unretained(web_state)), - command_prefix); + GetScriptCommandPrefix()); } WebFrameImpl::~WebFrameImpl() { @@ -55,16 +54,6 @@ DetachFromWebState(); } -void WebFrameImpl::DetachFromWebState() { - if (web_state_) { - const std::string command_prefix = - kJavaScriptReplyCommandPrefix + frame_id_; - web_state_->RemoveScriptCommandCallback(command_prefix); - web_state_->RemoveObserver(this); - web_state_ = nullptr; - } -} - WebState* WebFrameImpl::GetWebState() { return web_state_; } @@ -126,7 +115,7 @@ bool WebFrameImpl::CallJavaScriptFunction( const std::string& name, const std::vector<base::Value>& parameters) { - return CallJavaScriptFunction(name, parameters, /*replyWithResult=*/false); + return CallJavaScriptFunction(name, parameters, /*reply_with_result=*/false); } bool WebFrameImpl::CallJavaScriptFunction( @@ -145,7 +134,7 @@ base::PostDelayedTaskWithTraits(FROM_HERE, {web::WebThread::UI}, timeout_callback_ptr->callback(), timeout); - return CallJavaScriptFunction(name, parameters, /*replyWithResult=*/true); + return CallJavaScriptFunction(name, parameters, /*reply_with_result=*/true); } void WebFrameImpl::CancelRequest(int message_id) { @@ -182,8 +171,7 @@ } const std::string command_string = command->GetString(); - if (command_string != - (std::string(kJavaScriptReplyCommandPrefix) + frame_id_ + ".reply")) { + if (command_string != (GetScriptCommandPrefix() + ".reply")) { NOTREACHED(); return false; } @@ -211,6 +199,18 @@ return true; } +void WebFrameImpl::DetachFromWebState() { + if (web_state_) { + web_state_->RemoveScriptCommandCallback(GetScriptCommandPrefix()); + web_state_->RemoveObserver(this); + web_state_ = nullptr; + } +} + +const std::string WebFrameImpl::GetScriptCommandPrefix() { + return kJavaScriptReplyCommandPrefix + frame_id_; +} + void WebFrameImpl::WebStateDestroyed(web::WebState* web_state) { CancelPendingRequests(); DetachFromWebState(); @@ -221,6 +221,6 @@ std::unique_ptr<TimeoutCallback> timeout) : completion(std::move(completion)), timeout_callback(std::move(timeout)) {} -WebFrameImpl::RequestCallbacks::~RequestCallbacks(){}; +WebFrameImpl::RequestCallbacks::~RequestCallbacks() {} } // namespace web
diff --git a/ios/web/web_state/web_frame_impl_inttest.mm b/ios/web/web_state/web_frame_impl_inttest.mm index cbcc0b6..a028b50e 100644 --- a/ios/web/web_state/web_frame_impl_inttest.mm +++ b/ios/web/web_state/web_frame_impl_inttest.mm
@@ -165,7 +165,7 @@ return; } - ASSERT_TRUE(LoadHtml("<p>>")); + ASSERT_TRUE(LoadHtml("<p>")); WebFrame* main_frame = GetMainWebFrameForWebState(web_state()); ASSERT_TRUE(main_frame); @@ -220,13 +220,13 @@ return called; })); - EXPECT_EQ(1, [ExecuteJavaScript(@"sensitiveValue") intValue]); + EXPECT_NSEQ(@1, ExecuteJavaScript(@"sensitiveValue")); ExecuteJavaScript(@"replayInterceptedMessage()"); // Value should not increase because replaying message should not re-execute // the called function. - EXPECT_EQ(1, [ExecuteJavaScript(@"sensitiveValue") intValue]); + EXPECT_NSEQ(@1, ExecuteJavaScript(@"sensitiveValue")); } } // namespace web
diff --git a/media/capabilities/in_memory_video_decode_stats_db_impl.cc b/media/capabilities/in_memory_video_decode_stats_db_impl.cc index 1612245..0da134b 100644 --- a/media/capabilities/in_memory_video_decode_stats_db_impl.cc +++ b/media/capabilities/in_memory_video_decode_stats_db_impl.cc
@@ -15,6 +15,7 @@ #include "base/strings/stringprintf.h" #include "base/task/post_task.h" #include "media/base/bind_to_current_loop.h" +#include "media/capabilities/video_decode_stats_db_impl.h" #include "media/capabilities/video_decode_stats_db_provider.h" namespace media { @@ -41,6 +42,9 @@ InMemoryVideoDecodeStatsDBImpl::~InMemoryVideoDecodeStatsDBImpl() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (seed_db_) + seed_db_->set_dependent_db(nullptr); } void InMemoryVideoDecodeStatsDBImpl::Initialize(InitializeCB init_cb) { @@ -69,8 +73,13 @@ DVLOG(2) << __func__ << (db ? " has" : " null") << " seed db"; db_init_ = true; + + CHECK(!seed_db_) << __func__ << " Already have a seed_db_?"; seed_db_ = db; + if (seed_db_) + seed_db_->set_dependent_db(this); + // Hard coding success = true. There are rare cases (e.g. disk corruption) // where an incognito profile may fail to acquire a reference to the base // profile's DB. But this just means incognito is in the same boat as guest
diff --git a/media/capabilities/in_memory_video_decode_stats_db_unittest.cc b/media/capabilities/in_memory_video_decode_stats_db_unittest.cc index 6f6264bf..9083cf51 100644 --- a/media/capabilities/in_memory_video_decode_stats_db_unittest.cc +++ b/media/capabilities/in_memory_video_decode_stats_db_unittest.cc
@@ -8,8 +8,10 @@ #include "base/bind_helpers.h" #include "base/logging.h" #include "base/memory/ptr_util.h" +#include "base/test/gtest_util.h" #include "base/test/scoped_task_environment.h" #include "media/capabilities/in_memory_video_decode_stats_db_impl.h" +#include "media/capabilities/video_decode_stats_db_impl.h" #include "media/capabilities/video_decode_stats_db_provider.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -388,4 +390,14 @@ scoped_task_environment_.RunUntilIdle(); } +TEST_F(SeededInMemoryDBTest, SeedDBTearDownRace) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + + // Establish depends-on connection from InMemoryDB to SeedDB. + InitializeEmptyDB(); + + // Destroying the seed-db dependency should trigger a crash. + EXPECT_CHECK_DEATH(seed_db_.reset()); +} + } // namespace media
diff --git a/media/capabilities/video_decode_stats_db.cc b/media/capabilities/video_decode_stats_db.cc index 2978a28f..d8618911 100644 --- a/media/capabilities/video_decode_stats_db.cc +++ b/media/capabilities/video_decode_stats_db.cc
@@ -95,4 +95,9 @@ return !(x == y); } +VideoDecodeStatsDB::~VideoDecodeStatsDB() { + // Tracking down crash. See https://crbug/865321. + CHECK(!dependent_db_) << __func__ << " Destroying before dependent_db_!"; +} + } // namespace media
diff --git a/media/capabilities/video_decode_stats_db.h b/media/capabilities/video_decode_stats_db.h index 7656cc1..899f4fda 100644 --- a/media/capabilities/video_decode_stats_db.h +++ b/media/capabilities/video_decode_stats_db.h
@@ -66,7 +66,7 @@ uint64_t frames_decoded_power_efficient; }; - virtual ~VideoDecodeStatsDB() = default; + virtual ~VideoDecodeStatsDB(); // Run asynchronous initialization of database. Initialization must complete // before calling other APIs. Initialization must be RE-RUN after calling @@ -97,6 +97,22 @@ // DO NOT use the database until |callback| is run. When finished, users must // RE-RUN Initialize() before performing further I/O. virtual void DestroyStats(base::OnceClosure destroy_done_cb) = 0; + + // Tracking down root cause of crash probable UAF (https://crbug/865321). + // We will CHECK if a |dependent_db_| is found to be set during destruction. + // Dependent DB should always be destroyed and unhooked before |this|. + void set_dependent_db(VideoDecodeStatsDB* dependent) { + // One of these should be non-null. + CHECK(!dependent_db_ || !dependent); + // They shouldn't already match. + CHECK(dependent_db_ != dependent); + + dependent_db_ = dependent; + } + + private: + // See set_dependent_db(). + VideoDecodeStatsDB* dependent_db_ = nullptr; }; MEDIA_EXPORT bool operator==(const VideoDecodeStatsDB::VideoDescKey& x,
diff --git a/media/capabilities/video_decode_stats_db_unittest.cc b/media/capabilities/video_decode_stats_db_unittest.cc index 6bb83107..cd69e9c 100644 --- a/media/capabilities/video_decode_stats_db_unittest.cc +++ b/media/capabilities/video_decode_stats_db_unittest.cc
@@ -22,6 +22,7 @@ using leveldb_proto::test::FakeDB; using testing::Pointee; using testing::Eq; +using testing::_; namespace media {
diff --git a/media/gpu/h264_decoder.cc b/media/gpu/h264_decoder.cc index 260849b..ed0290b 100644 --- a/media/gpu/h264_decoder.cc +++ b/media/gpu/h264_decoder.cc
@@ -13,6 +13,7 @@ #include "base/optional.h" #include "base/stl_util.h" #include "media/gpu/h264_decoder.h" +#include "media/video/h264_level_limits.h" namespace media { @@ -964,42 +965,6 @@ return true; } -static int LevelToMaxDpbMbs(int level) { - // See table A-1 in spec. - switch (level) { - case 10: - return 396; - case 11: - return 900; - case 12: // fallthrough - case 13: // fallthrough - case 20: - return 2376; - case 21: - return 4752; - case 22: // fallthrough - case 30: - return 8100; - case 31: - return 18000; - case 32: - return 20480; - case 40: // fallthrough - case 41: - return 32768; - case 42: - return 34816; - case 50: - return 110400; - case 51: // fallthrough - case 52: - return 184320; - default: - DVLOG(1) << "Invalid codec level (" << level << ")"; - return 0; - } -} - bool H264Decoder::UpdateMaxNumReorderFrames(const H264SPS* sps) { if (sps->vui_parameters_present_flag && sps->bitstream_restriction_flag) { max_num_reorder_frames_ = @@ -1066,8 +1031,17 @@ return false; } - int level = sps->level_idc; - int max_dpb_mbs = LevelToMaxDpbMbs(level); + // Spec A.3.1 and A.3.2 + // For Baseline, Constrained Baseline and Main profile, the indicated level is + // Level 1b if level_idc is equal to 11 and constraint_set3_flag is equal to 1 + uint8_t level = base::checked_cast<uint8_t>(sps->level_idc); + if ((sps->profile_idc == H264SPS::kProfileIDCBaseline || + sps->profile_idc == H264SPS::kProfileIDCConstrainedBaseline || + sps->profile_idc == H264SPS::kProfileIDCMain) && + level == 11 && sps->constraint_set3_flag) { + level = 9; // Level 1b + } + int max_dpb_mbs = base::checked_cast<int>(H264LevelToMaxDpbMbs(level)); if (max_dpb_mbs == 0) return false;
diff --git a/media/gpu/v4l2/v4l2_device.cc b/media/gpu/v4l2/v4l2_device.cc index c548415..8f7b6de 100644 --- a/media/gpu/v4l2/v4l2_device.cc +++ b/media/gpu/v4l2/v4l2_device.cc
@@ -226,6 +226,79 @@ } // static +int32_t V4L2Device::VideoCodecProfileToV4L2H264Profile( + VideoCodecProfile profile) { + switch (profile) { + case H264PROFILE_BASELINE: + return V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; + case H264PROFILE_MAIN: + return V4L2_MPEG_VIDEO_H264_PROFILE_MAIN; + case H264PROFILE_EXTENDED: + return V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED; + case H264PROFILE_HIGH: + return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH; + case H264PROFILE_HIGH10PROFILE: + return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10; + case H264PROFILE_HIGH422PROFILE: + return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422; + case H264PROFILE_HIGH444PREDICTIVEPROFILE: + return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE; + case H264PROFILE_SCALABLEBASELINE: + return V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_BASELINE; + case H264PROFILE_SCALABLEHIGH: + return V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH; + case H264PROFILE_STEREOHIGH: + return V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH; + case H264PROFILE_MULTIVIEWHIGH: + return V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH; + default: + DVLOGF(1) << "Add more cases as needed"; + return -1; + } +} + +// static +int32_t V4L2Device::H264LevelIdcToV4L2H264Level(uint8_t level_idc) { + switch (level_idc) { + case 10: + return V4L2_MPEG_VIDEO_H264_LEVEL_1_0; + case 9: + return V4L2_MPEG_VIDEO_H264_LEVEL_1B; + case 11: + return V4L2_MPEG_VIDEO_H264_LEVEL_1_1; + case 12: + return V4L2_MPEG_VIDEO_H264_LEVEL_1_2; + case 13: + return V4L2_MPEG_VIDEO_H264_LEVEL_1_3; + case 20: + return V4L2_MPEG_VIDEO_H264_LEVEL_2_0; + case 21: + return V4L2_MPEG_VIDEO_H264_LEVEL_2_1; + case 22: + return V4L2_MPEG_VIDEO_H264_LEVEL_2_2; + case 30: + return V4L2_MPEG_VIDEO_H264_LEVEL_3_0; + case 31: + return V4L2_MPEG_VIDEO_H264_LEVEL_3_1; + case 32: + return V4L2_MPEG_VIDEO_H264_LEVEL_3_2; + case 40: + return V4L2_MPEG_VIDEO_H264_LEVEL_4_0; + case 41: + return V4L2_MPEG_VIDEO_H264_LEVEL_4_1; + case 42: + return V4L2_MPEG_VIDEO_H264_LEVEL_4_2; + case 50: + return V4L2_MPEG_VIDEO_H264_LEVEL_5_0; + case 51: + return V4L2_MPEG_VIDEO_H264_LEVEL_5_1; + default: + DVLOGF(1) << "Unrecognized level_idc: " << static_cast<int>(level_idc); + return -1; + } +} + +// static gfx::Size V4L2Device::CodedSizeFromV4L2Format(struct v4l2_format format) { gfx::Size coded_size; gfx::Size visible_size;
diff --git a/media/gpu/v4l2/v4l2_device.h b/media/gpu/v4l2/v4l2_device.h index 0fafd790..fa25dae 100644 --- a/media/gpu/v4l2/v4l2_device.h +++ b/media/gpu/v4l2/v4l2_device.h
@@ -49,6 +49,10 @@ // Convert format requirements requested by a V4L2 device to gfx::Size. static gfx::Size CodedSizeFromV4L2Format(struct v4l2_format format); + // Convert required H264 profile and level to V4L2 enums. + static int32_t VideoCodecProfileToV4L2H264Profile(VideoCodecProfile profile); + static int32_t H264LevelIdcToV4L2H264Level(uint8_t level_idc); + enum class Type { kDecoder, kEncoder,
diff --git a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc index 3d90254f..12f0d15 100644 --- a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc +++ b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
@@ -1017,6 +1017,7 @@ if (it == surfaces_at_device_.end()) { VLOGF(1) << "Got invalid surface from device."; NOTIFY_ERROR(PLATFORM_FAILURE); + return; } it->second->SetDecoded();
diff --git a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc index 5308e95e..85e90de0 100644 --- a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc +++ b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
@@ -20,6 +20,7 @@ #include "base/posix/eintr_wrapper.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" +#include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "media/base/media_switches.h" @@ -29,6 +30,7 @@ #include "media/video/h264_parser.h" #include "ui/gfx/geometry/rect.h" #include "ui/gl/gl_context.h" +#include "ui/gl/gl_fence_egl.h" #include "ui/gl/scoped_binders.h" #define DVLOGF(level) DVLOG(level) << __func__ << "(): " @@ -84,13 +86,6 @@ const int32_t input_id; }; -struct V4L2VideoDecodeAccelerator::EGLSyncKHRRef { - EGLSyncKHRRef(EGLDisplay egl_display, EGLSyncKHR egl_sync); - ~EGLSyncKHRRef(); - EGLDisplay const egl_display; - EGLSyncKHR egl_sync; -}; - V4L2VideoDecodeAccelerator::BitstreamBufferRef::BitstreamBufferRef( base::WeakPtr<Client>& client, scoped_refptr<base::SingleThreadTaskRunner>& client_task_runner, @@ -114,18 +109,6 @@ } } -V4L2VideoDecodeAccelerator::EGLSyncKHRRef::EGLSyncKHRRef(EGLDisplay egl_display, - EGLSyncKHR egl_sync) - : egl_display(egl_display), egl_sync(egl_sync) {} - -V4L2VideoDecodeAccelerator::EGLSyncKHRRef::~EGLSyncKHRRef() { - // We don't check for eglDestroySyncKHR failures, because if we get here - // with a valid sync object, something went wrong and we are getting - // destroyed anyway. - if (egl_sync != EGL_NO_SYNC_KHR) - eglDestroySyncKHR(egl_display, egl_sync); -} - V4L2VideoDecodeAccelerator::InputRecord::InputRecord() : at_device(false), address(NULL), length(0), bytes_used(0), input_id(-1) {} @@ -134,7 +117,6 @@ V4L2VideoDecodeAccelerator::OutputRecord::OutputRecord() : state(kFree), egl_image(EGL_NO_IMAGE_KHR), - egl_sync(EGL_NO_SYNC_KHR), picture_id(-1), texture_id(0), cleared(false) {} @@ -301,6 +283,9 @@ DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); DCHECK_EQ(decoder_state_, kInitialized); + if (IsDestroyPending()) + return; + // Subscribe to the resolution change event. struct v4l2_event_subscription sub; memset(&sub, 0, sizeof(sub)); @@ -355,6 +340,9 @@ DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); DCHECK_EQ(decoder_state_, kAwaitingPictureBuffers); + if (IsDestroyPending()) + return; + uint32_t req_buffer_count = output_dpb_size_ + kDpbOutputBufferExtraCount; if (image_processor_device_) req_buffer_count += kDpbOutputBufferExtraCountForImageProcessor; @@ -398,9 +386,9 @@ OutputRecord& output_record = output_buffer_map_[i]; DCHECK_EQ(output_record.state, kFree); DCHECK_EQ(output_record.egl_image, EGL_NO_IMAGE_KHR); - DCHECK_EQ(output_record.egl_sync, EGL_NO_SYNC_KHR); + DCHECK(!output_record.egl_fence); DCHECK_EQ(output_record.picture_id, -1); - DCHECK_EQ(output_record.cleared, false); + DCHECK(!output_record.cleared); DCHECK(output_record.processor_input_fds.empty()); output_record.picture_id = buffers[i].id(); @@ -499,6 +487,9 @@ DVLOGF(3) << "index=" << buffer_index << ", picture_id=" << picture_buffer_id; DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); + if (IsDestroyPending()) + return; + // It's possible that while waiting for the EGLImages to be allocated and // assigned, we have already decoded more of the stream and saw another // resolution change. This is a normal situation, in such a case either there @@ -517,7 +508,7 @@ OutputRecord& output_record = output_buffer_map_[buffer_index]; DCHECK_EQ(output_record.egl_image, EGL_NO_IMAGE_KHR); - DCHECK_EQ(output_record.egl_sync, EGL_NO_SYNC_KHR); + DCHECK(!output_record.egl_fence); DCHECK_EQ(output_record.state, kFree); DCHECK_EQ(std::count(free_output_buffers_.begin(), free_output_buffers_.end(), buffer_index), @@ -587,6 +578,9 @@ << ", stride=" << stride; DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); + if (IsDestroyPending()) + return; + const auto iter = std::find_if(output_buffer_map_.begin(), output_buffer_map_.end(), [picture_buffer_id](const OutputRecord& output_record) { @@ -678,7 +672,7 @@ // Must be run on child thread, as we'll insert a sync in the EGL context. DCHECK(child_task_runner_->BelongsToCurrentThread()); - std::unique_ptr<EGLSyncKHRRef> egl_sync_ref; + std::unique_ptr<gl::GLFenceEGL> egl_fence; if (!make_context_current_cb_.is_null()) { if (!make_context_current_cb_.Run()) { @@ -687,24 +681,21 @@ return; } - EGLSyncKHR egl_sync = EGL_NO_SYNC_KHR; // TODO(posciak): https://crbug.com/450898. #if defined(ARCH_CPU_ARMEL) - egl_sync = eglCreateSyncKHR(egl_display_, EGL_SYNC_FENCE_KHR, NULL); - if (egl_sync == EGL_NO_SYNC_KHR) { - VLOGF(1) << "eglCreateSyncKHR() failed"; + egl_fence = gl::GLFenceEGL::Create(); + if (!egl_fence) { + VLOGF(1) << "gl::GLFenceEGL::Create() failed"; NOTIFY_ERROR(PLATFORM_FAILURE); return; } #endif - - egl_sync_ref.reset(new EGLSyncKHRRef(egl_display_, egl_sync)); } decoder_thread_.task_runner()->PostTask( FROM_HERE, base::Bind(&V4L2VideoDecodeAccelerator::ReusePictureBufferTask, base::Unretained(this), picture_buffer_id, - base::Passed(&egl_sync_ref))); + base::Passed(&egl_fence))); } void V4L2VideoDecodeAccelerator::Flush() { @@ -727,6 +718,10 @@ VLOGF(2); DCHECK(child_task_runner_->BelongsToCurrentThread()); + // Signal any waiting/sleeping tasks to early exit as soon as possible to + // avoid waiting too long for the decoder_thread_ to Stop(). + destroy_pending_.Signal(); + // We're destroying; cancel all callbacks. client_ptr_factory_.reset(); weak_this_factory_.InvalidateWeakPtrs(); @@ -775,6 +770,9 @@ TRACE_EVENT1("media,gpu", "V4L2VDA::DecodeTask", "input_id", bitstream_buffer.id()); + if (IsDestroyPending()) + return; + std::unique_ptr<BitstreamBufferRef> bitstream_record( new BitstreamBufferRef(decode_client_, decode_task_runner_, &bitstream_buffer, bitstream_buffer.id())); @@ -815,6 +813,9 @@ DCHECK_NE(decoder_state_, kUninitialized); TRACE_EVENT0("media,gpu", "V4L2VDA::DecodeBufferTask"); + if (IsDestroyPending()) + return; + decoder_decode_buffer_tasks_scheduled_--; if (decoder_state_ != kInitialized && decoder_state_ != kDecoding) { @@ -1179,6 +1180,9 @@ DCHECK_NE(decoder_state_, kUninitialized); TRACE_EVENT0("media,gpu", "V4L2VDA::ServiceDeviceTask"); + if (IsDestroyPending()) + return; + if (decoder_state_ == kResetting) { DVLOGF(3) << "early out: kResetting state"; return; @@ -1533,23 +1537,33 @@ OutputRecord& output_record = output_buffer_map_[buffer]; DCHECK_EQ(output_record.state, kFree); DCHECK_NE(output_record.picture_id, -1); - if (output_record.egl_sync != EGL_NO_SYNC_KHR) { - TRACE_EVENT0("media,gpu", - "V4L2VDA::EnqueueOutputRecord: eglClientWaitSyncKHR"); - // If we have to wait for completion, wait. Note that - // free_output_buffers_ is a FIFO queue, so we always wait on the - // buffer that has been in the queue the longest. - if (eglClientWaitSyncKHR(egl_display_, output_record.egl_sync, 0, - EGL_FOREVER_KHR) == EGL_FALSE) { - // This will cause tearing, but is safe otherwise. - DVLOGF(1) << "eglClientWaitSyncKHR failed!"; + if (output_record.egl_fence) { + TRACE_EVENT0( + "media,gpu", + "V4L2VDA::EnqueueOutputRecord: GLFenceEGL::ClientWaitWithTimeoutNanos"); + // If we have to wait for completion, wait. Note that free_output_buffers_ + // is a FIFO queue, so we always wait on the buffer that has been in the + // queue the longest. Every 100ms we check whether the decoder is shutting + // down, or we might get stuck waiting on a fence that will never come. + while (!IsDestroyPending()) { + const EGLTimeKHR wait_ns = + base::TimeDelta::FromMilliseconds(100).InNanoseconds(); + EGLint result = + output_record.egl_fence->ClientWaitWithTimeoutNanos(wait_ns); + if (result == EGL_CONDITION_SATISFIED_KHR) { + break; + } else if (result == EGL_FALSE) { + // This will cause tearing, but is safe otherwise. + DVLOGF(1) << "GLFenceEGL::ClientWaitWithTimeoutNanos failed!"; + break; + } + DCHECK_EQ(result, EGL_TIMEOUT_EXPIRED_KHR); } - if (eglDestroySyncKHR(egl_display_, output_record.egl_sync) != EGL_TRUE) { - VLOGF(1) << "eglDestroySyncKHR failed!"; - NOTIFY_ERROR(PLATFORM_FAILURE); + + if (IsDestroyPending()) return false; - } - output_record.egl_sync = EGL_NO_SYNC_KHR; + + output_record.egl_fence.reset(); } struct v4l2_buffer qbuf; std::unique_ptr<struct v4l2_plane[]> qbuf_planes( @@ -1580,11 +1594,14 @@ void V4L2VideoDecodeAccelerator::ReusePictureBufferTask( int32_t picture_buffer_id, - std::unique_ptr<EGLSyncKHRRef> egl_sync_ref) { + std::unique_ptr<gl::GLFenceEGL> egl_fence) { DVLOGF(4) << "picture_buffer_id=" << picture_buffer_id; DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); TRACE_EVENT0("media,gpu", "V4L2VDA::ReusePictureBufferTask"); + if (IsDestroyPending()) + return; + // We run ReusePictureBufferTask even if we're in kResetting. if (decoder_state_ == kError) { DVLOGF(4) << "early out: kError state"; @@ -1605,7 +1622,7 @@ // It's possible that we've already posted a DismissPictureBuffer for this // picture, but it has not yet executed when this ReusePictureBuffer was // posted to us by the client. In that case just ignore this (we've already - // dismissed it and accounted for that) and let the sync object get + // dismissed it and accounted for that) and let the fence object get // destroyed. DVLOGF(3) << "got picture id= " << picture_buffer_id << " not in use (anymore?)."; @@ -1619,15 +1636,13 @@ return; } - DCHECK_EQ(output_record.egl_sync, EGL_NO_SYNC_KHR); + DCHECK(!output_record.egl_fence); output_record.state = kFree; free_output_buffers_.push_back(index); decoder_frames_at_client_--; - if (egl_sync_ref) { - output_record.egl_sync = egl_sync_ref->egl_sync; - // Take ownership of the EGLSync. - egl_sync_ref->egl_sync = EGL_NO_SYNC_KHR; - } + // Take ownership of the EGL fence. + output_record.egl_fence = std::move(egl_fence); + // We got a buffer back, so enqueue it back. Enqueue(); } @@ -1637,6 +1652,9 @@ DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); TRACE_EVENT0("media,gpu", "V4L2VDA::FlushTask"); + if (IsDestroyPending()) + return; + if (decoder_state_ == kError) { VLOGF(2) << "early out: kError state"; return; @@ -1751,6 +1769,9 @@ DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); TRACE_EVENT0("media,gpu", "V4L2VDA::ResetTask"); + if (IsDestroyPending()) + return; + if (decoder_state_ == kError) { VLOGF(2) << "early out: kError state"; return; @@ -1822,6 +1843,9 @@ DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); TRACE_EVENT0("media,gpu", "V4L2VDA::ResetDoneTask"); + if (IsDestroyPending()) + return; + if (decoder_state_ == kError) { VLOGF(2) << "early out: kError state"; return; @@ -1857,6 +1881,8 @@ // DestroyTask() should run regardless of decoder_state_. + decoder_state_ = kDestroying; + StopDevicePoll(); StopOutputStream(); StopInputStream(); @@ -1871,9 +1897,6 @@ image_processor_ = nullptr; - // Set our state to kError. Just in case. - decoder_state_ = kError; - DestroyInputBuffers(); DestroyOutputBuffers(); } @@ -1941,7 +1964,7 @@ if (output_record.state == kAtDevice) { output_record.state = kFree; free_output_buffers_.push_back(i); - DCHECK_EQ(output_record.egl_sync, EGL_NO_SYNC_KHR); + DCHECK(!output_record.egl_fence); } } output_buffer_queued_count_ = 0; @@ -2050,9 +2073,14 @@ base::Unretained(this), event_pending)); } +bool V4L2VideoDecodeAccelerator::IsDestroyPending() { + return destroy_pending_.IsSignaled(); +} + void V4L2VideoDecodeAccelerator::NotifyError(Error error) { VLOGF(1); + // Notifying the client should only happen from the client's thread. if (!child_task_runner_->BelongsToCurrentThread()) { child_task_runner_->PostTask( FROM_HERE, base::Bind(&V4L2VideoDecodeAccelerator::NotifyError, @@ -2060,6 +2088,7 @@ return; } + // Notify the decoder's client an error has occurred. if (client_) { client_->NotifyError(error); client_ptr_factory_.reset(); @@ -2077,9 +2106,11 @@ return; } - // Post NotifyError only if we are already initialized, as the API does - // not allow doing so before that. - if (decoder_state_ != kError && decoder_state_ != kUninitialized) + // Notifying the client of an error will only happen if we are already + // initialized, as the API does not allow doing so before that. Subsequent + // errors and errors while destroying will be suppressed. + if (decoder_state_ != kError && decoder_state_ != kUninitialized && + decoder_state_ != kDestroying) NotifyError(error); decoder_state_ = kError; @@ -2570,12 +2601,7 @@ egl_display_, output_record.egl_image)); } - if (output_record.egl_sync != EGL_NO_SYNC_KHR) { - if (eglDestroySyncKHR(egl_display_, output_record.egl_sync) != EGL_TRUE) { - VLOGF(1) << "eglDestroySyncKHR failed."; - success = false; - } - } + output_record.egl_fence.reset(); DVLOGF(3) << "dismissing PictureBuffer id=" << output_record.picture_id; child_task_runner_->PostTask(
diff --git a/media/gpu/v4l2/v4l2_video_decode_accelerator.h b/media/gpu/v4l2/v4l2_video_decode_accelerator.h index 7dbdeaf..d7d7bbd4 100644 --- a/media/gpu/v4l2/v4l2_video_decode_accelerator.h +++ b/media/gpu/v4l2/v4l2_video_decode_accelerator.h
@@ -33,6 +33,12 @@ #include "ui/gfx/geometry/size.h" #include "ui/gl/gl_bindings.h" +namespace gl { + +class GLFenceEGL; + +} // namespace gl + namespace media { class H264Parser; @@ -145,7 +151,8 @@ // Requested new PictureBuffers via ProvidePictureBuffers(), awaiting // AssignPictureBuffers(). kAwaitingPictureBuffers, - kError, // Error in kDecoding state. + kError, // Error in kDecoding state. + kDestroying, // Destroying state, when shutting down the decoder. }; enum OutputRecordState { @@ -163,9 +170,6 @@ // Decode() to DecodeTask(). struct BitstreamBufferRef; - // Auto-destruction reference for EGLSync (for message-passing). - struct EGLSyncKHRRef; - // Record for decoded pictures that can be sent to PictureReady. struct PictureRecord { PictureRecord(bool cleared, const Picture& picture); @@ -192,7 +196,8 @@ ~OutputRecord(); OutputRecordState state; EGLImageKHR egl_image; // EGLImageKHR for the output buffer. - EGLSyncKHR egl_sync; // sync the compositor's use of the EGLImage. + std::unique_ptr<gl::GLFenceEGL> egl_fence; // sync the compositor's use of + // the EGLImage. int32_t picture_id; // picture buffer id as returned to PictureReady(). GLuint texture_id; bool cleared; // Whether the texture is cleared and safe to render @@ -286,11 +291,11 @@ bool EnqueueInputRecord(); bool EnqueueOutputRecord(); - // Process a ReusePictureBuffer() API call. The API call create an EGLSync - // object on the main (GPU process) thread; we will record this object so we - // can wait on it before reusing the buffer. + // Task to flag the specified picture buffer for reuse, executed on the + // decoder_thread_. The picture buffer can only be reused after the specified + // fence has been signaled. void ReusePictureBufferTask(int32_t picture_buffer_id, - std::unique_ptr<EGLSyncKHRRef> egl_sync_ref); + std::unique_ptr<gl::GLFenceEGL> egl_fence); // Flush() task. Child thread should not submit any more buffers until it // receives the NotifyFlushDone callback. This task will schedule an empty @@ -356,6 +361,9 @@ // Safe from any thread. // + // Check whether a destroy is scheduled. + bool IsDestroyPending(); + // Error notification (using PostTask() to child thread, if necessary). void NotifyError(Error error); @@ -450,6 +458,9 @@ // Decoder state machine state. State decoder_state_; + // Waitable event signaled when the decoder is destroying. + base::WaitableEvent destroy_pending_; + Config::OutputMode output_mode_; // BitstreamBuffer we're presently reading.
diff --git a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc index fa56ddd..52ddbe7 100644 --- a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc +++ b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
@@ -244,10 +244,7 @@ free_image_processor_output_buffers_.push_back(i); } - // TODO(johnylin): pass |config.h264_output_level| to InitControl() for - // updating the correlative V4L2_CID_MPEG_VIDEO_H264_LEVEL - // ctrl value. https://crbug.com/863327 - if (!InitControls()) + if (!InitControls(config)) return false; if (!CreateOutputBuffers()) @@ -259,8 +256,8 @@ } RequestEncodingParametersChange( - config.initial_bitrate, - config.initial_framerate.value_or(kDefaultFramerate)); + config.initial_bitrate, config.initial_framerate.value_or( + VideoEncodeAccelerator::kDefaultFramerate)); encoder_state_ = kInitialized; @@ -1205,7 +1202,7 @@ return device_->Ioctl(VIDIOC_S_EXT_CTRLS, &ext_ctrls) == 0; } -bool V4L2VideoEncodeAccelerator::InitControls() { +bool V4L2VideoEncodeAccelerator::InitControls(const Config& config) { std::vector<struct v4l2_ext_control> ctrls; struct v4l2_ext_control ctrl; @@ -1257,10 +1254,29 @@ ctrl.value = 51; ctrls.push_back(ctrl); - // Use H.264 level 4.0 to match the supported max resolution. + // Set H.264 profile. + int32_t profile_value = + V4L2Device::VideoCodecProfileToV4L2H264Profile(config.output_profile); + if (profile_value < 0) { + NOTIFY_ERROR(kInvalidArgumentError); + return false; + } + memset(&ctrl, 0, sizeof(ctrl)); + ctrl.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE; + ctrl.value = profile_value; + ctrls.push_back(ctrl); + + // Set H.264 output level from config. Use Level 4.0 as fallback default. + int32_t level_value = V4L2Device::H264LevelIdcToV4L2H264Level( + config.h264_output_level.value_or( + VideoEncodeAccelerator::kDefaultH264Level)); + if (level_value < 0) { + NOTIFY_ERROR(kInvalidArgumentError); + return false; + } memset(&ctrl, 0, sizeof(ctrl)); ctrl.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL; - ctrl.value = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; + ctrl.value = level_value; ctrls.push_back(ctrl); // Ask not to put SPS and PPS into separate bitstream buffers.
diff --git a/media/gpu/v4l2/v4l2_video_encode_accelerator.h b/media/gpu/v4l2/v4l2_video_encode_accelerator.h index f52173ce..8c71a2be 100644 --- a/media/gpu/v4l2/v4l2_video_encode_accelerator.h +++ b/media/gpu/v4l2/v4l2_video_encode_accelerator.h
@@ -92,7 +92,6 @@ }; enum { - kDefaultFramerate = 30, // These are rather subjectively tuned. kInputBufferCount = 2, kOutputBufferCount = 2, @@ -195,8 +194,8 @@ // Set up the device to the output format requested in Initialize(). bool SetOutputFormat(VideoCodecProfile output_profile); - // Initialize device controls with default values. - bool InitControls(); + // Initialize device controls with |config| or default values. + bool InitControls(const Config& config); // Create the buffers we need. bool CreateInputBuffers();
diff --git a/media/gpu/vaapi/accelerated_video_encoder.h b/media/gpu/vaapi/accelerated_video_encoder.h index 4d7fa39c..95b6e07 100644 --- a/media/gpu/vaapi/accelerated_video_encoder.h +++ b/media/gpu/vaapi/accelerated_video_encoder.h
@@ -15,6 +15,7 @@ #include "media/base/video_bitrate_allocation.h" #include "media/base/video_codecs.h" #include "media/gpu/codec_picture.h" +#include "media/video/video_encode_accelerator.h" #include "ui/gfx/geometry/size.h" namespace media { @@ -116,14 +117,10 @@ DISALLOW_COPY_AND_ASSIGN(EncodeJob); }; - // Initializes the encoder to encode frames of |visible_size| into a stream - // for |profile|, at |initial_bitrate| and |initial_framerate|. + // Initializes the encoder with requested parameter set |config|. // Returns false if the requested set of parameters is not supported, // true on success. - virtual bool Initialize(const gfx::Size& visible_size, - VideoCodecProfile profile, - uint32_t initial_bitrate, - uint32_t initial_framerate) = 0; + virtual bool Initialize(const VideoEncodeAccelerator::Config& config) = 0; // Updates current framerate and/or bitrate to |framerate| in FPS // and the specified video bitrate allocation.
diff --git a/media/gpu/vaapi/h264_encoder.cc b/media/gpu/vaapi/h264_encoder.cc index a5919317..761b54b 100644 --- a/media/gpu/vaapi/h264_encoder.cc +++ b/media/gpu/vaapi/h264_encoder.cc
@@ -6,6 +6,7 @@ #include "base/bits.h" #include "base/stl_util.h" +#include "media/video/h264_level_limits.h" #define DVLOGF(level) DVLOG(level) << __func__ << "(): " @@ -31,9 +32,6 @@ constexpr int kBitRateScale = 0; // bit_rate_scale for SPS HRD parameters. constexpr int kCPBSizeScale = 0; // cpb_size_scale for SPS HRD parameters. -// Default to H264 profile 4.1. -constexpr int kDefaultLevelIDC = 41; - // 4:2:0 constexpr int kChromaFormatIDC = 1; } // namespace @@ -59,27 +57,30 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } -bool H264Encoder::Initialize(const gfx::Size& visible_size, - VideoCodecProfile profile, - uint32_t initial_bitrate, - uint32_t initial_framerate) { +bool H264Encoder::Initialize(const VideoEncodeAccelerator::Config& config) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - switch (profile) { + switch (config.output_profile) { case H264PROFILE_BASELINE: case H264PROFILE_MAIN: case H264PROFILE_HIGH: break; default: - NOTIMPLEMENTED() << "Unsupported profile " << GetProfileName(profile); + NOTIMPLEMENTED() << "Unsupported profile " + << GetProfileName(config.output_profile); return false; } - DCHECK(!visible_size.IsEmpty()); - visible_size_ = visible_size; + if (config.input_visible_size.IsEmpty()) { + DVLOGF(1) << "Input visible size could not be empty"; + return false; + } + visible_size_ = config.input_visible_size; // For 4:2:0, the pixel sizes have to be even. - DCHECK_EQ(visible_size_.width() % 2, 0); - DCHECK_EQ(visible_size_.height() % 2, 0); + if ((visible_size_.width() % 2 != 0) || (visible_size_.height() % 2 != 0)) { + DVLOGF(1) << "The pixel sizes are not even: " << visible_size_.ToString(); + return false; + } constexpr size_t kH264MacroblockSizeInPixels = 16; coded_size_ = gfx::Size( base::bits::Align(visible_size_.width(), kH264MacroblockSizeInPixels), @@ -87,9 +88,17 @@ mb_width_ = coded_size_.width() / kH264MacroblockSizeInPixels; mb_height_ = coded_size_.height() / kH264MacroblockSizeInPixels; - profile_ = profile; + profile_ = config.output_profile; + level_ = config.h264_output_level.value_or( + VideoEncodeAccelerator::kDefaultH264Level); + uint32_t initial_framerate = config.initial_framerate.value_or( + VideoEncodeAccelerator::kDefaultFramerate); + if (!CheckH264LevelLimits(profile_, level_, config.initial_bitrate, + initial_framerate, mb_width_ * mb_height_)) + return false; + VideoBitrateAllocation initial_bitrate_allocation; - initial_bitrate_allocation.SetBitrate(0, 0, initial_bitrate); + initial_bitrate_allocation.SetBitrate(0, 0, config.initial_bitrate); if (!UpdateRates(initial_bitrate_allocation, initial_framerate)) return false; @@ -234,7 +243,10 @@ return; } - current_sps_.level_idc = kDefaultLevelIDC; + H264SPS::GetLevelConfigFromProfileLevel(profile_, level_, + ¤t_sps_.level_idc, + ¤t_sps_.constraint_set3_flag); + current_sps_.seq_parameter_set_id = 0; current_sps_.chroma_format_idc = kChromaFormatIDC;
diff --git a/media/gpu/vaapi/h264_encoder.h b/media/gpu/vaapi/h264_encoder.h index 2d32081..3a834bb 100644 --- a/media/gpu/vaapi/h264_encoder.h +++ b/media/gpu/vaapi/h264_encoder.h
@@ -95,10 +95,7 @@ ~H264Encoder() override; // AcceleratedVideoEncoder implementation. - bool Initialize(const gfx::Size& visible_size, - VideoCodecProfile profile, - uint32_t initial_bitrate, - uint32_t initial_framerate) override; + bool Initialize(const VideoEncodeAccelerator::Config& config) override; bool UpdateRates(const VideoBitrateAllocation& bitrate_allocation, uint32_t framerate) override; gfx::Size GetCodedSize() const override; @@ -115,6 +112,10 @@ void GeneratePackedSPS(); void GeneratePackedPPS(); + // Check if |bitrate| and |framerate| and current coded size are supported by + // current profile and level. + bool CheckConfigValidity(uint32_t bitrate, uint32_t framerate); + // Current SPS, PPS and their packed versions. Packed versions are NALUs // in AnnexB format *without* emulation prevention three-byte sequences // (those are expected to be added by the client as needed). @@ -129,6 +130,9 @@ // H264 profile currently used. media::VideoCodecProfile profile_ = VIDEO_CODEC_PROFILE_UNKNOWN; + // H264 level currently used. + uint8_t level_ = 0; + // Current visible and coded sizes in pixels. gfx::Size visible_size_; gfx::Size coded_size_;
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc index 69c0856..70167c7 100644 --- a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc +++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
@@ -55,8 +55,6 @@ // reconstructed picture, which is later used for reference. constexpr size_t kNumSurfacesPerFrame = 2; -constexpr int kDefaultFramerate = 30; - // Percentage of bitrate set to be targeted by the HW encoder. constexpr unsigned int kTargetBitratePercentage = 90; @@ -293,12 +291,7 @@ return; } - // TODO(johnylin): pass |config.h264_output_level| to H264Encoder. - // https://crbug.com/863327 - if (!encoder_->Initialize( - config.input_visible_size, config.output_profile, - config.initial_bitrate, - config.initial_framerate.value_or(kDefaultFramerate))) { + if (!encoder_->Initialize(config)) { NOTIFY_ERROR(kInvalidArgumentError, "Failed initializing encoder"); return; }
diff --git a/media/gpu/vaapi/vp8_encoder.cc b/media/gpu/vaapi/vp8_encoder.cc index b7df863c..b527c5fa 100644 --- a/media/gpu/vaapi/vp8_encoder.cc +++ b/media/gpu/vaapi/vp8_encoder.cc
@@ -48,27 +48,36 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } -bool VP8Encoder::Initialize(const gfx::Size& visible_size, - VideoCodecProfile profile, - uint32_t initial_bitrate, - uint32_t initial_framerate) { +bool VP8Encoder::Initialize(const VideoEncodeAccelerator::Config& config) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(profile >= VP8PROFILE_MIN && profile <= VP8PROFILE_MAX); + if (VideoCodecProfileToVideoCodec(config.output_profile) != kCodecVP8) { + DVLOGF(1) << "Invalid profile: " << GetProfileName(config.output_profile); + return false; + } - DCHECK(!visible_size.IsEmpty()); + if (config.input_visible_size.IsEmpty()) { + DVLOGF(1) << "Input visible size could not be empty"; + return false; + } // 4:2:0 format has to be 2-aligned. - DCHECK_EQ(visible_size.width() % 2, 0); - DCHECK_EQ(visible_size.height() % 2, 0); + if ((config.input_visible_size.width() % 2 != 0) || + (config.input_visible_size.height() % 2 != 0)) { + DVLOGF(1) << "The pixel sizes are not even: " + << config.input_visible_size.ToString(); + return false; + } - visible_size_ = visible_size; + visible_size_ = config.input_visible_size; coded_size_ = gfx::Size(base::bits::Align(visible_size_.width(), 16), base::bits::Align(visible_size_.height(), 16)); Reset(); VideoBitrateAllocation initial_bitrate_allocation; - initial_bitrate_allocation.SetBitrate(0, 0, initial_bitrate); - return UpdateRates(initial_bitrate_allocation, initial_framerate); + initial_bitrate_allocation.SetBitrate(0, 0, config.initial_bitrate); + return UpdateRates(initial_bitrate_allocation, + config.initial_framerate.value_or( + VideoEncodeAccelerator::kDefaultFramerate)); } gfx::Size VP8Encoder::GetCodedSize() const {
diff --git a/media/gpu/vaapi/vp8_encoder.h b/media/gpu/vaapi/vp8_encoder.h index 0b9b0e88..1ed75b59 100644 --- a/media/gpu/vaapi/vp8_encoder.h +++ b/media/gpu/vaapi/vp8_encoder.h
@@ -72,10 +72,7 @@ ~VP8Encoder() override; // AcceleratedVideoEncoder implementation. - bool Initialize(const gfx::Size& visible_size, - VideoCodecProfile profile, - uint32_t initial_bitrate, - uint32_t initial_framerate) override; + bool Initialize(const VideoEncodeAccelerator::Config& config) override; bool UpdateRates(const VideoBitrateAllocation& bitrate_allocation, uint32_t framerate) override; gfx::Size GetCodedSize() const override;
diff --git a/media/video/BUILD.gn b/media/video/BUILD.gn index 1b677c1..23e5ace1 100644 --- a/media/video/BUILD.gn +++ b/media/video/BUILD.gn
@@ -21,6 +21,8 @@ "gpu_video_accelerator_factories.h", "h264_bit_reader.cc", "h264_bit_reader.h", + "h264_level_limits.cc", + "h264_level_limits.h", "h264_parser.cc", "h264_parser.h", "h264_poc.cc",
diff --git a/media/video/h264_level_limits.cc b/media/video/h264_level_limits.cc new file mode 100644 index 0000000..5437e7b --- /dev/null +++ b/media/video/h264_level_limits.cc
@@ -0,0 +1,144 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/video/h264_level_limits.h" + +#include "base/logging.h" +#include "media/video/h264_parser.h" + +namespace media { +namespace { +struct LevelLimits { + // All names and abbreviations are as in table A-1 in spec. + // MaxMBPS, Max. macroblock processing rate (MB/s) + uint32_t max_mbps; + // MaxFS, Max. frame size (MBs) + uint32_t max_fs; + // MaxDpbMbs, Max. decoded picture buffer size (MBs) + uint32_t max_dpb_mbs; + // MaxBR, Max. video bitrate for Baseline and Main Profiles (kbit/s) + uint32_t max_main_br; +}; + +LevelLimits LevelToLevelLimits(uint8_t level) { + // See table A-1 in spec + // { MaxMBPS, MaxFS, MaxDpbMbs, MaxBR} + switch (level) { + case H264SPS::kLevelIDC1p0: + return {1485, 99, 396, 64}; // Level 1.0 + case H264SPS::kLevelIDC1B: + return {1485, 99, 396, 128}; // Level 1b + case H264SPS::kLevelIDC1p1: + return {3000, 396, 900, 192}; // Level 1.1 + case H264SPS::kLevelIDC1p2: + return {6000, 396, 2376, 384}; // Level 1.2 + case H264SPS::kLevelIDC1p3: + return {11800, 396, 2376, 768}; // Level 1.3 + case H264SPS::kLevelIDC2p0: + return {11880, 396, 2376, 2000}; // Level 2.0 + case H264SPS::kLevelIDC2p1: + return {19800, 792, 4752, 4000}; // Level 2.1 + case H264SPS::kLevelIDC2p2: + return {20250, 1620, 8100, 4000}; // Level 2.2 + case H264SPS::kLevelIDC3p0: + return {40500, 1620, 8100, 10000}; // Level 3.0 + case H264SPS::kLevelIDC3p1: + return {108000, 3600, 18000, 14000}; // Level 3.1 + case H264SPS::kLevelIDC3p2: + return {216000, 5120, 20480, 20000}; // Level 3.2 + case H264SPS::kLevelIDC4p0: + return {245760, 8192, 32768, 20000}; // Level 4.0 + case H264SPS::kLevelIDC4p1: + return {245760, 8192, 32768, 50000}; // Level 4.1 + case H264SPS::kLevelIDC4p2: + return {522240, 8704, 34816, 50000}; // Level 4.2 + case H264SPS::kLevelIDC5p0: + return {589824, 22080, 110400, 135000}; // Level 5.0 + case H264SPS::kLevelIDC5p1: + return {983040, 36864, 184320, 240000}; // Level 5.1 + case H264SPS::kLevelIDC5p2: + return {2073600, 36864, 184320, 240000}; // Level 5.2 + case H264SPS::kLevelIDC6p0: + return {4177920, 139264, 696320, 240000}; // Level 6.0 + case H264SPS::kLevelIDC6p1: + return {8355840, 139264, 696320, 480000}; // Level 6.1 + case H264SPS::kLevelIDC6p2: + return {16711680, 139264, 696320, 800000}; // Level 6.2 + default: + DVLOG(1) << "Invalid codec level (" << static_cast<int>(level) << ")"; + return {0, 0, 0, 0}; + } +} +} // namespace + +uint32_t H264LevelToMaxMBPS(uint8_t level) { + return LevelToLevelLimits(level).max_mbps; +} + +uint32_t H264LevelToMaxFS(uint8_t level) { + return LevelToLevelLimits(level).max_fs; +} + +uint32_t H264LevelToMaxDpbMbs(uint8_t level) { + return LevelToLevelLimits(level).max_dpb_mbs; +} + +uint32_t H264ProfileLevelToMaxBR(VideoCodecProfile profile, uint8_t level) { + uint32_t max_main_br = LevelToLevelLimits(level).max_main_br; + + // See table A-2 in spec + // The maximum bit rate for High Profile is 1.25 times that of the + // Base/Extended/Main Profiles, 3 times for Hi10P, and 4 times for + // Hi422P/Hi444PP. + switch (profile) { + case H264PROFILE_BASELINE: + case H264PROFILE_MAIN: + case H264PROFILE_EXTENDED: + return max_main_br; + case H264PROFILE_HIGH: + return max_main_br * 5 / 4; + case H264PROFILE_HIGH10PROFILE: + return max_main_br * 3; + case H264PROFILE_HIGH422PROFILE: + case H264PROFILE_HIGH444PREDICTIVEPROFILE: + return max_main_br * 4; + default: + DVLOG(1) << "Failed to query MaxBR for profile: " + << GetProfileName(profile); + return 0; + } +} + +bool CheckH264LevelLimits(VideoCodecProfile profile, + uint8_t level, + uint32_t bitrate, + uint32_t framerate, + uint32_t framesize_in_mbs) { + uint32_t max_bitrate_kbs = H264ProfileLevelToMaxBR(profile, level); + DCHECK(base::IsValueInRangeForNumericType<uint32_t>(max_bitrate_kbs * 1000)); + + uint32_t max_bitrate = max_bitrate_kbs * 1000; + if (bitrate > max_bitrate) { + DVLOG(1) << "Target bitrate: " << bitrate << " exceeds Max: " << max_bitrate + << " bit/s"; + return false; + } + + if (framesize_in_mbs > H264LevelToMaxFS(level)) { + DVLOG(1) << "Target frame size: " << framesize_in_mbs + << " exceeds Max: " << H264LevelToMaxFS(level) << " Macroblocks"; + return false; + } + + uint32_t mbps = framesize_in_mbs * framerate; + if (mbps > H264LevelToMaxMBPS(level)) { + DVLOG(1) << "Target macroblock processing rate: " << mbps + << " exceeds Max: " << H264LevelToMaxMBPS(level) << "Macroblock/s"; + return false; + } + + return true; +} + +} // namespace media
diff --git a/media/video/h264_level_limits.h b/media/video/h264_level_limits.h new file mode 100644 index 0000000..0772dd2 --- /dev/null +++ b/media/video/h264_level_limits.h
@@ -0,0 +1,42 @@ +// 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 MEDIA_VIDEO_H264_LEVEL_LIMITS_H_ +#define MEDIA_VIDEO_H264_LEVEL_LIMITS_H_ + +#include <stddef.h> + +#include "media/base/media_export.h" +#include "media/base/video_codecs.h" + +namespace media { + +// Get max macroblock processing rate in macroblocks per second (MaxMBPS) from +// level. The abbreviation is as per spec table A-1. +uint32_t MEDIA_EXPORT H264LevelToMaxMBPS(uint8_t level); + +// Get max frame size in macroblocks (MaxFS) from level. The abbreviation is as +// per spec table A-1. +uint32_t MEDIA_EXPORT H264LevelToMaxFS(uint8_t level); + +// Get max decoded picture buffer size in macroblocks (MaxDpbMbs) from level. +// The abbreviation is as per spec table A-1. +uint32_t MEDIA_EXPORT H264LevelToMaxDpbMbs(uint8_t level); + +// Get max video bitrate in kbit per second (MaxBR) from profile and level. The +// abbreviation is as per spec table A-1. +uint32_t MEDIA_EXPORT H264ProfileLevelToMaxBR(VideoCodecProfile profile, + uint8_t level); + +// Check whether |bitrate|, |framerate|, and |framesize_in_mbs| are valid from +// the limits of |profile| and |level| from Table A-1 in spec. +bool MEDIA_EXPORT CheckH264LevelLimits(VideoCodecProfile profile, + uint8_t level, + uint32_t bitrate, + uint32_t framerate, + uint32_t framesize_in_mbs); + +} // namespace media + +#endif // MEDIA_VIDEO_H264_LEVEL_LIMITS_H_
diff --git a/media/video/h264_parser.cc b/media/video/h264_parser.cc index dc1c43f..f786ee3 100644 --- a/media/video/h264_parser.cc +++ b/media/video/h264_parser.cc
@@ -79,6 +79,27 @@ memset(this, 0, sizeof(*this)); } +// static +void H264SPS::GetLevelConfigFromProfileLevel(VideoCodecProfile profile, + uint8_t level, + int* level_idc, + bool* constraint_set3_flag) { + // Spec A.3.1. + // Note: we always use h264_output_level = 9 to indicate Level 1b in + // VideoEncodeAccelerator::Config, in order to tell apart from Level 1.1 + // which level IDC is also 11. + // For Baseline and Main profile, if requested level is Level 1b, set + // level_idc to 11 and constraint_set3_flag to true. Otherwise, set level_idc + // to 9 for Level 1b, and ten times level number for others. + if ((profile == H264PROFILE_BASELINE || profile == H264PROFILE_MAIN) && + level == kLevelIDC1B) { + *level_idc = 11; + *constraint_set3_flag = true; + } else { + *level_idc = level; + } +} + H264SPS::H264SPS() { memset(this, 0, sizeof(*this)); } @@ -173,6 +194,33 @@ } } +uint8_t H264SPS::GetIndicatedLevel() const { + // Spec A.3.1 and A.3.2 + // For Baseline, Constrained Baseline and Main profile, the indicated level is + // Level 1b if level_idc is equal to 11 and constraint_set3_flag is true. + if ((profile_idc == H264SPS::kProfileIDCBaseline || + profile_idc == H264SPS::kProfileIDCConstrainedBaseline || + profile_idc == H264SPS::kProfileIDCMain) && + level_idc == 11 && constraint_set3_flag) { + return kLevelIDC1B; // Level 1b + } + + // Otherwise, the level_idc is equal to 9 for Level 1b, and others are equal + // to values of ten times the level numbers. + return base::checked_cast<uint8_t>(level_idc); +} + +bool H264SPS::CheckIndicatedLevelWithinTarget(uint8_t target_level) const { + // See table A-1 in spec. + // Level 1.0 < 1b < 1.1 < 1.2 .... (in numeric order). + uint8_t level = GetIndicatedLevel(); + if (target_level == kLevelIDC1p0) + return level == kLevelIDC1p0; + if (target_level == kLevelIDC1B) + return level == kLevelIDC1p0 || level == kLevelIDC1B; + return level <= target_level; +} + H264PPS::H264PPS() { memset(this, 0, sizeof(*this)); }
diff --git a/media/video/h264_parser.h b/media/video/h264_parser.h index 141a0c18..4a701fe6 100644 --- a/media/video/h264_parser.h +++ b/media/video/h264_parser.h
@@ -92,6 +92,29 @@ kProfileIDHigh444Predictive = 244, }; + enum H264LevelIDC : uint8_t { + kLevelIDC1p0 = 10, + kLevelIDC1B = 9, + kLevelIDC1p1 = 11, + kLevelIDC1p2 = 12, + kLevelIDC1p3 = 13, + kLevelIDC2p0 = 20, + kLevelIDC2p1 = 21, + kLevelIDC2p2 = 22, + kLevelIDC3p0 = 30, + kLevelIDC3p1 = 31, + kLevelIDC3p2 = 32, + kLevelIDC4p0 = 40, + kLevelIDC4p1 = 41, + kLevelIDC4p2 = 42, + kLevelIDC5p0 = 50, + kLevelIDC5p1 = 51, + kLevelIDC5p2 = 52, + kLevelIDC6p0 = 60, + kLevelIDC6p1 = 61, + kLevelIDC6p2 = 62, + }; + enum AspectRatioIdc { kExtendedSar = 255, }; @@ -183,12 +206,27 @@ int chroma_array_type; + // Get corresponding SPS |level_idc| and |constraint_set3_flag| value from + // requested |profile| and |level| (see Spec A.3.1). + static void GetLevelConfigFromProfileLevel(VideoCodecProfile profile, + uint8_t level, + int* level_idc, + bool* constraint_set3_flag); + // Helpers to compute frequently-used values. These methods return // base::nullopt if they encounter integer overflow. They do not verify that // the results are in-spec for the given profile or level. base::Optional<gfx::Size> GetCodedSize() const; base::Optional<gfx::Rect> GetVisibleRect() const; VideoColorSpace GetColorSpace() const; + + // Helper to compute indicated level from parsed SPS data. The value of + // indicated level would be included in H264LevelIDC enum representing the + // level as in name. + uint8_t GetIndicatedLevel() const; + // Helper to check if indicated level is lower than or equal to + // |target_level|. + bool CheckIndicatedLevelWithinTarget(uint8_t target_level) const; }; struct MEDIA_EXPORT H264PPS {
diff --git a/media/video/video_encode_accelerator.cc b/media/video/video_encode_accelerator.cc index e351e188..bf05cb72 100644 --- a/media/video/video_encode_accelerator.cc +++ b/media/video/video_encode_accelerator.cc
@@ -47,8 +47,10 @@ input_visible_size(input_visible_size), output_profile(output_profile), initial_bitrate(initial_bitrate), - initial_framerate(initial_framerate), - h264_output_level(h264_output_level), + initial_framerate(initial_framerate.value_or( + VideoEncodeAccelerator::kDefaultFramerate)), + h264_output_level(h264_output_level.value_or( + VideoEncodeAccelerator::kDefaultH264Level)), content_type(content_type) {} VideoEncodeAccelerator::Config::~Config() = default; @@ -64,7 +66,8 @@ str += base::StringPrintf(", initial_framerate: %u", initial_framerate.value()); } - if (h264_output_level) { + if (h264_output_level && + VideoCodecProfileToVideoCodec(output_profile) == kCodecH264) { str += base::StringPrintf(", h264_output_level: %u", h264_output_level.value()); }
diff --git a/media/video/video_encode_accelerator.h b/media/video/video_encode_accelerator.h index 2ea66e2..15b3f2e 100644 --- a/media/video/video_encode_accelerator.h +++ b/media/video/video_encode_accelerator.h
@@ -21,6 +21,7 @@ #include "media/base/video_bitrate_allocation.h" #include "media/base/video_decoder_config.h" #include "media/base/video_frame.h" +#include "media/video/h264_parser.h" namespace media { @@ -93,6 +94,12 @@ kErrorMax = kPlatformFailureError }; + // Unified default values for all VEA implementations. + enum { + kDefaultFramerate = 30, + kDefaultH264Level = H264SPS::kLevelIDC4p0, + }; + // Parameters required for VEA initialization. struct MEDIA_EXPORT Config { // Indicates if video content should be treated as a "normal" camera feed @@ -129,14 +136,15 @@ uint32_t initial_bitrate; // Initial encoding framerate in frames per second. This is optional and - // VideoEncodeAccelerator should use default framerate if not given. + // VideoEncodeAccelerator should use |kDefaultFramerate| if not given. base::Optional<uint32_t> initial_framerate; // Codec level of encoded output stream for H264 only. This value should // be aligned to the H264 standard definition of SPS.level_idc. The only // exception is in Main and Baseline profile we still use // |h264_output_level|=9 for Level 1b, which should set level_idc to 11 and - // constraint_set3_flag to 1. (Spec A.3.1 and A.3.2) + // constraint_set3_flag to 1 (Spec A.3.1 and A.3.2). This is optional and + // use |kDefaultH264Level| if not given. base::Optional<uint8_t> h264_output_level; // Indicates captured video (from a camera) or generated (screen grabber).
diff --git a/net/base/filename_util_internal.cc b/net/base/filename_util_internal.cc index bf7a6a9..38ecd17 100644 --- a/net/base/filename_util_internal.cc +++ b/net/base/filename_util_internal.cc
@@ -125,10 +125,9 @@ if (!url.is_valid() || url.SchemeIs("about") || url.SchemeIs("data")) return std::string(); - const std::string unescaped_url_filename = UnescapeURLComponent( - url.ExtractFileName(), - UnescapeRule::SPACES | - UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS); + std::string unescaped_url_filename; + UnescapeBinaryURLComponent(url.ExtractFileName(), UnescapeRule::NORMAL, + &unescaped_url_filename); // The URL's path should be escaped UTF-8, but may not be. std::string decoded_filename = unescaped_url_filename;
diff --git a/net/base/filename_util_unittest.cc b/net/base/filename_util_unittest.cc index 17fb283..7f150af 100644 --- a/net/base/filename_util_unittest.cc +++ b/net/base/filename_util_unittest.cc
@@ -573,9 +573,14 @@ {// A normal avi should get .avi and not .avi.avi __LINE__, "https://example.com/misc/2.avi", "", "", "", "video/x-msvideo", L"download", L"2.avi"}, - {// Shouldn't unescape slashes. + {// Slashes are illegal, and should be replaced with underscores. __LINE__, "http://example.com/foo%2f..%2fbar.jpg", "", "", "", - "text/plain", L"download", L"foo%2f..%2fbar.jpg"}, + "text/plain", L"download", L"foo_.._bar.jpg"}, + {// "%00" decodes to the NUL byte, which is illegal and should be replaced + // with an underscore. (Note: This can't be tested with a URL, since "%00" + // is illegal in a URL. Only applies to Content-Disposition.) + __LINE__, "http://example.com/download.py", "filename=foo%00bar.jpg", "", + "", "text/plain", L"download", L"foo_bar.jpg"}, {// Extension generation for C-D derived filenames. __LINE__, "", "filename=my-cat", "", "", "image/jpeg", L"download", L"my-cat"}, @@ -744,6 +749,15 @@ __LINE__, "http://www.example.com/fooa%cc%88.txt", "", "", "", "image/jpeg", L"foo\xe4", L"foo\xe4.txt"}, #endif + + // U+3000 IDEOGRAPHIC SPACE (http://crbug.com/849794): In URL file name. + {__LINE__, "http://www.example.com/%E5%B2%A1%E3%80%80%E5%B2%A1.txt", "", "", + "", "text/plain", L"", L"\u5ca1\u3000\u5ca1.txt"}, + // U+3000 IDEOGRAPHIC SPACE (http://crbug.com/849794): In + // Content-Disposition filename. + {__LINE__, "http://www.example.com/download.py", + "filename=%E5%B2%A1%E3%80%80%E5%B2%A1.txt", "utf-8", "", "text/plain", L"", + L"\u5ca1\u3000\u5ca1.txt"}, }; for (const auto& selection_test : selection_tests)
diff --git a/net/http/http_content_disposition.cc b/net/http/http_content_disposition.cc index da72593..70d45f9 100644 --- a/net/http/http_content_disposition.cc +++ b/net/http/http_content_disposition.cc
@@ -189,7 +189,7 @@ // web browser. // What IE6/7 does: %-escaped UTF-8. - decoded_word = UnescapeURLComponent(encoded_word, UnescapeRule::SPACES); + UnescapeBinaryURLComponent(encoded_word, UnescapeRule::NORMAL, &decoded_word); if (decoded_word != encoded_word) *parse_result_flags |= HttpContentDisposition::HAS_PERCENT_ENCODED_STRINGS; if (base::IsStringUTF8(decoded_word)) {
diff --git a/remoting/protocol/ice_config.cc b/remoting/protocol/ice_config.cc index 87bf7e8..32fd3a0 100644 --- a/remoting/protocol/ice_config.cc +++ b/remoting/protocol/ice_config.cc
@@ -4,6 +4,8 @@ #include "remoting/protocol/ice_config.h" +#include <algorithm> + #include "base/json/json_reader.h" #include "base/json/json_writer.h" #include "base/strings/string_number_conversions.h" @@ -86,6 +88,21 @@ return true; } +// Returns the smallest specified value, or 0 if neither is specified. +// A value is "specified" if it is greater than 0. +int MinimumSpecified(int value1, int value2) { + if (value1 <= 0) { + // value1 is not specified, so return value2 (or 0). + return std::max(0, value2); + } + if (value2 <= 0) { + // value1 is specified, so return it directly. + return value1; + } + // Both values are specified, so return the minimum. + return std::min(value1, value2); +} + } // namespace IceConfig::IceConfig() = default; @@ -117,6 +134,7 @@ // Parse iceServers list and store them in |ice_config|. bool errors_found = false; + ice_config.max_bitrate_kbps = 0; for (const auto& server : *ice_servers_list) { const base::DictionaryValue* server_dict; if (!server.GetAsDictionary(&server_dict)) { @@ -136,6 +154,16 @@ std::string password; server_dict->GetString("credential", &password); + // Compute the lowest specified bitrate of all the ICE servers. + // Ideally the bitrate would be stored per ICE server, but it is not + // possible (at the application level) to look up which particular + // ICE server was used for the P2P connection. + double new_bitrate_double; + if (server_dict->GetDouble("maxRateKbps", &new_bitrate_double)) { + ice_config.max_bitrate_kbps = MinimumSpecified( + ice_config.max_bitrate_kbps, static_cast<int>(new_bitrate_double)); + } + for (const auto& url : *urls_list) { std::string url_str; if (!url.GetAsString(&url_str)) {
diff --git a/remoting/protocol/ice_config.h b/remoting/protocol/ice_config.h index 5831a5a9..38b9ef81 100644 --- a/remoting/protocol/ice_config.h +++ b/remoting/protocol/ice_config.h
@@ -42,6 +42,10 @@ // Standard TURN servers std::vector<cricket::RelayServerConfig> turn_servers; + + // If greater than 0, the max bandwidth used for relayed connections should + // be set to this value. + int max_bitrate_kbps = 0; }; } // namespace protocol
diff --git a/remoting/protocol/ice_config_unittest.cc b/remoting/protocol/ice_config_unittest.cc index ab7aa7c5..b315c44 100644 --- a/remoting/protocol/ice_config_unittest.cc +++ b/remoting/protocol/ice_config_unittest.cc
@@ -26,7 +26,8 @@ " \"turns:the_server.com?transport=udp\"" " ]," " \"username\": \"123\"," - " \"credential\": \"abc\"" + " \"credential\": \"abc\"," + " \"maxRateKbps\": 8000.0" " }," " {" " \"urls\": [" @@ -71,6 +72,7 @@ EXPECT_EQ(rtc::SocketAddress("stun_server.com", 18344), config.stun_servers[0]); EXPECT_EQ(rtc::SocketAddress("1.2.3.4", 3478), config.stun_servers[1]); + EXPECT_EQ(8000.0, config.max_bitrate_kbps); } TEST(IceConfigTest, ParseDataEnvelope) { @@ -126,5 +128,63 @@ EXPECT_TRUE(config.is_null()); } +TEST(IceConfigTest, UnspecifiedMaxRate_IsZero) { + const char kTestConfigJson[] = + "{" + " \"iceServers\": [" + " {" + " \"urls\": [" + " \"stun:1.2.3.4\"" + " ]" + " }" + " ]" + "}"; + + IceConfig config = IceConfig::Parse(kTestConfigJson); + EXPECT_EQ(0, config.max_bitrate_kbps); +} + +TEST(IceConfigTest, OneSpecifiedMaxRate_IsUsed) { + const char kTestConfigJson1[] = + "{" + " \"iceServers\": [" + " {" + " \"urls\": [" + " \"stun:1.2.3.4\"" + " ]," + " \"maxRateKbps\": 1000.0" + " }," + " {" + " \"urls\": [" + " \"stun:1.2.3.4\"" + " ]" + " }" + " ]" + "}"; + + IceConfig config1 = IceConfig::Parse(kTestConfigJson1); + EXPECT_EQ(1000, config1.max_bitrate_kbps); + + const char kTestConfigJson2[] = + "{" + " \"iceServers\": [" + " {" + " \"urls\": [" + " \"stun:1.2.3.4\"" + " ]" + " }," + " {" + " \"urls\": [" + " \"stun:1.2.3.4\"" + " ]," + " \"maxRateKbps\": 2000.0" + " }" + " ]" + "}"; + + IceConfig config2 = IceConfig::Parse(kTestConfigJson2); + EXPECT_EQ(2000, config2.max_bitrate_kbps); +} + } // namespace protocol } // namespace remoting
diff --git a/remoting/protocol/transport_context.cc b/remoting/protocol/transport_context.cc index 4ae8a88..6bae822 100644 --- a/remoting/protocol/transport_context.cc +++ b/remoting/protocol/transport_context.cc
@@ -122,5 +122,10 @@ } } +int TransportContext::GetTurnMaxRateKbps() const { + DCHECK_EQ(relay_mode_, RelayMode::TURN); + return ice_config_[RelayMode::TURN].max_bitrate_kbps; +} + } // namespace protocol } // namespace remoting
diff --git a/remoting/protocol/transport_context.h b/remoting/protocol/transport_context.h index 4df7a678..e93f4f9 100644 --- a/remoting/protocol/transport_context.h +++ b/remoting/protocol/transport_context.h
@@ -101,6 +101,11 @@ TransportRole role() const { return role_; } rtc::NetworkManager* network_manager() const { return network_manager_; } + // Returns the suggested bandwidth cap for TURN relay connections, or 0 if + // no rate-limit is set in the IceConfig. Currently this is not used for + // legacy GTURN connections. + int GetTurnMaxRateKbps() const; + private: friend class base::RefCountedThreadSafe<TransportContext>;
diff --git a/remoting/protocol/webrtc_transport.cc b/remoting/protocol/webrtc_transport.cc index 7f8df70..c3bf652 100644 --- a/remoting/protocol/webrtc_transport.cc +++ b/remoting/protocol/webrtc_transport.cc
@@ -53,13 +53,6 @@ // XML namespace for the transport elements. const char kTransportNamespace[] = "google:remoting:webrtc"; -// Bitrate cap applied to relay connections. This is done to prevent -// large amounts of packet loss, since the Google TURN/relay server drops -// packets to limit the connection to ~10Mbps. The rate-limiting behavior works -// badly with WebRTC's bandwidth-estimation, which results in the host process -// trying to send frames too rapidly over the connection. -constexpr int kMaxBitrateOnRelayKbps = 8000; - #if !defined(NDEBUG) // Command line switch used to disable signature verification. // TODO(sergeyu): Remove this flag. @@ -757,6 +750,16 @@ VLOG(0) << "Relay connection: " << (is_relay ? "true" : "false"); if (is_relay) { + int max_bitrate_kbps = transport_context_->GetTurnMaxRateKbps(); + if (max_bitrate_kbps <= 0) { + VLOG(0) << "No bitrate cap set."; + return; + } + VLOG(0) << "Capping bitrate to " << max_bitrate_kbps << "kbps."; + + // Apply the suggested bitrate cap to prevent large amounts of packet loss. + // The Google TURN/relay server limits the connection speed by dropping + // packets, which may interact badly with WebRTC's bandwidth-estimation. auto senders = peer_connection()->GetSenders(); for (rtc::scoped_refptr<webrtc::RtpSenderInterface> sender : senders) { // x-google-max-bitrate is only set for video codecs in the SDP exchange. @@ -777,7 +780,7 @@ << sender->id(); } - parameters.encodings[0].max_bitrate_bps = kMaxBitrateOnRelayKbps * 1000; + parameters.encodings[0].max_bitrate_bps = max_bitrate_kbps * 1000; sender->SetParameters(parameters); } }
diff --git a/services/catalog/entry.cc b/services/catalog/entry.cc index b79dbfa0..b5ef481 100644 --- a/services/catalog/entry.cc +++ b/services/catalog/entry.cc
@@ -189,10 +189,12 @@ options_struct.can_connect_to_other_services_as_any_user = can_connect_to_other_services_as_any_user_value->GetBool(); - if (const base::Value* allow_other_instance_names_value = - options->FindKey("allow_other_instance_names")) - options_struct.allow_other_instance_names = - allow_other_instance_names_value->GetBool(); + if (const base::Value* + can_connect_to_other_services_with_any_instance_name_value = + options->FindKey( + "can_connect_to_other_services_with_any_instance_name")) + options_struct.can_connect_to_other_services_with_any_instance_name = + can_connect_to_other_services_with_any_instance_name_value->GetBool(); if (const base::Value* instance_for_client_process_value = options->FindKey("instance_for_client_process"))
diff --git a/services/catalog/entry_unittest.cc b/services/catalog/entry_unittest.cc index ec360c6..78fe700f 100644 --- a/services/catalog/entry_unittest.cc +++ b/services/catalog/entry_unittest.cc
@@ -76,7 +76,8 @@ EXPECT_EQ(ServiceOptions::InstanceSharingType::SINGLETON, entry->options().instance_sharing); EXPECT_TRUE(entry->options().can_connect_to_other_services_as_any_user); - EXPECT_TRUE(entry->options().allow_other_instance_names); + EXPECT_TRUE( + entry->options().can_connect_to_other_services_with_any_instance_name); EXPECT_TRUE(entry->options().instance_for_client_process); EXPECT_EQ("", entry->sandbox_type());
diff --git a/services/catalog/service_options.h b/services/catalog/service_options.h index b5f14fe..9c73e420 100644 --- a/services/catalog/service_options.h +++ b/services/catalog/service_options.h
@@ -16,7 +16,7 @@ InstanceSharingType instance_sharing = InstanceSharingType::NONE; bool can_connect_to_other_services_as_any_user = false; - bool allow_other_instance_names = false; + bool can_connect_to_other_services_with_any_instance_name = false; bool instance_for_client_process = false; };
diff --git a/services/catalog/test_data/options b/services/catalog/test_data/options index 1917633..e3698b5 100644 --- a/services/catalog/test_data/options +++ b/services/catalog/test_data/options
@@ -4,7 +4,7 @@ "options": { "instance_sharing": "singleton", "can_connect_to_other_services_as_any_user": true, - "allow_other_instance_names": true, + "can_connect_to_other_services_with_any_instance_name": true, "instance_for_client_process": true }, "interface_provider_specs": { }
diff --git a/services/network/cors/cors_url_loader.cc b/services/network/cors/cors_url_loader.cc index 0bd18b7..8d95257d 100644 --- a/services/network/cors/cors_url_loader.cc +++ b/services/network/cors/cors_url_loader.cc
@@ -27,7 +27,7 @@ } bool NeedsPreflight(const ResourceRequest& request) { - if (!cors::IsCORSEnabledRequestMode(request.fetch_request_mode)) + if (!IsCORSEnabledRequestMode(request.fetch_request_mode)) return false; if (request.is_external_request) @@ -46,13 +46,9 @@ if (!IsCORSSafelistedMethod(request.method)) return true; - for (const auto& header : request.headers.GetHeaderVector()) { - if (!IsCORSSafelistedHeader(header.key, header.value) && - !IsForbiddenHeader(header.key)) { - return true; - } - } - return false; + return !CORSUnsafeNotForbiddenRequestHeaderNames( + request.headers.GetHeaderVector()) + .empty(); } } // namespace @@ -92,7 +88,7 @@ void CORSURLLoader::Start() { if (fetch_cors_flag_ && - cors::IsCORSEnabledRequestMode(request_.fetch_request_mode)) { + IsCORSEnabledRequestMode(request_.fetch_request_mode)) { // Username and password should be stripped in a CORS-enabled request. if (request_.url.has_username() || request_.url.has_password()) { GURL::Replacements replacements;
diff --git a/services/network/cors/preflight_controller.cc b/services/network/cors/preflight_controller.cc index 629fd22..8828fa4 100644 --- a/services/network/cors/preflight_controller.cc +++ b/services/network/cors/preflight_controller.cc
@@ -41,18 +41,11 @@ // - byte-lowercased std::string CreateAccessControlRequestHeadersHeader( const net::HttpRequestHeaders& headers) { - std::vector<std::string> filtered_headers; - for (const auto& header : headers.GetHeaderVector()) { - // Exclude CORS-safelisted headers. - if (cors::IsCORSSafelistedHeader(header.key, header.value)) - continue; - // Exclude the forbidden headers because they may be added by the user - // agent. They must be checked separately and rejected for - // JavaScript-initiated requests. - if (cors::IsForbiddenHeader(header.key)) - continue; - filtered_headers.push_back(base::ToLowerASCII(header.key)); - } + // Exclude the forbidden headers because they may be added by the user + // agent. They must be checked separately and rejected for + // JavaScript-initiated requests. + std::vector<std::string> filtered_headers = + CORSUnsafeNotForbiddenRequestHeaderNames(headers.GetHeaderVector()); if (filtered_headers.empty()) return std::string(); @@ -88,18 +81,18 @@ preflight_request->load_flags |= net::LOAD_DO_NOT_SEND_AUTH_DATA; preflight_request->headers.SetHeader( - cors::header_names::kAccessControlRequestMethod, request.method); + header_names::kAccessControlRequestMethod, request.method); std::string request_headers = CreateAccessControlRequestHeadersHeader(request.headers); if (!request_headers.empty()) { preflight_request->headers.SetHeader( - cors::header_names::kAccessControlRequestHeaders, request_headers); + header_names::kAccessControlRequestHeaders, request_headers); } if (request.is_external_request) { preflight_request->headers.SetHeader( - cors::header_names::kAccessControlRequestExternal, "true"); + header_names::kAccessControlRequestExternal, "true"); } DCHECK(request.request_initiator); @@ -130,10 +123,9 @@ // TODO(toyoshim): Reflect --allow-file-access-from-files flag. *detected_error_status = CheckPreflightAccess( final_url, head.headers->response_code(), + GetHeaderString(head.headers, header_names::kAccessControlAllowOrigin), GetHeaderString(head.headers, - cors::header_names::kAccessControlAllowOrigin), - GetHeaderString(head.headers, - cors::header_names::kAccessControlAllowCredentials), + header_names::kAccessControlAllowCredentials), original_request.fetch_credentials_mode, tainted ? url::Origin() : *original_request.request_initiator, false /* allow_file_origin */);
diff --git a/services/network/cors/preflight_controller_unittest.cc b/services/network/cors/preflight_controller_unittest.cc index a5cb266..e2f2e85 100644 --- a/services/network/cors/preflight_controller_unittest.cc +++ b/services/network/cors/preflight_controller_unittest.cc
@@ -50,7 +50,7 @@ EXPECT_EQ("null", header); EXPECT_TRUE(preflight->headers.GetHeader( - cors::header_names::kAccessControlRequestHeaders, &header)); + header_names::kAccessControlRequestHeaders, &header)); EXPECT_EQ("apple,content-type,kiwifruit,orange,strawberry", header); } @@ -73,7 +73,7 @@ // left out in the preflight request. std::string header; EXPECT_FALSE(preflight->headers.GetHeader( - cors::header_names::kAccessControlRequestHeaders, &header)); + header_names::kAccessControlRequestHeaders, &header)); } TEST(PreflightControllerCreatePreflightRequestTest, Credentials) { @@ -108,7 +108,7 @@ // Empty list also; see comment in test above. std::string header; EXPECT_FALSE(preflight->headers.GetHeader( - cors::header_names::kAccessControlRequestHeaders, &header)); + header_names::kAccessControlRequestHeaders, &header)); } TEST(PreflightControllerCreatePreflightRequestTest, IncludeNonSimpleHeader) { @@ -123,7 +123,7 @@ std::string header; EXPECT_TRUE(preflight->headers.GetHeader( - cors::header_names::kAccessControlRequestHeaders, &header)); + header_names::kAccessControlRequestHeaders, &header)); EXPECT_EQ("x-custom-header", header); } @@ -141,7 +141,7 @@ std::string header; EXPECT_TRUE(preflight->headers.GetHeader( - cors::header_names::kAccessControlRequestHeaders, &header)); + header_names::kAccessControlRequestHeaders, &header)); EXPECT_EQ("content-type", header); } @@ -157,7 +157,7 @@ std::string header; EXPECT_FALSE(preflight->headers.GetHeader( - cors::header_names::kAccessControlRequestHeaders, &header)); + header_names::kAccessControlRequestHeaders, &header)); } TEST(PreflightControllerCreatePreflightRequestTest, Tainted) { @@ -256,7 +256,7 @@ net::test_server::ShouldHandle(request, "/tainted") ? url::Origin() : url::Origin::Create(test_server_.base_url()); - response->AddCustomHeader(cors::header_names::kAccessControlAllowOrigin, + response->AddCustomHeader(header_names::kAccessControlAllowOrigin, origin.Serialize()); response->AddCustomHeader(header_names::kAccessControlAllowMethods, "GET, OPTIONS");
diff --git a/services/network/public/cpp/cors/cors.cc b/services/network/public/cpp/cors/cors.cc index 0076d1b..b117c22 100644 --- a/services/network/public/cpp/cors/cors.cc +++ b/services/network/public/cpp/cors/cors.cc
@@ -354,6 +354,10 @@ } bool IsCORSSafelistedHeader(const std::string& name, const std::string& value) { + // If |value|’s length is greater than 128, then return false. + if (value.size() > 128) + return false; + // https://fetch.spec.whatwg.org/#cors-safelisted-request-header // "A CORS-safelisted header is a header whose name is either one of `Accept`, // `Accept-Language`, and `Content-Language`, or whose name is @@ -395,12 +399,89 @@ if (lower_name == "save-data") return lower_value == "on"; + if (lower_name == "accept") { + return (value.end() == std::find_if(value.begin(), value.end(), [](char c) { + return (c < 0x20 && c != 0x09) || c == 0x22 || c == 0x28 || + c == 0x29 || c == 0x3a || c == 0x3c || c == 0x3e || + c == 0x3f || c == 0x40 || c == 0x5b || c == 0x5c || + c == 0x5d || c == 0x7b || c == 0x7d || c >= 0x7f; + })); + } + + if (lower_name == "accept-language" || lower_name == "content-language") { + return (value.end() == std::find_if(value.begin(), value.end(), [](char c) { + return !isalnum(c) && c != 0x20 && c != 0x2a && c != 0x2c && + c != 0x2d && c != 0x2e && c != 0x3b && c != 0x3d; + })); + } + if (lower_name == "content-type") return IsCORSSafelistedLowerCaseContentType(lower_value); return true; } +bool IsNoCORSSafelistedHeader(const std::string& name, + const std::string& value) { + const std::string lower_name = base::ToLowerASCII(name); + + if (lower_name != "accept" && lower_name != "accept-language" && + lower_name != "content-language" && lower_name != "content-type") { + return false; + } + + return IsCORSSafelistedHeader(lower_name, value); +} + +std::vector<std::string> CORSUnsafeRequestHeaderNames( + const net::HttpRequestHeaders::HeaderVector& headers) { + std::vector<std::string> potentially_unsafe_names; + std::vector<std::string> header_names; + + constexpr size_t kSafeListValueSizeMax = 1024; + size_t safe_list_value_size = 0; + + for (const auto& header : headers) { + if (!IsCORSSafelistedHeader(header.key, header.value)) { + header_names.push_back(base::ToLowerASCII(header.key)); + } else { + potentially_unsafe_names.push_back(base::ToLowerASCII(header.key)); + safe_list_value_size += header.value.size(); + } + } + if (safe_list_value_size > kSafeListValueSizeMax) { + header_names.insert(header_names.end(), potentially_unsafe_names.begin(), + potentially_unsafe_names.end()); + } + return header_names; +} + +std::vector<std::string> CORSUnsafeNotForbiddenRequestHeaderNames( + const net::HttpRequestHeaders::HeaderVector& headers) { + std::vector<std::string> header_names; + std::vector<std::string> potentially_unsafe_names; + + constexpr size_t kSafeListValueSizeMax = 1024; + size_t safe_list_value_size = 0; + + for (const auto& header : headers) { + if (IsForbiddenHeader(header.key)) + continue; + + if (!IsCORSSafelistedHeader(header.key, header.value)) { + header_names.push_back(base::ToLowerASCII(header.key)); + } else { + potentially_unsafe_names.push_back(base::ToLowerASCII(header.key)); + safe_list_value_size += header.value.size(); + } + } + if (safe_list_value_size > kSafeListValueSizeMax) { + header_names.insert(header_names.end(), potentially_unsafe_names.begin(), + potentially_unsafe_names.end()); + } + return header_names; +} + bool IsForbiddenMethod(const std::string& method) { static const std::vector<std::string> forbidden_methods = {"trace", "track", "connect"};
diff --git a/services/network/public/cpp/cors/cors.h b/services/network/public/cpp/cors/cors.h index ec91457..cbe6219 100644 --- a/services/network/public/cpp/cors/cors.h +++ b/services/network/public/cpp/cors/cors.h
@@ -6,9 +6,11 @@ #define SERVICES_NETWORK_PUBLIC_CPP_CORS_CORS_H_ #include <string> +#include <vector> #include "base/component_export.h" #include "base/optional.h" +#include "net/http/http_request_headers.h" #include "services/network/public/cpp/cors/cors_error_status.h" #include "services/network/public/mojom/cors.mojom-shared.h" #include "services/network/public/mojom/fetch_api.mojom-shared.h" @@ -117,6 +119,26 @@ bool IsCORSSafelistedContentType(const std::string& name); COMPONENT_EXPORT(NETWORK_CPP) bool IsCORSSafelistedHeader(const std::string& name, const std::string& value); +COMPONENT_EXPORT(NETWORK_CPP) +bool IsNoCORSSafelistedHeader(const std::string& name, + const std::string& value); + +// https://fetch.spec.whatwg.org/#cors-unsafe-request-header-names +// |headers| must not contain multiple headers for the same name. +// The returned list is NOT sorted. +// The returned list consists of lower-cased names. +COMPONENT_EXPORT(NETWORK_CPP) +std::vector<std::string> CORSUnsafeRequestHeaderNames( + const net::HttpRequestHeaders::HeaderVector& headers); + +// https://fetch.spec.whatwg.org/#cors-unsafe-request-header-names +// Returns header names which are not CORS-safelisted AND not forbidden. +// |headers| must not contain multiple headers for the same name. +// The returned list is NOT sorted. +// The returned list consists of lower-cased names. +COMPONENT_EXPORT(NETWORK_CPP) +std::vector<std::string> CORSUnsafeNotForbiddenRequestHeaderNames( + const net::HttpRequestHeaders::HeaderVector& headers); // Checks forbidden method in the fetch spec. // See https://fetch.spec.whatwg.org/#forbidden-method.
diff --git a/services/network/public/cpp/cors/cors_unittest.cc b/services/network/public/cpp/cors/cors_unittest.cc index 695b04fd..1bd3c49 100644 --- a/services/network/public/cpp/cors/cors_unittest.cc +++ b/services/network/public/cpp/cors/cors_unittest.cc
@@ -4,27 +4,29 @@ #include "services/network/public/cpp/cors/cors.h" +#include <limits.h> + #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" #include "url/origin.h" namespace network { - +namespace cors { namespace { using CORSTest = testing::Test; TEST_F(CORSTest, CheckAccessDetectsInvalidResponse) { - base::Optional<CORSErrorStatus> error_status = cors::CheckAccess( - GURL(), 0 /* response_status_code */, - base::nullopt /* allow_origin_header */, - base::nullopt /* allow_credentials_header */, - network::mojom::FetchCredentialsMode::kOmit, url::Origin()); + base::Optional<CORSErrorStatus> error_status = + CheckAccess(GURL(), 0 /* response_status_code */, + base::nullopt /* allow_origin_header */, + base::nullopt /* allow_credentials_header */, + network::mojom::FetchCredentialsMode::kOmit, url::Origin()); ASSERT_TRUE(error_status); EXPECT_EQ(mojom::CORSError::kInvalidResponse, error_status->cors_error); } -// Tests if cors::CheckAccess detects kWildcardOriginNotAllowed error correctly. +// Tests if CheckAccess detects kWildcardOriginNotAllowed error correctly. TEST_F(CORSTest, CheckAccessDetectsWildcardOriginNotAllowed) { const GURL response_url("http://example.com/data"); const url::Origin origin = url::Origin::Create(GURL("http://google.com")); @@ -33,24 +35,24 @@ // Access-Control-Allow-Origin '*' works. base::Optional<CORSErrorStatus> error1 = - cors::CheckAccess(response_url, response_status_code, - allow_all_header /* allow_origin_header */, - base::nullopt /* allow_credentials_header */, - network::mojom::FetchCredentialsMode::kOmit, origin); + CheckAccess(response_url, response_status_code, + allow_all_header /* allow_origin_header */, + base::nullopt /* allow_credentials_header */, + network::mojom::FetchCredentialsMode::kOmit, origin); EXPECT_FALSE(error1); // Access-Control-Allow-Origin '*' should not be allowed if credentials mode // is kInclude. base::Optional<CORSErrorStatus> error2 = - cors::CheckAccess(response_url, response_status_code, - allow_all_header /* allow_origin_header */, - base::nullopt /* allow_credentials_header */, - network::mojom::FetchCredentialsMode::kInclude, origin); + CheckAccess(response_url, response_status_code, + allow_all_header /* allow_origin_header */, + base::nullopt /* allow_credentials_header */, + network::mojom::FetchCredentialsMode::kInclude, origin); ASSERT_TRUE(error2); EXPECT_EQ(mojom::CORSError::kWildcardOriginNotAllowed, error2->cors_error); } -// Tests if cors::CheckAccess detects kMissingAllowOriginHeader error correctly. +// Tests if CheckAccess detects kMissingAllowOriginHeader error correctly. TEST_F(CORSTest, CheckAccessDetectsMissingAllowOriginHeader) { const GURL response_url("http://example.com/data"); const url::Origin origin = url::Origin::Create(GURL("http://google.com")); @@ -58,15 +60,15 @@ // Access-Control-Allow-Origin is missed. base::Optional<CORSErrorStatus> error = - cors::CheckAccess(response_url, response_status_code, - base::nullopt /* allow_origin_header */, - base::nullopt /* allow_credentials_header */, - network::mojom::FetchCredentialsMode::kOmit, origin); + CheckAccess(response_url, response_status_code, + base::nullopt /* allow_origin_header */, + base::nullopt /* allow_credentials_header */, + network::mojom::FetchCredentialsMode::kOmit, origin); ASSERT_TRUE(error); EXPECT_EQ(mojom::CORSError::kMissingAllowOriginHeader, error->cors_error); } -// Tests if cors::CheckAccess detects kMultipleAllowOriginValues error +// Tests if CheckAccess detects kMultipleAllowOriginValues error // correctly. TEST_F(CORSTest, CheckAccessDetectsMultipleAllowOriginValues) { const GURL response_url("http://example.com/data"); @@ -75,55 +77,55 @@ const std::string space_separated_multiple_origins( "http://example.com http://another.example.com"); - base::Optional<CORSErrorStatus> error1 = cors::CheckAccess( - response_url, response_status_code, - space_separated_multiple_origins /* allow_origin_header */, - base::nullopt /* allow_credentials_header */, - network::mojom::FetchCredentialsMode::kOmit, origin); + base::Optional<CORSErrorStatus> error1 = + CheckAccess(response_url, response_status_code, + space_separated_multiple_origins /* allow_origin_header */, + base::nullopt /* allow_credentials_header */, + network::mojom::FetchCredentialsMode::kOmit, origin); ASSERT_TRUE(error1); EXPECT_EQ(mojom::CORSError::kMultipleAllowOriginValues, error1->cors_error); const std::string comma_separated_multiple_origins( "http://example.com,http://another.example.com"); - base::Optional<CORSErrorStatus> error2 = cors::CheckAccess( - response_url, response_status_code, - comma_separated_multiple_origins /* allow_origin_header */, - base::nullopt /* allow_credentials_header */, - network::mojom::FetchCredentialsMode::kOmit, origin); + base::Optional<CORSErrorStatus> error2 = + CheckAccess(response_url, response_status_code, + comma_separated_multiple_origins /* allow_origin_header */, + base::nullopt /* allow_credentials_header */, + network::mojom::FetchCredentialsMode::kOmit, origin); ASSERT_TRUE(error2); EXPECT_EQ(mojom::CORSError::kMultipleAllowOriginValues, error2->cors_error); } -// Tests if cors::CheckAccess detects kInvalidAllowOriginValue error correctly. +// Tests if CheckAccess detects kInvalidAllowOriginValue error correctly. TEST_F(CORSTest, CheckAccessDetectsInvalidAllowOriginValue) { const GURL response_url("http://example.com/data"); const url::Origin origin = url::Origin::Create(GURL("http://google.com")); const int response_status_code = 200; base::Optional<CORSErrorStatus> error = - cors::CheckAccess(response_url, response_status_code, - std::string("invalid.origin") /* allow_origin_header */, - base::nullopt /* allow_credentials_header */, - network::mojom::FetchCredentialsMode::kOmit, origin); + CheckAccess(response_url, response_status_code, + std::string("invalid.origin") /* allow_origin_header */, + base::nullopt /* allow_credentials_header */, + network::mojom::FetchCredentialsMode::kOmit, origin); ASSERT_TRUE(error); EXPECT_EQ(mojom::CORSError::kInvalidAllowOriginValue, error->cors_error); EXPECT_EQ("invalid.origin", error->failed_parameter); } -// Tests if cors::CheckAccess detects kAllowOriginMismatch error correctly. +// Tests if CheckAccess detects kAllowOriginMismatch error correctly. TEST_F(CORSTest, CheckAccessDetectsAllowOriginMismatch) { const GURL response_url("http://example.com/data"); const url::Origin origin = url::Origin::Create(GURL("http://google.com")); const int response_status_code = 200; base::Optional<CORSErrorStatus> error1 = - cors::CheckAccess(response_url, response_status_code, - origin.Serialize() /* allow_origin_header */, - base::nullopt /* allow_credentials_header */, - network::mojom::FetchCredentialsMode::kOmit, origin); + CheckAccess(response_url, response_status_code, + origin.Serialize() /* allow_origin_header */, + base::nullopt /* allow_credentials_header */, + network::mojom::FetchCredentialsMode::kOmit, origin); ASSERT_FALSE(error1); - base::Optional<CORSErrorStatus> error2 = cors::CheckAccess( + base::Optional<CORSErrorStatus> error2 = CheckAccess( response_url, response_status_code, std::string("http://not.google.com") /* allow_origin_header */, base::nullopt /* allow_credentials_header */, @@ -137,37 +139,37 @@ const url::Origin null_origin; EXPECT_EQ(null_string, null_origin.Serialize()); - base::Optional<CORSErrorStatus> error3 = cors::CheckAccess( + base::Optional<CORSErrorStatus> error3 = CheckAccess( response_url, response_status_code, null_string /* allow_origin_header */, base::nullopt /* allow_credentials_header */, network::mojom::FetchCredentialsMode::kOmit, null_origin); EXPECT_FALSE(error3); } -// Tests if cors::CheckAccess detects kInvalidAllowCredentials error correctly. +// Tests if CheckAccess detects kInvalidAllowCredentials error correctly. TEST_F(CORSTest, CheckAccessDetectsInvalidAllowCredential) { const GURL response_url("http://example.com/data"); const url::Origin origin = url::Origin::Create(GURL("http://google.com")); const int response_status_code = 200; base::Optional<CORSErrorStatus> error1 = - cors::CheckAccess(response_url, response_status_code, - origin.Serialize() /* allow_origin_header */, - std::string("true") /* allow_credentials_header */, - network::mojom::FetchCredentialsMode::kInclude, origin); + CheckAccess(response_url, response_status_code, + origin.Serialize() /* allow_origin_header */, + std::string("true") /* allow_credentials_header */, + network::mojom::FetchCredentialsMode::kInclude, origin); ASSERT_FALSE(error1); base::Optional<CORSErrorStatus> error2 = - cors::CheckAccess(response_url, response_status_code, - origin.Serialize() /* allow_origin_header */, - std::string("fuga") /* allow_credentials_header */, - network::mojom::FetchCredentialsMode::kInclude, origin); + CheckAccess(response_url, response_status_code, + origin.Serialize() /* allow_origin_header */, + std::string("fuga") /* allow_credentials_header */, + network::mojom::FetchCredentialsMode::kInclude, origin); ASSERT_TRUE(error2); EXPECT_EQ(mojom::CORSError::kInvalidAllowCredentials, error2->cors_error); EXPECT_EQ("fuga", error2->failed_parameter); } -// Tests if cors::CheckRedirectLocation detects kCORSDisabledScheme and +// Tests if CheckRedirectLocation detects kCORSDisabledScheme and // kRedirectContainsCredentials errors correctly. TEST_F(CORSTest, CheckRedirectLocation) { struct TestCase { @@ -277,30 +279,30 @@ << ", tainted: " << test.tainted); EXPECT_EQ(test.expectation, - cors::CheckRedirectLocation(test.url, test.request_mode, origin, - test.cors_flag, test.tainted)); + CheckRedirectLocation(test.url, test.request_mode, origin, + test.cors_flag, test.tainted)); } } TEST_F(CORSTest, CheckPreflightDetectsErrors) { - EXPECT_FALSE(cors::CheckPreflight(200)); - EXPECT_FALSE(cors::CheckPreflight(299)); + EXPECT_FALSE(CheckPreflight(200)); + EXPECT_FALSE(CheckPreflight(299)); - base::Optional<mojom::CORSError> error1 = cors::CheckPreflight(300); + base::Optional<mojom::CORSError> error1 = CheckPreflight(300); ASSERT_TRUE(error1); EXPECT_EQ(mojom::CORSError::kPreflightInvalidStatus, *error1); - EXPECT_FALSE(cors::CheckExternalPreflight(std::string("true"))); + EXPECT_FALSE(CheckExternalPreflight(std::string("true"))); base::Optional<CORSErrorStatus> error2 = - cors::CheckExternalPreflight(base::nullopt); + CheckExternalPreflight(base::nullopt); ASSERT_TRUE(error2); EXPECT_EQ(mojom::CORSError::kPreflightMissingAllowExternal, error2->cors_error); EXPECT_EQ("", error2->failed_parameter); base::Optional<CORSErrorStatus> error3 = - cors::CheckExternalPreflight(std::string("TRUE")); + CheckExternalPreflight(std::string("TRUE")); ASSERT_TRUE(error3); EXPECT_EQ(mojom::CORSError::kPreflightInvalidAllowExternal, error3->cors_error); @@ -318,148 +320,386 @@ // CORS flag is false, same-origin request EXPECT_EQ(FetchResponseType::kBasic, - cors::CalculateResponseTainting( + CalculateResponseTainting( same_origin_url, FetchRequestMode::kSameOrigin, origin, false)); EXPECT_EQ(FetchResponseType::kBasic, - cors::CalculateResponseTainting( + CalculateResponseTainting( same_origin_url, FetchRequestMode::kNoCORS, origin, false)); EXPECT_EQ(FetchResponseType::kBasic, - cors::CalculateResponseTainting( - same_origin_url, FetchRequestMode::kCORS, origin, false)); + CalculateResponseTainting(same_origin_url, FetchRequestMode::kCORS, + origin, false)); EXPECT_EQ(FetchResponseType::kBasic, - cors::CalculateResponseTainting( + CalculateResponseTainting( same_origin_url, FetchRequestMode::kCORSWithForcedPreflight, origin, false)); EXPECT_EQ(FetchResponseType::kBasic, - cors::CalculateResponseTainting( + CalculateResponseTainting( same_origin_url, FetchRequestMode::kNavigate, origin, false)); // CORS flag is false, cross-origin request EXPECT_EQ(FetchResponseType::kOpaque, - cors::CalculateResponseTainting( + CalculateResponseTainting( cross_origin_url, FetchRequestMode::kNoCORS, origin, false)); EXPECT_EQ(FetchResponseType::kBasic, - cors::CalculateResponseTainting( + CalculateResponseTainting( cross_origin_url, FetchRequestMode::kNavigate, origin, false)); // CORS flag is true, same-origin request EXPECT_EQ(FetchResponseType::kCORS, - cors::CalculateResponseTainting( - same_origin_url, FetchRequestMode::kCORS, origin, true)); + CalculateResponseTainting(same_origin_url, FetchRequestMode::kCORS, + origin, true)); EXPECT_EQ(FetchResponseType::kCORS, - cors::CalculateResponseTainting( + CalculateResponseTainting( same_origin_url, FetchRequestMode::kCORSWithForcedPreflight, origin, true)); // CORS flag is true, cross-origin request EXPECT_EQ(FetchResponseType::kCORS, - cors::CalculateResponseTainting( - cross_origin_url, FetchRequestMode::kCORS, origin, true)); + CalculateResponseTainting(cross_origin_url, FetchRequestMode::kCORS, + origin, true)); EXPECT_EQ(FetchResponseType::kCORS, - cors::CalculateResponseTainting( + CalculateResponseTainting( cross_origin_url, FetchRequestMode::kCORSWithForcedPreflight, origin, true)); // Origin is not provided. EXPECT_EQ(FetchResponseType::kBasic, - cors::CalculateResponseTainting( + CalculateResponseTainting( same_origin_url, FetchRequestMode::kNoCORS, no_origin, false)); EXPECT_EQ( FetchResponseType::kBasic, - cors::CalculateResponseTainting( - same_origin_url, FetchRequestMode::kNavigate, no_origin, false)); + CalculateResponseTainting(same_origin_url, FetchRequestMode::kNavigate, + no_origin, false)); EXPECT_EQ(FetchResponseType::kBasic, - cors::CalculateResponseTainting( + CalculateResponseTainting( cross_origin_url, FetchRequestMode::kNoCORS, no_origin, false)); EXPECT_EQ( FetchResponseType::kBasic, - cors::CalculateResponseTainting( - cross_origin_url, FetchRequestMode::kNavigate, no_origin, false)); + CalculateResponseTainting(cross_origin_url, FetchRequestMode::kNavigate, + no_origin, false)); } -TEST_F(CORSTest, CheckCORSSafelist) { +TEST_F(CORSTest, SafelistedMethod) { // Method check should be case-insensitive. - EXPECT_TRUE(cors::IsCORSSafelistedMethod("get")); - EXPECT_TRUE(cors::IsCORSSafelistedMethod("Get")); - EXPECT_TRUE(cors::IsCORSSafelistedMethod("GET")); - EXPECT_TRUE(cors::IsCORSSafelistedMethod("HEAD")); - EXPECT_TRUE(cors::IsCORSSafelistedMethod("POST")); - EXPECT_FALSE(cors::IsCORSSafelistedMethod("OPTIONS")); + EXPECT_TRUE(IsCORSSafelistedMethod("get")); + EXPECT_TRUE(IsCORSSafelistedMethod("Get")); + EXPECT_TRUE(IsCORSSafelistedMethod("GET")); + EXPECT_TRUE(IsCORSSafelistedMethod("HEAD")); + EXPECT_TRUE(IsCORSSafelistedMethod("POST")); + EXPECT_FALSE(IsCORSSafelistedMethod("OPTIONS")); +} - // Content-Type check should be case-insensitive, and should ignore spaces and - // parameters such as charset after a semicolon. +TEST_F(CORSTest, SafelistedHeader) { + // See SafelistedAccept/AcceptLanguage/ContentLanguage/ContentType also. + + EXPECT_TRUE(IsCORSSafelistedHeader("accept", "foo")); + EXPECT_FALSE(IsCORSSafelistedHeader("foo", "bar")); + EXPECT_FALSE(IsCORSSafelistedHeader("user-agent", "foo")); +} + +TEST_F(CORSTest, SafelistedAccept) { + EXPECT_TRUE(IsCORSSafelistedHeader("accept", "text/html")); + EXPECT_TRUE(IsCORSSafelistedHeader("AccepT", "text/html")); + + constexpr char kAllowed[] = + "\t !#$%&'*+,-./0123456789;=" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~"; + for (int i = CHAR_MIN; i <= CHAR_MAX; ++i) { + SCOPED_TRACE(testing::Message() << "c = static_cast<char>(" << i << ")"); + char c = static_cast<char>(i); + // 1 for the trailing null character. + auto* end = kAllowed + base::size(kAllowed) - 1; + EXPECT_EQ(std::find(kAllowed, end, c) != end, + IsCORSSafelistedHeader("accept", std::string(1, c))); + EXPECT_EQ(std::find(kAllowed, end, c) != end, + IsCORSSafelistedHeader("AccepT", std::string(1, c))); + } + + EXPECT_TRUE(IsCORSSafelistedHeader("accept", std::string(128, 'a'))); + EXPECT_FALSE(IsCORSSafelistedHeader("accept", std::string(129, 'a'))); + EXPECT_TRUE(IsCORSSafelistedHeader("AccepT", std::string(128, 'a'))); + EXPECT_FALSE(IsCORSSafelistedHeader("AccepT", std::string(129, 'a'))); +} + +TEST_F(CORSTest, SafelistedAcceptLanguage) { + EXPECT_TRUE(IsCORSSafelistedHeader("accept-language", "en,ja")); + EXPECT_TRUE(IsCORSSafelistedHeader("aCcEPT-lAngUAge", "en,ja")); + + constexpr char kAllowed[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz *,-.;="; + for (int i = CHAR_MIN; i <= CHAR_MAX; ++i) { + SCOPED_TRACE(testing::Message() << "c = static_cast<char>(" << i << ")"); + char c = static_cast<char>(i); + // 1 for the trailing null character. + auto* end = kAllowed + base::size(kAllowed) - 1; + EXPECT_EQ(std::find(kAllowed, end, c) != end, + IsCORSSafelistedHeader("aCcEPT-lAngUAge", std::string(1, c))); + } + EXPECT_TRUE(IsCORSSafelistedHeader("accept-language", std::string(128, 'a'))); + EXPECT_FALSE( + IsCORSSafelistedHeader("accept-language", std::string(129, 'a'))); + EXPECT_TRUE(IsCORSSafelistedHeader("aCcEPT-lAngUAge", std::string(128, 'a'))); + EXPECT_FALSE( + IsCORSSafelistedHeader("aCcEPT-lAngUAge", std::string(129, 'a'))); +} + +TEST_F(CORSTest, SafelistedContentLanguage) { + EXPECT_TRUE(IsCORSSafelistedHeader("content-language", "en,ja")); + EXPECT_TRUE(IsCORSSafelistedHeader("cONTent-LANguaGe", "en,ja")); + + constexpr char kAllowed[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz *,-.;="; + for (int i = CHAR_MIN; i <= CHAR_MAX; ++i) { + SCOPED_TRACE(testing::Message() << "c = static_cast<char>(" << i << ")"); + char c = static_cast<char>(i); + // 1 for the trailing null character. + auto* end = kAllowed + base::size(kAllowed) - 1; + EXPECT_EQ(std::find(kAllowed, end, c) != end, + IsCORSSafelistedHeader("content-language", std::string(1, c))); + EXPECT_EQ(std::find(kAllowed, end, c) != end, + IsCORSSafelistedHeader("cONTent-LANguaGe", std::string(1, c))); + } EXPECT_TRUE( - cors::IsCORSSafelistedContentType("application/x-www-form-urlencoded")); - EXPECT_TRUE(cors::IsCORSSafelistedContentType("multipart/form-data")); - EXPECT_TRUE(cors::IsCORSSafelistedContentType("text/plain")); - EXPECT_TRUE(cors::IsCORSSafelistedContentType("TEXT/PLAIN")); - EXPECT_TRUE(cors::IsCORSSafelistedContentType("text/plain;charset=utf-8")); - EXPECT_TRUE(cors::IsCORSSafelistedContentType(" text/plain ;charset=utf-8")); - EXPECT_FALSE(cors::IsCORSSafelistedContentType("text/html")); + IsCORSSafelistedHeader("content-language", std::string(128, 'a'))); + EXPECT_FALSE( + IsCORSSafelistedHeader("content-language", std::string(129, 'a'))); + EXPECT_TRUE( + IsCORSSafelistedHeader("cONTent-LANguaGe", std::string(128, 'a'))); + EXPECT_FALSE( + IsCORSSafelistedHeader("cONTent-LANguaGe", std::string(129, 'a'))); +} - // Header check should be case-insensitive. Value must be considered only for - // Content-Type. - EXPECT_TRUE(cors::IsCORSSafelistedHeader("accept", "text/html")); - EXPECT_TRUE(cors::IsCORSSafelistedHeader("Accept-Language", "en")); - EXPECT_TRUE(cors::IsCORSSafelistedHeader("Content-Language", "ja")); - EXPECT_TRUE(cors::IsCORSSafelistedHeader("SAVE-DATA", "on")); - EXPECT_TRUE(cors::IsCORSSafelistedHeader("Intervention", "")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("Cache-Control", "")); - EXPECT_TRUE(cors::IsCORSSafelistedHeader("Content-Type", "text/plain")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("Content-Type", "image/png")); +TEST_F(CORSTest, SafelistedContentType) { + EXPECT_TRUE(IsCORSSafelistedHeader("content-type", "text/plain")); + EXPECT_TRUE(IsCORSSafelistedHeader("CoNtEnt-TyPE", "text/plain")); + EXPECT_TRUE( + IsCORSSafelistedHeader("content-type", "text/plain; charset=utf-8")); + EXPECT_TRUE( + IsCORSSafelistedHeader("content-type", " text/plain ; charset=UTF-8")); + EXPECT_TRUE( + IsCORSSafelistedHeader("content-type", "text/plain; param=BOGUS")); + EXPECT_TRUE(IsCORSSafelistedHeader("content-type", + "application/x-www-form-urlencoded")); + EXPECT_TRUE(IsCORSSafelistedHeader("content-type", "multipart/form-data")); + + EXPECT_TRUE(IsCORSSafelistedHeader("content-type", "Text/plain")); + EXPECT_TRUE(IsCORSSafelistedHeader("content-type", "tEXT/PLAIN")); + EXPECT_FALSE(IsCORSSafelistedHeader("content-type", "text/html")); + EXPECT_FALSE(IsCORSSafelistedHeader("CoNtEnt-TyPE", "text/html")); + + EXPECT_FALSE(IsCORSSafelistedHeader("content-type", "image/png")); + EXPECT_FALSE(IsCORSSafelistedHeader("CoNtEnt-TyPE", "image/png")); + EXPECT_TRUE(IsCORSSafelistedHeader( + "content-type", "text/plain; charset=" + std::string(108, 'a'))); + EXPECT_TRUE(IsCORSSafelistedHeader( + "cONTent-tYPE", "text/plain; charset=" + std::string(108, 'a'))); + EXPECT_FALSE(IsCORSSafelistedHeader( + "content-type", "text/plain; charset=" + std::string(109, 'a'))); + EXPECT_FALSE(IsCORSSafelistedHeader( + "cONTent-tYPE", "text/plain; charset=" + std::string(109, 'a'))); } TEST_F(CORSTest, CheckCORSClientHintsSafelist) { - EXPECT_FALSE(cors::IsCORSSafelistedHeader("device-memory", "")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("device-memory", "abc")); - EXPECT_TRUE(cors::IsCORSSafelistedHeader("device-memory", "1.25")); - EXPECT_TRUE(cors::IsCORSSafelistedHeader("DEVICE-memory", "1.25")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("device-memory", "1.25-2.5")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("device-memory", "-1.25")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("device-memory", "1e2")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("device-memory", "inf")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("device-memory", "-2.3")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("device-memory", "NaN")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("DEVICE-memory", "1.25.3")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("DEVICE-memory", "1.")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("DEVICE-memory", ".1")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("DEVICE-memory", ".")); - EXPECT_TRUE(cors::IsCORSSafelistedHeader("DEVICE-memory", "1")); + EXPECT_FALSE(IsCORSSafelistedHeader("device-memory", "")); + EXPECT_FALSE(IsCORSSafelistedHeader("device-memory", "abc")); + EXPECT_TRUE(IsCORSSafelistedHeader("device-memory", "1.25")); + EXPECT_TRUE(IsCORSSafelistedHeader("DEVICE-memory", "1.25")); + EXPECT_FALSE(IsCORSSafelistedHeader("device-memory", "1.25-2.5")); + EXPECT_FALSE(IsCORSSafelistedHeader("device-memory", "-1.25")); + EXPECT_FALSE(IsCORSSafelistedHeader("device-memory", "1e2")); + EXPECT_FALSE(IsCORSSafelistedHeader("device-memory", "inf")); + EXPECT_FALSE(IsCORSSafelistedHeader("device-memory", "-2.3")); + EXPECT_FALSE(IsCORSSafelistedHeader("device-memory", "NaN")); + EXPECT_FALSE(IsCORSSafelistedHeader("DEVICE-memory", "1.25.3")); + EXPECT_FALSE(IsCORSSafelistedHeader("DEVICE-memory", "1.")); + EXPECT_FALSE(IsCORSSafelistedHeader("DEVICE-memory", ".1")); + EXPECT_FALSE(IsCORSSafelistedHeader("DEVICE-memory", ".")); + EXPECT_TRUE(IsCORSSafelistedHeader("DEVICE-memory", "1")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("dpr", "")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("dpr", "abc")); - EXPECT_TRUE(cors::IsCORSSafelistedHeader("dpr", "1.25")); - EXPECT_TRUE(cors::IsCORSSafelistedHeader("Dpr", "1.25")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("dpr", "1.25-2.5")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("dpr", "-1.25")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("dpr", "1e2")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("dpr", "inf")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("dpr", "-2.3")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("dpr", "NaN")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("dpr", "1.25.3")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("dpr", "1.")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("dpr", ".1")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("dpr", ".")); - EXPECT_TRUE(cors::IsCORSSafelistedHeader("dpr", "1")); + EXPECT_FALSE(IsCORSSafelistedHeader("dpr", "")); + EXPECT_FALSE(IsCORSSafelistedHeader("dpr", "abc")); + EXPECT_TRUE(IsCORSSafelistedHeader("dpr", "1.25")); + EXPECT_TRUE(IsCORSSafelistedHeader("Dpr", "1.25")); + EXPECT_FALSE(IsCORSSafelistedHeader("dpr", "1.25-2.5")); + EXPECT_FALSE(IsCORSSafelistedHeader("dpr", "-1.25")); + EXPECT_FALSE(IsCORSSafelistedHeader("dpr", "1e2")); + EXPECT_FALSE(IsCORSSafelistedHeader("dpr", "inf")); + EXPECT_FALSE(IsCORSSafelistedHeader("dpr", "-2.3")); + EXPECT_FALSE(IsCORSSafelistedHeader("dpr", "NaN")); + EXPECT_FALSE(IsCORSSafelistedHeader("dpr", "1.25.3")); + EXPECT_FALSE(IsCORSSafelistedHeader("dpr", "1.")); + EXPECT_FALSE(IsCORSSafelistedHeader("dpr", ".1")); + EXPECT_FALSE(IsCORSSafelistedHeader("dpr", ".")); + EXPECT_TRUE(IsCORSSafelistedHeader("dpr", "1")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("width", "")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("width", "abc")); - EXPECT_TRUE(cors::IsCORSSafelistedHeader("width", "125")); - EXPECT_TRUE(cors::IsCORSSafelistedHeader("width", "1")); - EXPECT_TRUE(cors::IsCORSSafelistedHeader("WIDTH", "125")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("width", "125.2")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("width", "-125")); - EXPECT_TRUE(cors::IsCORSSafelistedHeader("width", "2147483648")); + EXPECT_FALSE(IsCORSSafelistedHeader("width", "")); + EXPECT_FALSE(IsCORSSafelistedHeader("width", "abc")); + EXPECT_TRUE(IsCORSSafelistedHeader("width", "125")); + EXPECT_TRUE(IsCORSSafelistedHeader("width", "1")); + EXPECT_TRUE(IsCORSSafelistedHeader("WIDTH", "125")); + EXPECT_FALSE(IsCORSSafelistedHeader("width", "125.2")); + EXPECT_FALSE(IsCORSSafelistedHeader("width", "-125")); + EXPECT_TRUE(IsCORSSafelistedHeader("width", "2147483648")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("viewport-width", "")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("viewport-width", "abc")); - EXPECT_TRUE(cors::IsCORSSafelistedHeader("viewport-width", "125")); - EXPECT_TRUE(cors::IsCORSSafelistedHeader("viewport-width", "1")); - EXPECT_TRUE(cors::IsCORSSafelistedHeader("viewport-Width", "125")); - EXPECT_FALSE(cors::IsCORSSafelistedHeader("viewport-width", "125.2")); - EXPECT_TRUE(cors::IsCORSSafelistedHeader("viewport-width", "2147483648")); + EXPECT_FALSE(IsCORSSafelistedHeader("viewport-width", "")); + EXPECT_FALSE(IsCORSSafelistedHeader("viewport-width", "abc")); + EXPECT_TRUE(IsCORSSafelistedHeader("viewport-width", "125")); + EXPECT_TRUE(IsCORSSafelistedHeader("viewport-width", "1")); + EXPECT_TRUE(IsCORSSafelistedHeader("viewport-Width", "125")); + EXPECT_FALSE(IsCORSSafelistedHeader("viewport-width", "125.2")); + EXPECT_TRUE(IsCORSSafelistedHeader("viewport-width", "2147483648")); +} + +TEST_F(CORSTest, CORSUnsafeRequestHeaderNames) { + // Needed because initializer list is not allowed for a macro argument. + using List = std::vector<std::string>; + + // Empty => Empty + EXPECT_EQ(CORSUnsafeRequestHeaderNames({}), List({})); + + // Some headers are safelisted. + EXPECT_EQ(CORSUnsafeRequestHeaderNames({{"content-type", "text/plain"}, + {"dpr", "12345"}, + {"aCCept", "en,ja"}, + {"accept-charset", "utf-8"}, + {"uSer-Agent", "foo"}, + {"hogE", "fuga"}}), + List({"accept-charset", "user-agent", "hoge"})); + + // All headers are not safelisted. + EXPECT_EQ( + CORSUnsafeRequestHeaderNames({{"content-type", "text/html"}, + {"dpr", "123-45"}, + {"aCCept", "en,ja"}, + {"accept-charset", "utf-8"}, + {"uSer-Agent", "foo"}, + {"hogE", "fuga"}}), + List({"content-type", "dpr", "accept-charset", "user-agent", "hoge"})); + + // |safelistValueSize| is 1024. + EXPECT_EQ( + CORSUnsafeRequestHeaderNames( + {{"content-type", "text/plain; charset=" + std::string(108, '1')}, + {"accept", std::string(128, '1')}, + {"accept-language", std::string(128, '1')}, + {"content-language", std::string(128, '1')}, + {"dpr", std::string(128, '1')}, + {"device-memory", std::string(128, '1')}, + {"save-data", "on"}, + {"viewport-width", std::string(128, '1')}, + {"width", std::string(126, '1')}, + {"hogE", "fuga"}}), + List({"hoge"})); + + // |safelistValueSize| is 1025. + EXPECT_EQ( + CORSUnsafeRequestHeaderNames( + {{"content-type", "text/plain; charset=" + std::string(108, '1')}, + {"accept", std::string(128, '1')}, + {"accept-language", std::string(128, '1')}, + {"content-language", std::string(128, '1')}, + {"dpr", std::string(128, '1')}, + {"device-memory", std::string(128, '1')}, + {"save-data", "on"}, + {"viewport-width", std::string(128, '1')}, + {"width", std::string(127, '1')}, + {"hogE", "fuga"}}), + List({"hoge", "content-type", "accept", "accept-language", + "content-language", "dpr", "device-memory", "save-data", + "viewport-width", "width"})); + + // |safelistValueSize| is 897 because "content-type" is not safelisted. + EXPECT_EQ( + CORSUnsafeRequestHeaderNames( + {{"content-type", "text/plain; charset=" + std::string(128, '1')}, + {"accept", std::string(128, '1')}, + {"accept-language", std::string(128, '1')}, + {"content-language", std::string(128, '1')}, + {"dpr", std::string(128, '1')}, + {"device-memory", std::string(128, '1')}, + {"save-data", "on"}, + {"viewport-width", std::string(128, '1')}, + {"width", std::string(127, '1')}, + {"hogE", "fuga"}}), + List({"content-type", "hoge"})); +} + +TEST_F(CORSTest, CORSUnsafeNotForbiddenRequestHeaderNames) { + // Needed because initializer list is not allowed for a macro argument. + using List = std::vector<std::string>; + + // Empty => Empty + EXPECT_EQ(CORSUnsafeNotForbiddenRequestHeaderNames({}), List({})); + + // "user-agent" is NOT forbidden per spec, but forbidden in Chromium. + EXPECT_EQ( + CORSUnsafeNotForbiddenRequestHeaderNames({{"content-type", "text/plain"}, + {"dpr", "12345"}, + {"aCCept", "en,ja"}, + {"accept-charset", "utf-8"}, + {"uSer-Agent", "foo"}, + {"hogE", "fuga"}}), + List({"hoge"})); + + EXPECT_EQ( + CORSUnsafeNotForbiddenRequestHeaderNames({{"content-type", "text/html"}, + {"dpr", "123-45"}, + {"aCCept", "en,ja"}, + {"accept-charset", "utf-8"}, + {"hogE", "fuga"}}), + List({"content-type", "dpr", "hoge"})); + + // |safelistValueSize| is 1024. + EXPECT_EQ( + CORSUnsafeNotForbiddenRequestHeaderNames( + {{"content-type", "text/plain; charset=" + std::string(108, '1')}, + {"accept", std::string(128, '1')}, + {"accept-language", std::string(128, '1')}, + {"content-language", std::string(128, '1')}, + {"dpr", std::string(128, '1')}, + {"device-memory", std::string(128, '1')}, + {"save-data", "on"}, + {"viewport-width", std::string(128, '1')}, + {"width", std::string(126, '1')}, + {"accept-charset", "utf-8"}, + {"hogE", "fuga"}}), + List({"hoge"})); + + // |safelistValueSize| is 1025. + EXPECT_EQ( + CORSUnsafeNotForbiddenRequestHeaderNames( + {{"content-type", "text/plain; charset=" + std::string(108, '1')}, + {"accept", std::string(128, '1')}, + {"accept-language", std::string(128, '1')}, + {"content-language", std::string(128, '1')}, + {"dpr", std::string(128, '1')}, + {"device-memory", std::string(128, '1')}, + {"save-data", "on"}, + {"viewport-width", std::string(128, '1')}, + {"width", std::string(127, '1')}, + {"accept-charset", "utf-8"}, + {"hogE", "fuga"}}), + List({"hoge", "content-type", "accept", "accept-language", + "content-language", "dpr", "device-memory", "save-data", + "viewport-width", "width"})); + + // |safelistValueSize| is 897 because "content-type" is not safelisted. + EXPECT_EQ( + CORSUnsafeNotForbiddenRequestHeaderNames( + {{"content-type", "text/plain; charset=" + std::string(128, '1')}, + {"accept", std::string(128, '1')}, + {"accept-language", std::string(128, '1')}, + {"content-language", std::string(128, '1')}, + {"dpr", std::string(128, '1')}, + {"device-memory", std::string(128, '1')}, + {"save-data", "on"}, + {"viewport-width", std::string(128, '1')}, + {"width", std::string(127, '1')}, + {"accept-charset", "utf-8"}, + {"hogE", "fuga"}}), + List({"content-type", "hoge"})); } } // namespace - +} // namespace cors } // namespace network
diff --git a/services/network/public/cpp/cors/preflight_result.cc b/services/network/public/cpp/cors/preflight_result.cc index 3cf0f25..516e7ef7 100644 --- a/services/network/public/cpp/cors/preflight_result.cc +++ b/services/network/public/cpp/cors/preflight_result.cc
@@ -136,20 +136,17 @@ if (!credentials_ && headers_.find("*") != headers_.end()) return base::nullopt; - for (const auto& header : headers.GetHeaderVector()) { + // Forbidden headers are forbidden to be used by JavaScript, and checked + // beforehand. But user-agents may add these headers internally, and it's + // fine. + for (const auto& name : + CORSUnsafeNotForbiddenRequestHeaderNames(headers.GetHeaderVector())) { // Header list check is performed in case-insensitive way. Here, we have a // parsed header list set in lower case, and search each header in lower // case. - const std::string key = base::ToLowerASCII(header.key); - if (headers_.find(key) == headers_.end() && - !IsCORSSafelistedHeader(key, header.value)) { - // Forbidden headers are forbidden to be used by JavaScript, and checked - // beforehand. But user-agents may add these headers internally, and it's - // fine. - if (IsForbiddenHeader(key)) - continue; + if (headers_.find(name) == headers_.end()) { return CORSErrorStatus( - mojom::CORSError::kHeaderDisallowedByPreflightResponse, header.key); + mojom::CORSError::kHeaderDisallowedByPreflightResponse, name); } } return base::nullopt;
diff --git a/services/network/public/cpp/cors/preflight_result_unittest.cc b/services/network/public/cpp/cors/preflight_result_unittest.cc index 2a83e34af..b7084eb 100644 --- a/services/network/public/cpp/cors/preflight_result_unittest.cc +++ b/services/network/public/cpp/cors/preflight_result_unittest.cc
@@ -135,15 +135,15 @@ {"GET", "", mojom::FetchCredentialsMode::kOmit, "GET", "X-MY-HEADER:t", mojom::FetchCredentialsMode::kOmit, CORSErrorStatus(mojom::CORSError::kHeaderDisallowedByPreflightResponse, - "X-MY-HEADER")}, + "x-my-header")}, {"GET", "X-SOME-OTHER-HEADER", mojom::FetchCredentialsMode::kOmit, "GET", "X-MY-HEADER:t", mojom::FetchCredentialsMode::kOmit, CORSErrorStatus(mojom::CORSError::kHeaderDisallowedByPreflightResponse, - "X-MY-HEADER")}, + "x-my-header")}, {"GET", "X-MY-HEADER", mojom::FetchCredentialsMode::kOmit, "GET", "X-MY-HEADER:t\r\nY-MY-HEADER:t", mojom::FetchCredentialsMode::kOmit, CORSErrorStatus(mojom::CORSError::kHeaderDisallowedByPreflightResponse, - "Y-MY-HEADER")}, + "y-my-header")}, }; TEST_F(PreflightResultTest, MaxAge) {
diff --git a/services/service_manager/manifest.json b/services/service_manager/manifest.json index 15feb7b..20150a85 100644 --- a/services/service_manager/manifest.json +++ b/services/service_manager/manifest.json
@@ -13,9 +13,6 @@ // Clients requesting this class are allowed to register clients for // processes they launch themselves. "service_manager:client_process": [ ], - // Clients requesting this class are allowed to connect to other clients - // in specific process instance groups. - "service_manager:instance_name": [ ], "service_manager:block_wildcard": [ ], "service_manager:service_manager": [
diff --git a/services/service_manager/service_manager.cc b/services/service_manager/service_manager.cc index 90e9538..52a1d0f8 100644 --- a/services/service_manager/service_manager.cc +++ b/services/service_manager/service_manager.cc
@@ -48,7 +48,6 @@ namespace { const char kCapability_ClientProcess[] = "service_manager:client_process"; -const char kCapability_InstanceName[] = "service_manager:instance_name"; const char kCapability_ServiceManager[] = "service_manager:service_manager"; bool Succeeded(mojom::ConnectResult result) { @@ -621,17 +620,16 @@ << " running as: " << identity_.user_id() << " attempting to connect to: " << target.name() << " as: " << target.user_id() << " without " - << " the service:service_manager{user_id} capability."; + << " the 'can_connect_to_other_services_as_any_user' option."; return mojom::ConnectResult::ACCESS_DENIED; } - if (!target.instance().empty() && - target.instance() != target.name() && - !HasCapability(connection_spec, kCapability_InstanceName)) { - LOG(ERROR) << "Instance: " << identity_.name() << " attempting to " - << "connect to " << target.name() - << " using Instance name: " << target.instance() - << " without the " - << "service_manager{instance_name} capability."; + if (!target.instance().empty() && target.instance() != target.name() && + !options_.can_connect_to_other_services_with_any_instance_name) { + LOG(ERROR) + << "Instance: " << identity_.name() << " attempting to " + << "connect to " << target.name() + << " using Instance name: " << target.instance() << " without the " + << " 'can_connect_to_other_services_with_any_instance_name' option."; return mojom::ConnectResult::ACCESS_DENIED; }
diff --git a/services/service_manager/tests/connect/connect_unittests_manifest.json b/services/service_manager/tests/connect/connect_unittests_manifest.json index 5434489..a84bee3 100644 --- a/services/service_manager/tests/connect/connect_unittests_manifest.json +++ b/services/service_manager/tests/connect/connect_unittests_manifest.json
@@ -1,7 +1,10 @@ { "name": "connect_unittests", "display_name": "Connect Unittests", - "options": { "can_connect_to_other_services_as_any_user": true }, + "options": { + "can_connect_to_other_services_as_any_user": true, + "can_connect_to_other_services_with_any_instance_name": true + }, "interface_provider_specs": { "service_manager:connector": { "provides": { @@ -26,10 +29,7 @@ "connect_unittests:standalone_app", "connect_unittests:user_id_test" ], - "connect_test_singleton_app": [], - "service_manager": [ - "service_manager:instance_name" - ] + "connect_test_singleton_app": [] } } }
diff --git a/services/service_manager/tests/lifecycle/lifecycle_unittest_manifest.json b/services/service_manager/tests/lifecycle/lifecycle_unittest_manifest.json index 251344d..5497ca8 100644 --- a/services/service_manager/tests/lifecycle/lifecycle_unittest_manifest.json +++ b/services/service_manager/tests/lifecycle/lifecycle_unittest_manifest.json
@@ -8,7 +8,6 @@ "lifecycle_unittest_parent": [ "lifecycle_unittest:parent" ], "service_manager": [ "service_manager:service_manager", - "service_manager:instance_name", "service_manager:client_process" ] }
diff --git a/services/service_manager/tests/service_manager/service_manager_unittest_manifest.json b/services/service_manager/tests/service_manager/service_manager_unittest_manifest.json index 21a15312..7e8d1ce1 100644 --- a/services/service_manager/tests/service_manager/service_manager_unittest_manifest.json +++ b/services/service_manager/tests/service_manager/service_manager_unittest_manifest.json
@@ -2,7 +2,8 @@ "name": "service_manager_unittest", "display_name": "Service Manager Unittest", "options": { - "instance_sharing" : "singleton" + "instance_sharing": "singleton", + "can_connect_to_other_services_with_any_instance_name": true }, "interface_provider_specs": { "service_manager:connector": { @@ -14,8 +15,7 @@ "requires": { "service_manager": [ "service_manager:service_manager", - "service_manager:client_process", - "service_manager:instance_name" + "service_manager:client_process" ], "service_manager_unittest_embedder": [ "service_manager_unittest:embedder"
diff --git a/services/viz/privileged/interfaces/gl/gpu_host.mojom b/services/viz/privileged/interfaces/gl/gpu_host.mojom index 11849c9..2e081bb 100644 --- a/services/viz/privileged/interfaces/gl/gpu_host.mojom +++ b/services/viz/privileged/interfaces/gl/gpu_host.mojom
@@ -35,8 +35,10 @@ // track of this decision in case the GPU process crashes. DisableGpuCompositing(); + [EnableIf=is_win] SetChildSurface(gpu.mojom.SurfaceHandle parent, gpu.mojom.SurfaceHandle child); + StoreShaderToDisk(int32 client_id, string key, string shader); RecordLogMessage(int32 severity, string header, string message);
diff --git a/services/ws/gpu_host/gpu_host.cc b/services/ws/gpu_host/gpu_host.cc index 24c3515..4606e3e 100644 --- a/services/ws/gpu_host/gpu_host.cc +++ b/services/ws/gpu_host/gpu_host.cc
@@ -193,9 +193,9 @@ void DefaultGpuHost::DisableGpuCompositing() {} +#if defined(OS_WIN) void DefaultGpuHost::SetChildSurface(gpu::SurfaceHandle parent, gpu::SurfaceHandle child) { -#if defined(OS_WIN) // Verify that |parent| was created by the window server. DWORD process_id = 0; DWORD thread_id = GetWindowThreadProcessId(parent, &process_id); @@ -210,10 +210,8 @@ child)) { OnBadMessageFromGpu(); } -#else - NOTREACHED(); -#endif } +#endif // defined(OS_WIN) void DefaultGpuHost::StoreShaderToDisk(int32_t client_id, const std::string& key,
diff --git a/services/ws/gpu_host/gpu_host.h b/services/ws/gpu_host/gpu_host.h index 0ff47fe..d81ca99 100644 --- a/services/ws/gpu_host/gpu_host.h +++ b/services/ws/gpu_host/gpu_host.h
@@ -110,8 +110,10 @@ gpu::error::ContextLostReason reason, const GURL& active_url) override; void DisableGpuCompositing() override; +#if defined(OS_WIN) void SetChildSurface(gpu::SurfaceHandle parent, gpu::SurfaceHandle child) override; +#endif void StoreShaderToDisk(int32_t client_id, const std::string& key, const std::string& shader) override;
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 9844f8cb..b51074b 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -2916,9 +2916,11 @@ ], "experiments": [ { - "name": "Intervention_R3", + "name": "Intervention_R7", "params": { - "pause_renderer": "true" + "navigate_ads": "true", + "pause_renderer": "true", + "renderer_workload_threshold_percentage": "16" }, "enable_features": [ "OomIntervention"
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG index 60ae0771..ab34111 100644 --- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG +++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -81,6 +81,7 @@ crbug.com/591099 external/wpt/css/CSS2/normal-flow/block-in-inline-nested-002.xht [ Pass ] crbug.com/591099 external/wpt/css/CSS2/normal-flow/block-in-inline-remove-006.xht [ Pass ] crbug.com/591099 external/wpt/css/CSS2/text/white-space-mixed-003.xht [ Pass ] +crbug.com/591099 external/wpt/css/css-contain/contain-size-scrollbars-001.html [ Failure ] crbug.com/591099 external/wpt/css/css-display/display-contents-fieldset-nested-legend.html [ Pass ] crbug.com/714962 external/wpt/css/css-fonts/font-features-across-space-1.html [ Pass ] crbug.com/714962 external/wpt/css/css-fonts/font-features-across-space-3.html [ Pass ] @@ -205,6 +206,9 @@ crbug.com/591099 external/wpt/css/selectors/selector-placeholder-shown-type-change-001.html [ Pass ] crbug.com/591099 external/wpt/css/selectors/selector-read-write-type-change-002.html [ Pass ] crbug.com/591099 external/wpt/css/selectors/selector-required-type-change-002.html [ Pass ] +crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-paint-clip-003.html [ Failure ] +crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-paint-clip-004.html [ Failure ] +crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-paint-clip-005.html [ Failure ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-002.xhtml [ Pass ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-011.html [ Pass ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-012.html [ Pass ] @@ -234,20 +238,13 @@ crbug.com/591099 external/wpt/editing/run/bold.html [ Pass ] crbug.com/591099 external/wpt/editing/run/fontname.html [ Pass ] crbug.com/591099 external/wpt/editing/run/formatblock.html [ Pass ] -crbug.com/591099 external/wpt/editing/run/inserthorizontalrule.html [ Pass ] -crbug.com/591099 external/wpt/editing/run/inserthtml.html [ Pass ] -crbug.com/591099 external/wpt/editing/run/insertimage.html [ Pass ] -crbug.com/591099 external/wpt/editing/run/insertlinebreak.html [ Pass ] crbug.com/591099 external/wpt/editing/run/insertparagraph.html [ Pass ] crbug.com/591099 external/wpt/editing/run/justifycenter.html [ Pass ] crbug.com/591099 external/wpt/editing/run/justifyfull.html [ Pass ] crbug.com/591099 external/wpt/editing/run/justifyleft.html [ Pass ] crbug.com/591099 external/wpt/editing/run/justifyright.html [ Pass ] -crbug.com/591099 external/wpt/editing/run/misc.html [ Pass ] crbug.com/591099 external/wpt/editing/run/multitest.html [ Pass ] crbug.com/591099 external/wpt/editing/run/strikethrough.html [ Pass ] -crbug.com/591099 external/wpt/editing/run/subscript.html [ Pass ] -crbug.com/591099 external/wpt/editing/run/unlink.html [ Pass ] crbug.com/591099 external/wpt/encoding/eof-utf-8-three.html [ Failure ] crbug.com/591099 external/wpt/encoding/eof-utf-8-two.html [ Failure ] crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601.html [ Timeout ] @@ -255,7 +252,7 @@ crbug.com/591099 external/wpt/fetch/api/redirect/redirect-count.any.worker.html [ Pass ] crbug.com/591099 external/wpt/fetch/api/request/request-keepalive-quota.html?include=slow-2 [ Pass ] crbug.com/591099 external/wpt/fetch/http-cache/basic-auth-cache-test.html [ Timeout ] -crbug.com/591099 external/wpt/geolocation-API/PositionOptions.https.html [ Failure Pass ] +crbug.com/591099 external/wpt/geolocation-API/PositionOptions.https.html [ Failure ] crbug.com/591099 external/wpt/html-media-capture/capture_audio_cancel-manual.html [ Failure ] crbug.com/591099 external/wpt/html-media-capture/capture_image_cancel-manual.html [ Failure ] crbug.com/591099 external/wpt/html-media-capture/capture_video_cancel-manual.html [ Failure ] @@ -290,7 +287,7 @@ crbug.com/591099 external/wpt/offscreen-canvas/convert-to-blob/offscreencanvas.convert.to.blob.html [ Pass ] crbug.com/591099 external/wpt/payment-handler/idlharness.https.any.serviceworker.html [ Pass ] crbug.com/591099 external/wpt/performance-timeline/po-observe.html [ Timeout ] -crbug.com/591099 external/wpt/picture-in-picture/request-picture-in-picture-twice.html [ Pass Timeout ] +crbug.com/591099 external/wpt/picture-in-picture/request-picture-in-picture-twice.html [ Pass ] crbug.com/591099 external/wpt/pointerevents/pointerevent_click_during_capture-manual.html [ Crash Timeout ] crbug.com/591099 external/wpt/push-api/idlharness.https.any.serviceworker.html [ Pass ] crbug.com/591099 external/wpt/quirks/line-height-calculation.html [ Failure ] @@ -394,6 +391,7 @@ crbug.com/835484 fast/inline/outline-offset.html [ Failure ] crbug.com/591099 fast/overflow/overflow-update-transform.html [ Failure ] crbug.com/591099 fast/overflow/recompute-overflow-of-layout-root-container.html [ Failure ] +crbug.com/591099 fast/reflections/opacity-reflection-transform.html [ Failure ] crbug.com/591099 fast/replaced/table-replaced-element.html [ Failure ] crbug.com/591099 fast/scrolling/content-box-smaller-than-scrollbar.html [ Failure ] crbug.com/591099 fast/scrolling/jquery-rtl-scroll-type.html [ Failure ] @@ -445,21 +443,18 @@ crbug.com/591099 http/tests/security/cors-rfc1918/addressspace-document-appcache.https.html [ Crash Failure ] crbug.com/591099 http/tests/security/cors-rfc1918/addressspace-document-csp-appcache.https.html [ Crash Failure Pass ] crbug.com/591099 http/tests/security/setDomainRelaxationForbiddenForURLScheme.html [ Crash ] -crbug.com/591099 virtual/outofblink-cors/http/tests/security/cors-rfc1918/addressspace-document-appcache.https.html [ Crash Failure ] -crbug.com/591099 virtual/outofblink-cors/http/tests/security/cors-rfc1918/addressspace-document-csp-appcache.https.html [ Crash Failure Pass ] -crbug.com/591099 virtual/outofblink-cors/http/tests/security/setDomainRelaxationForbiddenForURLScheme.html [ Crash ] -crbug.com/591099 virtual/outofblink-cors-ns/http/tests/security/cors-rfc1918/addressspace-document-appcache.https.html [ Crash Failure ] -crbug.com/591099 virtual/outofblink-cors-ns/http/tests/security/cors-rfc1918/addressspace-document-csp-appcache.https.html [ Crash Failure Pass ] -crbug.com/591099 virtual/outofblink-cors-ns/http/tests/security/setDomainRelaxationForbiddenForURLScheme.html [ Crash ] -crbug.com/591099 idle-callback/test-runner-run-idle-tasks.html [ Crash Pass ] +crbug.com/591099 idle-callback/test-runner-run-idle-tasks.html [ Crash Pass Timeout ] crbug.com/591099 images/color-profile-image-filter-all.html [ Failure ] crbug.com/591099 images/rendering-broken-block-flow-images.html [ Failure ] crbug.com/591099 images/rendering-broken-images.html [ Failure ] crbug.com/714962 inspector-protocol/css/css-get-platform-fonts.js [ Failure ] crbug.com/591099 inspector-protocol/dom-snapshot/dom-snapshot-getSnapshot-pseudo-element.js [ Failure ] +crbug.com/591099 inspector-protocol/dom-snapshot/dom-snapshot-getSnapshot-scroll-offset.js [ Failure ] +crbug.com/591099 inspector-protocol/dom-snapshot/dom-snapshot-getSnapshot-viewport.js [ Failure ] crbug.com/591099 inspector-protocol/dom-snapshot/dom-snapshot-getSnapshot.js [ Failure ] crbug.com/714962 inspector-protocol/layout-fonts/languages-emoji-rare-glyphs.js [ Failure ] crbug.com/591099 inspector-protocol/timeline/page-frames.js [ Failure ] +crbug.com/591099 media/video-aspect-ratio.html [ Failure ] crbug.com/591099 paint/background/scrolling-background-with-negative-z-child.html [ Failure ] crbug.com/591099 paint/float/float-under-inline-self-painting-change.html [ Failure ] crbug.com/835484 paint/inline/focus-ring-under-absolute-with-relative-continuation.html [ Failure ] @@ -508,6 +503,7 @@ crbug.com/591099 storage/indexeddb/objectstore-cursor.html [ Pass ] crbug.com/591099 svg/custom/object-sizing-no-width-height.xhtml [ Failure ] crbug.com/591099 svg/filters/feTurbulence-bad-seeds.html [ Failure ] +crbug.com/591099 svg/hixie/error/013.xml [ Failure ] crbug.com/591099 svg/in-html/sizing/svg-inline.html [ Failure ] crbug.com/591099 svg/transforms/text-with-pattern-inside-transformed-html.xhtml [ Failure ] crbug.com/591099 svg/zoom/page/zoom-img-preserveAspectRatio-support-1.html [ Failure ] @@ -549,10 +545,15 @@ crbug.com/591099 virtual/outofblink-cors-ns/external/wpt/fetch/api/redirect/redirect-count.any.html [ Pass ] crbug.com/591099 virtual/outofblink-cors-ns/external/wpt/fetch/api/redirect/redirect-count.any.worker.html [ Pass ] crbug.com/591099 virtual/outofblink-cors-ns/external/wpt/fetch/api/request/request-keepalive-quota.html?include=slow-2 [ Pass ] -crbug.com/591099 virtual/outofblink-cors-ns/external/wpt/xhr/send-content-type-string.htm [ Pass ] -crbug.com/591099 virtual/outofblink-cors-ns/external/wpt/xhr/send-entity-body-document.htm [ Pass ] -crbug.com/591099 virtual/outofblink-cors-ns/external/wpt/xhr/send-redirect-bogus-sync.htm [ Pass ] +crbug.com/591099 virtual/outofblink-cors-ns/http/tests/security/cors-rfc1918/addressspace-document-appcache.https.html [ Crash Failure ] +crbug.com/591099 virtual/outofblink-cors-ns/http/tests/security/cors-rfc1918/addressspace-document-csp-appcache.https.html [ Crash Failure Pass ] +crbug.com/591099 virtual/outofblink-cors-ns/http/tests/security/document-domain-canonicalizes.html [ Pass ] +crbug.com/591099 virtual/outofblink-cors-ns/http/tests/security/setDomainRelaxationForbiddenForURLScheme.html [ Crash ] +crbug.com/591099 virtual/outofblink-cors-ns/http/tests/security/video-poster-cross-origin-crash2.html [ Pass ] crbug.com/591099 virtual/outofblink-cors/ [ Skip ] +crbug.com/591099 virtual/outofblink-cors/http/tests/security/cors-rfc1918/addressspace-document-appcache.https.html [ Crash Failure ] +crbug.com/591099 virtual/outofblink-cors/http/tests/security/cors-rfc1918/addressspace-document-csp-appcache.https.html [ Crash Failure Pass ] +crbug.com/591099 virtual/outofblink-cors/http/tests/security/setDomainRelaxationForbiddenForURLScheme.html [ Pass ] crbug.com/591099 virtual/paint-timing/external/wpt/paint-timing/sibling-painting-first-image.html [ Failure ] crbug.com/591099 virtual/paint-touchaction-rects/fast/events/touch/compositor-touch-hit-rects-continuation.html [ Failure ] crbug.com/591099 virtual/paint-touchaction-rects/fast/events/touch/compositor-touch-hit-rects-list-translate.html [ Failure ] @@ -571,4 +572,5 @@ crbug.com/591099 virtual/threaded/ [ Skip ] crbug.com/591099 virtual/user-activation-v2/fast/events/mouse-cursor.html [ Failure ] crbug.com/591099 virtual/user-activation-v2/fast/events/touch/compositor-touch-hit-rects.html [ Failure ] -crbug.com/591099 virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCDTMFSender-ontonechange.https.html [ Failure Pass ] +crbug.com/591099 virtual/video-surface-layer/media/video-aspect-ratio.html [ Failure ] +crbug.com/591099 virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCDTMFSender-ontonechange.https.html [ Pass ]
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json index ea0b307..84b851715 100644 --- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json +++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json
@@ -35789,6 +35789,18 @@ {} ] ], + "css/css-contain/contain-animation-001.html": [ + [ + "/css/css-contain/contain-animation-001.html", + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], "css/css-contain/contain-layout-001.html": [ [ "/css/css-contain/contain-layout-001.html", @@ -97841,6 +97853,18 @@ {} ] ], + "html/rendering/the-details-element/details-display-property-is-ignored.html": [ + [ + "/html/rendering/the-details-element/details-display-property-is-ignored.html", + [ + [ + "/html/rendering/the-details-element/details-display-property-is-ignored-ref.html", + "==" + ] + ], + {} + ] + ], "html/semantics/document-metadata/the-link-element/stylesheet-change-href.html": [ [ "/html/semantics/document-metadata/the-link-element/stylesheet-change-href.html", @@ -147980,6 +148004,16 @@ {} ] ], + "fetch/api/cors/cors-preflight-not-cors-safelisted.any-expected.txt": [ + [ + {} + ] + ], + "fetch/api/cors/cors-preflight-not-cors-safelisted.any.worker-expected.txt": [ + [ + {} + ] + ], "fetch/api/cors/cors-preflight-redirect.any-expected.txt": [ [ {} @@ -148000,6 +148034,11 @@ {} ] ], + "fetch/api/headers/headers-no-cors.window-expected.txt": [ + [ + {} + ] + ], "fetch/api/headers/headers-record-expected.txt": [ [ {} @@ -158065,6 +158104,11 @@ {} ] ], + "html/rendering/the-details-element/details-display-property-is-ignored-ref.html": [ + [ + {} + ] + ], "html/rendering/the-details-element/single-summary.html": [ [ {} @@ -263225,6 +263269,12 @@ {} ] ], + "webaudio/the-audio-api/the-oscillatornode-interface/detune-limiting.html": [ + [ + "/webaudio/the-audio-api/the-oscillatornode-interface/detune-limiting.html", + {} + ] + ], "webaudio/the-audio-api/the-pannernode-interface/ctor-panner.html": [ [ "/webaudio/the-audio-api/the-pannernode-interface/ctor-panner.html", @@ -311778,6 +311828,10 @@ "259c00b2a587c9aa2d07de97fb547b32f9772b92", "support" ], + "css/css-contain/contain-animation-001.html": [ + "449221428c3d76d31ff84a5792c7578c36cbebed", + "reftest" + ], "css/css-contain/contain-layout-001.html": [ "85b959da2b9a151c13be3dc83485646341752915", "reftest" @@ -368462,10 +368516,18 @@ "ce6a169d8146750b183c9210d1b2041fac879248", "testharness" ], + "fetch/api/cors/cors-preflight-not-cors-safelisted.any-expected.txt": [ + "56141bc0d4147b276e2ab2fc795fb4032ed70e59", + "support" + ], "fetch/api/cors/cors-preflight-not-cors-safelisted.any.js": [ "b2747ccd5bc09e4174aa4c59244e386c80527b51", "testharness" ], + "fetch/api/cors/cors-preflight-not-cors-safelisted.any.worker-expected.txt": [ + "56141bc0d4147b276e2ab2fc795fb4032ed70e59", + "support" + ], "fetch/api/cors/cors-preflight-redirect.any-expected.txt": [ "8a420164e1b94e02f9d86d41790b44adc4f87cd5", "support" @@ -368550,6 +368612,10 @@ "194ff32f1559f2dd9b5903eb3738c17c061c7172", "testharness" ], + "fetch/api/headers/headers-no-cors.window-expected.txt": [ + "2241e22e63412b03a454626bfb48d65376b2428b", + "support" + ], "fetch/api/headers/headers-no-cors.window.js": [ "aa6562b7d377f4ad74456a87d7e37bf0bd18cb2b", "testharness" @@ -380962,6 +381028,14 @@ "3dd95e311aed7fc642e9370dad7cfee5a1eeac1c", "reftest" ], + "html/rendering/the-details-element/details-display-property-is-ignored-ref.html": [ + "6ebed6075de1e8cf62db7bee756b05d3e425e0ab", + "support" + ], + "html/rendering/the-details-element/details-display-property-is-ignored.html": [ + "445b4e483d00c8aabfa76a946d8cb0871e479c7d", + "reftest" + ], "html/rendering/the-details-element/single-summary.html": [ "1f09e7e75f9126982a07902ae0693f9ea2fd5823", "support" @@ -390531,7 +390605,7 @@ "testharness" ], "html/webappapis/dynamic-markup-insertion/opening-the-input-stream/reload.window.js": [ - "d6ff9dc7a45425cb688ed4b6c9ea2ab5c1c3ae5c", + "279020f64da8421e118a447bae1373da2e98d9c1", "testharness" ], "html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/aborted-parser-async-frame.html": [ @@ -423198,6 +423272,10 @@ "36bf604b296c63b213d99408ab38937c62a755dc", "testharness" ], + "webaudio/the-audio-api/the-oscillatornode-interface/detune-limiting.html": [ + "81a1293d0355ed448e60c0e31ad4435ea708e224", + "testharness" + ], "webaudio/the-audio-api/the-pannernode-interface/.gitkeep": [ "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", "support"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-contain/contain-animation-001.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-contain/contain-animation-001.html new file mode 100644 index 0000000..44922142 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-contain/contain-animation-001.html
@@ -0,0 +1,31 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Containment Test: contain is not animatable</title> +<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/"> +<link rel="help" href="https://drafts.csswg.org/css-contain/#contain-property"> +<link rel="match" href="../reference/ref-filled-green-100px-square.xht"> +<meta name=assert content="the contain property is not animatable"> +<style> +div { + border: 50px solid green; + background: red; + position: absolute; /* for shrinkwrap */ + contain: strict; + + animation-duration: 1s; + animation-name: bad; + animation-play-state: paused; + + font-size: 100px; +} + +@keyframes bad { + from { + contain: none; + } +} +</style> + +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div> </div> +
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-paint-api/registered-properties-in-custom-paint.https.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-paint-api/registered-properties-in-custom-paint.https.html deleted file mode 100644 index d9a63da..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-paint-api/registered-properties-in-custom-paint.https.html +++ /dev/null
@@ -1,67 +0,0 @@ -<!DOCTYPE html> -<html class="reftest-wait"> -<link rel="match" href="parse-input-arguments-ref.html"> -<style> -.container { - width: 100px; - height: 100px; - --length: 10px; - --number: 10; -} - -#canvas-geometry { - background-image: paint(geometry); -} -</style> -<script src="/common/reftest-wait.js"></script> -<script src="/common/worklet-reftest.js"></script> -<body> -<div id="canvas-geometry" class="container"></div> - -<script id="code" type="text/worklet"> -registerPaint('geometry', class { - static get inputProperties() { - return [ - '--length', - '--length-initial', - '--number', - ]; - } - paint(ctx, geom, styleMap) { - const properties = [...styleMap.keys()].sort(); - var serializedStrings = []; - for (let i = 0; i < properties.length; i++) { - const value = styleMap.get(properties[i]); - let serialized; - if (value) - serialized = properties[i].toString() + ': [' + value.constructor.name + '=' + value.toString() + ']'; - else - serialized = properties[i].toString() + ': [null]'; - serializedStrings.push(serialized); - } - ctx.strokeStyle = 'green'; - if (serializedStrings[0] != "--length: [CSSUnitValue=10px]") - ctx.strokeStyle = 'red'; - if (serializedStrings[1] != "--length-initial: [CSSUnitValue=20px]") - ctx.strokeStyle = 'blue'; - if (serializedStrings[2] != "--number: [CSSUnitValue=10]") - ctx.strokeStyle = 'yellow'; - ctx.lineWidth = 4; - ctx.strokeRect(0, 0, geom.width, geom.height); - } -}); -</script> - -<script> - try { - CSS.registerProperty({name: '--length', syntax: '<length>', initialValue: '0px', inherits: false}); - CSS.registerProperty({name: '--length-initial', syntax: '<length>', initialValue: '20px', inherits: false}); - CSS.registerProperty({name: '--number', syntax: '<number>', initialValue: '0', inherits: false}); - importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent); - } catch(e) { - document.body.textContent = e; - takeScreenshot(); - } -</script> -</body> -</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-paint-api/registered-property-type.https.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-paint-api/registered-property-type.https.html new file mode 100644 index 0000000..6ff7ce4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-paint-api/registered-property-type.https.html
@@ -0,0 +1,148 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="match" href="parse-input-arguments-ref.html"> +<style> +.container { + width: 100px; + height: 100px; +} + +#canvas-geometry { + background-image: paint(geometry); +} +</style> +<script src="/common/reftest-wait.js"></script> +<script src="/common/worklet-reftest.js"></script> +<body> +<div id="canvas-geometry" class="container"></div> +<script id="code" type="text/worklet"> + // Globals that must be prepended to this script: + // - debugLog: A function that logs errors. + // - props: Test data. + + registerPaint('geometry', class { + static get inputProperties() { return props.map(p => p.name); } + + paint(ctx, geom, styleMap) { + ctx.strokeStyle = 'green'; + for (let prop of props) { + let first = styleMap.get(prop.name); + let all = styleMap.getAll(prop.name); + let serialize = v => v.constructor.name + '=' + v.toString() + let actual = all.map(serialize).join(','); + let expected = prop.expected.join(','); + let pass = actual === expected + && serialize(first) === prop.expected[0]; + if (!pass) + ctx.strokeStyle = 'red'; + debugLog(pass ? 'PASS' : 'FAIL', prop.syntax, actual, expected); + } + ctx.lineWidth = 4; + ctx.strokeRect(0, 0, geom.width, geom.height); + } + }); +</script> +<script> + // A copy of this array (automatically enriched with 'name' and 'expected') + // is also available in the worklet. + let props = [ + // Initial values. + { syntax: '*', initialValue: 'if(){}' }, + { syntax: '<angle>', initialValue: '42deg' }, + { syntax: '<color>', initialValue: '#fefefe' }, + { syntax: '<custom-ident>', initialValue: 'none' }, + { syntax: '<image>', initialValue: 'linear-gradient(red, red)' }, + { syntax: '<image>', initialValue: 'url(http://a.com/a)' }, + { syntax: '<integer>', initialValue: '42' }, + { syntax: '<length-percentage>', initialValue: '10%' }, + { syntax: '<length-percentage>', initialValue: '10px' }, + { syntax: '<length-percentage>', initialValue: 'calc(10px + 10%)' }, + { syntax: '<length>', initialValue: '1337px' }, + { syntax: '<number>', initialValue: '42.5' }, + { syntax: '<percentage>', initialValue: '42%' }, + { syntax: '<resolution>', initialValue: '300dpi' }, + { syntax: '<time>', initialValue: '3600s' }, + { syntax: '<url>', initialValue: 'url(http://a.com/a)' }, + { syntax: 'thing', initialValue: 'thing' }, + { syntax: '<length> | <angle>', initialValue: '1337px' }, + { syntax: '<angle> | <image>', initialValue: '1turn' }, + { syntax: '<length>+', initialValue: '1337px' }, + { syntax: '<length>+', initialValue: '1337px 1338px', count: 2 }, + { syntax: '<length>#', initialValue: '1337px' }, + { syntax: '<length>#', initialValue: '1337px, 1338px', count: 2 }, + + // Non-initial values: + { syntax: '*', initialValue: 'fail', value: 'if(){}' }, + { syntax: '<angle> | fail', initialValue: 'fail', value: '42deg' }, + { syntax: '<color> | fail', initialValue: 'fail', value: '#fefefe' }, + { syntax: '<custom-ident> | fail', initialValue: 'fail', value: 'none' }, + { syntax: '<image> | fail', initialValue: 'fail', value: 'linear-gradient(red, red)' }, + { syntax: '<image> | fail', initialValue: 'fail', value: 'url(http://a.com/a)' }, + { syntax: '<integer> | fail', initialValue: 'fail', value: '42' }, + { syntax: '<length-percentage> | fail', initialValue: 'fail', value: '10%' }, + { syntax: '<length-percentage> | fail', initialValue: 'fail', value: '10px' }, + { syntax: '<length-percentage> | fail', initialValue: 'fail', value: 'calc(10px + 10%)' }, + { syntax: '<length> | fail', initialValue: 'fail', value: '1337px' }, + { syntax: '<number> | fail', initialValue: 'fail', value: '42.5' }, + { syntax: '<percentage> | fail', initialValue: 'fail', value: '42%' }, + { syntax: '<resolution> | fail', initialValue: 'fail', value: '300dpi' }, + { syntax: '<time> | fail', initialValue: 'fail', value: '3600s' }, + { syntax: '<url> | fail', initialValue: 'fail', value: 'url(http://a.com/a)' }, + { syntax: 'thing | fail', initialValue: 'fail', value: 'thing' }, + { syntax: '<length>+ | fail', initialValue: 'fail', value: '1337px' }, + { syntax: '<length>+ | fail', initialValue: 'fail', value: '1337px 1338px', count: 2 }, + { syntax: '<length># | fail', initialValue: 'fail', value: '1337px' }, + { syntax: '<length># | fail', initialValue: 'fail', value: '1337px, 1338px', count: 2 }, + ]; + + try { + let target = document.getElementById('canvas-geometry'); + let pid = 1; + + for (let p of props) { + p.name = `--prop-${++pid}`; + + CSS.registerProperty({ + name: p.name, + syntax: p.syntax, + initialValue: p.initialValue, + inherits: (typeof p.inherits !== 'undefined') ? p.inherits : false + }); + + if (typeof p.value !== 'undefined') + target.style.setProperty(p.name, p.value); + if (typeof p.count === 'undefined') + p.count = 1; + + let getValue = p => (typeof p.value !== 'undefined') ? p.value : p.initialValue; + let serialize = v => v.constructor.name + '=' + v.toString(); + + let parse = function (p) { + if (p.count == 1) + return [CSSStyleValue.parse(p.name, getValue(p))]; + return CSSStyleValue.parseAll(p.name, getValue(p)); + }; + + // Generate expected value. We assume that CSSStyleValue.parse/All + // returns the correct CSSStyleValue subclass and value. + p.expected = parse(p).map(serialize); + } + + // Adding '?debug' to the URL will cause this test to emit + // test results to console.log. + let debugMode = document.location.href.endsWith('?debug'); + let code = [ + `const props = ${JSON.stringify(props)};`, + `const debugLog = ${debugMode ? 'console.log' : 'function(){}'};`, + document.getElementById('code').textContent + ].join('\n'); + + importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, code); + } catch(e) { + document.body.textContent = e; + takeScreenshot(); + } +</script> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/dom/events/event-global-extra.window-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/dom/events/event-global-extra.window-expected.txt index beb08255..0c086b2 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/dom/events/event-global-extra.window-expected.txt +++ b/third_party/WebKit/LayoutTests/external/wpt/dom/events/event-global-extra.window-expected.txt
@@ -1,9 +1,9 @@ This is a testharness.js-based test. -PASS window.event for constructors from another global: EventTarget -PASS window.event for constructors from another global: XMLHttpRequest +FAIL window.event for constructors from another global: EventTarget assert_equals: expected (undefined) undefined but got (object) object "[object Event]" +FAIL window.event for constructors from another global: XMLHttpRequest assert_equals: expected (undefined) undefined but got (object) object "[object Event]" PASS window.event and element from another document -PASS window.event and moving an element post-dispatch +FAIL window.event and moving an element post-dispatch assert_equals: expected (object) object "[object Event]" but got (undefined) undefined FAIL window.event should not be affected by nodes moving post-dispatch assert_equals: expected (undefined) undefined but got (object) object "[object Event]" -PASS Listener from a different global +FAIL Listener from a different global assert_equals: expected (object) object "[object Event]" but got (undefined) undefined Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/cors/cors-preflight-not-cors-safelisted.any-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/cors/cors-preflight-not-cors-safelisted.any-expected.txt deleted file mode 100644 index 56141bc..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/cors/cors-preflight-not-cors-safelisted.any-expected.txt +++ /dev/null
@@ -1,13 +0,0 @@ -This is a testharness.js-based test. -PASS Loading data… -FAIL Need CORS-preflight for accept/" header assert_equals: Preflight request has been made expected "1" but got "0" -FAIL Need CORS-preflight for accept/012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 header assert_equals: Preflight request has been made expected "1" but got "0" -FAIL Need CORS-preflight for accept-language/ header assert_equals: Preflight request has been made expected "1" but got "0" -FAIL Need CORS-preflight for accept-language/@ header assert_equals: Preflight request has been made expected "1" but got "0" -FAIL Need CORS-preflight for content-language/ header assert_equals: Preflight request has been made expected "1" but got "0" -FAIL Need CORS-preflight for content-language/@ header assert_equals: Preflight request has been made expected "1" but got "0" -PASS Need CORS-preflight for content-type/text/html header -FAIL Need CORS-preflight for content-type/text/plain; long=0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901 header assert_equals: Preflight request has been made expected "1" but got "0" -PASS Need CORS-preflight for test/hi header -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/cors/cors-preflight-not-cors-safelisted.any.worker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/cors/cors-preflight-not-cors-safelisted.any.worker-expected.txt deleted file mode 100644 index 56141bc..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/cors/cors-preflight-not-cors-safelisted.any.worker-expected.txt +++ /dev/null
@@ -1,13 +0,0 @@ -This is a testharness.js-based test. -PASS Loading data… -FAIL Need CORS-preflight for accept/" header assert_equals: Preflight request has been made expected "1" but got "0" -FAIL Need CORS-preflight for accept/012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 header assert_equals: Preflight request has been made expected "1" but got "0" -FAIL Need CORS-preflight for accept-language/ header assert_equals: Preflight request has been made expected "1" but got "0" -FAIL Need CORS-preflight for accept-language/@ header assert_equals: Preflight request has been made expected "1" but got "0" -FAIL Need CORS-preflight for content-language/ header assert_equals: Preflight request has been made expected "1" but got "0" -FAIL Need CORS-preflight for content-language/@ header assert_equals: Preflight request has been made expected "1" but got "0" -PASS Need CORS-preflight for content-type/text/html header -FAIL Need CORS-preflight for content-type/text/plain; long=0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901 header assert_equals: Preflight request has been made expected "1" but got "0" -PASS Need CORS-preflight for test/hi header -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/headers/headers-no-cors.window-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/headers/headers-no-cors.window-expected.txt deleted file mode 100644 index 2241e22..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/headers/headers-no-cors.window-expected.txt +++ /dev/null
@@ -1,18 +0,0 @@ -This is a testharness.js-based test. -PASS Loading data… -FAIL "no-cors" Headers object cannot have accept/" as header assert_false: expected false got true -FAIL "no-cors" Headers object cannot have accept/012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 as header assert_false: expected false got true -FAIL "no-cors" Headers object cannot have accept-language/ as header assert_false: expected false got true -FAIL "no-cors" Headers object cannot have accept-language/@ as header assert_false: expected false got true -FAIL "no-cors" Headers object cannot have content-language/ as header assert_false: expected false got true -FAIL "no-cors" Headers object cannot have content-language/@ as header assert_false: expected false got true -PASS "no-cors" Headers object cannot have content-type/text/html as header -FAIL "no-cors" Headers object cannot have content-type/text/plain; long=0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901 as header assert_false: expected false got true -PASS "no-cors" Headers object cannot have test/hi as header -FAIL "no-cors" Headers object cannot have dpr/2 as header assert_false: expected false got true -PASS "no-cors" Headers object cannot have downlink/1 as header -FAIL "no-cors" Headers object cannot have save-data/on as header assert_false: expected false got true -FAIL "no-cors" Headers object cannot have viewport-width/100 as header assert_false: expected false got true -FAIL "no-cors" Headers object cannot have width/100 as header assert_false: expected false got true -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/rendering/the-details-element/details-display-property-is-ignored-ref.html b/third_party/WebKit/LayoutTests/external/wpt/html/rendering/the-details-element/details-display-property-is-ignored-ref.html new file mode 100644 index 0000000..6ebed60 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/html/rendering/the-details-element/details-display-property-is-ignored-ref.html
@@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<link rel="author" title="David Grogan" href="dgrogan@chromium.org"> +From <a href="https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements">html.spec.whatwg.org</a>: + +<blockquote> +The details element is expected to render as a block box. The element's shadow +tree is expected to take the element's first summary element child, if any, and +place it in a first block box container, and then take the element's remaining +descendants, if any, and place them in a second block box container. +</blockquote> + +<details display:flex> should be ignored. Otherwise details would render as +something other than a block box. +<hr> + +<details open> + <summary>This is the summary.</summary> + <div>thing 1</div> + thing 2 +</details>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/rendering/the-details-element/details-display-property-is-ignored.html b/third_party/WebKit/LayoutTests/external/wpt/html/rendering/the-details-element/details-display-property-is-ignored.html new file mode 100644 index 0000000..445b4e4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/html/rendering/the-details-element/details-display-property-is-ignored.html
@@ -0,0 +1,26 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<link rel="author" title="David Grogan" href="dgrogan@chromium.org"> +<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements"> +<link rel="match" href="details-display-property-is-ignored-ref.html"> +<link rel="bookmark" href="https://bugs.chromium.org/p/chromium/issues/detail?id=635282" /> +<meta name="assert" content="The display property is ignored on details elements and is instead always rendered as a block box." /> + +From <a href="https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements">html.spec.whatwg.org</a>: + +<blockquote> +The details element is expected to render as a block box. The element's shadow +tree is expected to take the element's first summary element child, if any, and +place it in a first block box container, and then take the element's remaining +descendants, if any, and place them in a second block box container. +</blockquote> + +<details display:flex> should be ignored. Otherwise details would render as +something other than a block box. +<hr> + +<details open style="display:flex;"> + <summary>This is the summary.</summary> + <div>thing 1</div> + thing 2 +</details>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/execution-timing/083-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/execution-timing/083-expected.txt index 7d8abb8..5f6926c 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/execution-timing/083-expected.txt +++ b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/execution-timing/083-expected.txt
@@ -1,4 +1,5 @@ This is a testharness.js-based test. +Harness Error. harness_status.status = 1 , harness_status.message = Uncaught TypeError: Cannot read property 'log' of null FAIL scheduler: event listener defined by script in a document in history assert_array_equals: lengths differ, expected 5 got 6 Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/execution-timing/084-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/execution-timing/084-expected.txt new file mode 100644 index 0000000..bfce201 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/execution-timing/084-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL scheduler: event listener defined by script in a removed IFRAME Uncaught TypeError: Cannot read property 'log' of null +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/reload.window.js b/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/reload.window.js index d6ff9dc7a..279020f 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/reload.window.js +++ b/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/reload.window.js
@@ -12,16 +12,16 @@ // that restriction. // // In any case, this test as the caller of `document.open()` would be used both -// as the test file and as part of the test file. The `if (!opener)` condition -// controls what role this file plays. +// as the test file and as part of the test file. The `if (window.name !== +// "opened-dummy-window")` condition controls what role this file plays. -if (!opener) { +if (window.name !== "opened-dummy-window") { async_test(t => { const testURL = document.URL; const dummyURL = new URL("resources/dummy.html", document.URL).href; // 1. Open an auxiliary window. - const win = window.open("resources/dummy.html"); + const win = window.open("resources/dummy.html", "opened-dummy-window"); t.add_cleanup(() => { win.close(); }); win.addEventListener("load", t.step_func(() => {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-1-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-1-expected.txt new file mode 100644 index 0000000..e65e176 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-1-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL The error event from an event listener should fire on that listener's global assert_true: expected true got false +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-2-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-2-expected.txt new file mode 100644 index 0000000..e65e176 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-2-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL The error event from an event listener should fire on that listener's global assert_true: expected true got false +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/fast/dom/margin-height-guarded-crash-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/margin-height-guarded-crash-expected.txt index 5bb876c..6401436 100644 --- a/third_party/WebKit/LayoutTests/fast/dom/margin-height-guarded-crash-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/dom/margin-height-guarded-crash-expected.txt
@@ -1,4 +1,4 @@ -CONSOLE ERROR: line 24: Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'. +CONSOLE ERROR: line 22: Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'. This is a testharness.js-based test. PASS try-trigger-crash Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/fast/dom/margin-height-guarded-crash.html b/third_party/WebKit/LayoutTests/fast/dom/margin-height-guarded-crash.html index 706e309..b0546e4a 100644 --- a/third_party/WebKit/LayoutTests/fast/dom/margin-height-guarded-crash.html +++ b/third_party/WebKit/LayoutTests/fast/dom/margin-height-guarded-crash.html
@@ -17,8 +17,6 @@ <iframe id=html_iframe></iframe> <div id=html_div></div> <script> -setup({ allow_uncaught_exception: true }); - function reactToWidthChange() { // null the body html_div.appendChild(html_iframe.contentDocument.body);
diff --git a/third_party/WebKit/LayoutTests/fast/dom/ready-state-change-crash-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/ready-state-change-crash-expected.txt index c559db5..1042c767 100644 --- a/third_party/WebKit/LayoutTests/fast/dom/ready-state-change-crash-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/dom/ready-state-change-crash-expected.txt
@@ -1,3 +1,2 @@ -CONSOLE ERROR: line 27: Uncaught NotFoundError: Failed to execute 'appendChild' on 'Node': The node to be removed is no longer a child of this node. Perhaps it was moved in response to a mutation? Test passes if it does not crash.
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-tap-frame-removed-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-tap-frame-removed-expected.txt index d1fe6f4..aa4c6432 100644 --- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-tap-frame-removed-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-tap-frame-removed-expected.txt
@@ -13,12 +13,19 @@ Test case: Remove during mousedown Sending GestureTap -FAIL document.getElementById('target') should be null. Was [object HTMLIFrameElement]. +Received mousemove in child frame +Received mousedown in child frame +Removing iframe +PASS document.getElementById('target') is null iframe loaded Test case: Remove during mouseup Sending GestureTap -FAIL document.getElementById('target') should be null. Was [object HTMLIFrameElement]. +Received mousemove in child frame +Received mousedown in child frame +Received mouseup in child frame +Removing iframe +PASS document.getElementById('target') is null iframe loaded PASS successfullyParsed is true
diff --git a/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_worker_termination_while_compiling.html b/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_worker_termination_while_compiling.html new file mode 100644 index 0000000..8dd42d6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_worker_termination_while_compiling.html
@@ -0,0 +1,60 @@ +<!DOCTYPE html> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> +<script src="resources/wasm-constants.js"></script> +<script src="resources/wasm-module-builder.js"></script> +<script> + +const test = async_test("TestWasmWorkerTerminationWhileCompiling"); + +const kNumWorkers = 8; +const kNumFunctions = 5000; + +// This function is executed by each worker. +function workerFunction() { + onmessage = function(event) { + // Start asynchronous compilation, then notify the main thread about that. + WebAssembly.compile(event.data) + .catch(e => postMessage("compilation error: " + e)); + postMessage("compiling"); + }; +} + +// Build a wasm module with a number of function, such that the workers need +// some time compiling it. +const builder = new WasmModuleBuilder(); +for (var i = 0; i < kNumFunctions; ++i) { + builder.addFunction('func' + i, kSig_v_v).addBody([kExprCallFunction, 0]); +} +const module_bytes = builder.toBuffer(); + +const blobURL = + URL.createObjectURL(new Blob(['(' + workerFunction.toString() + ')()'])); + +// Counter to wait for all workers to start compilation. +var outstanding_worker_events = kNumWorkers; +const workers = []; + +function workerEvent(event) { + // If the event data is not "compiling", this is an error in the worker. + assert_equals(event.data, "compiling"); + // Decrement counter of workers that did not start compiling yet. + // Do nothing if there are still outstanding workers. + assert_greater_than(outstanding_worker_events, 0); + if (--outstanding_worker_events > 0) return; + // All workers started compiling! Now terminate them all. This should not + // crash. + for (let i = 0; i < kNumWorkers; ++i) { + workers[i].terminate(); + } + test.done(); +} + +for (let i = 0; i < kNumWorkers; ++i) { + const worker = new Worker(blobURL); + worker.onmessage = test.step_func(workerEvent); + worker.postMessage(module_bytes); + workers.push(worker); +} + +</script>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-merged-nodes-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-merged-nodes-expected.txt index a9c29cd..9276a699 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-merged-nodes-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-merged-nodes-expected.txt
@@ -2,5 +2,5 @@ Took heap snapshot Parsed snapshot SUCCESS: found leaking -SUCCESS: retaining path = [Detached V8EventListener, Detached EventListener, Detached InternalNode, Detached InternalNode, Detached HTMLDivElement, Window / file://, ] +SUCCESS: retaining path = [Detached EventListener, Detached InternalNode, Detached InternalNode, Detached HTMLDivElement, Window / file://, ]
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-event-listener-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-event-listener-expected.txt index e53326f..10c73030 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-event-listener-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-event-listener-expected.txt
@@ -2,5 +2,5 @@ Took heap snapshot Parsed snapshot SUCCESS: found myEventListener -SUCCESS: retaining path = [V8EventListener, EventListener, InternalNode, InternalNode, HTMLBodyElement, HTMLHtmlElement, HTMLDocument, Window / file://, ] +SUCCESS: retaining path = [EventListener, InternalNode, InternalNode, HTMLBodyElement, HTMLHtmlElement, HTMLDocument, Window / file://, ]
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-multiple-retainers-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-multiple-retainers-expected.txt index 473e2fc..2c57bee 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-multiple-retainers-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-multiple-retainers-expected.txt
@@ -2,9 +2,7 @@ Took heap snapshot Parsed snapshot SUCCESS: found leaking -SUCCESS: immediate retainer is V8EventListener. SUCCESS: immediate retainer is EventListener. -SUCCESS: found single retaining path for v8EventListener. SUCCESS: found multiple retaining paths. SUCCESS: path1 = [InternalNode, HTMLBodyElement, HTMLHtmlElement, HTMLDocument, Window / file://, ] SUCCESS: path2 = [InternalNode, HTMLDivElement, HTMLBodyElement, HTMLHtmlElement, HTMLDocument, Window / file://, ]
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-multiple-retainers.js b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-multiple-retainers.js index 94f3d82..a2d852e 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-multiple-retainers.js +++ b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-multiple-retainers.js
@@ -31,14 +31,7 @@ else return testRunner.fail('cannot find the leaking node'); - var v8EventListener = helper.firstRetainingPath(node)[0]; - var eventListener = helper.firstRetainingPath(node)[1]; - - if (v8EventListener.name() == 'V8EventListener') { - testRunner.log('SUCCESS: immediate retainer is V8EventListener.'); - } else { - return testRunner.fail('cannot find the V8EventListener.'); - } + var eventListener = helper.firstRetainingPath(node)[0]; if (eventListener.name() == 'EventListener') { testRunner.log('SUCCESS: immediate retainer is EventListener.'); @@ -46,12 +39,6 @@ return testRunner.fail('cannot find the EventListener.'); } - if (v8EventListener.retainersCount() === 1) { - testRunner.log('SUCCESS: found single retaining path for v8EventListener.'); - } else { - return testRunner.fail('cannot find single retaining path for v8EventListener.'); - } - var retainingPaths = []; for (var iter = eventListener.retainers(); iter.hasNext(); iter.next()) { var path = helper.firstRetainingPath(iter.retainer.node());
diff --git a/third_party/WebKit/LayoutTests/invisible_dom/invisible-attribute.html b/third_party/WebKit/LayoutTests/invisible_dom/invisible-attribute.html index ab7000d..d41ea29d 100644 --- a/third_party/WebKit/LayoutTests/invisible_dom/invisible-attribute.html +++ b/third_party/WebKit/LayoutTests/invisible_dom/invisible-attribute.html
@@ -50,15 +50,24 @@ test(() => { setUp(); - // TODO(rakina): modify this test when we have levels ("invisible"/"static") outerDiv.setAttribute("invisible", "x"); assert_equals(outerDiv.getAttribute("invisible"), "x"); + assert_equals(outerDiv.invisible, "invisible"); assert_true(outerDiv.hasAttribute("invisible")); outerDiv.setAttribute("invisible", ""); assert_equals(outerDiv.getAttribute("invisible"), ""); + assert_equals(outerDiv.invisible, "invisible"); assert_true(outerDiv.hasAttribute("invisible")); -}, "Setting/removing invisible attribute preserves original value."); + + outerDiv.removeAttribute("invisible"); + assert_equals(outerDiv.invisible, ""); + + outerDiv.setAttribute("invisible", "static"); + assert_equals(outerDiv.getAttribute("invisible"), "static"); + assert_equals(outerDiv.invisible, "static"); + assert_true(outerDiv.hasAttribute("invisible")); +}, "Setting/removing invisible attribute preserves original value, but property returns only invisible/static"); test(() => { setUp();
diff --git a/third_party/WebKit/LayoutTests/invisible_dom/invisible-static.html b/third_party/WebKit/LayoutTests/invisible_dom/invisible-static.html new file mode 100644 index 0000000..5570405 --- /dev/null +++ b/third_party/WebKit/LayoutTests/invisible_dom/invisible-static.html
@@ -0,0 +1,79 @@ +<!DOCTYPE html> +<title>Invisible-static level</title> +<body> +<script src="../resources/testharness.js"></script> +<script src="../resources/testharnessreport.js"></script> + +<div> + <div id="normal"></div> + <div id="basicInvisible" invisible></div> + <div id="staticInvisible" invisible="static"></div> +</div> + +<script> +'use strict'; + +class TestElement extends HTMLElement { + constructor() { + super(); + this.innerHTML = "upgraded"; + } +} + +customElements.define("test-element", TestElement); + +const testElementString = "<test-element></test-element>"; +const testElementUpgradedString = "<test-element>upgraded</test-element>"; + +function setUp() { + normal.innerHTML = basicInvisible.innerHTML = staticInvisible.innerHTML = ""; + basicInvisible.invisible = "invisible"; + staticInvisible.invisible = "static"; +} + +test(() => { + setUp(); + normal.innerHTML = testElementString; + basicInvisible.innerHTML = testElementString; + staticInvisible.innerHTML = testElementString; + assert_equals(normal.innerHTML, testElementUpgradedString); + assert_equals(basicInvisible.innerHTML, testElementUpgradedString); + assert_equals(staticInvisible.innerHTML, testElementString); +}, "Custom elements inside invisible-static subtree is not upgraded."); + +test(() => { + setUp(); + staticInvisible.removeAttribute("invisible"); + staticInvisible.innerHTML = testElementString; + assert_equals(staticInvisible.innerHTML, testElementUpgradedString); +}, "Previously-static subtree should not block custom element upgrade"); + +test(() => { + setUp(); + staticInvisible.innerHTML = testElementString; + staticInvisible.invisible = "invisible"; + assert_equals(staticInvisible.innerHTML, testElementUpgradedString); +}, "Making an element not invisible='static' upgrades the custom elements inside"); + +test(() => { + setUp(); + staticInvisible.innerHTML = testElementString; + assert_equals(staticInvisible.innerHTML, testElementString, "Normally not upgraded"); + customElements.upgrade(staticInvisible); + assert_equals(staticInvisible.innerHTML, testElementUpgradedString, "After forcing got upgraded"); +}, "Upgrade by customElements.upgrade is not deferred"); + +test(() => { + setUp(); + staticInvisible.innerHTML = "<another-element></another-element>"; + customElements.define("another-element", class extends HTMLElement { + constructor() { + super(); + this.innerHTML = "upgraded"; + } + }); + assert_equals(staticInvisible.innerHTML, "<another-element></another-element>"); + staticInvisible.invisible = "invisible"; + assert_equals(staticInvisible.innerHTML, "<another-element>upgraded</another-element>"); +}, "Upgrade after defined is deferred"); +</script>
diff --git a/third_party/WebKit/LayoutTests/paint/background/background-clip-text-descendants-expected.html b/third_party/WebKit/LayoutTests/paint/background/background-clip-text-descendants-expected.html deleted file mode 100644 index deee47e..0000000 --- a/third_party/WebKit/LayoutTests/paint/background/background-clip-text-descendants-expected.html +++ /dev/null
@@ -1,25 +0,0 @@ -<!DOCTYPE html> -<style> -body { - font-size: 40px; -} -.transformed { - transform: translateX(0); -} -.clip-text { - background: blue; - -webkit-background-clip: text; - color: rgba(255,0,0,0.5); - clear: both; -} -</style> -Passes if all texts are purple. -<div class="clip-text">Block</div> -<div class="clip-text">Block transformed</div> -<div><div class="clip-text" style="float:left">Float</div><br></div> -<div><div class="clip-text" style="float:left">Float transformed</div><br></div> -<table><tr><td class="clip-text">Table</td></tr></table> -<table><tr><td class="clip-text">Table transformed</td></tr></table> -Except these (inline-block is not supported for text background clip yet): -<div style="color: rgba(255,0,0,0.5"><div style="display: inline-block">Inline block</div></div> -<div style="color: rgba(255,0,0,0.5"><div style="display: inline-block">Inline block transformed</div></div>
diff --git a/third_party/WebKit/LayoutTests/paint/background/background-clip-text-descendants.html b/third_party/WebKit/LayoutTests/paint/background/background-clip-text-descendants.html deleted file mode 100644 index 4e06c95d..0000000 --- a/third_party/WebKit/LayoutTests/paint/background/background-clip-text-descendants.html +++ /dev/null
@@ -1,24 +0,0 @@ -<!DOCTYPE html> -<style> -body { - font-size: 40px; -} -.transformed { - transform: translateX(0); -} -.clip-text { - background: blue; - -webkit-background-clip: text; - color: rgba(255,0,0,0.5); -} -</style> -Passes if all texts are purple. -<div class="clip-text"><div>Block</div></div> -<div class="clip-text transformed"><div>Block transformed</div></div> -<div class="clip-text"><div style="float:left">Float</div><br></div> -<div class="clip-text transformed"><div style="float:left">Float transformed</div><br></div> -<table class="clip-text"><tr><td>Table</td></tr></table> -<table class="clip-text transformed"><tr><td>Table transformed</td></tr></table> -Except these (inline-block is not supported for text background clip yet): -<div class="clip-text"><div style="display: inline-block">Inline block</div></div> -<div class="clip-text transformed"><div style="display: inline-block">Inline block transformed</div></div>
diff --git a/third_party/WebKit/LayoutTests/paint/background/background-clip-text-inline-expected.html b/third_party/WebKit/LayoutTests/paint/background/background-clip-text-inline-expected.html new file mode 100644 index 0000000..34c281e5 --- /dev/null +++ b/third_party/WebKit/LayoutTests/paint/background/background-clip-text-inline-expected.html
@@ -0,0 +1,15 @@ +<!DOCTYPE html> +<style> +body { + font-size: 40px; +} +.text-clip { + background: blue; + -webkit-background-clip: text; + color: rgba(255,0,0,0.5); +} +</style> +Passes if all texts below are purple. +<br> +<div class="text-clip"> Text1 Text2 </div> +<div class="text-clip"> Text3 Text4 </div>
diff --git a/third_party/WebKit/LayoutTests/paint/background/background-clip-text-inline.html b/third_party/WebKit/LayoutTests/paint/background/background-clip-text-inline.html new file mode 100644 index 0000000..175a353 --- /dev/null +++ b/third_party/WebKit/LayoutTests/paint/background/background-clip-text-inline.html
@@ -0,0 +1,21 @@ +<!DOCTYPE html> +<style> +body { + font-size: 40px; +} +.text-clip { + background: blue; + -webkit-background-clip: text; + color: rgba(255,0,0,0.5); +} +.relative { + position: relative; + left: 10px; +} +</style> +Passes if all texts below are purple. +<br> +<!-- The s are to avoid glyph overflows. --> +<span class="text-clip"> Text1 </span><span class="text-clip"> Text2 </span> +<br> +<span class="text-clip"> Text3 </span><span class="text-clip"> Text4 </span>
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/text/text-selection-align-01-b-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/text/text-selection-align-01-b-expected.png index 768a297..d589f5b1 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/text/text-selection-align-01-b-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/text/text-selection-align-01-b-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/text/text-selection-align-02-b-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/text/text-selection-align-02-b-expected.png index 72edaa1..1b8402a 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/text/text-selection-align-02-b-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/text/text-selection-align-02-b-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/text/text-selection-fonts-02-t-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/text/text-selection-fonts-02-t-expected.png index 9f4e21ca..68c79078 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/text/text-selection-fonts-02-t-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/svg/text/text-selection-fonts-02-t-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/shadow-dom/imperative-apis/custom-detail-summary.js b/third_party/WebKit/LayoutTests/shadow-dom/imperative-apis/custom-detail-summary.js new file mode 100644 index 0000000..70d2af03 --- /dev/null +++ b/third_party/WebKit/LayoutTests/shadow-dom/imperative-apis/custom-detail-summary.js
@@ -0,0 +1,34 @@ +// We reuse the "backend" of the imperative Shadow DOM Distribution API for a new custom element, <my-detail>/<my-summary>. +//TODO(crbug.com/869308):Emulate other <summary><details> features + +class MySummaryElement extends HTMLElement { + constructor() { + super(); + } +} +customElements.define("my-summary", MySummaryElement); + +customElements.define("my-detail", class extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: "open", slotting: "manual" }); + } + connectedCallback() { + const target = this; + if (!target.shadowRoot.querySelector(':scope > slot')) { + const slot1 = document.createElement("slot"); + const slot2 = document.createElement("slot"); + const shadowRoot = target.shadowRoot; + shadowRoot.appendChild(slot1); + shadowRoot.appendChild(slot2); + slot1.style.display = "block"; + slot1.style.backgroundColor = "red"; + const observer = new MutationObserver(function(mutations) { + //Get the first <my-summary> element from <my-detail>'s direct children + slot1.assign([target.querySelector(':scope > my-summary')]); + slot2.assign(target.childNodes); + }); + observer.observe(this, {childList: true}); + } + } +});
diff --git a/third_party/WebKit/LayoutTests/shadow-dom/imperative-apis/test-custom-detail-summary-expected.html b/third_party/WebKit/LayoutTests/shadow-dom/imperative-apis/test-custom-detail-summary-expected.html new file mode 100644 index 0000000..83138da --- /dev/null +++ b/third_party/WebKit/LayoutTests/shadow-dom/imperative-apis/test-custom-detail-summary-expected.html
@@ -0,0 +1,7 @@ +<html> +<body> + <p style="display: block; background-color: red;">added-summary</p> + <p>another test</p> + <p>summary first-summary</p> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/shadow-dom/imperative-apis/test-custom-detail-summary.html b/third_party/WebKit/LayoutTests/shadow-dom/imperative-apis/test-custom-detail-summary.html new file mode 100644 index 0000000..47a34944 --- /dev/null +++ b/third_party/WebKit/LayoutTests/shadow-dom/imperative-apis/test-custom-detail-summary.html
@@ -0,0 +1,15 @@ +<!DOCTYPE html> +<script src="custom-detail-summary.js"></script> + +<my-detail id="my-detail"> + <p>another test</p> + <my-summary id="my-summary">summary</my-summary> + <my-summary>first-summary</my-summary> +</my-detail> +<my-summary id="my-summary1">added-summary</my-summary> +<script> +const host = document.querySelector("#my-detail"); +const sum = document.querySelector("#my-summary"); +const sum1 = document.querySelector("#my-summary1"); +host.insertBefore(sum1, sum); +</script>
diff --git a/third_party/WebKit/LayoutTests/svg/dom/svganimatedinteger-initial-values.html b/third_party/WebKit/LayoutTests/svg/dom/svganimatedinteger-initial-values.html new file mode 100644 index 0000000..b9d99071 --- /dev/null +++ b/third_party/WebKit/LayoutTests/svg/dom/svganimatedinteger-initial-values.html
@@ -0,0 +1,20 @@ +<!DOCTYPE html> +<title>SVGAnimatedInteger, initial values</title> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script src="resources/initial-value-helper.js"></script> +<script> +const propertyToContentAttribute = { + orderX: 'order', + orderY: 'order', +}; + +assert_initial_values([ + { interface: 'SVGFEConvolveMatrixElement', + attributes: [ 'orderX', 'orderY', 'targetX', 'targetY' ], + orderX: { initial: 3 }, orderY: { initial: 3 } }, + { interface: 'SVGFETurbulenceElement', attributes: [ 'numOctaves' ], + numOctaves: { initial: 1 } }, +], { initial: 0, valid: '42', + mapProperty: propertyToContentAttribute }); +</script>
diff --git a/third_party/WebKit/LayoutTests/svg/parser/whitespace-integer-expected.txt b/third_party/WebKit/LayoutTests/svg/parser/whitespace-integer-expected.txt index 82d9a60..5bb52e7 100644 --- a/third_party/WebKit/LayoutTests/svg/parser/whitespace-integer-expected.txt +++ b/third_party/WebKit/LayoutTests/svg/parser/whitespace-integer-expected.txt
@@ -444,125 +444,125 @@ CONSOLE ERROR: line 63: Error: <feTurbulence> attribute numOctaves: Expected integer, " 1241245)90". This is a testharness.js-based test. PASS Test <integer> valid value: -47 -FAIL Test <integer> valid value: -47em assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47ex assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47px assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47in assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47cm assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47mm assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47pt assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47pc assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47% assert_equals: expected -47 but got 0 +FAIL Test <integer> valid value: -47em assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47ex assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47px assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47in assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47cm assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47mm assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47pt assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47pc assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47% assert_equals: expected -47 but got 1 PASS Test <integer> valid value: 0 -PASS Test <integer> valid value: 0em -PASS Test <integer> valid value: 0ex -PASS Test <integer> valid value: 0px -PASS Test <integer> valid value: 0in -PASS Test <integer> valid value: 0cm -PASS Test <integer> valid value: 0mm -PASS Test <integer> valid value: 0pt -PASS Test <integer> valid value: 0pc -PASS Test <integer> valid value: 0% +FAIL Test <integer> valid value: 0em assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0ex assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0px assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0in assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0cm assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0mm assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0pt assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0pc assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0% assert_equals: expected 0 but got 1 PASS Test <integer> valid value: +32 -FAIL Test <integer> valid value: +32em assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32ex assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32px assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32in assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32cm assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32mm assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32pt assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32pc assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32% assert_equals: expected 32 but got 0 +FAIL Test <integer> valid value: +32em assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32ex assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32px assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32in assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32cm assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32mm assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32pt assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32pc assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32% assert_equals: expected 32 but got 1 PASS Test <integer> valid value: 1241245 -FAIL Test <integer> valid value: 1241245em assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245ex assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245px assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245in assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245cm assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245mm assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245pt assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245pc assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245% assert_equals: expected 1241245 but got 0 +FAIL Test <integer> valid value: 1241245em assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245ex assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245px assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245in assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245cm assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245mm assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245pt assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245pc assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245% assert_equals: expected 1241245 but got 1 PASS Test <integer> valid value: -47 -FAIL Test <integer> valid value: -47em assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47ex assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47px assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47in assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47cm assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47mm assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47pt assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47pc assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47% assert_equals: expected -47 but got 0 +FAIL Test <integer> valid value: -47em assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47ex assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47px assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47in assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47cm assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47mm assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47pt assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47pc assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47% assert_equals: expected -47 but got 1 PASS Test <integer> valid value: 0 -PASS Test <integer> valid value: 0em -PASS Test <integer> valid value: 0ex -PASS Test <integer> valid value: 0px -PASS Test <integer> valid value: 0in -PASS Test <integer> valid value: 0cm -PASS Test <integer> valid value: 0mm -PASS Test <integer> valid value: 0pt -PASS Test <integer> valid value: 0pc -PASS Test <integer> valid value: 0% +FAIL Test <integer> valid value: 0em assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0ex assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0px assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0in assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0cm assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0mm assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0pt assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0pc assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0% assert_equals: expected 0 but got 1 PASS Test <integer> valid value: +32 -FAIL Test <integer> valid value: +32em assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32ex assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32px assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32in assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32cm assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32mm assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32pt assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32pc assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32% assert_equals: expected 32 but got 0 +FAIL Test <integer> valid value: +32em assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32ex assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32px assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32in assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32cm assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32mm assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32pt assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32pc assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32% assert_equals: expected 32 but got 1 PASS Test <integer> valid value: 1241245 -FAIL Test <integer> valid value: 1241245em assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245ex assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245px assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245in assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245cm assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245mm assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245pt assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245pc assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245% assert_equals: expected 1241245 but got 0 +FAIL Test <integer> valid value: 1241245em assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245ex assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245px assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245in assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245cm assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245mm assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245pt assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245pc assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245% assert_equals: expected 1241245 but got 1 PASS Test <integer> valid value: -47 -FAIL Test <integer> valid value: -47em assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47ex assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47px assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47in assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47cm assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47mm assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47pt assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47pc assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47% assert_equals: expected -47 but got 0 +FAIL Test <integer> valid value: -47em assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47ex assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47px assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47in assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47cm assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47mm assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47pt assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47pc assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47% assert_equals: expected -47 but got 1 PASS Test <integer> valid value: 0 -PASS Test <integer> valid value: 0em -PASS Test <integer> valid value: 0ex -PASS Test <integer> valid value: 0px -PASS Test <integer> valid value: 0in -PASS Test <integer> valid value: 0cm -PASS Test <integer> valid value: 0mm -PASS Test <integer> valid value: 0pt -PASS Test <integer> valid value: 0pc -PASS Test <integer> valid value: 0% +FAIL Test <integer> valid value: 0em assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0ex assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0px assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0in assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0cm assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0mm assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0pt assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0pc assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0% assert_equals: expected 0 but got 1 PASS Test <integer> valid value: +32 -FAIL Test <integer> valid value: +32em assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32ex assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32px assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32in assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32cm assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32mm assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32pt assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32pc assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32% assert_equals: expected 32 but got 0 +FAIL Test <integer> valid value: +32em assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32ex assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32px assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32in assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32cm assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32mm assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32pt assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32pc assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32% assert_equals: expected 32 but got 1 PASS Test <integer> valid value: 1241245 -FAIL Test <integer> valid value: 1241245em assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245ex assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245px assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245in assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245cm assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245mm assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245pt assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245pc assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245% assert_equals: expected 1241245 but got 0 +FAIL Test <integer> valid value: 1241245em assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245ex assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245px assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245in assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245cm assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245mm assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245pt assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245pc assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245% assert_equals: expected 1241245 but got 1 PASS Test <integer> trailing garbage, value: -47a PASS Test <integer> trailing garbage, value: 0a PASS Test <integer> trailing garbage, value: +32a @@ -580,125 +580,125 @@ PASS Test <integer> trailing garbage, value: +32)90 PASS Test <integer> trailing garbage, value: 1241245)90 PASS Test <integer> valid value: -47 -FAIL Test <integer> valid value: -47em assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47ex assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47px assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47in assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47cm assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47mm assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47pt assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47pc assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47% assert_equals: expected -47 but got 0 +FAIL Test <integer> valid value: -47em assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47ex assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47px assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47in assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47cm assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47mm assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47pt assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47pc assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47% assert_equals: expected -47 but got 1 PASS Test <integer> valid value: 0 -PASS Test <integer> valid value: 0em -PASS Test <integer> valid value: 0ex -PASS Test <integer> valid value: 0px -PASS Test <integer> valid value: 0in -PASS Test <integer> valid value: 0cm -PASS Test <integer> valid value: 0mm -PASS Test <integer> valid value: 0pt -PASS Test <integer> valid value: 0pc -PASS Test <integer> valid value: 0% +FAIL Test <integer> valid value: 0em assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0ex assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0px assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0in assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0cm assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0mm assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0pt assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0pc assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0% assert_equals: expected 0 but got 1 PASS Test <integer> valid value: +32 -FAIL Test <integer> valid value: +32em assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32ex assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32px assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32in assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32cm assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32mm assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32pt assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32pc assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32% assert_equals: expected 32 but got 0 +FAIL Test <integer> valid value: +32em assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32ex assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32px assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32in assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32cm assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32mm assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32pt assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32pc assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32% assert_equals: expected 32 but got 1 PASS Test <integer> valid value: 1241245 -FAIL Test <integer> valid value: 1241245em assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245ex assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245px assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245in assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245cm assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245mm assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245pt assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245pc assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245% assert_equals: expected 1241245 but got 0 +FAIL Test <integer> valid value: 1241245em assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245ex assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245px assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245in assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245cm assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245mm assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245pt assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245pc assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245% assert_equals: expected 1241245 but got 1 PASS Test <integer> valid value: -47 -FAIL Test <integer> valid value: -47em assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47ex assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47px assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47in assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47cm assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47mm assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47pt assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47pc assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47% assert_equals: expected -47 but got 0 +FAIL Test <integer> valid value: -47em assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47ex assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47px assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47in assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47cm assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47mm assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47pt assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47pc assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47% assert_equals: expected -47 but got 1 PASS Test <integer> valid value: 0 -PASS Test <integer> valid value: 0em -PASS Test <integer> valid value: 0ex -PASS Test <integer> valid value: 0px -PASS Test <integer> valid value: 0in -PASS Test <integer> valid value: 0cm -PASS Test <integer> valid value: 0mm -PASS Test <integer> valid value: 0pt -PASS Test <integer> valid value: 0pc -PASS Test <integer> valid value: 0% +FAIL Test <integer> valid value: 0em assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0ex assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0px assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0in assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0cm assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0mm assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0pt assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0pc assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0% assert_equals: expected 0 but got 1 PASS Test <integer> valid value: +32 -FAIL Test <integer> valid value: +32em assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32ex assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32px assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32in assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32cm assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32mm assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32pt assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32pc assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32% assert_equals: expected 32 but got 0 +FAIL Test <integer> valid value: +32em assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32ex assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32px assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32in assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32cm assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32mm assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32pt assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32pc assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32% assert_equals: expected 32 but got 1 PASS Test <integer> valid value: 1241245 -FAIL Test <integer> valid value: 1241245em assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245ex assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245px assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245in assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245cm assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245mm assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245pt assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245pc assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245% assert_equals: expected 1241245 but got 0 +FAIL Test <integer> valid value: 1241245em assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245ex assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245px assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245in assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245cm assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245mm assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245pt assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245pc assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245% assert_equals: expected 1241245 but got 1 PASS Test <integer> valid value: -47 -FAIL Test <integer> valid value: -47em assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47ex assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47px assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47in assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47cm assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47mm assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47pt assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47pc assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47% assert_equals: expected -47 but got 0 +FAIL Test <integer> valid value: -47em assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47ex assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47px assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47in assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47cm assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47mm assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47pt assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47pc assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47% assert_equals: expected -47 but got 1 PASS Test <integer> valid value: 0 -PASS Test <integer> valid value: 0em -PASS Test <integer> valid value: 0ex -PASS Test <integer> valid value: 0px -PASS Test <integer> valid value: 0in -PASS Test <integer> valid value: 0cm -PASS Test <integer> valid value: 0mm -PASS Test <integer> valid value: 0pt -PASS Test <integer> valid value: 0pc -PASS Test <integer> valid value: 0% +FAIL Test <integer> valid value: 0em assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0ex assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0px assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0in assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0cm assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0mm assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0pt assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0pc assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0% assert_equals: expected 0 but got 1 PASS Test <integer> valid value: +32 -FAIL Test <integer> valid value: +32em assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32ex assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32px assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32in assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32cm assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32mm assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32pt assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32pc assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32% assert_equals: expected 32 but got 0 +FAIL Test <integer> valid value: +32em assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32ex assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32px assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32in assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32cm assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32mm assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32pt assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32pc assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32% assert_equals: expected 32 but got 1 PASS Test <integer> valid value: 1241245 -FAIL Test <integer> valid value: 1241245em assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245ex assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245px assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245in assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245cm assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245mm assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245pt assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245pc assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245% assert_equals: expected 1241245 but got 0 +FAIL Test <integer> valid value: 1241245em assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245ex assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245px assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245in assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245cm assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245mm assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245pt assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245pc assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245% assert_equals: expected 1241245 but got 1 PASS Test <integer> WS invalid value: -47 em PASS Test <integer> WS invalid value: 0 em PASS Test <integer> WS invalid value: +32 em @@ -752,125 +752,125 @@ PASS Test <integer> trailing garbage, value: +32)90 PASS Test <integer> trailing garbage, value: 1241245)90 PASS Test <integer> valid value: -47 -FAIL Test <integer> valid value: -47em assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47ex assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47px assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47in assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47cm assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47mm assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47pt assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47pc assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47% assert_equals: expected -47 but got 0 +FAIL Test <integer> valid value: -47em assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47ex assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47px assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47in assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47cm assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47mm assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47pt assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47pc assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47% assert_equals: expected -47 but got 1 PASS Test <integer> valid value: 0 -PASS Test <integer> valid value: 0em -PASS Test <integer> valid value: 0ex -PASS Test <integer> valid value: 0px -PASS Test <integer> valid value: 0in -PASS Test <integer> valid value: 0cm -PASS Test <integer> valid value: 0mm -PASS Test <integer> valid value: 0pt -PASS Test <integer> valid value: 0pc -PASS Test <integer> valid value: 0% +FAIL Test <integer> valid value: 0em assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0ex assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0px assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0in assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0cm assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0mm assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0pt assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0pc assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0% assert_equals: expected 0 but got 1 PASS Test <integer> valid value: +32 -FAIL Test <integer> valid value: +32em assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32ex assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32px assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32in assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32cm assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32mm assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32pt assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32pc assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32% assert_equals: expected 32 but got 0 +FAIL Test <integer> valid value: +32em assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32ex assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32px assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32in assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32cm assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32mm assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32pt assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32pc assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32% assert_equals: expected 32 but got 1 PASS Test <integer> valid value: 1241245 -FAIL Test <integer> valid value: 1241245em assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245ex assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245px assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245in assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245cm assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245mm assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245pt assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245pc assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245% assert_equals: expected 1241245 but got 0 +FAIL Test <integer> valid value: 1241245em assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245ex assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245px assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245in assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245cm assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245mm assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245pt assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245pc assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245% assert_equals: expected 1241245 but got 1 PASS Test <integer> valid value: -47 -FAIL Test <integer> valid value: -47em assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47ex assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47px assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47in assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47cm assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47mm assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47pt assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47pc assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47% assert_equals: expected -47 but got 0 +FAIL Test <integer> valid value: -47em assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47ex assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47px assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47in assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47cm assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47mm assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47pt assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47pc assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47% assert_equals: expected -47 but got 1 PASS Test <integer> valid value: 0 -PASS Test <integer> valid value: 0em -PASS Test <integer> valid value: 0ex -PASS Test <integer> valid value: 0px -PASS Test <integer> valid value: 0in -PASS Test <integer> valid value: 0cm -PASS Test <integer> valid value: 0mm -PASS Test <integer> valid value: 0pt -PASS Test <integer> valid value: 0pc -PASS Test <integer> valid value: 0% +FAIL Test <integer> valid value: 0em assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0ex assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0px assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0in assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0cm assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0mm assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0pt assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0pc assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0% assert_equals: expected 0 but got 1 PASS Test <integer> valid value: +32 -FAIL Test <integer> valid value: +32em assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32ex assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32px assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32in assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32cm assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32mm assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32pt assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32pc assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32% assert_equals: expected 32 but got 0 +FAIL Test <integer> valid value: +32em assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32ex assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32px assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32in assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32cm assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32mm assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32pt assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32pc assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32% assert_equals: expected 32 but got 1 PASS Test <integer> valid value: 1241245 -FAIL Test <integer> valid value: 1241245em assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245ex assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245px assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245in assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245cm assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245mm assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245pt assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245pc assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245% assert_equals: expected 1241245 but got 0 +FAIL Test <integer> valid value: 1241245em assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245ex assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245px assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245in assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245cm assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245mm assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245pt assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245pc assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245% assert_equals: expected 1241245 but got 1 PASS Test <integer> valid value: -47 -FAIL Test <integer> valid value: -47em assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47ex assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47px assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47in assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47cm assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47mm assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47pt assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47pc assert_equals: expected -47 but got 0 -FAIL Test <integer> valid value: -47% assert_equals: expected -47 but got 0 +FAIL Test <integer> valid value: -47em assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47ex assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47px assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47in assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47cm assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47mm assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47pt assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47pc assert_equals: expected -47 but got 1 +FAIL Test <integer> valid value: -47% assert_equals: expected -47 but got 1 PASS Test <integer> valid value: 0 -PASS Test <integer> valid value: 0em -PASS Test <integer> valid value: 0ex -PASS Test <integer> valid value: 0px -PASS Test <integer> valid value: 0in -PASS Test <integer> valid value: 0cm -PASS Test <integer> valid value: 0mm -PASS Test <integer> valid value: 0pt -PASS Test <integer> valid value: 0pc -PASS Test <integer> valid value: 0% +FAIL Test <integer> valid value: 0em assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0ex assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0px assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0in assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0cm assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0mm assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0pt assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0pc assert_equals: expected 0 but got 1 +FAIL Test <integer> valid value: 0% assert_equals: expected 0 but got 1 PASS Test <integer> valid value: +32 -FAIL Test <integer> valid value: +32em assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32ex assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32px assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32in assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32cm assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32mm assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32pt assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32pc assert_equals: expected 32 but got 0 -FAIL Test <integer> valid value: +32% assert_equals: expected 32 but got 0 +FAIL Test <integer> valid value: +32em assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32ex assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32px assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32in assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32cm assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32mm assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32pt assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32pc assert_equals: expected 32 but got 1 +FAIL Test <integer> valid value: +32% assert_equals: expected 32 but got 1 PASS Test <integer> valid value: 1241245 -FAIL Test <integer> valid value: 1241245em assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245ex assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245px assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245in assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245cm assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245mm assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245pt assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245pc assert_equals: expected 1241245 but got 0 -FAIL Test <integer> valid value: 1241245% assert_equals: expected 1241245 but got 0 +FAIL Test <integer> valid value: 1241245em assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245ex assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245px assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245in assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245cm assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245mm assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245pt assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245pc assert_equals: expected 1241245 but got 1 +FAIL Test <integer> valid value: 1241245% assert_equals: expected 1241245 but got 1 PASS Test <integer> WS invalid value: -47 em PASS Test <integer> WS invalid value: 0 em PASS Test <integer> WS invalid value: +32 em
diff --git a/third_party/WebKit/LayoutTests/svg/parser/whitespace-integer.html b/third_party/WebKit/LayoutTests/svg/parser/whitespace-integer.html index 10b4a30..b503b22 100644 --- a/third_party/WebKit/LayoutTests/svg/parser/whitespace-integer.html +++ b/third_party/WebKit/LayoutTests/svg/parser/whitespace-integer.html
@@ -25,7 +25,7 @@ testType("<integer>", document.getElementsByTagName("feTurbulence")[0], // workaround for broken querySelector on camelcased elements "numOctaves", - 0, // expected default value (FIXME: should be 1) + 1, // expected default value whitespace, [ "-47", "0", "+32", "1241245" ], validunits,
diff --git a/third_party/blink/perf_tests/shadow_dom/custom-detail-summary.js b/third_party/blink/perf_tests/shadow_dom/custom-detail-summary.js new file mode 100644 index 0000000..91a798a3 --- /dev/null +++ b/third_party/blink/perf_tests/shadow_dom/custom-detail-summary.js
@@ -0,0 +1,34 @@ +// We reuse the "backend" of the imperative Shadow DOM Distribution API for a new custom element, <my-detail>/<my-summary>. +//TODO(crbug.com/869308):Emulate other <summary><details> features + +class MySummaryElement extends HTMLElement { + constructor() { + super(); + } +} +customElements.define("my-summary", MySummaryElement); + +customElements.define("my-detail", class extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: "open", slotting: "manual" }); + } + connectedCallback() { + const target = this; + if (!target.shadowRoot.querySelector(':scope > slot')) { + const slot1 = document.createElement("slot"); + const slot2 = document.createElement("slot"); + const shadowRoot = target.shadowRoot; + shadowRoot.appendChild(slot1); + shadowRoot.appendChild(slot2); + slot1.style.display = "block"; + slot1.style.backgroundColor = "red"; + const observer = new MutationObserver(function(mutations) { + //Get the first <my-summary> element from <my-detail>'s direct children + slot1.assign(target.querySelector(':scope > my-summary')); + slot2.assign(target.childNodes); + }); + observer.observe(this, {childList: true}); + } + } +});
diff --git a/third_party/blink/perf_tests/shadow_dom/imperative-api-custom-detail-summary.html b/third_party/blink/perf_tests/shadow_dom/imperative-api-custom-detail-summary.html new file mode 100644 index 0000000..5305cc7 --- /dev/null +++ b/third_party/blink/perf_tests/shadow_dom/imperative-api-custom-detail-summary.html
@@ -0,0 +1,32 @@ +<!DOCTYPE html> +<script src="../resources/runner.js"></script> +<script src="custom-detail-summary.js"></script> +<my-detail id="my-detail"> + <my-summary id="my-summary">summary</my-summary> +</my-detail> +<my-summary id="my-summary1">added-summary1</my-summary> +<my-summary id="my-summary2">added-summary2</my-summary> +<script> + const host = document.querySelector("#my-detail"); + const sum = document.querySelector("#my-summary"); + const sum1 = document.querySelector("#my-summary1"); + const sum2 = document.querySelector("#my-summary2"); + window.onload = function() { + PerfTestRunner.measureTime({ + description: "Measure performance of my-detail element in manual-slotting mode in shadow root when my-summary element is inserted.", + run: function() { + const start = PerfTestRunner.now(); + for (let i = 0; i < 100; i++) { + host.appendChild(sum1); + host.insertBefore(sum2, sum); + PerfTestRunner.forceLayout(); + sum1.remove(); + sum2.remove(); + PerfTestRunner.forceLayout(); + } + + return PerfTestRunner.now() - start; + } + }); + } +</script>
diff --git a/third_party/blink/perf_tests/shadow_dom/imperative-api-detail-summary.html b/third_party/blink/perf_tests/shadow_dom/imperative-api-detail-summary.html new file mode 100644 index 0000000..d712eae --- /dev/null +++ b/third_party/blink/perf_tests/shadow_dom/imperative-api-detail-summary.html
@@ -0,0 +1,31 @@ +<!doctype html> +<script src="../resources/runner.js"></script> +<detail id="detail"> + <summary id="summary">summary</summary> +</detail> +<summary id="summary1">added-summary1</summary> +<summary id="summary2">added-summary2</summary> +<script> + const host = document.querySelector("#detail"); + const sum = document.querySelector("#summary"); + const sum1 = document.querySelector("#summary1"); + const sum2 = document.querySelector("#summary2"); + window.onload = function() { + PerfTestRunner.measureTime({ + description: "Measure performance of built-in detail element when summary element is inserted.", + run: function() { + const start = PerfTestRunner.now(); + for (let i = 0; i < 100; i++) { + host.appendChild(sum1); + host.insertBefore(sum2, sum); + PerfTestRunner.forceLayout(); + sum1.remove(); + sum2.remove(); + PerfTestRunner.forceLayout(); + } + + return PerfTestRunner.now() - start; + } + }); + } +</script>
diff --git a/third_party/blink/public/platform/web_feature.mojom b/third_party/blink/public/platform/web_feature.mojom index 06e6910..e4e34cd 100644 --- a/third_party/blink/public/platform/web_feature.mojom +++ b/third_party/blink/public/platform/web_feature.mojom
@@ -1995,6 +1995,8 @@ kTextDecoderStreamConstructor = 2540, kSignedExchangeInnerResponse = 2541, kPaymentAddressLanguageCode = 2542, + kDocumentDomainBlockedCrossOriginAccess = 2543, + kDocumentDomainEnabledCrossOriginAccess = 2544, // Add new features immediately above this line. Don't change assigned // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/renderer/bindings/bindings.gni b/third_party/blink/renderer/bindings/bindings.gni index 1bcb1cf..b192bff 100644 --- a/third_party/blink/renderer/bindings/bindings.gni +++ b/third_party/blink/renderer/bindings/bindings.gni
@@ -98,8 +98,8 @@ "core/v8/use_counter_callback.h", "core/v8/v0_custom_element_constructor_builder.cc", "core/v8/v0_custom_element_constructor_builder.h", - "core/v8/v8_abstract_event_handler.cc", - "core/v8/v8_abstract_event_handler.h", + "core/v8/v8_abstract_event_listener.cc", + "core/v8/v8_abstract_event_listener.h", "core/v8/v8_binding_for_core.cc", "core/v8/v8_binding_for_core.h", "core/v8/v8_cache_options.h", @@ -112,8 +112,6 @@ "core/v8/v8_embedder_graph_builder.h", "core/v8/v8_error_handler.cc", "core/v8/v8_error_handler.h", - "core/v8/v8_event_listener_impl.cc", - "core/v8/v8_event_listener_impl.h", "core/v8/v8_event_listener_or_event_handler.cc", "core/v8/v8_event_listener_or_event_handler.h", "core/v8/v8_event_listener_helper.cc",
diff --git a/third_party/blink/renderer/bindings/core/v8/binding_security.cc b/third_party/blink/renderer/bindings/core/v8/binding_security.cc index 8acd39b..ddc0dda9 100644 --- a/third_party/blink/renderer/bindings/core/v8/binding_security.cc +++ b/third_party/blink/renderer/bindings/core/v8/binding_security.cc
@@ -65,10 +65,22 @@ const SecurityOrigin* accessing_origin = accessing_window->document()->GetSecurityOrigin(); const LocalDOMWindow* local_target_window = ToLocalDOMWindow(target_window); - if (!accessing_origin->CanAccess( - local_target_window->document()->GetSecurityOrigin())) { - return false; + + SecurityOrigin::AccessResultDomainDetail detail; + bool can_access = accessing_origin->CanAccess( + local_target_window->document()->GetSecurityOrigin(), detail); + if (detail == + SecurityOrigin::AccessResultDomainDetail::kDomainSetByOnlyOneOrigin || + detail == + SecurityOrigin::AccessResultDomainDetail::kDomainMatchNecessary || + detail == SecurityOrigin::AccessResultDomainDetail::kDomainMismatch) { + UseCounter::Count( + accessing_window->GetFrame(), + can_access ? WebFeature::kDocumentDomainEnabledCrossOriginAccess + : WebFeature::kDocumentDomainBlockedCrossOriginAccess); } + if (!can_access) + return false; // Notify the loader's client if the initial document has been accessed. LocalFrame* target_frame = local_target_window->GetFrame();
diff --git a/third_party/blink/renderer/bindings/core/v8/binding_security_test.cc b/third_party/blink/renderer/bindings/core/v8/binding_security_test.cc index d1e2a81f..fdab789 100644 --- a/third_party/blink/renderer/bindings/core/v8/binding_security_test.cc +++ b/third_party/blink/renderer/bindings/core/v8/binding_security_test.cc
@@ -17,56 +17,97 @@ namespace { const char kMainFrame[] = "https://example.com/main.html"; const char kSameOriginTarget[] = "https://example.com/target.html"; +const char kSameOriginDomainTarget[] = "https://sub.example.com/target.html"; const char kCrossOriginTarget[] = "https://not-example.com/target.html"; + +const char kTargetHTML[] = + "<!DOCTYPE html>" + "<script>" + " (window.opener || window.top).postMessage('yay', '*');" + "</script>"; +const char kSameOriginDomainTargetHTML[] = + "<!DOCTYPE html>" + "<script>" + " document.domain = 'example.com';" + " (window.opener || window.top).postMessage('yay', '*');" + "</script>"; } class BindingSecurityCounterTest : public SimTest, public testing::WithParamInterface<const char*> { public: - enum class OriginDisposition { CrossOrigin, SameOrigin }; + enum class OriginDisposition { CrossOrigin, SameOrigin, SameOriginDomain }; BindingSecurityCounterTest() = default; void LoadWindowAndAccessProperty(OriginDisposition which_origin, const String& property) { + const char* target_url; + const char* target_html; + switch (which_origin) { + case OriginDisposition::CrossOrigin: + target_url = kCrossOriginTarget; + target_html = kTargetHTML; + break; + case OriginDisposition::SameOrigin: + target_url = kSameOriginTarget; + target_html = kTargetHTML; + break; + case OriginDisposition::SameOriginDomain: + target_url = kSameOriginDomainTarget; + target_html = kSameOriginDomainTargetHTML; + break; + } + SimRequest main(kMainFrame, "text/html"); - SimRequest target(which_origin == OriginDisposition::CrossOrigin - ? kCrossOriginTarget - : kSameOriginTarget, - "text/html"); + SimRequest target(target_url, "text/html"); const String& document = String::Format( "<!DOCTYPE html>" "<script>" + " %s" " window.addEventListener('message', e => {" " window.other = e.source.%s;" " console.log('yay');" " });" " var w = window.open('%s');" "</script>", - property.Utf8().data(), - which_origin == OriginDisposition::CrossOrigin ? kCrossOriginTarget - : kSameOriginTarget); + which_origin == OriginDisposition::SameOriginDomain + ? "document.domain = 'example.com';" + : "", + property.Utf8().data(), target_url); LoadURL(kMainFrame); main.Complete(document); - target.Complete( - "<!DOCTYPE html>" - "<script>window.opener.postMessage('yay', '*');</script>"); + target.Complete(target_html); test::RunPendingTasks(); } void LoadFrameAndAccessProperty(OriginDisposition which_origin, const String& property) { + const char* target_url; + const char* target_html; + switch (which_origin) { + case OriginDisposition::CrossOrigin: + target_url = kCrossOriginTarget; + target_html = kTargetHTML; + break; + case OriginDisposition::SameOrigin: + target_url = kSameOriginTarget; + target_html = kTargetHTML; + break; + case OriginDisposition::SameOriginDomain: + target_url = kSameOriginDomainTarget; + target_html = kSameOriginDomainTargetHTML; + break; + } SimRequest main(kMainFrame, "text/html"); - SimRequest target(which_origin == OriginDisposition::CrossOrigin - ? kCrossOriginTarget - : kSameOriginTarget, - "text/html"); + SimRequest target(target_url, "text/html"); const String& document = String::Format( "<!DOCTYPE html>" "<body>" "<script>" + " %s" " var i = document.createElement('iframe');" " window.addEventListener('message', e => {" " window.other = e.source.%s;" @@ -75,15 +116,14 @@ " i.src = '%s';" " document.body.appendChild(i);" "</script>", - property.Utf8().data(), - which_origin == OriginDisposition::CrossOrigin ? kCrossOriginTarget - : kSameOriginTarget); + which_origin == OriginDisposition::SameOriginDomain + ? "document.domain = 'example.com';" + : "", + property.Utf8().data(), target_url); LoadURL(kMainFrame); main.Complete(document); - target.Complete( - "<!DOCTYPE html>" - "<script>window.top.postMessage('yay', '*');</script>"); + target.Complete(target_html); test::RunPendingTasks(); } }; @@ -110,6 +150,8 @@ WebFeature::kCrossOriginPropertyAccess)); EXPECT_TRUE(GetDocument().Loader()->GetUseCounter().HasRecordedMeasurement( WebFeature::kCrossOriginPropertyAccessFromOpener)); + EXPECT_FALSE(GetDocument().Loader()->GetUseCounter().HasRecordedMeasurement( + WebFeature::kDocumentDomainEnabledCrossOriginAccess)); } TEST_P(BindingSecurityCounterTest, SameOriginWindow) { @@ -118,6 +160,18 @@ WebFeature::kCrossOriginPropertyAccess)); EXPECT_FALSE(GetDocument().Loader()->GetUseCounter().HasRecordedMeasurement( WebFeature::kCrossOriginPropertyAccessFromOpener)); + EXPECT_FALSE(GetDocument().Loader()->GetUseCounter().HasRecordedMeasurement( + WebFeature::kDocumentDomainEnabledCrossOriginAccess)); +} + +TEST_P(BindingSecurityCounterTest, SameOriginDomainWindow) { + LoadWindowAndAccessProperty(OriginDisposition::SameOriginDomain, GetParam()); + EXPECT_FALSE(GetDocument().Loader()->GetUseCounter().HasRecordedMeasurement( + WebFeature::kCrossOriginPropertyAccess)); + EXPECT_FALSE(GetDocument().Loader()->GetUseCounter().HasRecordedMeasurement( + WebFeature::kCrossOriginPropertyAccessFromOpener)); + EXPECT_TRUE(GetDocument().Loader()->GetUseCounter().HasRecordedMeasurement( + WebFeature::kDocumentDomainEnabledCrossOriginAccess)); } TEST_P(BindingSecurityCounterTest, CrossOriginFrame) { @@ -126,6 +180,8 @@ WebFeature::kCrossOriginPropertyAccess)); EXPECT_FALSE(GetDocument().Loader()->GetUseCounter().HasRecordedMeasurement( WebFeature::kCrossOriginPropertyAccessFromOpener)); + EXPECT_FALSE(GetDocument().Loader()->GetUseCounter().HasRecordedMeasurement( + WebFeature::kDocumentDomainEnabledCrossOriginAccess)); } TEST_P(BindingSecurityCounterTest, SameOriginFrame) { @@ -134,6 +190,18 @@ WebFeature::kCrossOriginPropertyAccess)); EXPECT_FALSE(GetDocument().Loader()->GetUseCounter().HasRecordedMeasurement( WebFeature::kCrossOriginPropertyAccessFromOpener)); + EXPECT_FALSE(GetDocument().Loader()->GetUseCounter().HasRecordedMeasurement( + WebFeature::kDocumentDomainEnabledCrossOriginAccess)); +} + +TEST_P(BindingSecurityCounterTest, SameOriginDomainFrame) { + LoadFrameAndAccessProperty(OriginDisposition::SameOriginDomain, GetParam()); + EXPECT_FALSE(GetDocument().Loader()->GetUseCounter().HasRecordedMeasurement( + WebFeature::kCrossOriginPropertyAccess)); + EXPECT_FALSE(GetDocument().Loader()->GetUseCounter().HasRecordedMeasurement( + WebFeature::kCrossOriginPropertyAccessFromOpener)); + EXPECT_TRUE(GetDocument().Loader()->GetUseCounter().HasRecordedMeasurement( + WebFeature::kDocumentDomainEnabledCrossOriginAccess)); } } // namespace
diff --git a/third_party/blink/renderer/bindings/core/v8/script_event_listener.cc b/third_party/blink/renderer/bindings/core/v8/script_event_listener.cc index 01c7dbb8..3450578 100644 --- a/third_party/blink/renderer/bindings/core/v8/script_event_listener.cc +++ b/third_party/blink/renderer/bindings/core/v8/script_event_listener.cc
@@ -34,7 +34,6 @@ #include "third_party/blink/renderer/bindings/core/v8/script_controller.h" #include "third_party/blink/renderer/bindings/core/v8/source_location.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_event_listener_impl.h" #include "third_party/blink/renderer/bindings/core/v8/v8_lazy_event_listener.h" #include "third_party/blink/renderer/bindings/core/v8/window_proxy.h" #include "third_party/blink/renderer/core/dom/document.h" @@ -98,13 +97,11 @@ v8::Local<v8::Object> EventListenerHandler(ExecutionContext* execution_context, EventListener* listener) { - if (auto* v8_listener = V8AbstractEventHandler::Cast(listener)) { - return v8_listener->GetListenerObject(execution_context); - } - if (auto* v8_listener = V8EventListenerImpl::Cast(listener)) { - return v8_listener->GetListenerObject(); - } - return v8::Local<v8::Object>(); + if (listener->GetType() != EventListener::kJSEventListenerType) + return v8::Local<v8::Object>(); + V8AbstractEventListener* v8_listener = + static_cast<V8AbstractEventListener*>(listener); + return v8_listener->GetListenerObject(execution_context); } v8::Local<v8::Function> EventListenerEffectiveFunction( @@ -145,8 +142,6 @@ column_number = function->GetScriptColumnNumber(); } -// TODO(yukiy): move this method into V8EventListenerImpl or interface class -// of EventListener and EventHandler std::unique_ptr<SourceLocation> GetFunctionLocation( ExecutionContext* execution_context, EventListener* listener) {
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.cc b/third_party/blink/renderer/bindings/core/v8/v8_abstract_event_listener.cc similarity index 86% rename from third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.cc rename to third_party/blink/renderer/bindings/core/v8/v8_abstract_event_listener.cc index 9383312..2c686f39 100644 --- a/third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.cc +++ b/third_party/blink/renderer/bindings/core/v8/v8_abstract_event_listener.cc
@@ -28,7 +28,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_abstract_event_listener.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/bindings/core/v8/v8_event.h" @@ -48,24 +48,23 @@ namespace blink { -V8AbstractEventHandler::V8AbstractEventHandler(v8::Isolate* isolate, - bool is_attribute, - DOMWrapperWorld& world) - : EventListener(kJSEventHandlerType), +V8AbstractEventListener::V8AbstractEventListener(v8::Isolate* isolate, + bool is_attribute, + DOMWrapperWorld& world) + : EventListener(kJSEventListenerType), is_attribute_(is_attribute), world_(&world), isolate_(isolate) { - if (IsMainThread()) { + if (IsMainThread()) InstanceCounters::IncrementCounter( InstanceCounters::kJSEventListenerCounter); - } } -V8AbstractEventHandler::~V8AbstractEventHandler() { +V8AbstractEventListener::~V8AbstractEventListener() { // For non-main threads a termination garbage collection clears out the // wrapper links to CustomWrappable which result in CustomWrappable not being // rooted by JavaScript objects anymore. This means that - // V8AbstractEventHandler can be collected without while still holding a + // V8AbstractEventListener can be collected without while still holding a // valid weak references. if (IsMainThread()) { DCHECK(listener_.IsEmpty()); @@ -75,13 +74,13 @@ } // static -v8::Local<v8::Value> V8AbstractEventHandler::GetListenerOrNull( +v8::Local<v8::Value> V8AbstractEventListener::GetListenerOrNull( v8::Isolate* isolate, EventTarget* event_target, EventListener* listener) { - if (listener && listener->GetType() == kJSEventHandlerType) { + if (listener && listener->GetType() == kJSEventListenerType) { v8::Local<v8::Object> v8_listener = - static_cast<V8AbstractEventHandler*>(listener) + static_cast<V8AbstractEventListener*>(listener) ->GetListenerObjectInternal(event_target->GetExecutionContext()); if (!v8_listener.IsEmpty()) return v8_listener; @@ -89,8 +88,8 @@ return v8::Null(isolate); } -void V8AbstractEventHandler::handleEvent(ExecutionContext* execution_context, - Event* event) { +void V8AbstractEventListener::handleEvent(ExecutionContext* execution_context, + Event* event) { if (!execution_context) return; // Don't reenter V8 if execution was terminated in this instance of V8. @@ -111,8 +110,8 @@ HandleEvent(script_state, event); } -void V8AbstractEventHandler::HandleEvent(ScriptState* script_state, - Event* event) { +void V8AbstractEventListener::HandleEvent(ScriptState* script_state, + Event* event) { ScriptState::Scope scope(script_state); // Get the V8 wrapper for the event object. @@ -124,7 +123,7 @@ v8::Local<v8::Value>::New(GetIsolate(), js_event)); } -void V8AbstractEventHandler::SetListenerObject( +void V8AbstractEventListener::SetListenerObject( ScriptState* script_state, v8::Local<v8::Object> listener, const V8PrivateProperty::Symbol& property) { @@ -133,9 +132,10 @@ Attach(script_state, listener, property, this); } -void V8AbstractEventHandler::InvokeEventHandler(ScriptState* script_state, - Event* event, - v8::Local<v8::Value> js_event) { +void V8AbstractEventListener::InvokeEventHandler( + ScriptState* script_state, + Event* event, + v8::Local<v8::Value> js_event) { if (!event->CanBeDispatchedInWorld(World())) return; @@ -216,7 +216,7 @@ } } -bool V8AbstractEventHandler::ShouldPreventDefault( +bool V8AbstractEventListener::ShouldPreventDefault( v8::Local<v8::Value> return_value, Event*) { // Prevent default action if the return value is false in accord with the spec @@ -224,7 +224,7 @@ return return_value->IsBoolean() && !return_value.As<v8::Boolean>()->Value(); } -v8::Local<v8::Object> V8AbstractEventHandler::GetReceiverObject( +v8::Local<v8::Object> V8AbstractEventListener::GetReceiverObject( ScriptState* script_state, Event* event) { v8::Local<v8::Object> listener = listener_.NewLocal(GetIsolate()); @@ -240,7 +240,7 @@ v8::Local<v8::Object>::Cast(value)); } -bool V8AbstractEventHandler::BelongsToTheCurrentWorld( +bool V8AbstractEventListener::BelongsToTheCurrentWorld( ExecutionContext* execution_context) const { if (!GetIsolate()->GetCurrentContext().IsEmpty() && &World() == &DOMWrapperWorld::Current(GetIsolate())) @@ -256,19 +256,19 @@ return false; } -void V8AbstractEventHandler::ClearListenerObject() { +void V8AbstractEventListener::ClearListenerObject() { if (!HasExistingListenerObject()) return; probe::AsyncTaskCanceled(GetIsolate(), this); listener_.Clear(); } -void V8AbstractEventHandler::WrapperCleared( - const v8::WeakCallbackInfo<V8AbstractEventHandler>& data) { +void V8AbstractEventListener::WrapperCleared( + const v8::WeakCallbackInfo<V8AbstractEventListener>& data) { data.GetParameter()->ClearListenerObject(); } -void V8AbstractEventHandler::Trace(blink::Visitor* visitor) { +void V8AbstractEventListener::Trace(blink::Visitor* visitor) { visitor->Trace(listener_.Cast<v8::Value>()); EventListener::Trace(visitor); }
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.h b/third_party/blink/renderer/bindings/core/v8/v8_abstract_event_listener.h similarity index 89% rename from third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.h rename to third_party/blink/renderer/bindings/core/v8/v8_abstract_event_listener.h index 5f04bf7..7ecb25f9 100644 --- a/third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.h +++ b/third_party/blink/renderer/bindings/core/v8/v8_abstract_event_listener.h
@@ -28,8 +28,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_ABSTRACT_EVENT_HANDLER_H_ -#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_ABSTRACT_EVENT_HANDLER_H_ +#ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_ABSTRACT_EVENT_LISTENER_H_ +#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_ABSTRACT_EVENT_LISTENER_H_ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/dom/events/event_listener.h" @@ -51,18 +51,18 @@ // Why does this matter? // WebKit does not allow duplicated HTML event handlers of the same type, // but ALLOWs duplicated non-HTML event handlers. -class CORE_EXPORT V8AbstractEventHandler : public EventListener { +class CORE_EXPORT V8AbstractEventListener : public EventListener { public: - ~V8AbstractEventHandler() override; + ~V8AbstractEventListener() override; - static const V8AbstractEventHandler* Cast(const EventListener* listener) { - return listener->GetType() == kJSEventHandlerType - ? static_cast<const V8AbstractEventHandler*>(listener) + static const V8AbstractEventListener* Cast(const EventListener* listener) { + return listener->GetType() == kJSEventListenerType + ? static_cast<const V8AbstractEventListener*>(listener) : nullptr; } - static V8AbstractEventHandler* Cast(EventListener* listener) { - return const_cast<V8AbstractEventHandler*>( + static V8AbstractEventListener* Cast(EventListener* listener) { + return const_cast<V8AbstractEventListener*>( Cast(const_cast<const EventListener*>(listener))); } @@ -115,7 +115,7 @@ void Trace(blink::Visitor*) override; protected: - V8AbstractEventHandler(v8::Isolate*, bool is_attribute, DOMWrapperWorld&); + V8AbstractEventListener(v8::Isolate*, bool is_attribute, DOMWrapperWorld&); virtual v8::Local<v8::Object> GetListenerObjectInternal( ExecutionContext* execution_context) { @@ -141,7 +141,7 @@ virtual bool ShouldPreventDefault(v8::Local<v8::Value> return_value, Event*); static void WrapperCleared( - const v8::WeakCallbackInfo<V8AbstractEventHandler>&); + const v8::WeakCallbackInfo<V8AbstractEventListener>&); TraceWrapperV8Reference<v8::Object> listener_; @@ -154,4 +154,4 @@ } // namespace blink -#endif // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_ABSTRACT_EVENT_HANDLER_H_ +#endif // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_ABSTRACT_EVENT_LISTENER_H_
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_event_listener_helper.cc b/third_party/blink/renderer/bindings/core/v8/v8_event_listener_helper.cc index e9208db..ac940688 100644 --- a/third_party/blink/renderer/bindings/core/v8/v8_event_listener_helper.cc +++ b/third_party/blink/renderer/bindings/core/v8/v8_event_listener_helper.cc
@@ -33,7 +33,6 @@ #include "third_party/blink/renderer/bindings/core/v8/custom_wrappable_adapter.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/bindings/core/v8/v8_error_handler.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_event_listener_impl.h" #include "third_party/blink/renderer/bindings/core/v8/v8_event_listener_or_event_handler.h" #include "third_party/blink/renderer/bindings/core/v8/v8_window.h" #include "third_party/blink/renderer/platform/bindings/v8_private_property.h" @@ -77,16 +76,11 @@ ? V8PrivateProperty::GetCustomWrappableEventHandler(isolate) : V8PrivateProperty::GetCustomWrappableEventListener(isolate); - return GetEventListenerInternal<EventListener>( + return GetEventListenerInternal<V8AbstractEventListener>( script_state, object, listener_property, lookup, [object, is_attribute, script_state, listener_property]() { - return is_attribute - ? static_cast<EventListener*>( - V8EventListenerOrEventHandler::Create( - object, is_attribute, script_state, - listener_property)) - : static_cast<EventListener*>(V8EventListenerImpl::Create( - object, script_state, listener_property)); + return V8EventListenerOrEventHandler::Create( + object, is_attribute, script_state, listener_property); }); }
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_event_listener_impl.cc b/third_party/blink/renderer/bindings/core/v8/v8_event_listener_impl.cc deleted file mode 100644 index b48cda0f..0000000 --- a/third_party/blink/renderer/bindings/core/v8/v8_event_listener_impl.cc +++ /dev/null
@@ -1,135 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "third_party/blink/renderer/bindings/core/v8/v8_event_listener_impl.h" - -#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" -#include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h" -#include "third_party/blink/renderer/core/dom/events/event.h" -#include "third_party/blink/renderer/core/dom/events/event_target.h" -#include "third_party/blink/renderer/core/execution_context/execution_context.h" -#include "third_party/blink/renderer/core/probe/core_probes.h" -#include "third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h" -#include "third_party/blink/renderer/platform/bindings/v8_private_property.h" -#include "third_party/blink/renderer/platform/instance_counters.h" - -namespace blink { - -V8EventListenerImpl::V8EventListenerImpl( - v8::Local<v8::Object> listener, - ScriptState* script_state, - const V8PrivateProperty::Symbol& property) - : EventListener(kJSEventListenerType), - event_listener_(V8EventListener::Create(listener)) { - Attach(script_state, listener, property, this); - if (IsMainThread()) { - InstanceCounters::IncrementCounter( - InstanceCounters::kJSEventListenerCounter); - } -} - -V8EventListenerImpl::~V8EventListenerImpl() { - if (IsMainThread()) { - InstanceCounters::DecrementCounter( - InstanceCounters::kJSEventListenerCounter); - } -} - -// https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke -void V8EventListenerImpl::handleEvent( - ExecutionContext* execution_context_of_event_target, - Event* event) { - // Don't reenter V8 if execution was terminated in this instance of V8. - // For example, worker can be terminated in event listener, and also window - // can be terminated from inspector by the TerminateExecution method. - if (event_listener_->GetIsolate()->IsExecutionTerminating()) - return; - - DCHECK(execution_context_of_event_target); - DCHECK(event); - if (!event->CanBeDispatchedInWorld(World())) - return; - - ScriptState* script_state_of_listener = - event_listener_->CallbackRelevantScriptState(); - if (!script_state_of_listener->ContextIsValid()) - return; - - ScriptState::Scope scope(script_state_of_listener); - - // https://dom.spec.whatwg.org/#firing-events - // Step 2. of firing events: Let event be the result of creating an event - // given eventConstructor, in the relevant Realm of target. - // - // (Note: a |js_event|, an v8::Value for |event| in the relevant realm of - // |event|'s target, is created here. It should be done before dispatching - // events, but Event and EventTarget do not have world to get |v8_context|, so - // it is done here with |execution_context_of_event_target|.) - v8::Local<v8::Context> v8_context = - ToV8Context(execution_context_of_event_target, World()); - if (v8_context.IsEmpty()) - return; - v8::Local<v8::Value> js_event = - ToV8(event, v8_context->Global(), event_listener_->GetIsolate()); - if (js_event.IsEmpty()) - return; - - // Step 6: Let |global| be listener callback’s associated Realm’s global - // object. - v8::Local<v8::Object> global = - script_state_of_listener->GetContext()->Global(); - - // Step 8: If global is a Window object, then: - // Set currentEvent to global’s current event. - // If tuple’s item-in-shadow-tree is false, then set global’s current event to - // event. - V8PrivateProperty::Symbol event_symbol = - V8PrivateProperty::GetGlobalEvent(event_listener_->GetIsolate()); - ExecutionContext* execution_context_of_listener = - ExecutionContext::From(script_state_of_listener); - v8::Local<v8::Value> current_event; - if (execution_context_of_listener->IsDocument()) { - current_event = event_symbol.GetOrUndefined(global).ToLocalChecked(); - // Expose the event object as |window.event|, except when the event's target - // is in a V1 shadow tree. - Node* target_node = event->target()->ToNode(); - if (!(target_node && target_node->IsInV1ShadowTree())) - event_symbol.Set(global, js_event); - } - - { - // Catch exceptions thrown in the event listener if any and report them to - // DevTools console. - v8::TryCatch try_catch(event_listener_->GetIsolate()); - try_catch.SetVerbose(true); - - // Step 10: Call a listener with event's currentTarget as receiver and event - // and handle errors if thrown. - v8::Maybe<void> maybe_result = - event_listener_->handleEvent(event->currentTarget(), event); - ALLOW_UNUSED_LOCAL(maybe_result); - - if (try_catch.HasCaught()) { - // Step 10-2: Set legacyOutputDidListenersThrowFlag if given. - event->LegacySetDidListenersThrowFlag(); - } - - // TODO(yukiy): consider to set |global|’s current event to |current_event| - // after execution is terminated if it is necessary and possible. - if (event_listener_->GetIsolate()->IsExecutionTerminating()) - return; - } - - // Step 12: If |global| is a Window object, then set |global|’s current event - // to |current_event|. - if (execution_context_of_listener->IsDocument()) - event_symbol.Set(global, current_event); -} - -void V8EventListenerImpl::Trace(blink::Visitor* visitor) { - visitor->Trace(event_listener_); - EventListener::Trace(visitor); -} - -} // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_event_listener_impl.h b/third_party/blink/renderer/bindings/core/v8/v8_event_listener_impl.h deleted file mode 100644 index 4ac9ba2..0000000 --- a/third_party/blink/renderer/bindings/core/v8/v8_event_listener_impl.h +++ /dev/null
@@ -1,79 +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 THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_EVENT_LISTENER_IMPL_H_ -#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_EVENT_LISTENER_IMPL_H_ - -#include "third_party/blink/renderer/bindings/core/v8/v8_event_listener.h" -#include "third_party/blink/renderer/core/core_export.h" -#include "third_party/blink/renderer/core/dom/events/event_listener.h" -#include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h" -#include "v8/include/v8.h" - -namespace blink { - -class Event; - -class CORE_EXPORT V8EventListenerImpl final : public EventListener { - public: - static V8EventListenerImpl* Create( - v8::Local<v8::Object> listener, - ScriptState* script_state, - const V8PrivateProperty::Symbol& property) { - return new V8EventListenerImpl(listener, script_state, property); - } - - static const V8EventListenerImpl* Cast(const EventListener* listener) { - return listener->GetType() == kJSEventListenerType - ? static_cast<const V8EventListenerImpl*>(listener) - : nullptr; - } - - static V8EventListenerImpl* Cast(EventListener* listener) { - return const_cast<V8EventListenerImpl*>( - Cast(const_cast<const EventListener*>(listener))); - } - - ~V8EventListenerImpl() override; - - // Check the identity of |V8EventListener::callback_object_|. There can be - // multiple CallbackInterfaceBase objects that have the same - // |CallbackInterfaceBase::callback_object_| but have different - // |CallbackInterfaceBase::incumbent_script_state_|s. - bool operator==(const EventListener& other) const override { - const V8EventListenerImpl* other_event_listener = Cast(&other); - if (!other_event_listener) - return false; - return event_listener_->HasTheSameCallbackObject( - *other_event_listener->event_listener_); - } - - // blink::EventListener overrides: - v8::Local<v8::Object> GetListenerObjectForInspector( - ExecutionContext* execution_context) override { - return event_listener_->CallbackObject(); - } - DOMWrapperWorld* GetWorldForInspector() const override { return &World(); } - void handleEvent(ExecutionContext*, Event*) override; - void Trace(blink::Visitor*) override; - - v8::Local<v8::Object> GetListenerObject() const { - return event_listener_->CallbackObject(); - } - - private: - V8EventListenerImpl(v8::Local<v8::Object>, - ScriptState*, - const V8PrivateProperty::Symbol&); - - DOMWrapperWorld& World() const { - return event_listener_->CallbackRelevantScriptState()->World(); - } - - const TraceWrapperMember<V8EventListener> event_listener_; -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_EVENT_LISTENER_IMPL_H_
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_event_listener_or_event_handler.cc b/third_party/blink/renderer/bindings/core/v8/v8_event_listener_or_event_handler.cc index b345078b..4a87e26 100644 --- a/third_party/blink/renderer/bindings/core/v8/v8_event_listener_or_event_handler.cc +++ b/third_party/blink/renderer/bindings/core/v8/v8_event_listener_or_event_handler.cc
@@ -42,9 +42,9 @@ V8EventListenerOrEventHandler::V8EventListenerOrEventHandler( bool is_attribute, ScriptState* script_state) - : V8AbstractEventHandler(script_state->GetIsolate(), - is_attribute, - script_state->World()) {} + : V8AbstractEventListener(script_state->GetIsolate(), + is_attribute, + script_state->World()) {} v8::Local<v8::Function> V8EventListenerOrEventHandler::GetListenerFunction( ScriptState* script_state) {
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_event_listener_or_event_handler.h b/third_party/blink/renderer/bindings/core/v8/v8_event_listener_or_event_handler.h index b858e0c..c6c9680 100644 --- a/third_party/blink/renderer/bindings/core/v8/v8_event_listener_or_event_handler.h +++ b/third_party/blink/renderer/bindings/core/v8/v8_event_listener_or_event_handler.h
@@ -32,7 +32,7 @@ #define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_EVENT_LISTENER_OR_EVENT_HANDLER_H_ #include "base/memory/scoped_refptr.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_abstract_event_listener.h" #include "v8/include/v8.h" namespace blink { @@ -42,7 +42,7 @@ // V8EventListenerOrEventHandler is a wrapper of a JS object implements // EventListener interface (has handleEvent(event) method), or a JS function // that can handle the event. -class V8EventListenerOrEventHandler : public V8AbstractEventHandler { +class V8EventListenerOrEventHandler : public V8AbstractEventListener { public: static V8EventListenerOrEventHandler* Create( v8::Local<v8::Object> listener,
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_lazy_event_listener.cc b/third_party/blink/renderer/bindings/core/v8/v8_lazy_event_listener.cc index b36ccd6..6d103ef 100644 --- a/third_party/blink/renderer/bindings/core/v8/v8_lazy_event_listener.cc +++ b/third_party/blink/renderer/bindings/core/v8/v8_lazy_event_listener.cc
@@ -60,7 +60,7 @@ const String source_url, const TextPosition& position, Node* node) - : V8AbstractEventHandler(isolate, true, DOMWrapperWorld::MainWorld()), + : V8AbstractEventListener(isolate, true, DOMWrapperWorld::MainWorld()), was_compilation_failed_(false), function_name_(function_name), event_parameter_name_(event_parameter_name),
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_lazy_event_listener.h b/third_party/blink/renderer/bindings/core/v8/v8_lazy_event_listener.h index a936395..9bddefe 100644 --- a/third_party/blink/renderer/bindings/core/v8/v8_lazy_event_listener.h +++ b/third_party/blink/renderer/bindings/core/v8/v8_lazy_event_listener.h
@@ -32,7 +32,7 @@ #define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_LAZY_EVENT_LISTENER_H_ #include "base/memory/scoped_refptr.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_abstract_event_listener.h" #include "third_party/blink/renderer/platform/wtf/text/text_position.h" #include "v8/include/v8.h" @@ -44,7 +44,7 @@ // V8LazyEventListener is a wrapper for a JavaScript code string that is // compiled and evaluated when an event is fired. A V8LazyEventListener is // either a HTML or SVG event handler. -class V8LazyEventListener final : public V8AbstractEventHandler { +class V8LazyEventListener final : public V8AbstractEventListener { public: static V8LazyEventListener* Create(const AtomicString& function_name, const AtomicString& event_parameter_name, @@ -59,7 +59,7 @@ void Trace(blink::Visitor* visitor) override { visitor->Trace(node_); - V8AbstractEventHandler::Trace(visitor); + V8AbstractEventListener::Trace(visitor); } const String& Code() const override { return code_; }
diff --git a/third_party/blink/renderer/bindings/scripts/v8_types.py b/third_party/blink/renderer/bindings/scripts/v8_types.py index 5b90f734..84f53633 100644 --- a/third_party/blink/renderer/bindings/scripts/v8_types.py +++ b/third_party/blink/renderer/bindings/scripts/v8_types.py
@@ -412,7 +412,7 @@ 'core/typed_arrays/array_buffer_view_helpers.h', 'core/typed_arrays/flexible_array_buffer_view.h']), 'Dictionary': set(['bindings/core/v8/dictionary.h']), - 'EventHandler': set(['bindings/core/v8/v8_abstract_event_handler.h', + 'EventHandler': set(['bindings/core/v8/v8_abstract_event_listener.h', 'bindings/core/v8/v8_event_listener_helper.h']), 'EventListener': set(['bindings/core/v8/binding_security.h', 'bindings/core/v8/v8_event_listener_helper.h', @@ -1040,7 +1040,7 @@ # Special cases 'Dictionary': '{cpp_value}.V8Value()', 'EventHandler': - 'V8AbstractEventHandler::GetListenerOrNull({isolate}, impl, {cpp_value})', + 'V8AbstractEventListener::GetListenerOrNull({isolate}, impl, {cpp_value})', 'NodeFilter': 'ToV8({cpp_value}, {creation_context}, {isolate})', 'Record': 'ToV8({cpp_value}, {creation_context}, {isolate})', 'ScriptValue': '{cpp_value}.V8Value()',
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc index 41f24183..58935f73 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc +++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc
@@ -15,7 +15,7 @@ #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise.h" #include "third_party/blink/renderer/bindings/core/v8/script_value.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_abstract_event_listener.h" #include "third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.h" #include "third_party/blink/renderer/bindings/core/v8/v8_element.h" #include "third_party/blink/renderer/bindings/core/v8/v8_event_listener_helper.h" @@ -994,7 +994,7 @@ EventListener* cppValue(WTF::GetPtr(impl->implementsEventHandlerAttribute())); - V8SetReturnValue(info, V8AbstractEventHandler::GetListenerOrNull(info.GetIsolate(), impl, cppValue)); + V8SetReturnValue(info, V8AbstractEventListener::GetListenerOrNull(info.GetIsolate(), impl, cppValue)); } static void implementsEventHandlerAttributeAttributeSetter(v8::Local<v8::Value> v8Value, const v8::FunctionCallbackInfo<v8::Value>& info) {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.cc index 40df4e5..8d7ecc3 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.cc +++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.cc
@@ -13,7 +13,7 @@ #include "base/memory/scoped_refptr.h" #include "third_party/blink/renderer/bindings/core/v8/idl_types.h" #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_abstract_event_listener.h" #include "third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.h" #include "third_party/blink/renderer/bindings/core/v8/v8_event_listener_helper.h" #include "third_party/blink/renderer/bindings/core/v8/v8_test_interface_empty.h" @@ -133,7 +133,7 @@ EventListener* cppValue(WTF::GetPtr(impl->eventHandlerAttribute())); - V8SetReturnValue(info, V8AbstractEventHandler::GetListenerOrNull(info.GetIsolate(), impl, cppValue)); + V8SetReturnValue(info, V8AbstractEventListener::GetListenerOrNull(info.GetIsolate(), impl, cppValue)); } static void eventHandlerAttributeAttributeSetter(v8::Local<v8::Value> v8Value, const v8::FunctionCallbackInfo<v8::Value>& info) {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc index 371e7c6..0f6c0d0 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc +++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
@@ -20,7 +20,7 @@ #include "third_party/blink/renderer/bindings/core/v8/script_value.h" #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h" #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value_factory.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_abstract_event_listener.h" #include "third_party/blink/renderer/bindings/core/v8/v8_array_buffer.h" #include "third_party/blink/renderer/bindings/core/v8/v8_array_buffer_view.h" #include "third_party/blink/renderer/bindings/core/v8/v8_attr.h" @@ -1607,7 +1607,7 @@ EventListener* cppValue(WTF::GetPtr(impl->eventHandlerAttribute())); - V8SetReturnValue(info, V8AbstractEventHandler::GetListenerOrNull(info.GetIsolate(), impl, cppValue)); + V8SetReturnValue(info, V8AbstractEventListener::GetListenerOrNull(info.GetIsolate(), impl, cppValue)); } static void eventHandlerAttributeAttributeSetter(v8::Local<v8::Value> v8Value, const v8::FunctionCallbackInfo<v8::Value>& info) {
diff --git a/third_party/blink/renderer/core/core_idl_files.gni b/third_party/blink/renderer/core/core_idl_files.gni index f385828..68ec938 100644 --- a/third_party/blink/renderer/core/core_idl_files.gni +++ b/third_party/blink/renderer/core/core_idl_files.gni
@@ -132,7 +132,6 @@ "dom/xml_document.idl", "dom/events/custom_event.idl", "dom/events/event.idl", - "dom/events/event_listener.idl", "dom/events/event_target.idl", "trustedtypes/trusted_html.idl", "trustedtypes/trusted_script.idl", @@ -513,6 +512,7 @@ "dom/non_document_type_child_node.idl", "dom/non_element_parent_node.idl", "dom/parent_node.idl", + "dom/events/event_listener.idl", "events/navigator_events.idl", "fetch/window_fetch.idl", "fetch/worker_fetch.idl",
diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc index a3289f99..ba38716 100644 --- a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc +++ b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
@@ -567,7 +567,7 @@ element->LayoutObjectIsNeeded(style))) { // TODO(rakina): Move this attribute check somewhere else. if (RuntimeEnabledFeatures::InvisibleDOMEnabled() && - !element->invisible().IsNull()) + element->Invisible() != InvisibleState::kMissing) style.SetDisplay(EDisplay::kNone); else if (element->IsHTMLElement()) AdjustStyleForHTMLElement(style, ToHTMLElement(*element));
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc index 47590516..aaed7ad 100644 --- a/third_party/blink/renderer/core/css/style_engine.cc +++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -1545,6 +1545,21 @@ return *environment_variables_.get(); } +void StyleEngine::RecalcStyle(StyleRecalcChange change) { + DCHECK(GetDocument().documentElement()); + DCHECK(GetDocument().ChildNeedsStyleRecalc() || change == kForce); + + GetDocument().documentElement()->RecalcStyle(change); +} + +void StyleEngine::RebuildLayoutTree() { + DCHECK(GetDocument().documentElement()); + DCHECK(GetDocument().ChildNeedsReattachLayoutTree()); + + WhitespaceAttacher whitespace_attacher; + GetDocument().documentElement()->RebuildLayoutTree(whitespace_attacher); +} + void StyleEngine::Trace(blink::Visitor* visitor) { visitor->Trace(document_); visitor->Trace(injected_user_style_sheets_);
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h index 12d3e36..27ad76e 100644 --- a/third_party/blink/renderer/core/css/style_engine.h +++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -319,6 +319,9 @@ DocumentStyleEnvironmentVariables& EnsureEnvironmentVariables(); + void RecalcStyle(StyleRecalcChange change); + void RebuildLayoutTree(); + void Trace(blink::Visitor*) override; const char* NameInHeapSnapshot() const override { return "StyleEngine"; }
diff --git a/third_party/blink/renderer/core/css/style_engine_test.cc b/third_party/blink/renderer/core/css/style_engine_test.cc index f6ba60f0..2e11be1 100644 --- a/third_party/blink/renderer/core/css/style_engine_test.cc +++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -848,14 +848,12 @@ GetDocument().View()->UpdateAllLifecyclePhases(); } - EXPECT_TRUE( - GetDocument().GetStyleEngine().HasViewportDependentMediaQueries()); + EXPECT_TRUE(GetStyleEngine().HasViewportDependentMediaQueries()); GetDocument().body()->RemoveChild(style_element); GetDocument().View()->UpdateAllLifecyclePhases(); - EXPECT_FALSE( - GetDocument().GetStyleEngine().HasViewportDependentMediaQueries()); + EXPECT_FALSE(GetStyleEngine().HasViewportDependentMediaQueries()); } TEST_F(StyleEngineTest, StyleMediaAttributeStyleChange) { @@ -1482,7 +1480,7 @@ )HTML"); GetDocument().View()->UpdateAllLifecyclePhases(); - StyleEngine& engine = GetDocument().GetStyleEngine(); + StyleEngine& engine = GetStyleEngine(); engine.SetStatsEnabled(true); StyleResolverStats* stats = engine.Stats(); @@ -1493,7 +1491,7 @@ div->SetInlineStyleProperty(CSSPropertyColor, "green"); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc); - GetDocument().documentElement()->RecalcStyle(kNoChange); + GetStyleEngine().RecalcStyle(kNoChange); // Should fast reject ".not-in-filter div::before {}" for both the div and its // ::before pseudo element. @@ -1514,35 +1512,35 @@ GetDocument().View()->UpdateAllLifecyclePhases(); d1->firstChild()->remove(); - EXPECT_TRUE(GetDocument().GetStyleEngine().NeedsWhitespaceReattachment(d1)); + EXPECT_TRUE(GetStyleEngine().NeedsWhitespaceReattachment(d1)); EXPECT_FALSE(GetDocument().ChildNeedsStyleInvalidation()); EXPECT_FALSE(GetDocument().ChildNeedsStyleRecalc()); EXPECT_FALSE(GetDocument().ChildNeedsReattachLayoutTree()); - GetDocument().GetStyleEngine().MarkForWhitespaceReattachment(); + GetStyleEngine().MarkForWhitespaceReattachment(); EXPECT_FALSE(GetDocument().ChildNeedsReattachLayoutTree()); GetDocument().View()->UpdateAllLifecyclePhases(); d2->firstChild()->remove(); d2->firstChild()->remove(); - EXPECT_TRUE(GetDocument().GetStyleEngine().NeedsWhitespaceReattachment(d2)); + EXPECT_TRUE(GetStyleEngine().NeedsWhitespaceReattachment(d2)); EXPECT_FALSE(GetDocument().ChildNeedsStyleInvalidation()); EXPECT_FALSE(GetDocument().ChildNeedsStyleRecalc()); EXPECT_FALSE(GetDocument().ChildNeedsReattachLayoutTree()); - GetDocument().GetStyleEngine().MarkForWhitespaceReattachment(); + GetStyleEngine().MarkForWhitespaceReattachment(); EXPECT_FALSE(GetDocument().ChildNeedsReattachLayoutTree()); GetDocument().View()->UpdateAllLifecyclePhases(); d3->firstChild()->remove(); - EXPECT_TRUE(GetDocument().GetStyleEngine().NeedsWhitespaceReattachment(d3)); + EXPECT_TRUE(GetStyleEngine().NeedsWhitespaceReattachment(d3)); EXPECT_FALSE(GetDocument().ChildNeedsStyleInvalidation()); EXPECT_FALSE(GetDocument().ChildNeedsStyleRecalc()); EXPECT_FALSE(GetDocument().ChildNeedsReattachLayoutTree()); - GetDocument().GetStyleEngine().MarkForWhitespaceReattachment(); + GetStyleEngine().MarkForWhitespaceReattachment(); EXPECT_TRUE(GetDocument().ChildNeedsReattachLayoutTree()); }
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc index e628c1a..2be447d8 100644 --- a/third_party/blink/renderer/core/dom/document.cc +++ b/third_party/blink/renderer/core/dom/document.cc
@@ -2304,7 +2304,7 @@ TRACE_EVENT0("blink,blink_style", "Document::recalcStyle"); SCOPED_BLINK_UMA_HISTOGRAM_TIMER_HIGHRES("Style.RecalcTime"); Element* viewport_defining = ViewportDefiningElement(); - document_element->RecalcStyle(change); + GetStyleEngine().RecalcStyle(change); if (viewport_defining != ViewportDefiningElement()) ViewportDefiningElementDidChange(); } @@ -2315,8 +2315,7 @@ TRACE_EVENT0("blink,blink_style", "Document::rebuildLayoutTree"); SCOPED_BLINK_UMA_HISTOGRAM_TIMER_HIGHRES("Style.RebuildLayoutTreeTime"); ReattachLegacyLayoutObjectList legacy_layout_objects(*this); - WhitespaceAttacher whitespace_attacher; - document_element->RebuildLayoutTree(whitespace_attacher); + GetStyleEngine().RebuildLayoutTree(); legacy_layout_objects.ForceLegacyLayoutIfNeeded(); } }
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc index dfd5ac9a..c5d19bd 100644 --- a/third_party/blink/renderer/core/dom/element.cc +++ b/third_party/blink/renderer/core/dom/element.cc
@@ -1439,12 +1439,13 @@ return rare_data.EnsureAccessibleNode(this); } -const AtomicString& Element::invisible() const { - return FastGetAttribute(invisibleAttr); -} - -void Element::setInvisible(const AtomicString& value) { - setAttribute(invisibleAttr, value); +InvisibleState Element::Invisible() const { + const AtomicString& value = FastGetAttribute(invisibleAttr); + if (value.IsNull()) + return InvisibleState::kMissing; + if (EqualIgnoringASCIICase(value, "static")) + return InvisibleState::kStatic; + return InvisibleState::kInvisible; } void Element::DispatchActivateInvisibleEventIfNeeded() { @@ -1459,7 +1460,8 @@ HeapVector<Member<Element>> invisible_ancestors; HeapVector<Member<Element>> activated_elements; for (Node& ancestor : FlatTreeTraversal::InclusiveAncestorsOf(*this)) { - if (ancestor.IsElementNode() && ToElement(ancestor).invisible()) { + if (ancestor.IsElementNode() && + ToElement(ancestor).Invisible() != InvisibleState::kMissing) { invisible_ancestors.push_back(ToElement(ancestor)); activated_elements.push_back(ancestor.GetTreeScope().Retarget(*this)); } @@ -1473,10 +1475,28 @@ } } -void Element::InvisibleAttributeChanged() { - SetNeedsStyleRecalc( - kLocalStyleChange, - StyleChangeReasonForTracing::Create(StyleChangeReason::kInvisibleChange)); +bool Element::IsInsideInvisibleStaticSubtree() { + for (Node& ancestor : FlatTreeTraversal::InclusiveAncestorsOf(*this)) { + if (ancestor.IsElementNode() && + ToElement(ancestor).Invisible() == InvisibleState::kStatic) + return true; + } + return false; +} + +void Element::InvisibleAttributeChanged(const AtomicString& old_value, + const AtomicString& new_value) { + if (old_value.IsNull() != new_value.IsNull()) { + SetNeedsStyleRecalc(kLocalStyleChange, + StyleChangeReasonForTracing::Create( + StyleChangeReason::kInvisibleChange)); + } + if (EqualIgnoringASCIICase(old_value, "static") && + !IsInsideInvisibleStaticSubtree()) { + // This element and its descendants are not in an invisible="static" tree + // anymore. + CustomElement::Registry(*this)->upgrade(this); + } } void Element::DefaultEventHandler(Event& event) { @@ -1788,8 +1808,8 @@ StyleChangeReasonForTracing::FromAttribute(name)); } else if (RuntimeEnabledFeatures::InvisibleDOMEnabled() && name == HTMLNames::invisibleAttr && - params.old_value.IsNull() != params.new_value.IsNull()) { - InvisibleAttributeChanged(); + params.old_value != params.new_value) { + InvisibleAttributeChanged(params.old_value, params.new_value); } }
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h index 899ea5b..311b33d 100644 --- a/third_party/blink/renderer/core/dom/element.h +++ b/third_party/blink/renderer/core/dom/element.h
@@ -121,6 +121,12 @@ kNameOrIdWithName, }; +enum class InvisibleState { + kMissing, + kStatic, + kInvisible, +}; + struct FocusParams { STACK_ALLOCATED(); @@ -333,9 +339,9 @@ AccessibleNode* ExistingAccessibleNode() const; AccessibleNode* accessibleNode(); - const AtomicString& invisible() const; - void setInvisible(const AtomicString&); + InvisibleState Invisible() const; void DispatchActivateInvisibleEventIfNeeded(); + bool IsInsideInvisibleStaticSubtree(); void DefaultEventHandler(Event&) override; @@ -508,9 +514,8 @@ void RecalcStyle(StyleRecalcChange); bool NeedsRebuildLayoutTree( const WhitespaceAttacher& whitespace_attacher) const { - // TODO(futhark@chromium.org): !CanParticipateInFlatTree() can be replaced - // by IsActiveV0InsertionPoint() when slots are always part of the flat - // tree, and removed completely when Shadow DOM V0 support is removed. + // TODO(futhark@chromium.org): !CanParticipateInFlatTree() can be removed + // when Shadow DOM V0 support is removed. return NeedsReattachLayoutTree() || ChildNeedsReattachLayoutTree() || !CanParticipateInFlatTree() || (whitespace_attacher.TraverseIntoDisplayContents() && @@ -992,7 +997,8 @@ void InlineStyleChanged(); void SetInlineStyleFromString(const AtomicString&); - void InvisibleAttributeChanged(); + void InvisibleAttributeChanged(const AtomicString& old_value, + const AtomicString& new_value); // If the only inherited changes in the parent element are independent, // these changes can be directly propagated to this element (the child).
diff --git a/third_party/blink/renderer/core/dom/element.idl b/third_party/blink/renderer/core/dom/element.idl index 4aba4f7..0726b0d 100644 --- a/third_party/blink/renderer/core/dom/element.idl +++ b/third_party/blink/renderer/core/dom/element.idl
@@ -135,7 +135,7 @@ // Accessibility Object Model [RuntimeEnabled=AccessibilityObjectModel] readonly attribute AccessibleNode? accessibleNode; - [RuntimeEnabled=InvisibleDOM, CEReactions, CustomElementCallbacks] attribute DOMString invisible; + [RuntimeEnabled=InvisibleDOM, CEReactions, CustomElementCallbacks, Reflect, ReflectOnly=("static","invisible"), ReflectEmpty="invisible", ReflectInvalid="invisible"] attribute DOMString invisible; // Event handler attributes attribute EventHandler onbeforecopy;
diff --git a/third_party/blink/renderer/core/dom/events/event_listener.h b/third_party/blink/renderer/core/dom/events/event_listener.h index 0e73cdb..70942bb3 100644 --- a/third_party/blink/renderer/core/dom/events/event_listener.h +++ b/third_party/blink/renderer/core/dom/events/event_listener.h
@@ -36,13 +36,7 @@ class CORE_EXPORT EventListener : public CustomWrappableAdapter { public: enum ListenerType { - // |kJSEventListenerType| corresponds to EventListener defined in standard: - // https://dom.spec.whatwg.org/#callbackdef-eventlistener kJSEventListenerType, - // |kJSEventHandlerType| corresponds to EventHandler defined in standard: - // https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-attributes - kJSEventHandlerType, - // These are for C++ native callbacks. kImageEventListenerType, kCPPEventListenerType, kConditionEventListenerType, @@ -70,14 +64,6 @@ ListenerType GetType() const { return type_; } - // Returns true if this EventListener is implemented based on JS object. - bool IsJSBased() const { - return type_ == kJSEventListenerType || type_ == kJSEventHandlerType; - } - - // Returns true if this EventListener is C++ native callback. - bool IsNativeBased() const { return !IsJSBased(); } - const char* NameInHeapSnapshot() const override { return "EventListener"; } protected:
diff --git a/third_party/blink/renderer/core/dom/events/event_target.cc b/third_party/blink/renderer/core/dom/events/event_target.cc index c42026d..80ceabc 100644 --- a/third_party/blink/renderer/core/dom/events/event_target.cc +++ b/third_party/blink/renderer/core/dom/events/event_target.cc
@@ -39,8 +39,7 @@ #include "third_party/blink/renderer/bindings/core/v8/event_listener_options_or_boolean.h" #include "third_party/blink/renderer/bindings/core/v8/script_event_listener.h" #include "third_party/blink/renderer/bindings/core/v8/source_location.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_event_listener_impl.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_abstract_event_listener.h" #include "third_party/blink/renderer/core/dom/events/event.h" #include "third_party/blink/renderer/core/dom/events/event_dispatch_forbidden_scope.h" #include "third_party/blink/renderer/core/dom/events/event_target_impl.h" @@ -290,34 +289,31 @@ if (RuntimeEnabledFeatures::SmoothScrollJSInterventionEnabled() && event_type == EventTypeNames::mousewheel && ToLocalDOMWindow() && event_listener && !options.hasPassive()) { - v8::Local<v8::Object> callback_object; - if (V8AbstractEventHandler* v8_listener = - V8AbstractEventHandler::Cast(event_listener)) - callback_object = v8_listener->GetExistingListenerObject(); - if (V8EventListenerImpl* v8_listener = - V8EventListenerImpl::Cast(event_listener)) - callback_object = v8_listener->GetListenerObject(); - if (!callback_object.IsEmpty() && callback_object->IsFunction() && - strcmp( - "ssc_wheel", - *v8::String::Utf8Value( - v8::Isolate::GetCurrent(), - v8::Local<v8::Function>::Cast(callback_object)->GetName())) == - 0) { - options.setPassive(true); - if (executing_window) { - UseCounter::Count(executing_window->document(), - WebFeature::kSmoothScrollJSInterventionActivated); + if (V8AbstractEventListener* v8_listener = + V8AbstractEventListener::Cast(event_listener)) { + v8::Local<v8::Object> function = v8_listener->GetExistingListenerObject(); + if (function->IsFunction() && + strcmp("ssc_wheel", + *v8::String::Utf8Value( + v8::Isolate::GetCurrent(), + v8::Local<v8::Function>::Cast(function)->GetName())) == + 0) { + options.setPassive(true); + if (executing_window) { + UseCounter::Count(executing_window->document(), + WebFeature::kSmoothScrollJSInterventionActivated); - executing_window->GetFrame()->Console().AddMessage( - ConsoleMessage::Create( - kInterventionMessageSource, kWarningMessageLevel, - "Registering mousewheel event as passive due to " - "smoothscroll.js usage. The smoothscroll.js library is " - "buggy, no longer necessary and degrades performance. See " - "https://www.chromestatus.com/feature/5749447073988608")); + executing_window->GetFrame()->Console().AddMessage( + ConsoleMessage::Create( + kInterventionMessageSource, kWarningMessageLevel, + "Registering mousewheel event as passive due to " + "smoothscroll.js usage. The smoothscroll.js library is " + "buggy, no longer necessary and degrades performance. See " + "https://www.chromestatus.com/feature/5749447073988608")); + } + + return; } - return; } } @@ -406,7 +402,8 @@ event_type, listener, options, ®istered_listener); if (added) { AddedEventListener(event_type, registered_listener); - if (listener->IsJSBased() && IsInstrumentedForAsyncStack(event_type)) { + if (V8AbstractEventListener::Cast(listener) && + IsInstrumentedForAsyncStack(event_type)) { probe::AsyncTaskScheduled(GetExecutionContext(), event_type, listener); } } @@ -541,7 +538,8 @@ return false; } if (registered_listener) { - if (listener->IsJSBased() && IsInstrumentedForAsyncStack(event_type)) { + if (V8AbstractEventListener::Cast(listener) && + IsInstrumentedForAsyncStack(event_type)) { probe::AsyncTaskScheduled(GetExecutionContext(), event_type, listener); } registered_listener->SetCallback(listener);
diff --git a/third_party/blink/renderer/core/dom/layout_tree_builder.cc b/third_party/blink/renderer/core/dom/layout_tree_builder.cc index cc566ec..0c2925b 100644 --- a/third_party/blink/renderer/core/dom/layout_tree_builder.cc +++ b/third_party/blink/renderer/core/dom/layout_tree_builder.cc
@@ -298,10 +298,8 @@ for (const LayoutObject* block : blocks_) ToElement(*block->GetNode()).LazyReattachIfAttached(); state_ = State::kForcingLegacyLayout; - Element& document_element = *document_->documentElement(); - document_element.RecalcStyle(kNoChange); - WhitespaceAttacher whitespace_attacher; - document_element.RebuildLayoutTree(whitespace_attacher); + document_->GetStyleEngine().RecalcStyle(kNoChange); + document_->GetStyleEngine().RebuildLayoutTree(); state_ = State::kClosed; }
diff --git a/third_party/blink/renderer/core/dom/node_test.cc b/third_party/blink/renderer/core/dom/node_test.cc index b03e16f1..a977ddc6 100644 --- a/third_party/blink/renderer/core/dom/node_test.cc +++ b/third_party/blink/renderer/core/dom/node_test.cc
@@ -6,6 +6,7 @@ #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/core/css/resolver/style_resolver.h" +#include "third_party/blink/renderer/core/css/style_engine.h" #include "third_party/blink/renderer/core/dom/comment.h" #include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/dom/flat_tree_traversal.h" @@ -37,7 +38,7 @@ LayoutObject* ReattachLayoutTreeForNode(Node& node) { node.LazyReattachIfAttached(); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc); - GetDocument().documentElement()->RecalcStyle(kNoChange); + GetDocument().GetStyleEngine().RecalcStyle(kNoChange); PushSelectorFilterAncestors( GetDocument().EnsureStyleResolver().GetSelectorFilter(), node); ReattachLegacyLayoutObjectList legacy_objects(GetDocument());
diff --git a/third_party/blink/renderer/core/fetch/fetch_header_list.cc b/third_party/blink/renderer/core/fetch/fetch_header_list.cc index 2839f94..7bacec12 100644 --- a/third_party/blink/renderer/core/fetch/fetch_header_list.cc +++ b/third_party/blink/renderer/core/fetch/fetch_header_list.cc
@@ -128,13 +128,6 @@ header_list_.clear(); } -bool FetchHeaderList::ContainsNonCORSSafelistedHeader() const { - return std::any_of( - header_list_.cbegin(), header_list_.cend(), [](const Header& header) { - return !CORS::IsCORSSafelistedHeader(header.first, header.second); - }); -} - Vector<FetchHeaderList::Header> FetchHeaderList::SortAndCombine() const { // https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine // "To sort and combine a header list (|list|), run these steps:
diff --git a/third_party/blink/renderer/core/fetch/fetch_header_list.h b/third_party/blink/renderer/core/fetch/fetch_header_list.h index e84f0985..7cd863c 100644 --- a/third_party/blink/renderer/core/fetch/fetch_header_list.h +++ b/third_party/blink/renderer/core/fetch/fetch_header_list.h
@@ -41,7 +41,6 @@ bool Has(const String&) const; void ClearList(); - bool ContainsNonCORSSafelistedHeader() const; Vector<Header> SortAndCombine() const; const std::multimap<String, String, ByteCaseInsensitiveCompare>& List() @@ -67,8 +66,7 @@ // This would cause FetchHeaderList::size() to have to manually // iterate through all keys and vectors in the HashMap. Similarly, // list() would require callers to manually iterate through the - // HashMap's keys and value vector, and so would - // ContainsNonCORSSafelistedHeader(). + // HashMap's keys and value vector. std::multimap<String, String, ByteCaseInsensitiveCompare> header_list_; };
diff --git a/third_party/blink/renderer/core/fetch/fetch_header_list_test.cc b/third_party/blink/renderer/core/fetch/fetch_header_list_test.cc index e23bc70..05b0f54 100644 --- a/third_party/blink/renderer/core/fetch/fetch_header_list_test.cc +++ b/third_party/blink/renderer/core/fetch/fetch_header_list_test.cc
@@ -117,26 +117,6 @@ EXPECT_FALSE(headerList->Has("X-Bar")); } -TEST(FetchHeaderListTest, ContainsNonCORSSafelistedHeader) { - FetchHeaderList* headerList = FetchHeaderList::Create(); - EXPECT_FALSE(headerList->ContainsNonCORSSafelistedHeader()); - - headerList->Append("Host", "foobar"); - headerList->Append("X-Foo", "bar"); - EXPECT_TRUE(headerList->ContainsNonCORSSafelistedHeader()); - - headerList->ClearList(); - headerList->Append("ConTenT-TyPe", "text/plain"); - headerList->Append("content-type", "application/xml"); - headerList->Append("X-Foo", "bar"); - EXPECT_TRUE(headerList->ContainsNonCORSSafelistedHeader()); - - headerList->ClearList(); - headerList->Append("ConTenT-TyPe", "multipart/form-data"); - headerList->Append("Accept", "xyz"); - EXPECT_FALSE(headerList->ContainsNonCORSSafelistedHeader()); -} - TEST(FetchHeaderListTest, SortAndCombine) { FetchHeaderList* headerList = FetchHeaderList::Create(); EXPECT_TRUE(headerList->SortAndCombine().IsEmpty());
diff --git a/third_party/blink/renderer/core/fetch/fetch_manager.cc b/third_party/blink/renderer/core/fetch/fetch_manager.cc index 9cc875b..9a2df6fc 100644 --- a/third_party/blink/renderer/core/fetch/fetch_manager.cc +++ b/third_party/blink/renderer/core/fetch/fetch_manager.cc
@@ -858,27 +858,9 @@ request.SetUseStreamOnResponse(true); request.SetExternalRequestStateFromRequestorAddressSpace( execution_context_->GetSecurityContext().AddressSpace()); + request.SetReferrerString(fetch_request_data_->ReferrerString()); + request.SetReferrerPolicy(fetch_request_data_->GetReferrerPolicy()); - // "2. Append `Referer`/empty byte sequence, if |HTTPRequest|'s |referrer| - // is none, and `Referer`/|HTTPRequest|'s referrer, serialized and utf-8 - // encoded, otherwise, to HTTPRequest's header list. - // - // The following code also invokes "determine request's referrer" which is - // written in "Main fetch" operation. - const ReferrerPolicy referrer_policy = - fetch_request_data_->GetReferrerPolicy() == kReferrerPolicyDefault - ? execution_context_->GetReferrerPolicy() - : fetch_request_data_->GetReferrerPolicy(); - const String referrer_string = - fetch_request_data_->ReferrerString() == Referrer::ClientReferrerString() - ? execution_context_->OutgoingReferrer() - : fetch_request_data_->ReferrerString(); - // Note that generateReferrer generates |no-referrer| from |no-referrer| - // referrer string (i.e. String()). - // TODO(domfarolino): Can we use ResourceRequest's SetReferrerString() and - // SetReferrerPolicy() instead of calling SetHTTPReferrer()? - request.SetHTTPReferrer(SecurityPolicy::GenerateReferrer( - referrer_policy, fetch_request_data_->Url(), referrer_string)); request.SetSkipServiceWorker(is_isolated_world_); if (fetch_request_data_->Keepalive()) {
diff --git a/third_party/blink/renderer/core/fetch/form_data_bytes_consumer.cc b/third_party/blink/renderer/core/fetch/form_data_bytes_consumer.cc index 2f69f7c..80737ae 100644 --- a/third_party/blink/renderer/core/fetch/form_data_bytes_consumer.cc +++ b/third_party/blink/renderer/core/fetch/form_data_bytes_consumer.cc
@@ -475,7 +475,7 @@ FormDataBytesConsumer::FormDataBytesConsumer(const String& string) : impl_(new SimpleFormDataBytesConsumer(EncodedFormData::Create( - UTF8Encoding().Encode(string, WTF::kEntitiesForUnencodables)))) {} + UTF8Encoding().Encode(string, WTF::kNoUnencodables)))) {} FormDataBytesConsumer::FormDataBytesConsumer(DOMArrayBuffer* buffer) : FormDataBytesConsumer(buffer->Data(), buffer->ByteLength()) {}
diff --git a/third_party/blink/renderer/core/fetch/headers.cc b/third_party/blink/renderer/core/fetch/headers.cc index fc60cc3c..a581c74 100644 --- a/third_party/blink/renderer/core/fetch/headers.cc +++ b/third_party/blink/renderer/core/fetch/headers.cc
@@ -98,9 +98,9 @@ if (guard_ == kRequestGuard && CORS::IsForbiddenHeaderName(name)) return; // "5. Otherwise, if guard is |request-no-CORS| and |name|/|value| is not a - // CORS-safelisted header, return." + // no-CORS-safelisted header, return." if (guard_ == kRequestNoCORSGuard && - !CORS::IsCORSSafelistedHeader(name, normalized_value)) { + !CORS::IsNoCORSSafelistedHeader(name, normalized_value)) { return; } // "6. Otherwise, if guard is |response| and |name| is a forbidden response @@ -130,9 +130,9 @@ if (guard_ == kRequestGuard && CORS::IsForbiddenHeaderName(name)) return; // "4. Otherwise, if guard is |request-no-CORS| and |name|/`invalid` is not - // a CORS-safelisted header, return." + // a no-CORS-safelisted header, return." if (guard_ == kRequestNoCORSGuard && - !CORS::IsCORSSafelistedHeader(name, "invalid")) { + !CORS::IsNoCORSSafelistedHeader(name, "invalid")) { return; } // "5. Otherwise, if guard is |response| and |name| is a forbidden response @@ -198,9 +198,9 @@ if (guard_ == kRequestGuard && CORS::IsForbiddenHeaderName(name)) return; // "5. Otherwise, if guard is |request-no-CORS| and |name|/|value| is not a - // CORS-safelisted header, return." + // no-CORS-safelisted header, return." if (guard_ == kRequestNoCORSGuard && - !CORS::IsCORSSafelistedHeader(name, normalized_value)) { + !CORS::IsNoCORSSafelistedHeader(name, normalized_value)) { return; } // "6. Otherwise, if guard is |response| and |name| is a forbidden response
diff --git a/third_party/blink/renderer/core/html/custom/custom_element.cc b/third_party/blink/renderer/core/html/custom/custom_element.cc index 2675165..a089d6b 100644 --- a/third_party/blink/renderer/core/html/custom/custom_element.cc +++ b/third_party/blink/renderer/core/html/custom/custom_element.cc
@@ -230,7 +230,8 @@ new_value); } -void CustomElement::TryToUpgrade(Element* element) { +void CustomElement::TryToUpgrade(Element* element, + bool upgrade_invisible_elements) { // Try to upgrade an element // https://html.spec.whatwg.org/multipage/scripting.html#concept-try-upgrade @@ -244,7 +245,7 @@ registry->DefinitionFor(CustomElementDescriptor( is_value.IsNull() ? element->localName() : is_value, element->localName()))) - definition->EnqueueUpgradeReaction(element); + definition->EnqueueUpgradeReaction(element, upgrade_invisible_elements); else registry->AddCandidate(element); }
diff --git a/third_party/blink/renderer/core/html/custom/custom_element.h b/third_party/blink/renderer/core/html/custom/custom_element.h index 9dc8887..4d21f54 100644 --- a/third_party/blink/renderer/core/html/custom/custom_element.h +++ b/third_party/blink/renderer/core/html/custom/custom_element.h
@@ -94,7 +94,7 @@ const AtomicString& old_value, const AtomicString& new_value); - static void TryToUpgrade(Element*); + static void TryToUpgrade(Element*, bool upgrade_invisible_elements = false); private: // Some existing specs have element names with hyphens in them,
diff --git a/third_party/blink/renderer/core/html/custom/custom_element_definition.cc b/third_party/blink/renderer/core/html/custom/custom_element_definition.cc index c927986..1dc7f5275 100644 --- a/third_party/blink/renderer/core/html/custom/custom_element_definition.cc +++ b/third_party/blink/renderer/core/html/custom/custom_element_definition.cc
@@ -223,8 +223,11 @@ return has_style_attribute_changed_callback_; } -void CustomElementDefinition::EnqueueUpgradeReaction(Element* element) { - CustomElement::Enqueue(element, new CustomElementUpgradeReaction(this)); +void CustomElementDefinition::EnqueueUpgradeReaction( + Element* element, + bool upgrade_invisible_elements) { + CustomElement::Enqueue(element, new CustomElementUpgradeReaction( + this, upgrade_invisible_elements)); } void CustomElementDefinition::EnqueueConnectedCallback(Element* element) {
diff --git a/third_party/blink/renderer/core/html/custom/custom_element_definition.h b/third_party/blink/renderer/core/html/custom/custom_element_definition.h index 009e8c08..1c47dec2 100644 --- a/third_party/blink/renderer/core/html/custom/custom_element_definition.h +++ b/third_party/blink/renderer/core/html/custom/custom_element_definition.h
@@ -79,7 +79,8 @@ const AtomicString& old_value, const AtomicString& new_value) = 0; - void EnqueueUpgradeReaction(Element*); + void EnqueueUpgradeReaction(Element*, + bool upgrade_invisible_elements = false); void EnqueueConnectedCallback(Element*); void EnqueueDisconnectedCallback(Element*); void EnqueueAdoptedCallback(Element*,
diff --git a/third_party/blink/renderer/core/html/custom/custom_element_registry.cc b/third_party/blink/renderer/core/html/custom/custom_element_registry.cc index e1f7583d..b6e28d1 100644 --- a/third_party/blink/renderer/core/html/custom/custom_element_registry.cc +++ b/third_party/blink/renderer/core/html/custom/custom_element_registry.cc
@@ -371,7 +371,8 @@ // 2. For each candidate of candidates, try to upgrade candidate. for (auto& candidate : candidates) { - CustomElement::TryToUpgrade(candidate); + CustomElement::TryToUpgrade(candidate, + true /* upgrade_invisible_elements */); } }
diff --git a/third_party/blink/renderer/core/html/custom/custom_element_upgrade_reaction.cc b/third_party/blink/renderer/core/html/custom/custom_element_upgrade_reaction.cc index 7a91660..8c49a3e6 100644 --- a/third_party/blink/renderer/core/html/custom/custom_element_upgrade_reaction.cc +++ b/third_party/blink/renderer/core/html/custom/custom_element_upgrade_reaction.cc
@@ -5,19 +5,28 @@ #include "third_party/blink/renderer/core/html/custom/custom_element_upgrade_reaction.h" #include "third_party/blink/renderer/core/html/custom/custom_element_definition.h" +#include "third_party/blink/renderer/platform/runtime_enabled_features.h" namespace blink { CustomElementUpgradeReaction::CustomElementUpgradeReaction( - CustomElementDefinition* definition) - : CustomElementReaction(definition) {} + CustomElementDefinition* definition, + bool upgrade_invisible_elements) + : CustomElementReaction(definition), + upgrade_invisible_elements_(upgrade_invisible_elements) {} void CustomElementUpgradeReaction::Invoke(Element* element) { // Don't call upgrade() if it's already upgraded. Multiple upgrade reactions // could be enqueued because the state changes in step 10 of upgrades. // https://html.spec.whatwg.org/multipage/scripting.html#upgrades - if (element->GetCustomElementState() == CustomElementState::kUndefined) - definition_->Upgrade(element); + if (element->GetCustomElementState() == CustomElementState::kUndefined) { + // Don't upgrade elements inside an invisible-static tree, unless it was + // triggered by CustomElementRegistry::upgrade. + if (!RuntimeEnabledFeatures::InvisibleDOMEnabled() || + !element->IsInsideInvisibleStaticSubtree() || + upgrade_invisible_elements_) + definition_->Upgrade(element); + } } } // namespace blink
diff --git a/third_party/blink/renderer/core/html/custom/custom_element_upgrade_reaction.h b/third_party/blink/renderer/core/html/custom/custom_element_upgrade_reaction.h index 4e3ee67a..7cb4ad0 100644 --- a/third_party/blink/renderer/core/html/custom/custom_element_upgrade_reaction.h +++ b/third_party/blink/renderer/core/html/custom/custom_element_upgrade_reaction.h
@@ -17,11 +17,14 @@ class CORE_EXPORT CustomElementUpgradeReaction final : public CustomElementReaction { public: - CustomElementUpgradeReaction(CustomElementDefinition*); + CustomElementUpgradeReaction(CustomElementDefinition*, + bool upgrade_invisible_elements); private: void Invoke(Element*) override; + bool upgrade_invisible_elements_; + DISALLOW_COPY_AND_ASSIGN(CustomElementUpgradeReaction); };
diff --git a/third_party/blink/renderer/core/html/html_content_element_test.cc b/third_party/blink/renderer/core/html/html_content_element_test.cc index 7b36534d..0fbc4ee2 100644 --- a/third_party/blink/renderer/core/html/html_content_element_test.cc +++ b/third_party/blink/renderer/core/html/html_content_element_test.cc
@@ -5,6 +5,7 @@ #include "third_party/blink/renderer/core/html/html_content_element.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/core/css/style_engine.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/shadow_root.h" #include "third_party/blink/renderer/core/testing/dummy_page_holder.h" @@ -38,7 +39,7 @@ GetDocument().UpdateDistributionForLegacyDistributedNodes(); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc); - GetDocument().documentElement()->RecalcStyle(kNoChange); + GetDocument().GetStyleEngine().RecalcStyle(kNoChange); EXPECT_TRUE(fallback->GetNonAttachedStyle()); }
diff --git a/third_party/blink/renderer/core/html/html_object_element_test.cc b/third_party/blink/renderer/core/html/html_object_element_test.cc index 04582be..4e10fe86 100644 --- a/third_party/blink/renderer/core/html/html_object_element_test.cc +++ b/third_party/blink/renderer/core/html/html_object_element_test.cc
@@ -5,6 +5,7 @@ #include "third_party/blink/renderer/core/html/html_object_element.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/core/css/style_engine.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/shadow_root.h" #include "third_party/blink/renderer/core/testing/dummy_page_holder.h" @@ -38,7 +39,7 @@ object->RenderFallbackContent(); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc); - GetDocument().documentElement()->RecalcStyle(kForce); + GetDocument().GetStyleEngine().RecalcStyle(kForce); EXPECT_TRUE(IsHTMLSlotElement(slot)); EXPECT_TRUE(object->UseFallbackContent());
diff --git a/third_party/blink/renderer/core/html/html_slot_element_test.cc b/third_party/blink/renderer/core/html/html_slot_element_test.cc index 0dd9204..9d4ac83 100644 --- a/third_party/blink/renderer/core/html/html_slot_element_test.cc +++ b/third_party/blink/renderer/core/html/html_slot_element_test.cc
@@ -7,6 +7,7 @@ #include <array> #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/core/css/style_engine.h" #include "third_party/blink/renderer/core/dom/shadow_root.h" #include "third_party/blink/renderer/core/testing/dummy_page_holder.h" @@ -161,7 +162,7 @@ shadow_span.setAttribute(HTMLNames::styleAttr, "display:block"); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc); - GetDocument().documentElement()->RecalcStyle(kNoChange); + GetDocument().GetStyleEngine().RecalcStyle(kNoChange); EXPECT_TRUE(shadow_span.GetNonAttachedStyle()); EXPECT_TRUE(span.GetNonAttachedStyle());
diff --git a/third_party/blink/renderer/core/html/shadow/progress_shadow_element_test.cc b/third_party/blink/renderer/core/html/shadow/progress_shadow_element_test.cc index 8f23c8d..213362b 100644 --- a/third_party/blink/renderer/core/html/shadow/progress_shadow_element_test.cc +++ b/third_party/blink/renderer/core/html/shadow/progress_shadow_element_test.cc
@@ -5,6 +5,7 @@ #include "third_party/blink/renderer/core/html/shadow/progress_shadow_element.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/core/css/style_engine.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/node_computed_style.h" #include "third_party/blink/renderer/core/dom/shadow_root.h" @@ -40,7 +41,7 @@ progress->LazyReattachIfAttached(); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc); - GetDocument().documentElement()->RecalcStyle(kForce); + GetDocument().GetStyleEngine().RecalcStyle(kForce); EXPECT_TRUE(shadow_element->GetNonAttachedStyle()); scoped_refptr<ComputedStyle> style = shadow_element->StyleForLayoutObject();
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc index 6267cd8..211bc57 100644 --- a/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc +++ b/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc
@@ -108,7 +108,7 @@ continue; for (size_t k = 0; k < listeners->size(); ++k) { EventListener* event_listener = listeners->at(k).Callback(); - if (event_listener->IsNativeBased()) + if (event_listener->GetType() != EventListener::kJSEventListenerType) continue; // TODO(yukiy): Use a child class of blink::EventListener that is for v8 // event listeners here if it is implemented in redesigning @@ -118,8 +118,8 @@ // Optionally hide listeners from other contexts. if (!report_for_all_contexts && context != isolate->GetCurrentContext()) continue; - // GetListenerObjectForInspector() may cause JS in the event attribute to - // get compiled, potentially unsuccessfully. In that case, the function + // getListenerObject() may cause JS in the event attribute to get + // compiled, potentially unsuccessfully. In that case, the function // returns the empty handle without an exception. v8::Local<v8::Object> handler = event_listener->GetListenerObjectForInspector(execution_context);
diff --git a/third_party/blink/renderer/core/loader/threadable_loader.cc b/third_party/blink/renderer/core/loader/threadable_loader.cc index 15acdfc..040bd2e 100644 --- a/third_party/blink/renderer/core/loader/threadable_loader.cc +++ b/third_party/blink/renderer/core/loader/threadable_loader.cc
@@ -79,20 +79,8 @@ // Fetch API Spec: https://fetch.spec.whatwg.org/#cors-preflight-fetch-0 AtomicString CreateAccessControlRequestHeadersHeader( const HTTPHeaderMap& headers) { - Vector<String> filtered_headers; - for (const auto& header : headers) { - // Exclude CORS-safelisted headers. - if (CORS::IsCORSSafelistedHeader(header.key, header.value)) - continue; - // Calling a deprecated function, but eventually this function, - // |CreateAccessControlRequestHeadersHeader| will be removed. - // When the request is from a Worker, referrer header was added by - // WorkerThreadableLoader. But it should not be added to - // Access-Control-Request-Headers header. - if (DeprecatedEqualIgnoringCase(header.key, "referer")) - continue; - filtered_headers.push_back(header.key.DeprecatedLower()); - } + Vector<String> filtered_headers = CORS::CORSUnsafeRequestHeaderNames(headers); + if (!filtered_headers.size()) return g_null_atom; @@ -176,9 +164,7 @@ preflight_request->SetFetchCredentialsMode( network::mojom::FetchCredentialsMode::kOmit); preflight_request->SetSkipServiceWorker(true); - // TODO(domfarolino): Use ReferrerString() once https://crbug.com/850813 is - // closed and we stop storing the referrer string as a `Referer` header. - preflight_request->SetReferrerString(request.HttpReferrer()); + preflight_request->SetReferrerString(request.ReferrerString()); preflight_request->SetReferrerPolicy(request.GetReferrerPolicy()); if (request.IsExternalRequest()) {
diff --git a/third_party/blink/renderer/core/paint/box_model_object_painter.cc b/third_party/blink/renderer/core/paint/box_model_object_painter.cc index bd4552d..1e10f88 100644 --- a/third_party/blink/renderer/core/paint/box_model_object_painter.cc +++ b/third_party/blink/renderer/core/paint/box_model_object_painter.cc
@@ -4,10 +4,12 @@ #include "third_party/blink/renderer/core/paint/box_model_object_painter.h" -#include "third_party/blink/renderer/core/layout/layout_block.h" +#include "third_party/blink/renderer/core/layout/layout_block_flow.h" #include "third_party/blink/renderer/core/layout/layout_box_model_object.h" +#include "third_party/blink/renderer/core/layout/layout_inline.h" #include "third_party/blink/renderer/core/layout/line/root_inline_box.h" #include "third_party/blink/renderer/core/paint/background_image_geometry.h" +#include "third_party/blink/renderer/core/paint/line_box_list_painter.h" #include "third_party/blink/renderer/core/paint/object_painter.h" #include "third_party/blink/renderer/core/paint/paint_info.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" @@ -83,9 +85,14 @@ flow_box_->Paint(paint_info, paint_offset - local_offset, root.LineTop(), root.LineBottom()); } else { - // We should go through the above path for LayoutInlines. - DCHECK(box_model_.IsLayoutBlock()); - ToLayoutBlock(box_model_).PaintObject(paint_info, paint_offset); + const LineBoxList* line_boxes = nullptr; + if (box_model_.IsLayoutBlockFlow()) + line_boxes = &ToLayoutBlockFlow(box_model_).LineBoxes(); + else if (box_model_.IsLayoutInline()) + line_boxes = ToLayoutInline(box_model_).LineBoxes(); + if (!line_boxes) + return; + LineBoxListPainter(*line_boxes).Paint(box_model_, paint_info, paint_offset); } }
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc index 2722d868..43f1acd 100644 --- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc +++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
@@ -1482,6 +1482,11 @@ LayoutPoint host_layer_position; if (NeedsToReparentOverflowControls()) { + // This should never be true, but for some reason it is. + // See https://crbug.com/880930. + if (!compositing_stacking_context) + return; + CompositedLayerMapping* stacking_clm = compositing_stacking_context->GetCompositedLayerMapping(); DCHECK(stacking_clm);
diff --git a/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc b/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc index d6b476c..97610c21 100644 --- a/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc +++ b/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc
@@ -398,6 +398,16 @@ return GetVisualViewport().ScrollLayer(); } +#if DCHECK_IS_ON() +static void AssertWholeTreeNotComposited(const PaintLayer& paint_layer) { + DCHECK(paint_layer.GetCompositingState() == kNotComposited); + for (PaintLayer* child = paint_layer.FirstChild(); child; + child = child->NextSibling()) { + AssertWholeTreeNotComposited(*child); + } +} +#endif + void PaintLayerCompositor::UpdateIfNeeded( DocumentLifecycle::LifecycleState target_state, CompositingReasonsStats& compositing_reasons_stats) { @@ -485,6 +495,12 @@ ->Parent(); } +#if DCHECK_IS_ON() + if (update_root->GetCompositingState() != kPaintsIntoOwnBacking) { + AssertWholeTreeNotComposited(*update_root); + } +#endif + GraphicsLayerUpdater updater; updater.Update(*update_root, layers_needing_paint_invalidation);
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc index f17d4cd1d..10fc9e5 100644 --- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc +++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
@@ -762,8 +762,8 @@ bool object_has_multiple_boxes) { PaintInfo paint_info(context, mask_rect, PaintPhase::kTextClip, kGlobalPaintNormalPhase, 0); - LayoutSize local_offset = box_fragment_.Offset().ToLayoutSize(); if (object_has_multiple_boxes) { + LayoutSize local_offset = box_fragment_.Offset().ToLayoutSize(); NGInlineBoxFragmentPainter inline_box_painter(box_fragment_); if (box_fragment_.Style().BoxDecorationBreak() == EBoxDecorationBreak::kSlice) { @@ -778,7 +778,7 @@ } inline_box_painter.Paint(paint_info, paint_offset - local_offset); } else { - PaintObject(paint_info, paint_offset - local_offset); + PaintObject(paint_info, paint_offset); } }
diff --git a/third_party/blink/renderer/core/svg/svg_animated_integer.h b/third_party/blink/renderer/core/svg/svg_animated_integer.h index 4c9ad639..aed647f 100644 --- a/third_party/blink/renderer/core/svg/svg_animated_integer.h +++ b/third_party/blink/renderer/core/svg/svg_animated_integer.h
@@ -49,6 +49,13 @@ public: static SVGAnimatedInteger* Create(SVGElement* context_element, const QualifiedName& attribute_name, + int initial) { + SVGInteger* initial_value = SVGInteger::Create(initial); + return new SVGAnimatedInteger(context_element, attribute_name, + initial_value); + } + static SVGAnimatedInteger* Create(SVGElement* context_element, + const QualifiedName& attribute_name, SVGInteger* initial_value) { return new SVGAnimatedInteger(context_element, attribute_name, initial_value); @@ -69,7 +76,9 @@ SVGInteger* initial_value) : SVGAnimatedProperty<SVGInteger>(context_element, attribute_name, - initial_value), + initial_value, + CSSPropertyInvalid, + initial_value->Value()), parent_integer_optional_integer_(nullptr) {} Member<SVGAnimatedIntegerOptionalInteger> parent_integer_optional_integer_;
diff --git a/third_party/blink/renderer/core/svg/svg_animated_integer_optional_integer.cc b/third_party/blink/renderer/core/svg/svg_animated_integer_optional_integer.cc index 9d4b1ce..a3460f0 100644 --- a/third_party/blink/renderer/core/svg/svg_animated_integer_optional_integer.cc +++ b/third_party/blink/renderer/core/svg/svg_animated_integer_optional_integer.cc
@@ -35,14 +35,14 @@ SVGAnimatedIntegerOptionalInteger::SVGAnimatedIntegerOptionalInteger( SVGElement* context_element, const QualifiedName& attribute_name, - float initial_first_value, - float initial_second_value) + int initial_value) : SVGAnimatedPropertyCommon<SVGIntegerOptionalInteger>( context_element, attribute_name, - SVGIntegerOptionalInteger::Create( - SVGInteger::Create(initial_first_value), - SVGInteger::Create(initial_second_value))), + SVGIntegerOptionalInteger::Create(SVGInteger::Create(initial_value), + SVGInteger::Create(initial_value)), + CSSPropertyInvalid, + initial_value), first_integer_(SVGAnimatedInteger::Create(context_element, attribute_name, BaseValue()->FirstInteger())),
diff --git a/third_party/blink/renderer/core/svg/svg_animated_integer_optional_integer.h b/third_party/blink/renderer/core/svg/svg_animated_integer_optional_integer.h index 09e6942..a7a095b 100644 --- a/third_party/blink/renderer/core/svg/svg_animated_integer_optional_integer.h +++ b/third_party/blink/renderer/core/svg/svg_animated_integer_optional_integer.h
@@ -52,11 +52,9 @@ static SVGAnimatedIntegerOptionalInteger* Create( SVGElement* context_element, const QualifiedName& attribute_name, - float initial_first_value = 0, - float initial_second_value = 0) { - return new SVGAnimatedIntegerOptionalInteger( - context_element, attribute_name, initial_first_value, - initial_second_value); + int initial_value) { + return new SVGAnimatedIntegerOptionalInteger(context_element, + attribute_name, initial_value); } void SetAnimatedValue(SVGPropertyBase*) override; @@ -71,8 +69,7 @@ protected: SVGAnimatedIntegerOptionalInteger(SVGElement* context_element, const QualifiedName& attribute_name, - float initial_first_value, - float initial_second_value); + int initial_value); Member<SVGAnimatedInteger> first_integer_; Member<SVGAnimatedInteger> second_integer_;
diff --git a/third_party/blink/renderer/core/svg/svg_fe_convolve_matrix_element.cc b/third_party/blink/renderer/core/svg/svg_fe_convolve_matrix_element.cc index f9634c7..78cda0df 100644 --- a/third_party/blink/renderer/core/svg/svg_fe_convolve_matrix_element.cc +++ b/third_party/blink/renderer/core/svg/svg_fe_convolve_matrix_element.cc
@@ -50,8 +50,7 @@ SVGAnimatedOrder(SVGElement* context_element) : SVGAnimatedIntegerOptionalInteger(context_element, SVGNames::orderAttr, - 0, - 0) {} + 3) {} static SVGParsingError CheckValue(SVGParsingError parse_status, int value) { if (parse_status != SVGParseStatus::kNoError) @@ -94,12 +93,8 @@ order_(SVGAnimatedOrder::Create(this)), preserve_alpha_( SVGAnimatedBoolean::Create(this, SVGNames::preserveAlphaAttr)), - target_x_(SVGAnimatedInteger::Create(this, - SVGNames::targetXAttr, - SVGInteger::Create())), - target_y_(SVGAnimatedInteger::Create(this, - SVGNames::targetYAttr, - SVGInteger::Create())) { + target_x_(SVGAnimatedInteger::Create(this, SVGNames::targetXAttr, 0)), + target_y_(SVGAnimatedInteger::Create(this, SVGNames::targetYAttr, 0)) { AddToPropertyMap(preserve_alpha_); AddToPropertyMap(divisor_); AddToPropertyMap(bias_);
diff --git a/third_party/blink/renderer/core/svg/svg_fe_turbulence_element.cc b/third_party/blink/renderer/core/svg/svg_fe_turbulence_element.cc index 2a5626c2..6ace7ca2 100644 --- a/third_party/blink/renderer/core/svg/svg_fe_turbulence_element.cc +++ b/third_party/blink/renderer/core/svg/svg_fe_turbulence_element.cc
@@ -61,9 +61,8 @@ this, SVGNames::typeAttr, FETURBULENCE_TYPE_TURBULENCE)), - num_octaves_(SVGAnimatedInteger::Create(this, - SVGNames::numOctavesAttr, - SVGInteger::Create(1))) { + num_octaves_( + SVGAnimatedInteger::Create(this, SVGNames::numOctavesAttr, 1)) { AddToPropertyMap(base_frequency_); AddToPropertyMap(seed_); AddToPropertyMap(stitch_tiles_);
diff --git a/third_party/blink/renderer/core/svg/svg_integer.h b/third_party/blink/renderer/core/svg/svg_integer.h index b08fc11..2abffc47 100644 --- a/third_party/blink/renderer/core/svg/svg_integer.h +++ b/third_party/blink/renderer/core/svg/svg_integer.h
@@ -64,6 +64,9 @@ static AnimatedPropertyType ClassType() { return kAnimatedInteger; } + void SetInitial(unsigned value) { SetValue(static_cast<int>(value)); } + static constexpr int kInitialValueBits = 2; + protected: explicit SVGInteger(int);
diff --git a/third_party/blink/renderer/core/svg/svg_integer_optional_integer.cc b/third_party/blink/renderer/core/svg/svg_integer_optional_integer.cc index 4ea35ac..02481d3 100644 --- a/third_party/blink/renderer/core/svg/svg_integer_optional_integer.cc +++ b/third_party/blink/renderer/core/svg/svg_integer_optional_integer.cc
@@ -81,6 +81,12 @@ return parse_status; } +void SVGIntegerOptionalInteger::SetInitial(unsigned value) { + // Propagate the value to the split representation. + first_integer_->SetInitial(value); + second_integer_->SetInitial(value); +} + void SVGIntegerOptionalInteger::Add(SVGPropertyBase* other, SVGElement*) { SVGIntegerOptionalInteger* other_integer_optional_integer = ToSVGIntegerOptionalInteger(other);
diff --git a/third_party/blink/renderer/core/svg/svg_integer_optional_integer.h b/third_party/blink/renderer/core/svg/svg_integer_optional_integer.h index fbdaf92..2caaf58 100644 --- a/third_party/blink/renderer/core/svg/svg_integer_optional_integer.h +++ b/third_party/blink/renderer/core/svg/svg_integer_optional_integer.h
@@ -53,6 +53,8 @@ String ValueAsString() const override; SVGParsingError SetValueAsString(const String&); + void SetInitial(unsigned); + static constexpr int kInitialValueBits = SVGInteger::kInitialValueBits; void Add(SVGPropertyBase*, SVGElement*) override; void CalculateAnimatedValue(SVGAnimationElement*,
diff --git a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc index c35bbd7e..d7ce75e 100644 --- a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc +++ b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
@@ -836,7 +836,7 @@ String body = CreateMarkup(document); http_body = EncodedFormData::Create( - UTF8Encoding().Encode(body, WTF::kEntitiesForUnencodables)); + UTF8Encoding().Encode(body, WTF::kNoUnencodables)); } CreateRequest(std::move(http_body), exception_state); @@ -852,7 +852,7 @@ if (!body.IsNull() && AreMethodAndURLValidForSend()) { http_body = EncodedFormData::Create( - UTF8Encoding().Encode(body, WTF::kEntitiesForUnencodables)); + UTF8Encoding().Encode(body, WTF::kNoUnencodables)); UpdateContentTypeAndCharset("text/plain;charset=UTF-8", "UTF-8"); }
diff --git a/third_party/blink/renderer/devtools/front_end/quick_open/module.json b/third_party/blink/renderer/devtools/front_end/quick_open/module.json index 7fa61716..ccdeab8e7 100644 --- a/third_party/blink/renderer/devtools/front_end/quick_open/module.json +++ b/third_party/blink/renderer/devtools/front_end/quick_open/module.json
@@ -14,6 +14,7 @@ { "type": "action", "actionId": "commandMenu.show", + "title": "Run command", "className": "QuickOpen.CommandMenu.ShowActionDelegate", "bindings": [ { @@ -27,6 +28,11 @@ ] }, { + "type": "context-menu-item", + "location": "mainMenu/default", + "actionId": "commandMenu.show" + }, + { "type": "action", "actionId": "quickOpen.show", "title": "Open file",
diff --git a/third_party/blink/renderer/modules/encoding/text_encoder.cc b/third_party/blink/renderer/modules/encoding/text_encoder.cc index 81726d58..3f9161f 100644 --- a/third_party/blink/renderer/modules/encoding/text_encoder.cc +++ b/third_party/blink/renderer/modules/encoding/text_encoder.cc
@@ -67,10 +67,10 @@ // are present in the input. if (input.Is8Bit()) { result = codec_->Encode(input.Characters8(), input.length(), - WTF::kEntitiesForUnencodables); + WTF::kNoUnencodables); } else { result = codec_->Encode(input.Characters16(), input.length(), - WTF::kEntitiesForUnencodables); + WTF::kNoUnencodables); } const char* buffer = result.data();
diff --git a/third_party/blink/renderer/modules/encoding/text_encoder_stream.cc b/third_party/blink/renderer/modules/encoding/text_encoder_stream.cc index 130874d..ee4599e 100644 --- a/third_party/blink/renderer/modules/encoding/text_encoder_stream.cc +++ b/third_party/blink/renderer/modules/encoding/text_encoder_stream.cc
@@ -60,14 +60,8 @@ // check is needed. prefix = ReplacementCharacterInUtf8(); } - // Note that the third argument here is ignored since the encoding is - // UTF-8, which will use U+FFFD-replacement rather than ASCII fallback - // substitution when unencodable sequences (for instance, unpaired UTF-16 - // surrogates) are present in the input. - // TODO(ricea): Add WTF::kNoUnencodables enum value to make this - // behaviour explicit for UTF-N encodings. result = encoder_->Encode(input.Characters8(), input.length(), - WTF::kEntitiesForUnencodables); + WTF::kNoUnencodables); } else { bool have_output = Encode16BitString(input, high_surrogate, &prefix, &result); @@ -134,7 +128,7 @@ // Third argument is ignored, as above. *prefix = encoder_->Encode(astral_character, base::size(astral_character), - WTF::kEntitiesForUnencodables); + WTF::kNoUnencodables); ++begin; if (begin == end) return true;
diff --git a/third_party/blink/renderer/modules/push_messaging/push_message_data.cc b/third_party/blink/renderer/modules/push_messaging/push_message_data.cc index e5f0b9a..6282e7a 100644 --- a/third_party/blink/renderer/modules/push_messaging/push_message_data.cc +++ b/third_party/blink/renderer/modules/push_messaging/push_message_data.cc
@@ -43,7 +43,7 @@ if (message_data.IsUSVString()) { CString encoded_string = UTF8Encoding().Encode( - message_data.GetAsUSVString(), WTF::kEntitiesForUnencodables); + message_data.GetAsUSVString(), WTF::kNoUnencodables); return new PushMessageData(encoded_string.data(), encoded_string.length()); }
diff --git a/third_party/blink/renderer/modules/service_worker/navigator_service_worker.cc b/third_party/blink/renderer/modules/service_worker/navigator_service_worker.cc index 157a288..4c438f47 100644 --- a/third_party/blink/renderer/modules/service_worker/navigator_service_worker.cc +++ b/third_party/blink/renderer/modules/service_worker/navigator_service_worker.cc
@@ -22,6 +22,9 @@ if (!frame) return nullptr; + // TODO(kouhei): Remove below after M72, since the check is now done in + // RenderFrameImpl::CreateServiceWorkerProvider instead. + // // Bail-out if we are about to be navigated away. // We check that DocumentLoader is attached since: // - This serves as the signal since the DocumentLoader is detached in
diff --git a/third_party/blink/renderer/platform/bindings/callback_interface_base.h b/third_party/blink/renderer/platform/bindings/callback_interface_base.h index dd83a4e5..4f86ee39 100644 --- a/third_party/blink/renderer/platform/bindings/callback_interface_base.h +++ b/third_party/blink/renderer/platform/bindings/callback_interface_base.h
@@ -38,21 +38,9 @@ virtual void Trace(blink::Visitor*); - // Check the identity of |callback_object_|. There can be multiple - // CallbackInterfaceBase objects that have the same |callback_object_| but - // have different |incumbent_script_state_|s. - bool HasTheSameCallbackObject(const CallbackInterfaceBase& other) const { - return callback_object_ == other.callback_object_; - } - - v8::Local<v8::Object> CallbackObject() { - return callback_object_.NewLocal(GetIsolate()); - } - v8::Isolate* GetIsolate() { return callback_relevant_script_state_->GetIsolate(); } - ScriptState* CallbackRelevantScriptState() { return callback_relevant_script_state_; } @@ -67,6 +55,9 @@ CallbackInterfaceBase(v8::Local<v8::Object> callback_object, SingleOperationOrNot); + v8::Local<v8::Object> CallbackObject() { + return callback_object_.NewLocal(GetIsolate()); + } // Returns true iff the callback interface is a single operation callback // interface and the callback interface type value is callable. bool IsCallbackObjectCallable() const { return is_callback_object_callable_; }
diff --git a/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h b/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h index 78f0ba2..f8c9446 100644 --- a/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h +++ b/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h
@@ -28,10 +28,6 @@ ~TraceWrapperV8Reference() { Clear(); } - bool operator==(const TraceWrapperV8Reference& other) const { - return handle_ == other.handle_; - } - void Set(v8::Isolate* isolate, v8::Local<T> handle) { InternalSet(isolate, handle); handle_.SetWeak();
diff --git a/third_party/blink/renderer/platform/blob/blob_data.cc b/third_party/blink/renderer/platform/blob/blob_data.cc index 678cee04..57889d8d 100644 --- a/third_party/blink/renderer/platform/blob/blob_data.cc +++ b/third_party/blink/renderer/platform/blob/blob_data.cc
@@ -215,8 +215,7 @@ bool do_normalize_line_endings_to_native) { DCHECK_EQ(file_composition_, FileCompositionStatus::NO_UNKNOWN_SIZE_FILES) << "Blobs with a unknown-size file cannot have other items."; - CString utf8_text = - UTF8Encoding().Encode(text, WTF::kEntitiesForUnencodables); + CString utf8_text = UTF8Encoding().Encode(text, WTF::kNoUnencodables); if (do_normalize_line_endings_to_native) { if (utf8_text.length() >
diff --git a/third_party/blink/renderer/platform/fonts/font.cc b/third_party/blink/renderer/platform/fonts/font.cc index 2df09085..1a86e0c3 100644 --- a/third_party/blink/renderer/platform/fonts/font.cc +++ b/third_party/blink/renderer/platform/fonts/font.cc
@@ -364,7 +364,7 @@ FloatRect Font::SelectionRectForText(const TextRun& run, const FloatPoint& point, - int height, + float height, int from, int to) const { to = (to == -1 ? run.length() : to);
diff --git a/third_party/blink/renderer/platform/fonts/font.h b/third_party/blink/renderer/platform/fonts/font.h index a661792..559bd97f 100644 --- a/third_party/blink/renderer/platform/fonts/font.h +++ b/third_party/blink/renderer/platform/fonts/font.h
@@ -147,7 +147,7 @@ BreakGlyphsOption) const; FloatRect SelectionRectForText(const TextRun&, const FloatPoint&, - int h, + float height, int from = 0, int to = -1) const; FloatRect BoundingBox(const TextRun&, int from = 0, int to = -1) const;
diff --git a/third_party/blink/renderer/platform/loader/cors/cors.cc b/third_party/blink/renderer/platform/loader/cors/cors.cc index 297a18e..e514903 100644 --- a/third_party/blink/renderer/platform/loader/cors/cors.cc +++ b/third_party/blink/renderer/platform/loader/cors/cors.cc
@@ -232,6 +232,26 @@ std::string(utf8_value.data(), utf8_value.length())); } +bool IsNoCORSSafelistedHeader(const String& name, const String& value) { + DCHECK(!name.IsNull()); + DCHECK(!value.IsNull()); + return network::cors::IsNoCORSSafelistedHeader(WebString(name).Latin1(), + WebString(value).Latin1()); +} + +Vector<String> CORSUnsafeRequestHeaderNames(const HTTPHeaderMap& headers) { + net::HttpRequestHeaders::HeaderVector in; + for (const auto& entry : headers) { + in.push_back(net::HttpRequestHeaders::HeaderKeyValuePair( + WebString(entry.key).Latin1(), WebString(entry.value).Latin1())); + } + + Vector<String> header_names; + for (const auto& name : network::cors::CORSUnsafeRequestHeaderNames(in)) + header_names.push_back(WebString::FromLatin1(name)); + return header_names; +} + bool IsForbiddenHeaderName(const String& name) { CString utf8_name = name.Utf8(); return network::cors::IsForbiddenHeader( @@ -239,21 +259,20 @@ } bool ContainsOnlyCORSSafelistedHeaders(const HTTPHeaderMap& header_map) { - for (const auto& header : header_map) { - if (!IsCORSSafelistedHeader(header.key, header.value)) - return false; - } - return true; + Vector<String> header_names = CORSUnsafeRequestHeaderNames(header_map); + return header_names.IsEmpty(); } bool ContainsOnlyCORSSafelistedOrForbiddenHeaders( - const HTTPHeaderMap& header_map) { - for (const auto& header : header_map) { - if (!IsCORSSafelistedHeader(header.key, header.value) && - !IsForbiddenHeaderName(header.key)) - return false; + const HTTPHeaderMap& headers) { + Vector<String> header_names; + + net::HttpRequestHeaders::HeaderVector in; + for (const auto& entry : headers) { + in.push_back(net::HttpRequestHeaders::HeaderKeyValuePair( + WebString(entry.key).Latin1(), WebString(entry.value).Latin1())); } - return true; + return network::cors::CORSUnsafeNotForbiddenRequestHeaderNames(in).empty(); } bool IsOkStatus(int status) {
diff --git a/third_party/blink/renderer/platform/loader/cors/cors.h b/third_party/blink/renderer/platform/loader/cors/cors.h index 9d5eb4e..de2f73f4 100644 --- a/third_party/blink/renderer/platform/loader/cors/cors.h +++ b/third_party/blink/renderer/platform/loader/cors/cors.h
@@ -11,6 +11,7 @@ #include "services/network/public/mojom/fetch_api.mojom-shared.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" namespace blink { @@ -89,6 +90,10 @@ PLATFORM_EXPORT bool IsCORSSafelistedContentType(const String&); PLATFORM_EXPORT bool IsCORSSafelistedHeader(const String& name, const String& value); +PLATFORM_EXPORT bool IsNoCORSSafelistedHeader(const String& name, + const String& value); +PLATFORM_EXPORT Vector<String> CORSUnsafeRequestHeaderNames( + const HTTPHeaderMap& headers); PLATFORM_EXPORT bool IsForbiddenHeaderName(const String& name); PLATFORM_EXPORT bool ContainsOnlyCORSSafelistedHeaders(const HTTPHeaderMap&); PLATFORM_EXPORT bool ContainsOnlyCORSSafelistedOrForbiddenHeaders(
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index ad012e8..fdd0999 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -674,7 +674,7 @@ }, { name:"ManualSlotting", - status:"test", + status:"experimental", }, { name: "MediaCapabilities",
diff --git a/third_party/blink/renderer/platform/weborigin/kurl.cc b/third_party/blink/renderer/platform/weborigin/kurl.cc index 4afb31a3..d4e6803 100644 --- a/third_party/blink/renderer/platform/weborigin/kurl.cc +++ b/third_party/blink/renderer/platform/weborigin/kurl.cc
@@ -632,8 +632,8 @@ } String EncodeWithURLEscapeSequences(const String& not_encoded_string) { - CString utf8 = UTF8Encoding().Encode(not_encoded_string, - WTF::kURLEncodedEntitiesForUnencodables); + CString utf8 = + UTF8Encoding().Encode(not_encoded_string, WTF::kNoUnencodables); url::RawCanonOutputT<char> buffer; int input_length = utf8.length();
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin.cc b/third_party/blink/renderer/platform/weborigin/security_origin.cc index 7fc040b..fc86c2d 100644 --- a/third_party/blink/renderer/platform/weborigin/security_origin.cc +++ b/third_party/blink/renderer/platform/weborigin/security_origin.cc
@@ -244,15 +244,22 @@ return false; } -bool SecurityOrigin::CanAccess(const SecurityOrigin* other) const { - if (universal_access_) +bool SecurityOrigin::CanAccess(const SecurityOrigin* other, + AccessResultDomainDetail& detail) const { + if (universal_access_) { + detail = AccessResultDomainDetail::kDomainNotRelevant; return true; + } - if (this == other) + if (this == other) { + detail = AccessResultDomainDetail::kDomainNotRelevant; return true; + } - if (IsOpaque() || other->IsOpaque()) + if (IsOpaque() || other->IsOpaque()) { + detail = AccessResultDomainDetail::kDomainNotRelevant; return false; + } // document.domain handling, as per // https://html.spec.whatwg.org/multipage/browsers.html#dom-document-domain: @@ -266,6 +273,7 @@ bool can_access = false; if (protocol_ == other->protocol_) { if (!domain_was_set_in_dom_ && !other->domain_was_set_in_dom_) { + detail = AccessResultDomainDetail::kDomainNotSet; if (host_ == other->host_ && port_ == other->port_) can_access = true; } else if (domain_was_set_in_dom_ && other->domain_was_set_in_dom_) { @@ -274,12 +282,25 @@ // https://crbug.com/733150 if (domain_ == other->domain_ && domain_ != "null") { can_access = true; + detail = (host_ == other->host_ && port_ == other->port_) + ? AccessResultDomainDetail::kDomainMatchUnnecessary + : AccessResultDomainDetail::kDomainMatchNecessary; + } else { + detail = (host_ == other->host_ && port_ == other->port_) + ? AccessResultDomainDetail::kDomainMismatch + : AccessResultDomainDetail::kDomainNotRelevant; } + } else { + detail = (host_ == other->host_ && port_ == other->port_) + ? AccessResultDomainDetail::kDomainSetByOnlyOneOrigin + : AccessResultDomainDetail::kDomainNotRelevant; } } - if (can_access && IsLocal()) - can_access = PassesFileCheck(other); + if (can_access && IsLocal() && !PassesFileCheck(other)) { + detail = AccessResultDomainDetail::kDomainNotRelevant; + can_access = false; + } return can_access; }
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin.h b/third_party/blink/renderer/platform/weborigin/security_origin.h index 9f7ba5b..08dba0c9 100644 --- a/third_party/blink/renderer/platform/weborigin/security_origin.h +++ b/third_party/blink/renderer/platform/weborigin/security_origin.h
@@ -55,6 +55,15 @@ WTF_MAKE_NONCOPYABLE(SecurityOrigin); public: + enum class AccessResultDomainDetail { + kDomainNotRelevant, + kDomainNotSet, + kDomainSetByOnlyOneOrigin, + kDomainMatchNecessary, + kDomainMatchUnnecessary, + kDomainMismatch, + }; + static scoped_refptr<SecurityOrigin> Create(const KURL&); // Creates a new opaque SecurityOrigin that is guaranteed to be cross-origin // to all currently existing SecurityOrigins. @@ -112,7 +121,17 @@ // SecurityOrigin. For example, call this function before allowing // script from one security origin to read or write objects from // another SecurityOrigin. - bool CanAccess(const SecurityOrigin*) const; + bool CanAccess(const SecurityOrigin* other) const { + AccessResultDomainDetail unused_detail; + return CanAccess(other, unused_detail); + } + + // Returns true if this SecurityOrigin can script objects in |other|, just + // as above, but also returns the category into which the access check fell. + // + // TODO(crbug.com/787905): Remove this variant once we have enough data to + // make decisions about `document.domain`. + bool CanAccess(const SecurityOrigin* other, AccessResultDomainDetail&) const; // Returns true if this SecurityOrigin can read content retrieved from // the given URL.
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin_test.cc b/third_party/blink/renderer/platform/weborigin/security_origin_test.cc index 0b34431..5069782 100644 --- a/third_party/blink/renderer/platform/weborigin/security_origin_test.cc +++ b/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
@@ -247,6 +247,79 @@ } } +TEST_F(SecurityOriginTest, CanAccessDetail) { + struct TestCase { + SecurityOrigin::AccessResultDomainDetail expected; + const char* origin1; + const char* domain1; + const char* origin2; + const char* domain2; + }; + + TestCase tests[] = { + // Actually cross-origin origins + {SecurityOrigin::AccessResultDomainDetail::kDomainNotSet, + "https://example.com", nullptr, "https://not-example.com", nullptr}, + {SecurityOrigin::AccessResultDomainDetail::kDomainNotRelevant, + "https://example.com", "example.com", "https://not-example.com", + nullptr}, + {SecurityOrigin::AccessResultDomainDetail::kDomainNotRelevant, + "https://example.com", nullptr, "https://not-example.com", + "not-example.com"}, + {SecurityOrigin::AccessResultDomainDetail::kDomainNotRelevant, + "https://example.com", "example.com", "https://not-example.com", + "not-example.com"}, + + // Same-origin origins + {SecurityOrigin::AccessResultDomainDetail::kDomainNotSet, + "https://example.com", nullptr, "https://example.com", nullptr}, + {SecurityOrigin::AccessResultDomainDetail::kDomainSetByOnlyOneOrigin, + "https://example.com", "example.com", "https://example.com", nullptr}, + {SecurityOrigin::AccessResultDomainDetail::kDomainSetByOnlyOneOrigin, + "https://example.com", nullptr, "https://example.com", "example.com"}, + {SecurityOrigin::AccessResultDomainDetail::kDomainMismatch, + "https://www.example.com", "www.example.com", "https://www.example.com", + "example.com"}, + {SecurityOrigin::AccessResultDomainDetail::kDomainMatchUnnecessary, + "https://example.com", "example.com", "https://example.com", + "example.com"}, + + // Same-origin-domain origins + {SecurityOrigin::AccessResultDomainDetail::kDomainNotSet, + "https://a.example.com", nullptr, "https://b.example.com", nullptr}, + {SecurityOrigin::AccessResultDomainDetail::kDomainNotRelevant, + "https://a.example.com", "example.com", "https://b.example.com", + nullptr}, + {SecurityOrigin::AccessResultDomainDetail::kDomainNotRelevant, + "https://a.example.com", nullptr, "https://b.example.com", + "example.com"}, + {SecurityOrigin::AccessResultDomainDetail::kDomainMatchNecessary, + "https://a.example.com", "example.com", "https://b.example.com", + "example.com"}, + }; + + for (TestCase test : tests) { + SCOPED_TRACE(testing::Message() + << "\nOrigin 1: `" << test.origin1 << "` (" + << (test.domain1 ? test.domain1 : "") << ") \n" + << "Origin 2: `" << test.origin2 << "` (" + << (test.domain2 ? test.domain2 : "") << ")\n"); + scoped_refptr<SecurityOrigin> origin1 = + SecurityOrigin::CreateFromString(test.origin1); + if (test.domain1) + origin1->SetDomainFromDOM(test.domain1); + scoped_refptr<SecurityOrigin> origin2 = + SecurityOrigin::CreateFromString(test.origin2); + if (test.domain2) + origin2->SetDomainFromDOM(test.domain2); + SecurityOrigin::AccessResultDomainDetail detail; + origin1->CanAccess(origin2.get(), detail); + EXPECT_EQ(test.expected, detail); + origin2->CanAccess(origin1.get(), detail); + EXPECT_EQ(test.expected, detail); + } +} + TEST_F(SecurityOriginTest, CanRequest) { struct TestCase { bool can_request;
diff --git a/third_party/blink/renderer/platform/wtf/text/text_codec.cc b/third_party/blink/renderer/platform/wtf/text/text_codec.cc index 39ee19f0..2943851 100644 --- a/third_party/blink/renderer/platform/wtf/text/text_codec.cc +++ b/third_party/blink/renderer/platform/wtf/text/text_codec.cc
@@ -48,6 +48,9 @@ snprintf(replacement, sizeof(UnencodableReplacementArray), "\\%x ", code_point); return static_cast<uint32_t>(strlen(replacement)); + + case kNoUnencodables: + break; } NOTREACHED(); replacement[0] = 0;
diff --git a/third_party/blink/renderer/platform/wtf/text/text_codec.h b/third_party/blink/renderer/platform/wtf/text/text_codec.h index e13cad7..51c0777 100644 --- a/third_party/blink/renderer/platform/wtf/text/text_codec.h +++ b/third_party/blink/renderer/platform/wtf/text/text_codec.h
@@ -52,6 +52,10 @@ // Encodes the character as a CSS entity. For example U+06DE // would be \06de. See: https://www.w3.org/TR/css-syntax-3/#escaping kCSSEncodedEntitiesForUnencodables, + + // Used when all characters can be encoded in the character set. Only + // applicable to UTF-N encodings. + kNoUnencodables, }; typedef char UnencodableReplacementArray[32];
diff --git a/third_party/blink/renderer/platform/wtf/text/text_codec_icu.cc b/third_party/blink/renderer/platform/wtf/text/text_codec_icu.cc index 5b93374..2406032e 100644 --- a/third_party/blink/renderer/platform/wtf/text/text_codec_icu.cc +++ b/third_party/blink/renderer/platform/wtf/text/text_codec_icu.cc
@@ -616,6 +616,16 @@ } #endif // USING_SYSTEM_ICU +static void NotReachedEntityCallback(const void* context, + UConverterFromUnicodeArgs* from_u_args, + const UChar* code_units, + int32_t length, + UChar32 code_point, + UConverterCallbackReason reason, + UErrorCode* err) { + NOTREACHED(); +} + class TextCodecInput final { STACK_ALLOCATED(); @@ -685,6 +695,13 @@ 0, 0, 0, &err); #endif break; + case kNoUnencodables: + DCHECK(encoding_ == UTF16BigEndianEncoding() || + encoding_ == UTF16LittleEndianEncoding() || + encoding_ == UTF8Encoding()); + ucnv_setFromUCallBack(converter_icu_, NotReachedEntityCallback, nullptr, + nullptr, nullptr, &err); + break; } DCHECK(U_SUCCESS(err));
diff --git a/third_party/blink/renderer/platform/wtf/text/text_codec_latin1.cc b/third_party/blink/renderer/platform/wtf/text/text_codec_latin1.cc index 966ebbc..ebd699c 100644 --- a/third_party/blink/renderer/platform/wtf/text/text_codec_latin1.cc +++ b/third_party/blink/renderer/platform/wtf/text/text_codec_latin1.cc
@@ -26,6 +26,7 @@ #include "third_party/blink/renderer/platform/wtf/text/text_codec_latin1.h" #include <memory> +#include "third_party/blink/renderer/platform/wtf/assertions.h" #include "third_party/blink/renderer/platform/wtf/text/cstring.h" #include "third_party/blink/renderer/platform/wtf/text/string_buffer.h" #include "third_party/blink/renderer/platform/wtf/text/text_codec_ascii_fast_path.h" @@ -205,6 +206,7 @@ static CString EncodeComplexWindowsLatin1(const CharType* characters, size_t length, UnencodableHandling handling) { + DCHECK_NE(handling, kNoUnencodables); size_t target_length = length; Vector<char> result(target_length); char* bytes = result.data();
diff --git a/third_party/blink/renderer/platform/wtf/text/text_codec_user_defined.cc b/third_party/blink/renderer/platform/wtf/text/text_codec_user_defined.cc index 635815a8..20a1bb9 100644 --- a/third_party/blink/renderer/platform/wtf/text/text_codec_user_defined.cc +++ b/third_party/blink/renderer/platform/wtf/text/text_codec_user_defined.cc
@@ -26,8 +26,7 @@ #include "third_party/blink/renderer/platform/wtf/text/text_codec_user_defined.h" #include <memory> - -#include <memory> +#include "third_party/blink/renderer/platform/wtf/assertions.h" #include "third_party/blink/renderer/platform/wtf/text/cstring.h" #include "third_party/blink/renderer/platform/wtf/text/string_buffer.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" @@ -70,6 +69,7 @@ static CString EncodeComplexUserDefined(const CharType* characters, size_t length, UnencodableHandling handling) { + DCHECK_NE(handling, kNoUnencodables); size_t target_length = length; Vector<char> result(target_length); char* bytes = result.data();
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/android.py b/third_party/blink/tools/blinkpy/web_tests/port/android.py index b388554..b3b215e 100644 --- a/third_party/blink/tools/blinkpy/web_tests/port/android.py +++ b/third_party/blink/tools/blinkpy/web_tests/port/android.py
@@ -793,7 +793,7 @@ stderr += '********* [%s] breakpad minidump %s:\n%s' % ( self._port.host.filesystem.basename(crash), self._device.serial, - stack) + stack.encode('ascii', 'replace')) return super(ChromiumAndroidDriver, self)._get_crash_log( stdout, stderr, newer_than)
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml index be3b603..025864ac3 100644 --- a/tools/metrics/actions/actions.xml +++ b/tools/metrics/actions/actions.xml
@@ -11766,6 +11766,57 @@ </description> </action> +<action name="MobileTabGridBeganReordering"> + <owner>edchin@chromium.org</owner> + <owner>marq@chromium.org</owner> + <description>User in the iOS tab grid began reordering tabs.</description> +</action> + +<action name="MobileTabGridCloseAllIncognitoTabs"> + <owner>edchin@chromium.org</owner> + <owner>marq@chromium.org</owner> + <description> + User in the iOS tab grid used the Close All control while viewing the + incognito tabs. + </description> +</action> + +<action name="MobileTabGridCloseAllRegularTabs"> + <owner>edchin@chromium.org</owner> + <owner>marq@chromium.org</owner> + <description> + User in the iOS tab grid used the Close All control while viewing the + regular tabs. + </description> +</action> + +<action name="MobileTabGridEndedWithoutReordering"> + <owner>edchin@chromium.org</owner> + <owner>marq@chromium.org</owner> + <description> + User in the iOS tab grid finished reordering tabs, but didn't change the tab + order. + </description> +</action> + +<action name="MobileTabGridReordered"> + <owner>edchin@chromium.org</owner> + <owner>marq@chromium.org</owner> + <description> + User in the iOS tab grid finished reordering tabs, and changed the tab + order. + </description> +</action> + +<action name="MobileTabGridUndoCloseAllRegularTabs"> + <owner>edchin@chromium.org</owner> + <owner>marq@chromium.org</owner> + <description> + User in the iOS tab grid used the Undo control after closing all regular + tabs. + </description> +</action> + <action name="MobileTabReturnedToCurrentTab"> <owner>rlanday@chromium.org</owner> <description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 218d050f..962ff65 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -19945,6 +19945,8 @@ <int value="2540" label="TextDecoderStreamConstructor"/> <int value="2541" label="SignedExchangeInnerResponse"/> <int value="2542" label="PaymentAddressLanguageCode"/> + <int value="2543" label="DocumentDomainBlockedCrossOriginAccess"/> + <int value="2544" label="DocumentDomainEnabledCrossOriginAccess"/> </enum> <enum name="FeaturePolicyFeature">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 362fb9ab..e735c07d 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -36065,6 +36065,37 @@ <summary>The time it takes to open a hyphenation dictionary file.</summary> </histogram> +<histogram name="ImageLoader.Client.Cache.HitMiss" enum="BooleanCacheHit" + expires_after="2019-01-01"> + <owner>chromeos-files-app@google.com</owner> + <owner>tapted@chromium.org</owner> + <summary> + For each image load request that requested caching, records whether or not + it was found in the client-side cache. A hit means the request was not + forwarded to the ImageLoader extension. + </summary> +</histogram> + +<histogram name="ImageLoader.Client.Cache.Usage" units="%" + expires_after="2019-01-01"> + <owner>chromeos-files-app@google.com</owner> + <owner>tapted@chromium.org</owner> + <summary> + Returns the percentage of the client-side cache that is used for loading + images, before they are sent to the ImageLoader extension. Expressed as a + percentage of ImageLoaderClient.CACHE_MEMORY_LIMIT (e.g. 20MB). + </summary> +</histogram> + +<histogram name="ImageLoader.Client.Cached" enum="BooleanRequested" + expires_after="2019-01-01"> + <owner>chromeos-files-app@google.com</owner> + <owner>tapted@chromium.org</owner> + <summary> + For each image load request records whether or not it requested caching. + </summary> +</histogram> + <histogram name="Import.ImportedHistorySize.AutoImportFromIE" units="urls"> <owner>gcomanici@chromium.org</owner> <summary>
diff --git a/tools/perf/contrib/cros_benchmarks/cros_utils.py b/tools/perf/contrib/cros_benchmarks/cros_utils.py index 400c0421..58916f7 100644 --- a/tools/perf/contrib/cros_benchmarks/cros_utils.py +++ b/tools/perf/contrib/cros_benchmarks/cros_utils.py
@@ -126,7 +126,9 @@ for i in range(5): kbd.SwitchTab() """ - REMOTE_LOG_KEY_FILENAME = '/usr/local/tmp/log_key_tab_switch' + REMOTE_TEMP_DIR = '/usr/local/tmp/' + REMOTE_LOG_KEY_FILENAME = 'log_key_tab_switch' + REMOTE_KEY_PROP_FILENAME = 'keyboard.prop' def __init__(self, dut_ip): """Inits KeyboardEmulator. @@ -146,7 +148,8 @@ Raises: RuntimeError: Keyboard emulation failed. """ - kbd_prop_filename = '/usr/local/autotest/cros/input_playback/keyboard.prop' + kbd_prop_filename = os.path.join(KeyboardEmulator.REMOTE_TEMP_DIR, + KeyboardEmulator.REMOTE_KEY_PROP_FILENAME) ret = _RunCommand(self._dut_ip, 'test -e %s' % kbd_prop_filename) if ret != 0: @@ -171,22 +174,25 @@ return key_device_name def _SetupKeyDispatch(self): - """Uploads the script to send key to switch tabs.""" + """Uploads required files to emulate keyboard.""" cur_dir = os.path.dirname(os.path.abspath(__file__)) - log_key_filename = os.path.join(cur_dir, 'data', 'log_key_tab_switch') - _CopyToDUT(self._dut_ip, log_key_filename, - KeyboardEmulator.REMOTE_LOG_KEY_FILENAME) + for filename in (KeyboardEmulator.REMOTE_KEY_PROP_FILENAME, + KeyboardEmulator.REMOTE_LOG_KEY_FILENAME): + src = os.path.join(cur_dir, 'data', filename) + dest = os.path.join(KeyboardEmulator.REMOTE_TEMP_DIR, filename) + _CopyToDUT(self._dut_ip, src, dest) def __enter__(self): - self._key_device_name = self._StartRemoteKeyboardEmulator() self._SetupKeyDispatch() + self._key_device_name = self._StartRemoteKeyboardEmulator() return self def SwitchTab(self): """Sending Ctrl-tab key to trigger tab switching.""" + log_key_filename = os.path.join(KeyboardEmulator.REMOTE_TEMP_DIR, + KeyboardEmulator.REMOTE_LOG_KEY_FILENAME) cmd = ('evemu-play --insert-slot0 %s < %s' % - (self._key_device_name, - KeyboardEmulator.REMOTE_LOG_KEY_FILENAME)) + (self._key_device_name, log_key_filename)) _RunCommand(self._dut_ip, cmd) def __exit__(self, exc_type, exc_value, traceback):
diff --git a/tools/perf/contrib/cros_benchmarks/data/keyboard.prop b/tools/perf/contrib/cros_benchmarks/data/keyboard.prop new file mode 100644 index 0000000..09b1accc --- /dev/null +++ b/tools/perf/contrib/cros_benchmarks/data/keyboard.prop
@@ -0,0 +1,24 @@ +N: Emulated Keyboard +I: 0003 0461 4e05 0111 +P: 00 00 00 00 00 00 00 00 +B: 00 13 00 12 00 00 00 00 00 +B: 01 fe ff ff ff ff ff ff ff +B: 01 ff ff ef ff df ff be fe +B: 01 ff 57 40 c1 7a 20 9f ff +B: 01 07 00 00 00 00 00 01 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 02 00 00 00 00 00 00 00 00 +B: 03 00 00 00 00 00 00 00 00 +B: 04 10 00 00 00 00 00 00 00 +B: 05 00 00 00 00 00 00 00 00 +B: 11 1f 00 00 00 00 00 00 00 +B: 12 00 00 00 00 00 00 00 00 +B: 15 00 00 00 00 00 00 00 00 +B: 15 00 00 00 00 00 00 00 00
diff --git a/ui/accessibility/BUILD.gn b/ui/accessibility/BUILD.gn index 9232ca1..bc7470d 100644 --- a/ui/accessibility/BUILD.gn +++ b/ui/accessibility/BUILD.gn
@@ -4,6 +4,7 @@ import("//build/config/linux/pkg_config.gni") import("//build/config/features.gni") +import("//build/config/jumbo.gni") import("//build/config/ui.gni") import("//mojo/public/tools/bindings/mojom.gni") import("//services/service_manager/public/service_manifest.gni") @@ -28,7 +29,7 @@ ] } -component("accessibility") { +jumbo_component("accessibility") { sources = [ "ax_action_data.cc", "ax_action_data.h",
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn index 07d7594..54900335 100644 --- a/ui/base/BUILD.gn +++ b/ui/base/BUILD.gn
@@ -4,6 +4,7 @@ import("//build/buildflag_header.gni") import("//build/config/compiler/compiler.gni") +import("//build/config/jumbo.gni") import("//build/config/linux/pangocairo/pangocairo.gni") import("//build/config/sanitizers/sanitizers.gni") import("//build/config/ui.gni") @@ -62,7 +63,7 @@ ] } -component("base") { +jumbo_component("base") { output_name = "ui_base" sources = [ @@ -682,7 +683,7 @@ } } -static_library("test_support") { +jumbo_static_library("test_support") { testonly = true sources = [ "test/material_design_controller_test_api.cc",
diff --git a/ui/base/ime/BUILD.gn b/ui/base/ime/BUILD.gn index f143cd4d..75290424 100644 --- a/ui/base/ime/BUILD.gn +++ b/ui/base/ime/BUILD.gn
@@ -4,6 +4,7 @@ import("//build/config/jumbo.gni") import("//build/config/linux/pangocairo/pangocairo.gni") +import("//build/config/jumbo.gni") import("//build/config/ui.gni") import("//testing/test.gni")
diff --git a/ui/compositor/BUILD.gn b/ui/compositor/BUILD.gn index e7f9be1..ab830a6 100644 --- a/ui/compositor/BUILD.gn +++ b/ui/compositor/BUILD.gn
@@ -115,7 +115,7 @@ } } -static_library("test_support") { +jumbo_static_library("test_support") { testonly = true sources = [ "test/context_factories_for_test.cc",
diff --git a/ui/display/BUILD.gn b/ui/display/BUILD.gn index bd7de89c..1b7c1b6 100644 --- a/ui/display/BUILD.gn +++ b/ui/display/BUILD.gn
@@ -2,11 +2,12 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/jumbo.gni") import("//build/config/ui.gni") import("//testing/test.gni") import("//ui/display/display.gni") -component("display") { +jumbo_component("display") { sources = [ "display.cc", "display.h", @@ -101,7 +102,7 @@ ] } -static_library("test_support") { +jumbo_static_library("test_support") { testonly = true sources = [ "test/display_matchers.cc",
diff --git a/ui/events/BUILD.gn b/ui/events/BUILD.gn index 25b3dc3..3361550 100644 --- a/ui/events/BUILD.gn +++ b/ui/events/BUILD.gn
@@ -3,6 +3,7 @@ # found in the LICENSE file. import("//build/config/features.gni") +import("//build/config/jumbo.gni") import("//build/config/ui.gni") import("//testing/test.gni") import("//ui/base/ui_features.gni") @@ -16,7 +17,7 @@ import("//ios/build/config.gni") } -static_library("dom_keycode_converter") { +jumbo_static_library("dom_keycode_converter") { public = [ "keycodes/dom/dom_code.h", "keycodes/dom/dom_codes.h", @@ -92,7 +93,7 @@ ] } -component("events_base") { +jumbo_component("events_base") { sources = [ "base_event_utils.cc", "base_event_utils.h", @@ -153,7 +154,7 @@ } } -component("events") { +jumbo_component("events") { sources = [ "cocoa/cocoa_event_utils.h", "cocoa/cocoa_event_utils.mm", @@ -300,7 +301,7 @@ } } -component("gesture_detection") { +jumbo_component("gesture_detection") { sources = [ "gesture_detection/bitset_32.h", "gesture_detection/filtered_gesture_provider.cc", @@ -364,7 +365,7 @@ } } -static_library("test_support") { +jumbo_static_library("test_support") { sources = [ "test/cocoa_test_event_utils.h", "test/cocoa_test_event_utils.mm",
diff --git a/ui/file_manager/BUILD.gn b/ui/file_manager/BUILD.gn index 01983b8..0b6badbb 100644 --- a/ui/file_manager/BUILD.gn +++ b/ui/file_manager/BUILD.gn
@@ -50,7 +50,9 @@ group("unit_test_data") { deps = [ + "file_manager/foreground/js:unit_tests", "gallery/js:unit_tests", "gallery/js/image_editor:unit_tests", + "image_loader:unit_tests", ] }
diff --git a/ui/file_manager/externs/BUILD.gn b/ui/file_manager/externs/BUILD.gn index 1d7d52a..fef4d25 100644 --- a/ui/file_manager/externs/BUILD.gn +++ b/ui/file_manager/externs/BUILD.gn
@@ -15,3 +15,16 @@ "webview_tag.js", ] } + +js_library("file_manager_private") { + sources = [] + + # The file_manager_private extern depends on file_system_provider and + # extension APIs. Ensure they're pulled in together. + externs_list = [ + "$externs_path/chrome.js", + "$externs_path/chrome_extensions.js", + "$externs_path/file_manager_private.js", + "$externs_path/file_system_provider.js", + ] +}
diff --git a/ui/file_manager/file_manager/background/js/BUILD.gn b/ui/file_manager/file_manager/background/js/BUILD.gn index d6a933f..a0a7e43 100644 --- a/ui/file_manager/file_manager/background/js/BUILD.gn +++ b/ui/file_manager/file_manager/background/js/BUILD.gn
@@ -40,8 +40,6 @@ sources = [] externs_list = [ "$externs_path/command_line_private.js", - "$externs_path/file_manager_private.js", - "$externs_path/file_system_provider.js", "$externs_path/metrics_private.js", "../../../externs/background/file_browser_background.js", "../../../externs/background/file_browser_background_full.js",
diff --git a/ui/file_manager/file_manager/common/js/BUILD.gn b/ui/file_manager/file_manager/common/js/BUILD.gn index 506fde5f..a2ff1b61 100644 --- a/ui/file_manager/file_manager/common/js/BUILD.gn +++ b/ui/file_manager/file_manager/common/js/BUILD.gn
@@ -58,11 +58,10 @@ js_library("metrics") { deps = [ ":metrics_base", + "../../../externs:file_manager_private", "//ui/webui/resources/js:assert", ] externs_list = [ - "$externs_path/file_manager_private.js", - "$externs_path/file_system_provider.js", "$externs_path/metrics_private.js", "//third_party/analytics/externs.js", ] @@ -97,14 +96,13 @@ deps = [ ":files_app_entry_types", ":volume_manager_common", + "../../../externs:file_manager_private", "//ui/webui/resources/js:load_time_data", "//ui/webui/resources/js:util", "//ui/webui/resources/js/cr:event_target", "//ui/webui/resources/js/cr:ui", ] externs_list = [ - "$externs_path/chrome.js", - "$externs_path/chrome_extensions.js", "$externs_path/command_line_private.js", "../../../externs/app_window_common.js", "../../../externs/entry_location.js",
diff --git a/ui/file_manager/file_manager/foreground/js/BUILD.gn b/ui/file_manager/file_manager/foreground/js/BUILD.gn index 4d9de83b..7350b5ac 100644 --- a/ui/file_manager/file_manager/foreground/js/BUILD.gn +++ b/ui/file_manager/file_manager/foreground/js/BUILD.gn
@@ -3,8 +3,9 @@ # found in the LICENSE file. import("//third_party/closure_compiler/compile_js.gni") +import("//ui/file_manager/js_unit_tests.gni") -js_type_check("closure_compile") { +js_type_check("closure_compile_module") { deps = [ ":actions_controller", ":actions_model", @@ -586,6 +587,15 @@ ] } +js_library("thumbnail_loader_unittest") { + deps = [ + ":thumbnail_loader", + "../../common/js:mock_entry", + "../../common/js:unittest_util", + "//ui/webui/resources/js:webui_resource_test", + ] +} + js_library("toolbar_controller") { deps = [ ":file_selection", @@ -598,6 +608,7 @@ js_library("volume_manager_wrapper") { deps = [ + "../../../externs:file_manager_private", "../../common/js:async_util", "../../common/js:volume_manager_common", "//ui/webui/resources/js:cr", @@ -605,11 +616,6 @@ "//ui/webui/resources/js/cr/ui:array_data_model", ] externs_list = [ - # Note: volume_info has a dependency on chrome.fileManagerPrivate.IconSet. - # Also, fileManagerPrivate depends on chrome.fileSystemProvider, so these - # must be introduced together. - "$externs_path/file_manager_private.js", - "$externs_path/file_system_provider.js", "../../../externs/background/volume_manager_factory.js", "../../../externs/volume_info.js", "../../../externs/volume_info_list.js", @@ -629,3 +635,16 @@ "//ui/webui/resources/js/cr/ui:command", ] } + +js_unit_tests("unit_tests") { + deps = [ + ":thumbnail_loader_unittest", + ] +} + +group("closure_compile") { + deps = [ + ":closure_compile_module", + ":unit_tests_type_check", + ] +}
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/BUILD.gn b/ui/file_manager/file_manager/foreground/js/metadata/BUILD.gn index 4269da7..ae6b0098 100644 --- a/ui/file_manager/file_manager/foreground/js/metadata/BUILD.gn +++ b/ui/file_manager/file_manager/foreground/js/metadata/BUILD.gn
@@ -36,8 +36,6 @@ sources = [] externs_list = [ "$externs_path/command_line_private.js", - "$externs_path/file_manager_private.js", - "$externs_path/file_system_provider.js", "../../../../externs/app_window_common.js", "../../../../externs/entry_location.js", "../../../../externs/platform.js",
diff --git a/ui/file_manager/file_manager/foreground/js/thumbnail_loader_unittest.html b/ui/file_manager/file_manager/foreground/js/thumbnail_loader_unittest.html deleted file mode 100644 index a8e2aec..0000000 --- a/ui/file_manager/file_manager/foreground/js/thumbnail_loader_unittest.html +++ /dev/null
@@ -1,12 +0,0 @@ -<!DOCTYPE html> -<!-- 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. - --> -<script src="../../../../webui/resources/js/assert.js"></script> -<script src="../../common/js/file_type.js"></script> -<script src="../../common/js/mock_entry.js"></script> -<script src="../../common/js/unittest_util.js"></script> -<script src="../../../image_loader/image_loader_client.js"></script> -<script src="thumbnail_loader.js"></script> -<script src="thumbnail_loader_unittest.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/thumbnail_loader_unittest.js b/ui/file_manager/file_manager/foreground/js/thumbnail_loader_unittest.js index 807fc8a..4ee77adc 100644 --- a/ui/file_manager/file_manager/foreground/js/thumbnail_loader_unittest.js +++ b/ui/file_manager/file_manager/foreground/js/thumbnail_loader_unittest.js
@@ -3,7 +3,8 @@ // found in the LICENSE file. function getLoadTarget(entry, metadata) { - return new ThumbnailLoader(entry, null, metadata).getLoadTarget(); + return new ThumbnailLoader(entry, ThumbnailLoader.LoaderType.CANVAS, metadata) + .getLoadTarget(); } /** @@ -26,6 +27,17 @@ return canvas.toDataURL('image/png'); } +/** + * Installs a mock ImageLoader with a compatible load method. + * + * @param {function(string, function(!Object), Object=)} mockLoad + */ +function installMockLoad(mockLoad) { + ImageLoaderClient.getInstance = function() { + return /** @type {!ImageLoaderClient} */ ({load: mockLoad}); + }; +} + function testShouldUseMetadataThumbnail() { var mockFileSystem = new MockFileSystem('volumeId'); var imageEntry = new MockEntry(mockFileSystem, '/test.jpg'); @@ -55,14 +67,9 @@ } function testLoadAsDataUrlFromImageClient(callback) { - ImageLoaderClient.getInstance = function() { - return { - load: function(url, callback, opt_option) { - callback({ - status: 'success', data: 'imageDataUrl', width: 32, height: 32}); - } - }; - }; + installMockLoad(function(url, callback, opt_option) { + callback({status: 'success', data: 'imageDataUrl', width: 32, height: 32}); + }); var fileSystem = new MockFileSystem('volume-id'); var entry = new MockEntry(fileSystem, '/Test1.jpg'); @@ -75,15 +82,11 @@ } function testLoadAsDataUrlFromExifThumbnail(callback) { - ImageLoaderClient.getInstance = function() { - return { - load: function(url, callback, opt_option) { - // Assert that data url is passed. - assertTrue(/^data:/i.test(url)); - callback({status: 'success', data: url, width: 32, height: 32}); - } - }; - }; + installMockLoad(function(url, callback, opt_option) { + // Assert that data url is passed. + assertTrue(/^data:/i.test(url)); + callback({status: 'success', data: url, width: 32, height: 32}); + }); var metadata = { thumbnail: { @@ -102,17 +105,17 @@ } function testLoadAsDataUrlFromExifThumbnailPropagatesTransform(callback) { - ImageLoaderClient.getInstance = function() { - return { - load: function(url, callback, opt_option) { - // Assert that data url and transform info is passed. - assertTrue(/^data:/i.test(url)); - assertEquals(1, opt_option.orientation.rotate90); - callback({status: 'success', data: generateSampleImageDataUrl(32, 64), - width: 32, height: 64}); - } - }; - }; + installMockLoad(function(url, callback, opt_option) { + // Assert that data url and transform info is passed. + assertTrue(/^data:/i.test(url)); + assertEquals(1, opt_option.orientation.rotate90); + callback({ + status: 'success', + data: generateSampleImageDataUrl(32, 64), + width: 32, + height: 64 + }); + }); var metadata = { thumbnail: { @@ -142,15 +145,15 @@ var externalCroppedThumbnailUrl = 'https://external-cropped-thumbnail-url/'; var externalThumbnailDataUrl = generateSampleImageDataUrl(32, 32); - ImageLoaderClient.getInstance = function() { - return { - load: function(url, callback, opt_option) { - assertEquals(externalCroppedThumbnailUrl, url); - callback({status: 'success', data: externalThumbnailDataUrl, - width: 32, height: 32}); - } - }; - }; + installMockLoad(function(url, callback, opt_option) { + assertEquals(externalCroppedThumbnailUrl, url); + callback({ + status: 'success', + data: externalThumbnailDataUrl, + width: 32, + height: 32 + }); + }); var metadata = { external: { @@ -170,19 +173,19 @@ } function testLoadDetachedFromExifInCavnasModeThumbnailDoesNotRotate(callback) { - ImageLoaderClient.getInstance = function() { - return { - load: function(url, callback, opt_option) { - // Assert that data url is passed. - assertTrue(/^data:/i.test(url)); - // Assert that the rotation is propagated to ImageLoader. - assertEquals(1, opt_option.orientation.rotate90); - // ImageLoader returns rotated image. - callback({status: 'success', data: generateSampleImageDataUrl(32, 64), - width: 32, height: 64}); - } - }; - }; + installMockLoad(function(url, callback, opt_option) { + // Assert that data url is passed. + assertTrue(/^data:/i.test(url)); + // Assert that the rotation is propagated to ImageLoader. + assertEquals(1, opt_option.orientation.rotate90); + // ImageLoader returns rotated image. + callback({ + status: 'success', + data: generateSampleImageDataUrl(32, 64), + width: 32, + height: 64 + }); + }); var metadata = { thumbnail: {
diff --git a/ui/file_manager/file_manager/test/BUILD.gn b/ui/file_manager/file_manager/test/BUILD.gn index 6ec1da2..e4916d3 100644 --- a/ui/file_manager/file_manager/test/BUILD.gn +++ b/ui/file_manager/file_manager/test/BUILD.gn
@@ -50,8 +50,6 @@ externs_list = [ "js/externs.js", "$externs_path/command_line_private.js", - "$externs_path/file_manager_private.js", - "$externs_path/file_system_provider.js", "$externs_path/metrics_private.js", "../../externs/app_window_common.js", "../../externs/background/file_browser_background.js",
diff --git a/ui/file_manager/gallery/js/BUILD.gn b/ui/file_manager/gallery/js/BUILD.gn index 4462d03..b66006f 100644 --- a/ui/file_manager/gallery/js/BUILD.gn +++ b/ui/file_manager/gallery/js/BUILD.gn
@@ -52,13 +52,10 @@ js_library("entry_list_watcher") { deps = [ + "../../externs:file_manager_private", "//ui/webui/resources/js:assert", "//ui/webui/resources/js/cr/ui:array_data_model", ] - externs_list = [ - "$externs_path/file_system_provider.js", - "$externs_path/file_manager_private.js", - ] } js_library("entry_list_watcher_unittest") {
diff --git a/ui/file_manager/image_loader/BUILD.gn b/ui/file_manager/image_loader/BUILD.gn index 1b461874..7398c50 100644 --- a/ui/file_manager/image_loader/BUILD.gn +++ b/ui/file_manager/image_loader/BUILD.gn
@@ -3,12 +3,12 @@ # found in the LICENSE file. import("//third_party/closure_compiler/compile_js.gni") +import("//ui/file_manager/js_unit_tests.gni") -js_type_check("closure_compile") { +js_type_check("closure_compile_module") { deps = [ ":background", ":cache", - ":closure_compile_externs", ":image_loader", ":image_loader_client", ":image_loader_util", @@ -18,17 +18,6 @@ ] } -js_library("closure_compile_externs") { - sources = [] - externs_list = [ - "$externs_path/chrome_extensions.js", - "$externs_path/file_manager_private.js", - "$externs_path/file_system_provider.js", - "$externs_path/metrics_private.js", - "//third_party/analytics/externs.js", - ] -} - js_library("background") { deps = [ ":image_loader", @@ -38,12 +27,27 @@ js_library("cache") { } +js_library("cache_unittest") { + deps = [ + ":cache", + "//ui/webui/resources/js:webui_resource_test", + ] +} + js_library("image_loader") { deps = [ ":cache", ":piex_loader", ":request", ":scheduler", + "../externs:file_manager_private", + ] +} + +js_library("image_loader_unittest") { + deps = [ + ":image_loader", + "//ui/webui/resources/js:webui_resource_test", ] } @@ -58,6 +62,19 @@ deps = [ "../file_manager/common/js:lru_cache", ] + externs_list = [ + "$externs_path/chrome.js", + "$externs_path/chrome_extensions.js", + "$externs_path/metrics_private.js", + ] +} + +js_library("image_loader_client_unittest") { + deps = [ + ":image_loader_client", + "../file_manager/common/js:unittest_util", + "//ui/webui/resources/js:webui_resource_test", + ] } js_library("piex_loader") { @@ -66,6 +83,15 @@ ] } +js_library("piex_loader_unittest") { + deps = [ + ":piex_loader", + "../file_manager/common/js:unittest_util", + "//ui/webui/resources/js:webui_resource_test", + "//ui/webui/resources/js/cr:ui", + ] +} + js_library("request") { deps = [ ":cache", @@ -82,3 +108,19 @@ ":request", ] } + +js_unit_tests("unit_tests") { + deps = [ + ":cache_unittest", + ":image_loader_client_unittest", + ":image_loader_unittest", + ":piex_loader_unittest", + ] +} + +group("closure_compile") { + deps = [ + ":closure_compile_module", + ":unit_tests_type_check", + ] +}
diff --git a/ui/file_manager/image_loader/cache_unittest.html b/ui/file_manager/image_loader/cache_unittest.html deleted file mode 100644 index ab844595..0000000 --- a/ui/file_manager/image_loader/cache_unittest.html +++ /dev/null
@@ -1,6 +0,0 @@ -<!-- Copyright 2014 The Chromium Authors. All rights reserved. - -- Use of this source code is governed by a BSD-style license that can be - -- found in the LICENSE file. - --> -<script src="cache.js"></script> -<script src="cache_unittest.js"></script>
diff --git a/ui/file_manager/image_loader/image_loader_client_unittest.html b/ui/file_manager/image_loader/image_loader_client_unittest.html deleted file mode 100644 index 5de38cdb..0000000 --- a/ui/file_manager/image_loader/image_loader_client_unittest.html +++ /dev/null
@@ -1,9 +0,0 @@ -<!-- Copyright 2014 The Chromium Authors. All rights reserved. - -- Use of this source code is governed by a BSD-style license that can be - -- found in the LICENSE file. - --> -<script src="../file_manager/common/js/lru_cache.js"></script> -<script src="../file_manager/common/js/unittest_util.js"></script> -<script src="image_loader_client.js"></script> - -<script src="image_loader_client_unittest.js"></script>
diff --git a/ui/file_manager/image_loader/image_loader_client_unittest.js b/ui/file_manager/image_loader/image_loader_client_unittest.js index dc31dcd1..2c1de53 100644 --- a/ui/file_manager/image_loader/image_loader_client_unittest.js +++ b/ui/file_manager/image_loader/image_loader_client_unittest.js
@@ -4,19 +4,19 @@ 'use strict'; -var chrome = { - metricsPrivate: { - MetricTypeType: { - HISTOGRAM_LOG: 'histogram-log', - HISTOGRAM_LINEAR: 'histogram-linear' - }, +/** @suppress {const|checkTypes} */ +function setUp() { + chrome.metricsPrivate = { + MetricTypeType: + {HISTOGRAM_LOG: 'histogram-log', HISTOGRAM_LINEAR: 'histogram-linear'}, recordPercentage: function() {}, recordValue: function() {} - }, - i18n: { - getMessage: function() {} - } -}; + }; + + chrome.i18n = { + getMessage: function() {}, + }; +} /** * Lets the client to load URL and returns the local cache (not caches in the @@ -30,6 +30,7 @@ function loadAndCheckCacheUsed(client, url, options) { var cacheUsed = true; + /** @suppress {accessControls} */ ImageLoaderClient.sendMessage_ = function(message, callback) { cacheUsed = false; if (callback)
diff --git a/ui/file_manager/image_loader/image_loader_unittest.html b/ui/file_manager/image_loader/image_loader_unittest.html deleted file mode 100644 index 03e7b2b7..0000000 --- a/ui/file_manager/image_loader/image_loader_unittest.html +++ /dev/null
@@ -1,12 +0,0 @@ -<!DOCTYPE html> -<!-- 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. - --> - -<script src="../../webui/resources/js/assert.js"></script> -<script src="../file_manager/foreground/js/metadata/image_orientation.js"></script> -<script src="image_loader_util.js"></script> - -<script src="image_loader.js"></script> -<script src="image_loader_unittest.js"></script>
diff --git a/ui/file_manager/image_loader/piex_loader_unittest.html b/ui/file_manager/image_loader/piex_loader_unittest.html deleted file mode 100644 index def61cc..0000000 --- a/ui/file_manager/image_loader/piex_loader_unittest.html +++ /dev/null
@@ -1,14 +0,0 @@ -<!DOCTYPE html> -<!-- Copyright 2017 The Chromium Authors. All rights reserved. - -- Use of this source code is governed by a BSD-style license that can be - -- found in the LICENSE file. - --> - -<script src="../../webui/resources/js/assert.js"></script> -<script src="../../webui/resources/js/cr.js"></script> -<script src="../../webui/resources/js/cr/ui.js"></script> -<script src="../file_manager/common/js/unittest_util.js"></script> -<script src="../file_manager/foreground/js/metadata/image_orientation.js"></script> - -<script src="piex_loader.js"></script> -<script src="piex_loader_unittest.js"></script>
diff --git a/ui/file_manager/image_loader/piex_loader_unittest.js b/ui/file_manager/image_loader/piex_loader_unittest.js index 240f31a..9d09aea 100644 --- a/ui/file_manager/image_loader/piex_loader_unittest.js +++ b/ui/file_manager/image_loader/piex_loader_unittest.js
@@ -8,7 +8,11 @@ } }; -var MockModule = cr.ui.define('div'); +/** + * @constructor + * @extends {HTMLDivElement} + */ +var MockModule = /** @type{function(new:MockModule)}*/ (cr.ui.define('div')); MockModule.prototype = Object.create(HTMLDivElement.prototype); MockModule.prototype.constructor = MockModule; @@ -70,7 +74,7 @@ assertEquals('thumbnail-data', data.thumbnail); assertEquals(0, unloadCount); assertEquals(1, loadCount); - return loader.load('http://foobar/another.raw') + return loader.load('http://foobar/another.raw'); }) .then(function(data) { // The NaCl module is not unloaded, as the next request came @@ -85,7 +89,7 @@ // unload the NaCl module. loader.simulateIdleTimeoutPassedForTests(); assertEquals(1, unloadCount); - return loader.load('http://foobar/chocolate.raw') + return loader.load('http://foobar/chocolate.raw'); }) .then(function(data) { // Following requests should reload the NaCl module. @@ -97,4 +101,4 @@ unloadPromise ]), callback); -}; +}
diff --git a/ui/file_manager/video_player/js/cast/BUILD.gn b/ui/file_manager/video_player/js/cast/BUILD.gn index 2435e5b..b0ca2c8 100644 --- a/ui/file_manager/video_player/js/cast/BUILD.gn +++ b/ui/file_manager/video_player/js/cast/BUILD.gn
@@ -19,8 +19,6 @@ externs_list = [ "$externs_path/chrome_extensions.js", "$externs_path/command_line_private.js", - "$externs_path/file_manager_private.js", - "$externs_path/file_system_provider.js", "$externs_path/media_player_private.js", "$externs_path/metrics_private.js", "../../../externs/app_window_common.js",
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn index 9bfe0d99..26c1273 100644 --- a/ui/gfx/BUILD.gn +++ b/ui/gfx/BUILD.gn
@@ -483,7 +483,7 @@ } # Cannot be a static_library in component builds due to exported functions -source_set("memory_buffer_sources") { +jumbo_source_set("memory_buffer_sources") { visibility = [ ":*" ] # Depend on through ":memory_buffer". # TODO(brettw) refactor this so these sources are in a coherent directory @@ -574,7 +574,7 @@ ] } -static_library("test_support") { +jumbo_static_library("test_support") { testonly = true sources = [ "animation/animation_test_api.cc",
diff --git a/ui/gfx/mojo/BUILD.gn b/ui/gfx/mojo/BUILD.gn index 046afff7..fb02266 100644 --- a/ui/gfx/mojo/BUILD.gn +++ b/ui/gfx/mojo/BUILD.gn
@@ -15,6 +15,7 @@ "overlay_transform.mojom", "presentation_feedback.mojom", "selection_bound.mojom", + "swap_result.mojom", "transform.mojom", ]
diff --git a/ui/gfx/mojo/swap_result.mojom b/ui/gfx/mojo/swap_result.mojom new file mode 100644 index 0000000..b73d4e43 --- /dev/null +++ b/ui/gfx/mojo/swap_result.mojom
@@ -0,0 +1,16 @@ +// 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 gfx.mojom; + +// SwapResult information which is used to indicate whether buffer swap +// succeeded or not. These values correspond to gfx::SwapResult values in +// ui/gfx/swap_result.h. Currently, it is used by the Ozone/Wayland to identify +// whether a buffer swap requested by the GPU process has been successful on +// the browser process side or not. +enum SwapResult { + ACK, + FAILED, + NAK_RECREATE_BUFFERS, +};
diff --git a/ui/gfx/mojo/swap_result.typemap b/ui/gfx/mojo/swap_result.typemap new file mode 100644 index 0000000..76d24a50 --- /dev/null +++ b/ui/gfx/mojo/swap_result.typemap
@@ -0,0 +1,8 @@ +# 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. + +mojom = "//ui/gfx/mojo/swap_result.mojom" +public_headers = [ "//ui/gfx/swap_result.h" ] +traits_headers = [ "//ui/gfx/mojo/swap_result_enum_traits.h" ] +type_mappings = [ "gfx.mojom.SwapResult=gfx::SwapResult" ]
diff --git a/ui/gfx/mojo/swap_result_enum_traits.h b/ui/gfx/mojo/swap_result_enum_traits.h new file mode 100644 index 0000000..c32ca59b --- /dev/null +++ b/ui/gfx/mojo/swap_result_enum_traits.h
@@ -0,0 +1,48 @@ +// 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 UI_GFX_MOJO_SWAP_RESULT_ENUM_TRAITS_H_ +#define UI_GFX_MOJO_SWAP_RESULT_ENUM_TRAITS_H_ + +#include "mojo/public/cpp/bindings/enum_traits.h" +#include "ui/gfx/mojo/swap_result.mojom-shared.h" +#include "ui/gfx/swap_result.h" + +namespace mojo { + +template <> +struct EnumTraits<gfx::mojom::SwapResult, gfx::SwapResult> { + static gfx::mojom::SwapResult ToMojom(gfx::SwapResult input) { + switch (input) { + case gfx::SwapResult::SWAP_ACK: + return gfx::mojom::SwapResult::ACK; + case gfx::SwapResult::SWAP_FAILED: + return gfx::mojom::SwapResult::FAILED; + case gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS: + return gfx::mojom::SwapResult::NAK_RECREATE_BUFFERS; + } + NOTREACHED(); + return gfx::mojom::SwapResult::FAILED; + } + + static bool FromMojom(gfx::mojom::SwapResult input, gfx::SwapResult* out) { + switch (input) { + case gfx::mojom::SwapResult::ACK: + *out = gfx::SwapResult::SWAP_ACK; + return true; + case gfx::mojom::SwapResult::FAILED: + *out = gfx::SwapResult::SWAP_FAILED; + return true; + case gfx::mojom::SwapResult::NAK_RECREATE_BUFFERS: + *out = gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS; + return true; + } + NOTREACHED(); + return false; + } +}; + +} // namespace mojo + +#endif // UI_GFX_MOJO_SWAP_RESULT_ENUM_TRAITS_H_
diff --git a/ui/gfx/typemaps.gni b/ui/gfx/typemaps.gni index ba383a8..c4ea97d 100644 --- a/ui/gfx/typemaps.gni +++ b/ui/gfx/typemaps.gni
@@ -13,6 +13,7 @@ "//ui/gfx/mojo/gpu_fence_handle.typemap", "//ui/gfx/mojo/overlay_transform.typemap", "//ui/gfx/mojo/presentation_feedback.typemap", + "//ui/gfx/mojo/swap_result.typemap", "//ui/gfx/mojo/selection_bound.typemap", "//ui/gfx/mojo/transform.typemap", "//ui/gfx/range/mojo/range.typemap",
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn index 2699acc..c92de71 100644 --- a/ui/gl/BUILD.gn +++ b/ui/gl/BUILD.gn
@@ -3,6 +3,7 @@ # found in the LICENSE file. import("//build/buildflag_header.gni") +import("//build/config/jumbo.gni") import("//build/config/chrome_build.gni") import("//build/config/linux/pkg_config.gni") import("//build/config/ui.gni") @@ -46,7 +47,7 @@ } } -component("gl") { +jumbo_component("gl") { output_name = "gl_wrapper" # Avoid colliding with OS X"s libGL.dylib. sources = [ @@ -375,7 +376,7 @@ } } -static_library("gl_unittest_utils") { +jumbo_static_library("gl_unittest_utils") { testonly = true sources = [ "egl_bindings_autogen_mock.cc", @@ -401,7 +402,7 @@ ] } -static_library("test_support") { +jumbo_static_library("test_support") { testonly = true sources = [ "test/gl_image_test_support.cc",
diff --git a/ui/ozone/common/linux/gbm_wrapper.cc b/ui/ozone/common/linux/gbm_wrapper.cc index 99eed07..b9a05676 100644 --- a/ui/ozone/common/linux/gbm_wrapper.cc +++ b/ui/ozone/common/linux/gbm_wrapper.cc
@@ -4,7 +4,9 @@ #include "ui/ozone/common/linux/gbm_wrapper.h" +#include <fcntl.h> #include <gbm.h> +#include <xf86drm.h> #include "base/posix/eintr_wrapper.h" #include "ui/gfx/buffer_format_util.h" @@ -14,6 +16,91 @@ namespace gbm_wrapper { +namespace { + +// Minigbm and system linux gbm have some differences. There is no clear way how +// to distinguish linux gbm (Mesa, basically) and minigbm, which can be both +// third_party and minigbm. Thus, use GBM_BO_IMPORT_FD_PLANAR define to +// identify, which gbm is used. +#if defined(GBM_BO_IMPORT_FD_PLANAR) +// Minigbm defines GBM_BO_IMPORT_FD_PLANAR, which is unknown in linux gbm. +// Redefine it in a common define. +#define GBM_BO_IMPORT_FD_DATA GBM_BO_IMPORT_FD_PLANAR + +// There are some methods, which require knowing whether minigbm is used or not. +#ifndef USING_MINIGBM +#define USING_MINIGBM +#endif // USING_MINIGBM + +// Minigbm and system linux gbm have alike gbm_bo_import* structures, but some +// of the data variables have different type. +using gbm_bo_import_fd_data_with_modifier = struct gbm_import_fd_planar_data; +#else +// See comment above. Linux defines GBM_BO_IMPORT_FD_MODIFIER, thus, redefine it +// in a common define. +#define GBM_BO_IMPORT_FD_DATA GBM_BO_IMPORT_FD_MODIFIER +// See comment above. +using gbm_bo_import_fd_data_with_modifier = struct gbm_import_fd_modifier_data; +#endif + +void InitializeImportData(uint32_t format, + const gfx::Size& size, + const std::vector<base::ScopedFD>& fds, + const std::vector<gfx::NativePixmapPlane>& planes, + gbm_bo_import_fd_data_with_modifier* fd_data) { + fd_data->width = size.width(); + fd_data->height = size.height(); + fd_data->format = format; + + DCHECK_LE(planes.size(), 3u); + for (size_t i = 0; i < planes.size(); ++i) { + fd_data->fds[i] = fds[i < fds.size() ? i : 0].get(); + fd_data->strides[i] = planes[i].stride; + fd_data->offsets[i] = planes[i].offset; +#if defined(USING_MINIGBM) + fd_data->format_modifiers[i] = planes[i].modifier; +#else + fd_data->modifier = planes[i].modifier; +#endif + } +} + +int GetPlaneFdForBo(gbm_bo* bo, size_t plane) { + DCHECK(plane < gbm_bo_get_plane_count(bo)); + + // System linux gbm (or Mesa gbm) does not provide fds per plane basis. Thus, + // get plane handle and use drm ioctl to get a prime fd out of it avoid having + // two different branches for minigbm and Mesa gbm here. + gbm_device* gbm_dev = gbm_bo_get_device(bo); + int dev_fd = gbm_device_get_fd(gbm_dev); + if (dev_fd <= 0) { + LOG(ERROR) << "Unable to get device fd"; + return -1; + } + + const uint32_t plane_handle = gbm_bo_get_handle_for_plane(bo, plane).u32; + int fd = -1; + int ret; + // Use DRM_RDWR to allow the fd to be mappable in another process. + ret = drmPrimeHandleToFD(dev_fd, plane_handle, DRM_CLOEXEC | DRM_RDWR, &fd); + + // Older DRM implementations blocked DRM_RDWR, but gave a read/write mapping + // anyways + if (ret) + ret = drmPrimeHandleToFD(dev_fd, plane_handle, DRM_CLOEXEC, &fd); + + return ret ? ret : fd; +} + +size_t GetSizeOfPlane(gbm_bo* bo, size_t plane) { + // System linux gbm (or Mesa gbm) does not provide plane size. Thus, calculate + // it by ourselves and avoid having two different branches for minigbm and + // Mesa gbm here. + return gbm_bo_get_height(bo) * gbm_bo_get_stride_for_plane(bo, plane); +} + +} // namespace + class Buffer final : public ui::GbmBuffer { public: Buffer(struct gbm_bo* bo, @@ -72,7 +159,7 @@ } uint32_t GetPlaneHandle(size_t plane) const override { DCHECK_LT(plane, planes_.size()); - return gbm_bo_get_plane_handle(bo_, plane).u32; + return gbm_bo_get_handle_for_plane(bo_, plane).u32; } uint32_t GetHandle() const override { return gbm_bo_get_handle(bo_).u32; } gfx::NativePixmapHandle ExportHandle() const override { @@ -121,11 +208,18 @@ std::vector<base::ScopedFD> fds; std::vector<gfx::NativePixmapPlane> planes; - const uint64_t modifier = gbm_bo_get_format_modifier(bo); - for (size_t i = 0; i < gbm_bo_get_num_planes(bo); ++i) { + const uint64_t modifier = gbm_bo_get_modifier(bo); + const int plane_count = gbm_bo_get_plane_count(bo); + // The Mesa's gbm implementation explicitly checks whether plane count <= and + // returns 1 if the condition is true. Nevertheless, use a DCHECK here to make + // sure the condition is not broken there. + DCHECK(plane_count > 0); + // Ensure there are no differences in integer signs by casting any possible + // values to size_t. + for (size_t i = 0; i < static_cast<size_t>(plane_count); ++i) { // The fd returned by gbm_bo_get_fd is not ref-counted and need to be // kept open for the lifetime of the buffer. - base::ScopedFD fd(gbm_bo_get_plane_fd(bo, i)); + base::ScopedFD fd(GetPlaneFdForBo(bo, i)); // TODO(dcastagna): support multiple fds. // crbug.com/642410 @@ -138,9 +232,9 @@ fds.emplace_back(std::move(fd)); } - planes.emplace_back(gbm_bo_get_plane_stride(bo, i), - gbm_bo_get_plane_offset(bo, i), - gbm_bo_get_plane_size(bo, i), modifier); + planes.emplace_back(gbm_bo_get_stride_for_plane(bo, i), + gbm_bo_get_offset(bo, i), GetSizeOfPlane(bo, i), + modifier); } return std::make_unique<Buffer>(bo, format, flags, modifier, std::move(fds), size, std::move(planes)); @@ -187,28 +281,21 @@ DCHECK_EQ(planes[0].offset, 0); // Try to use scanout if supported. - int gbm_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_TEXTURING; + int gbm_flags = GBM_BO_USE_SCANOUT; +#if defined(GBM_BO_USE_TEXTURING) + gbm_flags |= GBM_BO_USE_TEXTURING; +#endif if (!gbm_device_is_format_supported(device_, format, gbm_flags)) gbm_flags &= ~GBM_BO_USE_SCANOUT; struct gbm_bo* bo = nullptr; if (gbm_device_is_format_supported(device_, format, gbm_flags)) { - struct gbm_import_fd_planar_data fd_data; - fd_data.width = size.width(); - fd_data.height = size.height(); - fd_data.format = format; - - DCHECK_LE(planes.size(), 3u); - for (size_t i = 0; i < planes.size(); ++i) { - fd_data.fds[i] = fds[i < fds.size() ? i : 0].get(); - fd_data.strides[i] = planes[i].stride; - fd_data.offsets[i] = planes[i].offset; - fd_data.format_modifiers[i] = planes[i].modifier; - } + gbm_bo_import_fd_data_with_modifier fd_data; + InitializeImportData(format, size, fds, planes, &fd_data); // The fd passed to gbm_bo_import is not ref-counted and need to be // kept open for the lifetime of the buffer. - bo = gbm_bo_import(device_, GBM_BO_IMPORT_FD_PLANAR, &fd_data, gbm_flags); + bo = gbm_bo_import(device_, GBM_BO_IMPORT_FD_DATA, &fd_data, gbm_flags); if (!bo) { LOG(ERROR) << "nullptr returned from gbm_bo_import"; return nullptr;
diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn index 7a846766..76dcdb5 100644 --- a/ui/ozone/platform/wayland/BUILD.gn +++ b/ui/ozone/platform/wayland/BUILD.gn
@@ -85,6 +85,7 @@ "//third_party/minigbm", "//third_party/wayland:wayland_client", "//third_party/wayland-protocols:linux_dmabuf_protocol", + "//third_party/wayland-protocols:presentation_time_protocol", "//third_party/wayland-protocols:xdg_shell_protocol", "//ui/base", "//ui/base:ui_features", @@ -157,6 +158,7 @@ "//testing/gtest", "//third_party/wayland:wayland_server", "//third_party/wayland-protocols:xdg_shell_protocol", + "//ui/base", "//ui/base:ui_features", "//ui/events/ozone:events_ozone_layout", "//ui/ozone:platform",
diff --git a/ui/ozone/platform/wayland/DEPS b/ui/ozone/platform/wayland/DEPS index fde6dba3..42e7c86b 100644 --- a/ui/ozone/platform/wayland/DEPS +++ b/ui/ozone/platform/wayland/DEPS
@@ -1,5 +1,7 @@ include_rules = [ "+ui/base/ui_features.h", # UI features doesn't bring in all of ui/base. "+mojo/public", + "+ui/base/dragdrop/drag_drop_types.h", + "+ui/base/dragdrop/os_exchange_data.h", + "+ui/base/dragdrop/os_exchange_data_provider_aura.h", ] -
diff --git a/ui/ozone/platform/wayland/fake_server.cc b/ui/ozone/platform/wayland/fake_server.cc index 6a63f3a..2bd0f9c 100644 --- a/ui/ozone/platform/wayland/fake_server.cc +++ b/ui/ozone/platform/wayland/fake_server.cc
@@ -290,6 +290,15 @@ // wl_data_device +void DataDeviceStartDrag(wl_client* client, + wl_resource* resource, + wl_resource* source, + wl_resource* origin, + wl_resource* icon, + uint32_t serial) { + NOTIMPLEMENTED(); +} + void DataDeviceSetSelection(wl_client* client, wl_resource* resource, wl_resource* data_source, @@ -304,8 +313,7 @@ } const struct wl_data_device_interface data_device_impl = { - nullptr /*data_device_start_drag*/, &DataDeviceSetSelection, - &DataDeviceRelease}; + &DataDeviceStartDrag, &DataDeviceSetSelection, &DataDeviceRelease}; // wl_data_device_manager @@ -347,6 +355,13 @@ // wl_data_offer +void DataOfferAccept(wl_client* client, + wl_resource* resource, + uint32_t serial, + const char* mime_type) { + NOTIMPLEMENTED(); +} + void DataOfferReceive(wl_client* client, wl_resource* resource, const char* mime_type, @@ -359,10 +374,20 @@ wl_resource_destroy(resource); } +void DataOfferFinish(wl_client* client, wl_resource* resource) { + NOTIMPLEMENTED(); +} + +void DataOfferSetActions(wl_client* client, + wl_resource* resource, + uint32_t dnd_actions, + uint32_t preferred_action) { + NOTIMPLEMENTED(); +} + const struct wl_data_offer_interface data_offer_impl = { - nullptr /* data_offer_accept*/, DataOfferReceive, - nullptr /*data_offer_finish*/, DataOfferDestroy, - nullptr /*data_offer_set_actions*/}; + DataOfferAccept, DataOfferReceive, DataOfferDestroy, DataOfferFinish, + DataOfferSetActions}; // wl_data_source @@ -376,8 +401,14 @@ wl_resource_destroy(resource); } +void SetActions(wl_client* client, + wl_resource* resource, + uint32_t dnd_actions) { + NOTIMPLEMENTED(); +} + const struct wl_data_source_interface data_source_impl = { - DataSourceOffer, DataSourceDestroy, nullptr /*data_source_set_actions*/}; + DataSourceOffer, DataSourceDestroy, SetActions}; // wl_seat @@ -784,10 +815,15 @@ void MockDataOffer::Receive(const std::string& mime_type, base::ScopedFD fd) { DCHECK(fd.is_valid()); - std::string text_utf8(kSampleClipboardText); + std::string text_data; + if (mime_type == kTextMimeTypeUtf8) + text_data = kSampleClipboardText; + else if (mime_type == kTextMimeTypeText) + text_data = kSampleTextForDragAndDrop; + io_thread_.task_runner()->PostTask( FROM_HERE, - base::BindOnce(&WriteDataOnWorkerThread, std::move(fd), text_utf8)); + base::BindOnce(&WriteDataOnWorkerThread, std::move(fd), text_data)); } void MockDataOffer::OnOffer(const std::string& mime_type) { @@ -816,6 +852,27 @@ return GetUserDataAs<MockDataOffer>(data_offer_resource); } +void MockDataDevice::OnEnter(uint32_t serial, + wl_resource* surface, + wl_fixed_t x, + wl_fixed_t y, + MockDataOffer& data_offer) { + wl_data_device_send_enter(resource(), serial, surface, x, y, + data_offer.resource()); +} + +void MockDataDevice::OnLeave() { + wl_data_device_send_leave(resource()); +} + +void MockDataDevice::OnMotion(uint32_t time, wl_fixed_t x, wl_fixed_t y) { + wl_data_device_send_motion(resource(), time, x, y); +} + +void MockDataDevice::OnDrop() { + wl_data_device_send_drop(resource()); +} + void MockDataDevice::OnSelection(MockDataOffer& data_offer) { wl_data_device_send_selection(resource(), data_offer.resource()); }
diff --git a/ui/ozone/platform/wayland/fake_server.h b/ui/ozone/platform/wayland/fake_server.h index 7739e6d..4ee8348 100644 --- a/ui/ozone/platform/wayland/fake_server.h +++ b/ui/ozone/platform/wayland/fake_server.h
@@ -24,7 +24,10 @@ namespace wl { constexpr char kTextMimeTypeUtf8[] = "text/plain;charset=utf-8"; +constexpr char kTextMimeTypeText[] = "text/plain"; constexpr char kSampleClipboardText[] = "This is a sample text for clipboard."; +constexpr char kSampleTextForDragAndDrop[] = + "This is a sample text for drag-and-drop."; // Base class for managing the life cycle of server objects. class ServerObject { @@ -222,6 +225,14 @@ void SetSelection(MockDataSource* data_source, uint32_t serial); MockDataOffer* OnDataOffer(); + void OnEnter(uint32_t serial, + wl_resource* surface, + wl_fixed_t x, + wl_fixed_t y, + MockDataOffer& data_offer); + void OnLeave(); + void OnMotion(uint32_t time, wl_fixed_t x, wl_fixed_t y); + void OnDrop(); void OnSelection(MockDataOffer& data_offer); private:
diff --git a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc index 5403739..2e2e352 100644 --- a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc +++ b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc
@@ -69,8 +69,7 @@ } bool GbmSurfacelessWayland::SupportsPostSubBuffer() { - // TODO(msisov): figure out how to enable subbuffers with wayland/dmabuf. - return false; + return true; } gfx::SwapResult GbmSurfacelessWayland::PostSubBuffer( @@ -135,8 +134,10 @@ int height, const SwapCompletionCallback& completion_callback, const PresentationCallback& presentation_callback) { - // See the comment in SupportsPostSubBuffer. - NOTREACHED(); + PendingFrame* frame = unsubmitted_frames_.back().get(); + frame->damage_region_ = gfx::Rect(x, y, width, height); + + SwapBuffersAsync(completion_callback, presentation_callback); } EGLConfig GbmSurfacelessWayland::GetConfig() { @@ -198,16 +199,13 @@ return; } + auto callback = + base::BindOnce(&GbmSurfacelessWayland::OnScheduleBufferSwapDone, + weak_factory_.GetWeakPtr()); uint32_t buffer_id = planes_.back().pixmap->GetUniqueId(); - surface_factory_->ScheduleBufferSwap(widget_, buffer_id); - - // Check comment in ::SupportsPresentationCallback. - OnSubmission(gfx::SwapResult::SWAP_ACK, nullptr); - OnPresentation( - gfx::PresentationFeedback(base::TimeTicks::Now(), base::TimeDelta(), - gfx::PresentationFeedback::kZeroCopy)); - - planes_.clear(); + surface_factory_->ScheduleBufferSwap(widget_, buffer_id, + submitted_frame_->damage_region_, + std::move(callback)); } } @@ -224,6 +222,14 @@ SubmitFrame(); } +void GbmSurfacelessWayland::OnScheduleBufferSwapDone( + gfx::SwapResult result, + const gfx::PresentationFeedback& feedback) { + OnSubmission(result, nullptr); + OnPresentation(feedback); + planes_.clear(); +} + void GbmSurfacelessWayland::OnSubmission( gfx::SwapResult result, std::unique_ptr<gfx::GpuFence> out_fence) {
diff --git a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h index cc660f3..bfb8c9f9 100644 --- a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h +++ b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h
@@ -68,6 +68,10 @@ bool ready = false; gfx::SwapResult swap_result = gfx::SwapResult::SWAP_FAILED; + // A region of the updated content in a corresponding frame. It's used to + // advice Wayland which part of a buffer is going to be updated. Passing {0, + // 0, 0, 0} results in a whole buffer update on the Wayland compositor side. + gfx::Rect damage_region_ = gfx::Rect(); std::vector<gl::GLSurfaceOverlay> overlays; SwapCompletionCallback completion_callback; PresentationCallback presentation_callback; @@ -78,6 +82,8 @@ EGLSyncKHR InsertFence(bool implicit); void FenceRetired(PendingFrame* frame); + void OnScheduleBufferSwapDone(gfx::SwapResult result, + const gfx::PresentationFeedback& feedback); void OnSubmission(gfx::SwapResult result, std::unique_ptr<gfx::GpuFence> out_fence); void OnPresentation(const gfx::PresentationFeedback& feedback);
diff --git a/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.cc b/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.cc index 88b9515..226f6d4 100644 --- a/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.cc +++ b/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.cc
@@ -86,11 +86,15 @@ wc_ptr_->DestroyZwpLinuxDmabuf(buffer_id); } -void WaylandConnectionProxy::ScheduleBufferSwap(gfx::AcceleratedWidget widget, - uint32_t buffer_id) { +void WaylandConnectionProxy::ScheduleBufferSwap( + gfx::AcceleratedWidget widget, + uint32_t buffer_id, + const gfx::Rect& damage_region, + wl::BufferSwapCallback callback) { DCHECK(gpu_thread_runner_->BelongsToCurrentThread()); DCHECK(wc_ptr_); - wc_ptr_->ScheduleBufferSwap(widget, buffer_id); + wc_ptr_->ScheduleBufferSwap(widget, buffer_id, damage_region, + std::move(callback)); } WaylandWindow* WaylandConnectionProxy::GetWindow(
diff --git a/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h b/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h index 9126716..f06f9b9a 100644 --- a/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h +++ b/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h
@@ -11,6 +11,7 @@ #include "mojo/public/cpp/bindings/binding_set.h" #include "ui/gfx/native_widget_types.h" #include "ui/ozone/platform/wayland/wayland_connection.h" +#include "ui/ozone/platform/wayland/wayland_util.h" #include "ui/ozone/public/interfaces/wayland/wayland_connection.mojom.h" #if defined(WAYLAND_GBM) @@ -19,6 +20,11 @@ struct wl_shm; +namespace gfx { +enum class SwapResult; +class Rect; +} // namespace gfx + namespace ui { class WaylandConnection; @@ -58,7 +64,12 @@ // Asks Wayland to find a wl_buffer with the |buffer_id| and schedule a // buffer swap for a WaylandWindow, which backs the following |widget|. - void ScheduleBufferSwap(gfx::AcceleratedWidget widget, uint32_t buffer_id); + // The |callback| is called once a frame callback from the Wayland server + // is received. + void ScheduleBufferSwap(gfx::AcceleratedWidget widget, + uint32_t buffer_id, + const gfx::Rect& damage_region, + wl::BufferSwapCallback callback); #if defined(WAYLAND_GBM) // Returns a gbm_device based on a DRM render node.
diff --git a/ui/ozone/platform/wayland/wayland_buffer_manager.cc b/ui/ozone/platform/wayland/wayland_buffer_manager.cc index 71e71d06..2c9a02f 100644 --- a/ui/ozone/platform/wayland/wayland_buffer_manager.cc +++ b/ui/ozone/platform/wayland/wayland_buffer_manager.cc
@@ -5,8 +5,8 @@ #include "ui/ozone/platform/wayland/wayland_buffer_manager.h" #include <drm_fourcc.h> - #include <linux-dmabuf-unstable-v1-client-protocol.h> +#include <presentation-time-client-protocol.h> #include "base/trace_event/trace_event.h" #include "ui/ozone/common/linux/drm_util_linux.h" @@ -15,6 +15,33 @@ namespace ui { +namespace { + +uint32_t GetPresentationKindFlags(uint32_t flags) { + uint32_t presentation_flags = 0; + if (flags & WP_PRESENTATION_FEEDBACK_KIND_VSYNC) + presentation_flags |= gfx::PresentationFeedback::kVSync; + if (flags & WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK) + presentation_flags |= gfx::PresentationFeedback::kHWClock; + if (flags & WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION) + presentation_flags |= gfx::PresentationFeedback::kHWCompletion; + if (flags & WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY) + presentation_flags |= gfx::PresentationFeedback::kZeroCopy; + + return presentation_flags; +} + +base::TimeTicks GetPresentationFeedbackTimeStamp(uint32_t tv_sec_hi, + uint32_t tv_sec_lo, + uint32_t tv_nsec) { + const int64_t seconds = (static_cast<int64_t>(tv_sec_hi) << 32) + tv_sec_lo; + const int64_t microseconds = seconds * base::Time::kMicrosecondsPerSecond + + tv_nsec / base::Time::kNanosecondsPerMicrosecond; + return base::TimeTicks() + base::TimeDelta::FromMicroseconds(microseconds); +} + +} // namespace + WaylandBufferManager::Buffer::Buffer() = default; WaylandBufferManager::Buffer::Buffer(uint32_t id, zwp_linux_buffer_params_v1* zwp_params) @@ -89,9 +116,11 @@ // TODO(msisov): handle buffer swap failure or success. bool WaylandBufferManager::ScheduleBufferSwap(gfx::AcceleratedWidget widget, - uint32_t buffer_id) { - TRACE_EVENT1("Wayland", "WaylandBufferManager::SwapBuffer", "Buffer id", - buffer_id); + uint32_t buffer_id, + const gfx::Rect& damage_region, + wl::BufferSwapCallback callback) { + TRACE_EVENT1("Wayland", "WaylandBufferManager::ScheduleSwapBuffer", + "Buffer id", buffer_id); if (!ValidateDataFromGpu(widget, buffer_id)) return false; @@ -109,6 +138,8 @@ // Assign a widget to this buffer, which is used to find a corresponding // WaylandWindow. buffer->widget = widget; + buffer->buffer_swap_callback = std::move(callback); + buffer->damage_region = damage_region; if (buffer->wl_buffer) { // A wl_buffer might not exist by this time. Silently return. @@ -127,6 +158,15 @@ error_message_ = "Trying to destroy non-existing buffer"; return false; } + // It can happen that a buffer is destroyed before a frame callback comes. + // Thus, just mark this as a successful swap, which is ok to do. + Buffer* buffer = it->second.get(); + if (!buffer->buffer_swap_callback.is_null()) { + std::move(buffer->buffer_swap_callback) + .Run(gfx::SwapResult::SWAP_ACK, + gfx::PresentationFeedback(base::TimeTicks::Now(), + base::TimeDelta(), 0)); + } buffers_.erase(it); connection_->ScheduleFlush(); @@ -139,20 +179,40 @@ // TODO(msisov): handle buffer swap failure or success. bool WaylandBufferManager::SwapBuffer(Buffer* buffer) { + TRACE_EVENT1("Wayland", "WaylandBufferManager::SwapBuffer", "Buffer id", + buffer->buffer_id); + WaylandWindow* window = connection_->GetWindow(buffer->widget); if (!window) { error_message_ = "A WaylandWindow with current widget does not exist"; return false; } - // TODO(msisov): it would be beneficial to use real damage regions to improve - // performance. - // - // TODO(msisov): also start using wl_surface_frame callbacks for better - // performance. - wl_surface_damage(window->surface(), 0, 0, window->GetBounds().width(), - window->GetBounds().height()); + wl_surface_damage(window->surface(), buffer->damage_region.x(), + buffer->damage_region.y(), buffer->damage_region.width(), + buffer->damage_region.height()); wl_surface_attach(window->surface(), buffer->wl_buffer.get(), 0, 0); + + static const wl_callback_listener frame_listener = { + WaylandBufferManager::FrameCallbackDone}; + DCHECK(!buffer->wl_frame_callback); + buffer->wl_frame_callback.reset(wl_surface_frame(window->surface())); + wl_callback_add_listener(buffer->wl_frame_callback.get(), &frame_listener, + this); + + // Set up presentation feedback. + static const wp_presentation_feedback_listener feedback_listener = { + WaylandBufferManager::FeedbackSyncOutput, + WaylandBufferManager::FeedbackPresented, + WaylandBufferManager::FeedbackDiscarded}; + if (connection_->presentation()) { + DCHECK(!buffer->wp_presentation_feedback); + buffer->wp_presentation_feedback.reset(wp_presentation_feedback( + connection_->presentation(), window->surface())); + wp_presentation_feedback_add_listener( + buffer->wp_presentation_feedback.get(), &feedback_listener, this); + } + wl_surface_commit(window->surface()); connection_->ScheduleFlush(); @@ -250,7 +310,13 @@ zwp_linux_buffer_params_v1_destroy(params); if (buffer->widget != gfx::kNullAcceleratedWidget) - ScheduleBufferSwap(buffer->widget, buffer->buffer_id); + SwapBuffer(buffer); +} + +void WaylandBufferManager::OnBufferSwapped(Buffer* buffer) { + DCHECK(!buffer->buffer_swap_callback.is_null()); + std::move(buffer->buffer_swap_callback) + .Run(buffer->swap_result, std::move(buffer->feedback)); } // static @@ -294,4 +360,95 @@ LOG(FATAL) << "zwp_linux_buffer_params.create failed"; } +// static +void WaylandBufferManager::FrameCallbackDone(void* data, + wl_callback* callback, + uint32_t time) { + WaylandBufferManager* self = static_cast<WaylandBufferManager*>(data); + DCHECK(self); + for (auto& item : self->buffers_) { + Buffer* buffer = item.second.get(); + if (buffer->wl_frame_callback.get() == callback) { + buffer->swap_result = gfx::SwapResult::SWAP_ACK; + buffer->wl_frame_callback.reset(); + + // If presentation feedback is not supported, use fake feedback and + // trigger the callback. + if (!self->connection_->presentation()) { + buffer->feedback = gfx::PresentationFeedback(base::TimeTicks::Now(), + base::TimeDelta(), 0); + self->OnBufferSwapped(buffer); + } + return; + } + } + + NOTREACHED(); +} + +// static +void WaylandBufferManager::FeedbackSyncOutput( + void* data, + struct wp_presentation_feedback* wp_presentation_feedback, + struct wl_output* output) { + NOTIMPLEMENTED_LOG_ONCE(); +} + +// static +void WaylandBufferManager::FeedbackPresented( + void* data, + struct wp_presentation_feedback* wp_presentation_feedback, + uint32_t tv_sec_hi, + uint32_t tv_sec_lo, + uint32_t tv_nsec, + uint32_t refresh, + uint32_t seq_hi, + uint32_t seq_lo, + uint32_t flags) { + WaylandBufferManager* self = static_cast<WaylandBufferManager*>(data); + DCHECK(self); + + for (auto& item : self->buffers_) { + Buffer* buffer = item.second.get(); + if (buffer->wp_presentation_feedback.get() == wp_presentation_feedback) { + // Frame callback must come before a feedback is presented. + DCHECK(!buffer->wl_frame_callback); + + buffer->feedback = gfx::PresentationFeedback( + GetPresentationFeedbackTimeStamp(tv_sec_hi, tv_sec_lo, tv_nsec), + base::TimeDelta::FromNanoseconds(refresh), + GetPresentationKindFlags(flags)); + + buffer->wp_presentation_feedback.reset(); + self->OnBufferSwapped(buffer); + return; + } + } + + NOTREACHED(); +} + +// static +void WaylandBufferManager::FeedbackDiscarded( + void* data, + struct wp_presentation_feedback* wp_presentation_feedback) { + WaylandBufferManager* self = static_cast<WaylandBufferManager*>(data); + DCHECK(self); + + for (auto& item : self->buffers_) { + Buffer* buffer = item.second.get(); + if (buffer->wp_presentation_feedback.get() == wp_presentation_feedback) { + // Frame callback must come before a feedback is presented. + DCHECK(!buffer->wl_frame_callback); + buffer->feedback = gfx::PresentationFeedback::Failure(); + + buffer->wp_presentation_feedback.reset(); + self->OnBufferSwapped(buffer); + return; + } + } + + NOTREACHED(); +} + } // namespace ui
diff --git a/ui/ozone/platform/wayland/wayland_buffer_manager.h b/ui/ozone/platform/wayland/wayland_buffer_manager.h index d52d508..cf2d9720 100644 --- a/ui/ozone/platform/wayland/wayland_buffer_manager.h +++ b/ui/ozone/platform/wayland/wayland_buffer_manager.h
@@ -11,11 +11,16 @@ #include "base/containers/flat_map.h" #include "base/files/file.h" #include "base/macros.h" +#include "ui/gfx/geometry/rect.h" #include "ui/gfx/native_widget_types.h" +#include "ui/gfx/presentation_feedback.h" +#include "ui/gfx/swap_result.h" #include "ui/ozone/platform/wayland/wayland_object.h" +#include "ui/ozone/platform/wayland/wayland_util.h" struct zwp_linux_dmabuf_v1; struct zwp_linux_buffer_params_v1; +struct wp_presentation_feedback; namespace gfx { enum class BufferFormat; @@ -52,8 +57,13 @@ uint32_t buffer_id); // Assigns a wl_buffer with |buffer_id| to a window with the same |widget|. On - // error, false is returned and |error_message_| is set. - bool ScheduleBufferSwap(gfx::AcceleratedWidget widget, uint32_t buffer_id); + // error, false is returned and |error_message_| is set. A |damage_region| + // identifies which part of the buffer is updated. If an empty region is + // provided, the whole buffer is updated. + bool ScheduleBufferSwap(gfx::AcceleratedWidget widget, + uint32_t buffer_id, + const gfx::Rect& damage_region, + wl::BufferSwapCallback callback); // Destroys a buffer with |buffer_id| in |buffers_|. On error, false is // returned and |error_message_| is set. @@ -81,12 +91,37 @@ // Widget to attached/being attach WaylandWindow. gfx::AcceleratedWidget widget = gfx::kNullAcceleratedWidget; + // Describes the region where the pending buffer is different from the + // current surface contents, and where the surface therefore needs to be + // repainted. + gfx::Rect damage_region; + + // A buffer swap result once the buffer is committed. + gfx::SwapResult swap_result; + + // A feedback, which is received if a presentation feedback protocol is + // supported. + gfx::PresentationFeedback feedback; + // Params that are used to create a wl_buffer. zwp_linux_buffer_params_v1* params = nullptr; // A wl_buffer backed by a dmabuf created on the GPU side. wl::Object<wl_buffer> wl_buffer; + // A callback, which is called once the |wl_frame_callback| from the server + // is received. + wl::BufferSwapCallback buffer_swap_callback; + + // A Wayland callback, which is triggered once wl_buffer has been committed + // and it is right time to notify the GPU that it can start a new drawing + // operation. + wl::Object<wl_callback> wl_frame_callback; + + // A presentation feedback provided by the Wayland server once frame is + // shown. + wl::Object<wp_presentation_feedback> wp_presentation_feedback; + DISALLOW_COPY_AND_ASSIGN(Buffer); }; @@ -109,6 +144,8 @@ void CreateSucceededInternal(struct zwp_linux_buffer_params_v1* params, struct wl_buffer* new_buffer); + void OnBufferSwapped(Buffer* buffer); + // zwp_linux_dmabuf_v1_listener static void Modifiers(void* data, struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf, @@ -126,6 +163,30 @@ static void CreateFailed(void* data, struct zwp_linux_buffer_params_v1* params); + // wl_callback_listener + static void FrameCallbackDone(void* data, + wl_callback* callback, + uint32_t time); + + // wp_presentation_feedback_listener + static void FeedbackSyncOutput( + void* data, + struct wp_presentation_feedback* wp_presentation_feedback, + struct wl_output* output); + static void FeedbackPresented( + void* data, + struct wp_presentation_feedback* wp_presentation_feedback, + uint32_t tv_sec_hi, + uint32_t tv_sec_lo, + uint32_t tv_nsec, + uint32_t refresh, + uint32_t seq_hi, + uint32_t seq_lo, + uint32_t flags); + static void FeedbackDiscarded( + void* data, + struct wp_presentation_feedback* wp_presentation_feedback); + // Stores announced buffer formats supported by the compositor. std::vector<gfx::BufferFormat> supported_buffer_formats_;
diff --git a/ui/ozone/platform/wayland/wayland_connection.cc b/ui/ozone/platform/wayland/wayland_connection.cc index 2f836c76..b0cd50e3 100644 --- a/ui/ozone/platform/wayland/wayland_connection.cc +++ b/ui/ozone/platform/wayland/wayland_connection.cc
@@ -14,6 +14,7 @@ #include "base/message_loop/message_loop_current.h" #include "base/strings/string_util.h" #include "base/threading/thread_task_runner_handle.h" +#include "ui/gfx/swap_result.h" #include "ui/ozone/platform/wayland/wayland_buffer_manager.h" #include "ui/ozone/platform/wayland/wayland_object.h" #include "ui/ozone/platform/wayland/wayland_window.h" @@ -23,11 +24,20 @@ namespace ui { namespace { -const uint32_t kMaxCompositorVersion = 4; -const uint32_t kMaxLinuxDmabufVersion = 1; -const uint32_t kMaxSeatVersion = 4; -const uint32_t kMaxShmVersion = 1; -const uint32_t kMaxXdgShellVersion = 1; +constexpr uint32_t kMaxCompositorVersion = 4; +constexpr uint32_t kMaxLinuxDmabufVersion = 1; +constexpr uint32_t kMaxSeatVersion = 4; +constexpr uint32_t kMaxShmVersion = 1; +constexpr uint32_t kMaxXdgShellVersion = 1; +constexpr uint32_t kMaxDeviceManagerVersion = 3; +constexpr uint32_t kMaxWpPresentationVersion = 1; + +std::unique_ptr<WaylandDataSource> CreateWaylandDataSource( + WaylandDataDeviceManager* data_device_manager, + WaylandConnection* connection) { + wl_data_source* data_source = data_device_manager->CreateSource(); + return std::make_unique<WaylandDataSource>(data_source, connection); +} } // namespace WaylandConnection::WaylandConnection() @@ -174,10 +184,14 @@ } } -void WaylandConnection::ScheduleBufferSwap(gfx::AcceleratedWidget widget, - uint32_t buffer_id) { +void WaylandConnection::ScheduleBufferSwap( + gfx::AcceleratedWidget widget, + uint32_t buffer_id, + const gfx::Rect& damage_region, + ScheduleBufferSwapCallback callback) { DCHECK(base::MessageLoopForUI::IsCurrent()); - if (!buffer_manager_->ScheduleBufferSwap(widget, buffer_id)) { + if (!buffer_manager_->ScheduleBufferSwap(widget, buffer_id, damage_region, + std::move(callback))) { TerminateGpuProcess(buffer_manager_->error_message()); } } @@ -190,9 +204,7 @@ const ClipboardDelegate::DataMap& data_map, ClipboardDelegate::OfferDataClosure callback) { if (!data_source_) { - wl_data_source* data_source = data_device_manager_->CreateSource(); - data_source_.reset(new WaylandDataSource(data_source)); - data_source_->set_connection(this); + data_source_ = CreateWaylandDataSource(data_device_manager_.get(), this); data_source_->WriteToClipboard(data_map); } data_source_->UpdataDataMap(data_map); @@ -234,6 +246,36 @@ terminate_gpu_cb_ = std::move(terminate_callback); } +void WaylandConnection::StartDrag(const ui::OSExchangeData& data, + int operation) { + if (!drag_data_source_) { + drag_data_source_ = + CreateWaylandDataSource(data_device_manager_.get(), this); + } + drag_data_source_->Offer(data); + drag_data_source_->SetAction(operation); + data_device_->StartDrag(*(drag_data_source_->data_source()), data); +} + +void WaylandConnection::FinishDragSession(uint32_t dnd_action, + WaylandWindow* source_window) { + if (source_window) + source_window->OnDragSessionClose(dnd_action); + data_device_->ResetSourceData(); + drag_data_source_.reset(); +} + +void WaylandConnection::DeliverDragData(const std::string& mime_type, + std::string* buffer) { + data_device_->DeliverDragData(mime_type, buffer); +} + +void WaylandConnection::RequestDragData( + const std::string& mime_type, + base::OnceCallback<void(const std::string&)> callback) { + data_device_->RequestDragData(mime_type, std::move(callback)); +} + void WaylandConnection::GetAvailableMimeTypes( ClipboardDelegate::GetMimeTypesClosure callback) { std::move(callback).Run(data_device_->GetAvailableMimeTypes()); @@ -383,7 +425,8 @@ } else if (!connection->data_device_manager_ && strcmp(interface, "wl_data_device_manager") == 0) { wl::Object<wl_data_device_manager> data_device_manager = - wl::Bind<wl_data_device_manager>(registry, name, 1); + wl::Bind<wl_data_device_manager>( + registry, name, std::min(version, kMaxDeviceManagerVersion)); if (!data_device_manager) { LOG(ERROR) << "Failed to bind to wl_data_device_manager global"; return; @@ -398,6 +441,10 @@ registry, name, std::min(version, kMaxLinuxDmabufVersion)); connection->buffer_manager_.reset( new WaylandBufferManager(zwp_linux_dmabuf.release(), connection)); + } else if (!connection->presentation_ && + (strcmp(interface, "wp_presentation") == 0)) { + connection->presentation_ = + wl::Bind<wp_presentation>(registry, name, kMaxWpPresentationVersion); } connection->ScheduleFlush();
diff --git a/ui/ozone/platform/wayland/wayland_connection.h b/ui/ozone/platform/wayland/wayland_connection.h index 521f957..961bf6fa5 100644 --- a/ui/ozone/platform/wayland/wayland_connection.h +++ b/ui/ozone/platform/wayland/wayland_connection.h
@@ -60,7 +60,9 @@ // Called by the GPU and asks to attach a wl_buffer with a |buffer_id| to a // WaylandWindow with the specified |widget|. void ScheduleBufferSwap(gfx::AcceleratedWidget widget, - uint32_t buffer_id) override; + uint32_t buffer_id, + const gfx::Rect& damage_region, + ScheduleBufferSwapCallback callback) override; // Schedules a flush of the Wayland connection. void ScheduleFlush(); @@ -73,6 +75,7 @@ zxdg_shell_v6* shell_v6() { return shell_v6_.get(); } wl_seat* seat() { return seat_.get(); } wl_data_device* data_device() { return data_device_->data_device(); } + wp_presentation* presentation() const { return presentation_.get(); } WaylandWindow* GetWindow(gfx::AcceleratedWidget widget); WaylandWindow* GetCurrentFocusedWindow(); @@ -94,6 +97,8 @@ // Returns the current pointer, which may be null. WaylandPointer* pointer() { return pointer_.get(); } + WaylandDataSource* drag_data_source() { return drag_data_source_.get(); } + // Clipboard implementation. ClipboardDelegate* GetClipboardDelegate(); void DataSourceCancelled(); @@ -120,6 +125,23 @@ void SetTerminateGpuCallback( base::OnceCallback<void(std::string)> terminate_gpu_cb); + // Starts drag with |data| to be delivered, |operation| supported by the + // source side initiated the dragging. + void StartDrag(const ui::OSExchangeData& data, int operation); + // Finishes drag and drop session. It happens when WaylandDataSource gets + // 'OnDnDFinished' or 'OnCancel', which means the drop is performed or + // canceled on others. + void FinishDragSession(uint32_t dnd_action, WaylandWindow* source_window); + // Delivers the data owned by Chromium which initiates drag-and-drop. |buffer| + // is an output parameter and it should be filled with the data corresponding + // to mime_type. + void DeliverDragData(const std::string& mime_type, std::string* buffer); + // Requests the data to the platform when Chromium gets drag-and-drop started + // by others. Once reading the data from platform is done, |callback| should + // be called with the data. + void RequestDragData(const std::string& mime_type, + base::OnceCallback<void(const std::string&)> callback); + private: void Flush(); void DispatchUiEvent(Event* event); @@ -162,10 +184,12 @@ wl::Object<wl_shm> shm_; wl::Object<xdg_shell> shell_; wl::Object<zxdg_shell_v6> shell_v6_; + wl::Object<wp_presentation> presentation_; std::unique_ptr<WaylandDataDeviceManager> data_device_manager_; std::unique_ptr<WaylandDataDevice> data_device_; std::unique_ptr<WaylandDataSource> data_source_; + std::unique_ptr<WaylandDataSource> drag_data_source_; std::unique_ptr<WaylandPointer> pointer_; std::unique_ptr<WaylandKeyboard> keyboard_; std::unique_ptr<WaylandTouch> touch_;
diff --git a/ui/ozone/platform/wayland/wayland_data_device.cc b/ui/ozone/platform/wayland/wayland_data_device.cc index f0f144c..0677426 100644 --- a/ui/ozone/platform/wayland/wayland_data_device.cc +++ b/ui/ozone/platform/wayland/wayland_data_device.cc
@@ -5,10 +5,67 @@ #include "ui/ozone/platform/wayland/wayland_data_device.h" #include "base/bind.h" +#include "base/memory/shared_memory.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/dragdrop/os_exchange_data_provider_aura.h" #include "ui/ozone/platform/wayland/wayland_connection.h" +#include "ui/ozone/platform/wayland/wayland_util.h" +#include "ui/ozone/platform/wayland/wayland_window.h" namespace ui { +namespace { + +constexpr char kMimeTypeText[] = "text/plain"; +constexpr char kMimeTypeTextUTF8[] = "text/plain;charset=utf-8"; + +int GetOperation(uint32_t source_actions, uint32_t dnd_action) { + uint32_t action = dnd_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE + ? dnd_action + : source_actions; + + int operation = DragDropTypes::DRAG_NONE; + if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) + operation |= DragDropTypes::DRAG_COPY; + if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) + operation |= DragDropTypes::DRAG_MOVE; + // TODO(jkim): Implement branch for WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK + if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) + operation |= DragDropTypes::DRAG_COPY; + return operation; +} + +void AddStringToOSExchangeData(const std::string& data, + OSExchangeData* os_exchange_data) { + DCHECK(os_exchange_data); + if (data.empty()) + return; + + base::string16 string16 = base::UTF8ToUTF16(data); + os_exchange_data->SetString(string16); +} + +void AddToOSExchangeData(const std::string& data, + const std::string& mime_type, + OSExchangeData* os_exchange_data) { + DCHECK(os_exchange_data); + if ((mime_type == kMimeTypeText || mime_type == kMimeTypeTextUTF8)) { + DCHECK(!os_exchange_data->HasString()); + AddStringToOSExchangeData(data, os_exchange_data); + return; + } + + // TODO(jkim): Handle other mime types as well. + NOTREACHED(); +} + +} // namespace + // static const wl_callback_listener WaylandDataDevice::callback_listener_ = { WaylandDataDevice::SyncCallback, @@ -16,18 +73,22 @@ WaylandDataDevice::WaylandDataDevice(WaylandConnection* connection, wl_data_device* data_device) - : data_device_(data_device), connection_(connection) { + : data_device_(data_device), + connection_(connection), + shared_memory_(new base::SharedMemory()) { static const struct wl_data_device_listener kDataDeviceListener = { - WaylandDataDevice::OnDataOffer, - nullptr /*OnEnter*/, - nullptr /*OnLeave*/, - nullptr /*OnMotion*/, - nullptr /*OnDrop*/, - WaylandDataDevice::OnSelection}; + WaylandDataDevice::OnDataOffer, WaylandDataDevice::OnEnter, + WaylandDataDevice::OnLeave, WaylandDataDevice::OnMotion, + WaylandDataDevice::OnDrop, WaylandDataDevice::OnSelection}; wl_data_device_add_listener(data_device_.get(), &kDataDeviceListener, this); } -WaylandDataDevice::~WaylandDataDevice() {} +WaylandDataDevice::~WaylandDataDevice() { + if (!shared_memory_->handle().GetHandle()) + return; + shared_memory_->Unmap(); + shared_memory_->Close(); +} void WaylandDataDevice::RequestSelectionData(const std::string& mime_type) { base::ScopedFD fd = selection_offer_->Receive(mime_type); @@ -44,6 +105,81 @@ RegisterSyncCallback(); } +void WaylandDataDevice::RequestDragData( + const std::string& mime_type, + base::OnceCallback<void(const std::string&)> callback) { + base::ScopedFD fd = drag_offer_->Receive(mime_type); + if (!fd.is_valid()) { + LOG(ERROR) << "Failed to open file descriptor."; + return; + } + + // Ensure there is not pending operation to be performed by the compositor, + // otherwise read(..) can block awaiting data to be sent to pipe. + read_from_fd_closure_ = base::BindOnce(&WaylandDataDevice::ReadDragDataFromFD, + base::Unretained(this), std::move(fd), + std::move(callback)); + RegisterSyncCallback(); +} + +void WaylandDataDevice::DeliverDragData(const std::string& mime_type, + std::string* buffer) { + DCHECK(buffer); + DCHECK(source_data_); + + if (mime_type != kMimeTypeText && mime_type != kMimeTypeTextUTF8) + return; + + const OSExchangeData::FilenameToURLPolicy policy = + OSExchangeData::FilenameToURLPolicy::DO_NOT_CONVERT_FILENAMES; + // TODO(jkim): Handle other data format as well. + if (source_data_->HasURL(policy)) { + GURL url; + base::string16 title; + source_data_->GetURLAndTitle(policy, &url, &title); + buffer->append(url.spec()); + return; + } + + if (source_data_->HasString()) { + base::string16 data; + source_data_->GetString(&data); + buffer->append(base::UTF16ToUTF8(data)); + return; + } +} + +void WaylandDataDevice::StartDrag(const wl_data_source& data_source, + const ui::OSExchangeData& data) { + WaylandWindow* window = connection_->GetCurrentFocusedWindow(); + if (!window) { + LOG(ERROR) << "Failed to get focused window."; + return; + } + + wl_surface* surface = window->surface(); + const SkBitmap* icon = data.provider().GetDragImage().bitmap(); + if (icon && !icon->empty()) + CreateDragImage(icon); + + source_data_ = std::make_unique<ui::OSExchangeData>(data.provider().Clone()); + wl_data_device_start_drag(data_device_.get(), + const_cast<wl_data_source*>(&data_source), surface, + icon_surface_.get(), connection_->serial()); + connection_->ScheduleFlush(); +} + +void WaylandDataDevice::ResetSourceData() { + source_data_.reset(); +} + +std::vector<std::string> WaylandDataDevice::GetAvailableMimeTypes() { + if (selection_offer_) + return selection_offer_->GetAvailableMimeTypes(); + + return std::vector<std::string>(); +} + void WaylandDataDevice::ReadClipboardDataFromFD(base::ScopedFD fd, const std::string& mime_type) { std::string contents; @@ -51,6 +187,14 @@ connection_->SetClipboardData(contents, mime_type); } +void WaylandDataDevice::ReadDragDataFromFD( + base::ScopedFD fd, + base::OnceCallback<void(const std::string&)> callback) { + std::string contents; + ReadDataFromFD(std::move(fd), &contents); + std::move(callback).Run(contents); +} + void WaylandDataDevice::RegisterSyncCallback() { DCHECK(!sync_callback_); sync_callback_.reset(wl_display_sync(connection_->display())); @@ -67,11 +211,11 @@ contents->append(buffer, length); } -std::vector<std::string> WaylandDataDevice::GetAvailableMimeTypes() { - if (selection_offer_) - return selection_offer_->GetAvailableMimeTypes(); +void WaylandDataDevice::HandleDeferredLeaveIfNeeded() { + if (!is_leaving_) + return; - return std::vector<std::string>(); + OnLeave(this, data_device_.get()); } // static @@ -84,6 +228,116 @@ self->new_offer_.reset(new WaylandDataOffer(offer)); } +void WaylandDataDevice::OnEnter(void* data, + wl_data_device* data_device, + uint32_t serial, + wl_surface* surface, + wl_fixed_t x, + wl_fixed_t y, + wl_data_offer* offer) { + WaylandWindow* window = + static_cast<WaylandWindow*>(wl_surface_get_user_data(surface)); + if (!window) { + LOG(ERROR) << "Failed to get window."; + return; + } + + auto* self = static_cast<WaylandDataDevice*>(data); + DCHECK(self->new_offer_); + DCHECK(!self->drag_offer_); + self->drag_offer_ = std::move(self->new_offer_); + self->window_ = window; + + // TODO(jkim): Set mime type the client can accept. Now it sets all mime types + // offered because current implementation doesn't decide action based on mime + // type. + const std::vector<std::string>& mime_types = + self->drag_offer_->GetAvailableMimeTypes(); + for (auto mime : mime_types) + self->drag_offer_->Accept(serial, mime); + + std::copy(mime_types.begin(), mime_types.end(), + std::insert_iterator<std::list<std::string>>( + self->unprocessed_mime_types_, + self->unprocessed_mime_types_.begin())); + + int operation = GetOperation(self->drag_offer_->source_actions(), + self->drag_offer_->dnd_action()); + gfx::PointF point(wl_fixed_to_double(x), wl_fixed_to_double(y)); + + // If it has |source_data_|, it means that the dragging is started from the + // same window and it doesn't need to read the data through Wayland. + if (self->source_data_) { + std::unique_ptr<OSExchangeData> data = std::make_unique<OSExchangeData>( + self->source_data_->provider().Clone()); + self->window_->OnDragEnter(point, std::move(data), operation); + return; + } + + self->window_->OnDragEnter(point, nullptr, operation); +} + +void WaylandDataDevice::OnMotion(void* data, + wl_data_device* data_device, + uint32_t time, + wl_fixed_t x, + wl_fixed_t y) { + auto* self = static_cast<WaylandDataDevice*>(data); + if (!self->window_) { + LOG(ERROR) << "Failed to get window."; + return; + } + + int operation = GetOperation(self->drag_offer_->source_actions(), + self->drag_offer_->dnd_action()); + gfx::PointF point(wl_fixed_to_double(x), wl_fixed_to_double(y)); + int client_operation = self->window_->OnDragMotion(point, time, operation); + self->SetOperation(client_operation); +} + +void WaylandDataDevice::OnDrop(void* data, wl_data_device* data_device) { + auto* self = static_cast<WaylandDataDevice*>(data); + if (!self->window_) { + LOG(ERROR) << "Failed to get window."; + return; + } + + // Creates buffer to receive data from Wayland. + self->received_data_ = std::make_unique<OSExchangeData>( + std::make_unique<OSExchangeDataProviderAura>()); + + // Starts to read the data on Drop event because read(..) API blocks + // awaiting data to be sent to pipe if we try to read the data on OnEnter. + // 'Weston' also reads data on OnDrop event and other examples do as well. + self->HandleNextMimeType(); + + // In order to guarantee all data received, it sets + // |is_handling_dropped_data_| and defers OnLeave event handling if it gets + // OnLeave event before completing to read the data. + self->is_handling_dropped_data_ = true; +} + +void WaylandDataDevice::OnLeave(void* data, wl_data_device* data_device) { + // While reading data, it could get OnLeave event. We don't handle OnLeave + // event directly if |is_handling_dropped_data_| is set. + auto* self = static_cast<WaylandDataDevice*>(data); + if (!self->window_) { + LOG(ERROR) << "Failed to get window."; + return; + } + + if (self->is_handling_dropped_data_) { + self->is_leaving_ = true; + return; + } + + self->window_->OnDragLeave(); + self->window_ = nullptr; + self->drag_offer_.reset(); + self->is_handling_dropped_data_ = false; + self->is_leaving_ = false; +} + // static void WaylandDataDevice::OnSelection(void* data, wl_data_device* data_device, @@ -118,4 +372,88 @@ data_device->sync_callback_.reset(); } +void WaylandDataDevice::CreateDragImage(const SkBitmap* bitmap) { + DCHECK(bitmap); + gfx::Size size(bitmap->width(), bitmap->height()); + + if (size != icon_buffer_size_) { + wl_buffer* buffer = + wl::CreateSHMBuffer(size, shared_memory_.get(), connection_->shm()); + if (!buffer) + return; + + buffer_.reset(buffer); + icon_buffer_size_ = size; + } + wl::DrawBitmapToSHMB(icon_buffer_size_, *shared_memory_, *bitmap); + + icon_surface_.reset(wl_compositor_create_surface(connection_->compositor())); + wl_surface_attach(icon_surface_.get(), buffer_.get(), 0, 0); + wl_surface_damage(icon_surface_.get(), 0, 0, icon_buffer_size_.width(), + icon_buffer_size_.height()); + wl_surface_commit(icon_surface_.get()); +} + +void WaylandDataDevice::OnDragDataReceived(const std::string& contents) { + if (!contents.empty()) { + AddToOSExchangeData(contents, unprocessed_mime_types_.front(), + received_data_.get()); + } + + unprocessed_mime_types_.erase(unprocessed_mime_types_.begin()); + + // Read next data corresponding to the mime type. + HandleNextMimeType(); +} + +void WaylandDataDevice::OnDragDataCollected() { + unprocessed_mime_types_.clear(); + window_->OnDragDrop(std::move(received_data_)); + drag_offer_->FinishOffer(); + is_handling_dropped_data_ = false; + + HandleDeferredLeaveIfNeeded(); +} + +std::string WaylandDataDevice::SelectNextMimeType() { + while (!unprocessed_mime_types_.empty()) { + std::string& mime_type = unprocessed_mime_types_.front(); + if ((mime_type == kMimeTypeText || mime_type == kMimeTypeTextUTF8) && + !received_data_->HasString()) { + return mime_type; + } + // TODO(jkim): Handle other mime types as well. + unprocessed_mime_types_.erase(unprocessed_mime_types_.begin()); + } + return std::string(); +} + +void WaylandDataDevice::HandleNextMimeType() { + std::string mime_type = SelectNextMimeType(); + if (!mime_type.empty()) { + RequestDragData(mime_type, + base::BindOnce(&WaylandDataDevice::OnDragDataReceived, + base::Unretained(this))); + } else { + OnDragDataCollected(); + } +} + +void WaylandDataDevice::SetOperation(const int operation) { + uint32_t dnd_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + uint32_t preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + + if (operation & DragDropTypes::DRAG_COPY) { + dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + } + + if (operation & DragDropTypes::DRAG_MOVE) { + dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + if (preferred_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE) + preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + } + drag_offer_->SetAction(dnd_actions, preferred_action); +} + } // namespace ui
diff --git a/ui/ozone/platform/wayland/wayland_data_device.h b/ui/ozone/platform/wayland/wayland_data_device.h index 3d1eecd..f098670 100644 --- a/ui/ozone/platform/wayland/wayland_data_device.h +++ b/ui/ozone/platform/wayland/wayland_data_device.h
@@ -6,23 +6,31 @@ #define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_DATA_DEVICE_H_ #include <wayland-client.h> +#include <list> #include <string> #include "base/callback.h" #include "base/files/scoped_file.h" #include "base/macros.h" +#include "ui/gfx/geometry/size.h" #include "ui/ozone/platform/wayland/wayland_data_offer.h" #include "ui/ozone/platform/wayland/wayland_object.h" +class SkBitmap; + +namespace base { +class SharedMemory; +} + namespace ui { +class OSExchangeData; class WaylandDataOffer; class WaylandConnection; +class WaylandWindow; // This class provides access to inter-client data transfer mechanisms // such as copy-and-paste and drag-and-drop mechanisms. -// -// TODO(tonikitoo,msisov): Add drag&drop support. class WaylandDataDevice { public: WaylandDataDevice(WaylandConnection* connection, wl_data_device* data_device); @@ -30,23 +38,66 @@ void RequestSelectionData(const std::string& mime_type); - wl_data_device* data_device() { return data_device_.get(); } + // Requests the data to the platform when Chromium gets drag-and-drop started + // by others. Once reading the data from platform is done, |callback| should + // be called with the data. + void RequestDragData(const std::string& mime_type, + base::OnceCallback<void(const std::string&)> callback); + // Delivers the data owned by Chromium which initiates drag-and-drop. |buffer| + // is an output parameter and it should be filled with the data corresponding + // to mime_type. + void DeliverDragData(const std::string& mime_type, std::string* buffer); + // Starts drag with |data| to be delivered, |operation| supported by the + // source side initiated the dragging. + void StartDrag(const wl_data_source& data_source, + const ui::OSExchangeData& data); + // Resets |source_data_| when the dragging is finished. + void ResetSourceData(); std::vector<std::string> GetAvailableMimeTypes(); + wl_data_device* data_device() const { return data_device_.get(); } + private: void ReadClipboardDataFromFD(base::ScopedFD fd, const std::string& mime_type); + void ReadDragDataFromFD( + base::ScopedFD fd, + base::OnceCallback<void(const std::string&)> callback); + // Registers display sync callback. Once it's called, it's reset. void RegisterSyncCallback(); // Helper function to read data from fd. void ReadDataFromFD(base::ScopedFD fd, std::string* contents); + // If OnLeave event occurs while it's reading drag data, it defers handling + // it. Once reading data is completed, it's handled. + void HandleDeferredLeaveIfNeeded(); + // wl_data_device_listener callbacks static void OnDataOffer(void* data, wl_data_device* data_device, wl_data_offer* id); + + static void OnEnter(void* data, + wl_data_device* data_device, + uint32_t serial, + wl_surface* surface, + wl_fixed_t x, + wl_fixed_t y, + wl_data_offer* offer); + + static void OnMotion(void* data, + struct wl_data_device* data_device, + uint32_t time, + wl_fixed_t x, + wl_fixed_t y); + + static void OnDrop(void* data, struct wl_data_device* data_device); + + static void OnLeave(void* data, struct wl_data_device* data_device); + // Called by the compositor when the window gets pointer or keyboard focus, // or clipboard content changes behind the scenes. // @@ -57,6 +108,22 @@ static void SyncCallback(void* data, struct wl_callback* cb, uint32_t time); + bool CreateSHMBuffer(const gfx::Size& size); + void CreateDragImage(const SkBitmap* bitmap); + + void OnDragDataReceived(const std::string& contents); + void OnDragDataCollected(); + + // Returns the next MIME type to be received from the source process, or an + // empty string if there are no more interesting MIME types left to process. + std::string SelectNextMimeType(); + // If it has |unprocessed_mime_types_|, it takes the mime type in front and + // requests the data corresponding to the mime type to wayland. + void HandleNextMimeType(); + + // Set drag operation decided by client. + void SetOperation(const int operation); + // The wl_data_device wrapped by this WaylandDataDevice. wl::Object<wl_data_device> data_device_; @@ -75,11 +142,36 @@ // clipboard data is available. std::unique_ptr<WaylandDataOffer> selection_offer_; + // Offer to receive data from another process via drag-and-drop, or null if no + // drag-and-drop from another process is in progress. + std::unique_ptr<WaylandDataOffer> drag_offer_; + + WaylandWindow* window_ = nullptr; + // Make sure server has written data on the pipe, before block on read(). static const wl_callback_listener callback_listener_; base::OnceClosure read_from_fd_closure_; wl::Object<wl_callback> sync_callback_; + bool is_handling_dropped_data_ = false; + bool is_leaving_ = false; + + std::unique_ptr<base::SharedMemory> shared_memory_; + + wl::Object<wl_buffer> buffer_; + wl::Object<wl_surface> icon_surface_; + gfx::Size icon_buffer_size_; + + // Mime types to be handled. + std::list<std::string> unprocessed_mime_types_; + + // The data delivered from Wayland + std::unique_ptr<ui::OSExchangeData> received_data_; + + // When dragging is started from Chromium, |source_data_| is forwarded to + // Wayland when they are ready to get the data. + std::unique_ptr<ui::OSExchangeData> source_data_; + DISALLOW_COPY_AND_ASSIGN(WaylandDataDevice); };
diff --git a/ui/ozone/platform/wayland/wayland_data_device_unittest.cc b/ui/ozone/platform/wayland/wayland_data_device_unittest.cc index 03c567e..f8fe5ab 100644 --- a/ui/ozone/platform/wayland/wayland_data_device_unittest.cc +++ b/ui/ozone/platform/wayland/wayland_data_device_unittest.cc
@@ -5,6 +5,9 @@ #include <wayland-server.h> #include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/events/base_event_utils.h" #include "ui/ozone/platform/wayland/fake_server.h" #include "ui/ozone/platform/wayland/wayland_test.h" #include "ui/ozone/public/clipboard_delegate.h" @@ -131,6 +134,63 @@ ASSERT_FALSE(clipboard_client_->IsSelectionOwner()); } +TEST_P(WaylandDataDeviceManagerTest, StartDrag) { + bool restored_focus = window_->has_pointer_focus(); + window_->set_pointer_focus(true); + + // The client starts dragging. + std::unique_ptr<OSExchangeData> os_exchange_data = + std::make_unique<OSExchangeData>(); + int operation = DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE; + connection_->StartDrag(*os_exchange_data, operation); + + WaylandDataSource::DragDataMap data; + data[wl::kTextMimeTypeText] = wl::kSampleTextForDragAndDrop; + connection_->drag_data_source()->SetDragData(data); + + Sync(); + // The server reads the data and the callback gets it. + data_device_manager_->data_source()->ReadData( + base::BindOnce([](const std::vector<uint8_t>& data) { + std::string string_data(data.begin(), data.end()); + EXPECT_EQ(wl::kSampleTextForDragAndDrop, string_data); + })); + + window_->set_pointer_focus(restored_focus); +} + +TEST_P(WaylandDataDeviceManagerTest, ReceiveDrag) { + auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); + data_offer->OnOffer(wl::kTextMimeTypeText); + + gfx::Point entered_point(10, 10); + // The server sends an enter event. + data_device_manager_->data_device()->OnEnter( + 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()), + wl_fixed_from_int(entered_point.y()), *data_offer); + + int64_t time = + (ui::EventTimeForNow() - base::TimeTicks()).InMilliseconds() & UINT32_MAX; + gfx::Point motion_point(11, 11); + + // The server sends an motion event. + data_device_manager_->data_device()->OnMotion( + time, wl_fixed_from_int(motion_point.x()), + wl_fixed_from_int(motion_point.y())); + + Sync(); + + auto callback = base::BindOnce([](const std::string& contents) { + EXPECT_EQ(wl::kSampleTextForDragAndDrop, contents); + }); + + // The client requests the data and gets callback with it. + connection_->RequestDragData(wl::kTextMimeTypeText, std::move(callback)); + Sync(); + + data_device_manager_->data_device()->OnLeave(); +} + INSTANTIATE_TEST_CASE_P(XdgVersionV5Test, WaylandDataDeviceManagerTest, ::testing::Values(kXdgShellV5));
diff --git a/ui/ozone/platform/wayland/wayland_data_offer.cc b/ui/ozone/platform/wayland/wayland_data_offer.cc index e68554ef..a3900dd 100644 --- a/ui/ozone/platform/wayland/wayland_data_offer.cc +++ b/ui/ozone/platform/wayland/wayland_data_offer.cc
@@ -30,9 +30,12 @@ } // namespace WaylandDataOffer::WaylandDataOffer(wl_data_offer* data_offer) - : data_offer_(data_offer) { + : data_offer_(data_offer), + source_actions_(WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE), + dnd_action_(WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE) { static const struct wl_data_offer_listener kDataOfferListener = { - WaylandDataOffer::OnOffer}; + WaylandDataOffer::OnOffer, WaylandDataOffer::OnSourceAction, + WaylandDataOffer::OnAction}; wl_data_offer_add_listener(data_offer, &kDataOfferListener, this); } @@ -40,6 +43,23 @@ data_offer_.reset(); } +void WaylandDataOffer::SetAction(uint32_t dnd_actions, + uint32_t preferred_action) { + if (wl_data_offer_get_version(data_offer_.get()) >= + WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) { + wl_data_offer_set_actions(data_offer_.get(), dnd_actions, preferred_action); + } +} + +void WaylandDataOffer::Accept(uint32_t serial, const std::string& mime_type) { + wl_data_offer_accept(data_offer_.get(), serial, mime_type.c_str()); +} + +void WaylandDataOffer::Reject(uint32_t serial) { + // Passing a null MIME type means "reject." + wl_data_offer_accept(data_offer_.get(), serial, nullptr); +} + void WaylandDataOffer::EnsureTextMimeTypeIfNeeded() { if (base::ContainsValue(mime_types_, kTextPlain)) return; @@ -76,6 +96,20 @@ return read_fd; } +void WaylandDataOffer::FinishOffer() { + if (wl_data_offer_get_version(data_offer_.get()) >= + WL_DATA_OFFER_FINISH_SINCE_VERSION) + wl_data_offer_finish(data_offer_.get()); +} + +uint32_t WaylandDataOffer::source_actions() const { + return source_actions_; +} + +uint32_t WaylandDataOffer::dnd_action() const { + return dnd_action_; +} + // static void WaylandDataOffer::OnOffer(void* data, wl_data_offer* data_offer, @@ -84,4 +118,18 @@ self->mime_types_.push_back(mime_type); } +void WaylandDataOffer::OnSourceAction(void* data, + wl_data_offer* offer, + uint32_t source_actions) { + auto* self = static_cast<WaylandDataOffer*>(data); + self->source_actions_ = source_actions; +} + +void WaylandDataOffer::OnAction(void* data, + wl_data_offer* offer, + uint32_t dnd_action) { + auto* self = static_cast<WaylandDataOffer*>(data); + self->dnd_action_ = dnd_action; +} + } // namespace ui
diff --git a/ui/ozone/platform/wayland/wayland_data_offer.h b/ui/ozone/platform/wayland/wayland_data_offer.h index 3b9e06f..a2e8170 100644 --- a/ui/ozone/platform/wayland/wayland_data_offer.h +++ b/ui/ozone/platform/wayland/wayland_data_offer.h
@@ -23,8 +23,6 @@ // The offer describes the different mime types that the data can be // converted to and provides the mechanism for transferring the data // directly from the source client. -// -// TODO(tonikitoo,msisov): Add drag&drop support. class WaylandDataOffer { public: // Takes ownership of data_offer. @@ -42,20 +40,36 @@ // list of provided mime types so that Chrome clipboard's machinery // works fine. void EnsureTextMimeTypeIfNeeded(); + void SetAction(uint32_t dnd_actions, uint32_t preferred_action); + void Accept(uint32_t serial, const std::string& mime_type); + void Reject(uint32_t serial); // Creates a pipe (read & write FDs), passing the write-end of to pipe // to the compositor (via wl_data_offer_receive) and returning the // read-end to the pipe. base::ScopedFD Receive(const std::string& mime_type); + void FinishOffer(); + uint32_t source_actions() const; + uint32_t dnd_action() const; private: // wl_data_offer_listener callbacks. static void OnOffer(void* data, wl_data_offer* data_offer, const char* mime_type); + // Notifies the source-side available actions + static void OnSourceAction(void* data, + wl_data_offer* offer, + uint32_t source_actions); + // Notifies the selected action + static void OnAction(void* data, wl_data_offer* offer, uint32_t dnd_action); wl::Object<wl_data_offer> data_offer_; std::vector<std::string> mime_types_; + // Actions offered by the data source + uint32_t source_actions_; + // Action selected by the compositor + uint32_t dnd_action_; bool text_plain_mime_type_inserted_ = false;
diff --git a/ui/ozone/platform/wayland/wayland_data_source.cc b/ui/ozone/platform/wayland/wayland_data_source.cc index 7b61ad2..e1fe23b6 100644 --- a/ui/ozone/platform/wayland/wayland_data_source.cc +++ b/ui/ozone/platform/wayland/wayland_data_source.cc
@@ -5,17 +5,22 @@ #include "ui/ozone/platform/wayland/wayland_data_source.h" #include "base/files/file_util.h" +#include "ui/base/dragdrop/drag_drop_types.h" #include "ui/ozone/platform/wayland/wayland_connection.h" +#include "ui/ozone/platform/wayland/wayland_window.h" namespace ui { +constexpr char kTextMimeType[] = "text/plain"; constexpr char kTextMimeTypeUtf8[] = "text/plain;charset=utf-8"; -WaylandDataSource::WaylandDataSource(wl_data_source* data_source) - : data_source_(data_source) { +WaylandDataSource::WaylandDataSource(wl_data_source* data_source, + WaylandConnection* connection) + : data_source_(data_source), connection_(connection) { static const struct wl_data_source_listener kDataSourceListener = { - WaylandDataSource::OnTarget, WaylandDataSource::OnSend, - WaylandDataSource::OnCancel}; + WaylandDataSource::OnTarget, WaylandDataSource::OnSend, + WaylandDataSource::OnCancel, WaylandDataSource::OnDnDDropPerformed, + WaylandDataSource::OnDnDFinished, WaylandDataSource::OnAction}; wl_data_source_add_listener(data_source, &kDataSourceListener, this); } @@ -25,7 +30,7 @@ const ClipboardDelegate::DataMap& data_map) { for (const auto& data : data_map) { wl_data_source_offer(data_source_.get(), data.first.c_str()); - if (strcmp(data.first.c_str(), "text/plain") == 0) + if (strcmp(data.first.c_str(), kTextMimeType) == 0) wl_data_source_offer(data_source_.get(), kTextMimeTypeUtf8); } wl_data_device_set_selection(connection_->data_device(), data_source_.get(), @@ -39,11 +44,39 @@ data_map_ = data_map; } +void WaylandDataSource::Offer(const ui::OSExchangeData& data) { + // TODO(jkim): Handle mime types based on data. + std::vector<std::string> mime_types; + mime_types.push_back(kTextMimeType); + mime_types.push_back(kTextMimeTypeUtf8); + + source_window_ = connection_->GetCurrentFocusedWindow(); + for (auto& mime_type : mime_types) + wl_data_source_offer(data_source_.get(), mime_type.data()); +} + +void WaylandDataSource::SetDragData(const DragDataMap& data_map) { + DCHECK(drag_data_map_.empty()); + drag_data_map_ = data_map; +} + +void WaylandDataSource::SetAction(int operation) { + if (wl_data_source_get_version(data_source_.get()) >= + WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION) { + uint32_t dnd_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + if (operation & DragDropTypes::DRAG_COPY) + dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + if (operation & DragDropTypes::DRAG_MOVE) + dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + wl_data_source_set_actions(data_source_.get(), dnd_actions); + } +} + // static void WaylandDataSource::OnTarget(void* data, wl_data_source* source, const char* mime_type) { - NOTIMPLEMENTED(); + NOTIMPLEMENTED_LOG_ONCE(); } // static @@ -52,12 +85,18 @@ const char* mime_type, int32_t fd) { WaylandDataSource* self = static_cast<WaylandDataSource*>(data); - base::Optional<std::vector<uint8_t>> mime_data; - self->GetClipboardData(mime_type, &mime_data); - if (!mime_data.has_value() && strcmp(mime_type, kTextMimeTypeUtf8) == 0) - self->GetClipboardData("text/plain", &mime_data); - - std::string contents(mime_data->begin(), mime_data->end()); + std::string contents; + if (self->source_window_) { + // If |source_window_| is valid when OnSend() is called, it means that DnD + // is working. + self->GetDragData(mime_type, &contents); + } else { + base::Optional<std::vector<uint8_t>> mime_data; + self->GetClipboardData(mime_type, &mime_data); + if (!mime_data.has_value() && strcmp(mime_type, kTextMimeTypeUtf8) == 0) + self->GetClipboardData(kTextMimeType, &mime_data); + contents.assign(mime_data->begin(), mime_data->end()); + } bool result = base::WriteFileDescriptor(fd, contents.data(), contents.length()); DCHECK(result); @@ -67,7 +106,30 @@ // static void WaylandDataSource::OnCancel(void* data, wl_data_source* source) { WaylandDataSource* self = static_cast<WaylandDataSource*>(data); - self->connection_->DataSourceCancelled(); + if (self->source_window_) { + // If it has |source_window_|, it is in the middle of 'drag and drop'. it + // cancels 'drag and drop'. + self->connection_->FinishDragSession(self->dnd_action_, + self->source_window_); + } else { + self->connection_->DataSourceCancelled(); + } +} + +void WaylandDataSource::OnDnDDropPerformed(void* data, wl_data_source* source) { + NOTIMPLEMENTED_LOG_ONCE(); +} + +void WaylandDataSource::OnDnDFinished(void* data, wl_data_source* source) { + WaylandDataSource* self = static_cast<WaylandDataSource*>(data); + self->connection_->FinishDragSession(self->dnd_action_, self->source_window_); +} + +void WaylandDataSource::OnAction(void* data, + wl_data_source* source, + uint32_t dnd_action) { + WaylandDataSource* self = static_cast<WaylandDataSource*>(data); + self->dnd_action_ = dnd_action; } void WaylandDataSource::GetClipboardData( @@ -81,4 +143,15 @@ } } +void WaylandDataSource::GetDragData(const std::string& mime_type, + std::string* contents) { + auto it = drag_data_map_.find(mime_type); + if (it != drag_data_map_.end()) { + *contents = it->second; + return; + } + + connection_->DeliverDragData(mime_type, contents); +} + } // namespace ui
diff --git a/ui/ozone/platform/wayland/wayland_data_source.h b/ui/ozone/platform/wayland/wayland_data_source.h index 9ee3397..33dfeb2 100644 --- a/ui/ozone/platform/wayland/wayland_data_source.h +++ b/ui/ozone/platform/wayland/wayland_data_source.h
@@ -7,8 +7,8 @@ #include <wayland-client.h> +#include <map> #include <string> -#include <unordered_map> #include <vector> #include "base/logging.h" @@ -19,7 +19,9 @@ namespace ui { +class OSExchangeData; class WaylandConnection; +class WaylandWindow; // The WaylandDataSource object represents the source side of a // WaylandDataOffer. It is created by the source client in a data @@ -28,8 +30,11 @@ // transfer the data (OnSend listener). class WaylandDataSource { public: + using DragDataMap = std::map<std::string, std::string>; + // Takes ownership of data_source. - explicit WaylandDataSource(wl_data_source* data_source); + explicit WaylandDataSource(wl_data_source* data_source, + WaylandConnection* connection); ~WaylandDataSource(); void set_connection(WaylandConnection* connection) { @@ -39,6 +44,11 @@ void WriteToClipboard(const ClipboardDelegate::DataMap& data_map); void UpdataDataMap(const ClipboardDelegate::DataMap& data_map); + void Offer(const ui::OSExchangeData& data); + void SetAction(int operation); + void SetDragData(const DragDataMap& data_map); + + const wl_data_source* data_source() const { return data_source_.get(); } private: static void OnTarget(void* data, @@ -49,14 +59,22 @@ const char* mime_type, int32_t fd); static void OnCancel(void* data, wl_data_source* source); + static void OnDnDDropPerformed(void* data, wl_data_source* source); + static void OnDnDFinished(void* data, wl_data_source* source); + static void OnAction(void* data, wl_data_source* source, uint32_t dnd_action); void GetClipboardData(const std::string& mime_type, base::Optional<std::vector<uint8_t>>* data); + void GetDragData(const std::string& mime_type, std::string* contents); wl::Object<wl_data_source> data_source_; WaylandConnection* connection_ = nullptr; + WaylandWindow* source_window_ = nullptr; ClipboardDelegate::DataMap data_map_; + DragDataMap drag_data_map_; + // Action selected by the compositor + uint32_t dnd_action_; DISALLOW_COPY_AND_ASSIGN(WaylandDataSource); };
diff --git a/ui/ozone/platform/wayland/wayland_object.cc b/ui/ozone/platform/wayland/wayland_object.cc index ec41d32..c3cbb08 100644 --- a/ui/ozone/platform/wayland/wayland_object.cc +++ b/ui/ozone/platform/wayland/wayland_object.cc
@@ -5,6 +5,7 @@ #include "ui/ozone/platform/wayland/wayland_object.h" #include <linux-dmabuf-unstable-v1-client-protocol.h> +#include <presentation-time-client-protocol.h> #include <wayland-client.h> #include <xdg-shell-unstable-v5-client-protocol.h> #include <xdg-shell-unstable-v6-client-protocol.h> @@ -40,6 +41,15 @@ wl_touch_destroy(touch); } +void delete_data_device(wl_data_device* data_device) { + if (wl_data_device_get_version(data_device) >= + WL_DATA_DEVICE_RELEASE_SINCE_VERSION) { + wl_data_device_release(data_device); + } else { + wl_data_device_destroy(data_device); + } +} + } // namespace const wl_interface* ObjectTraits<wl_buffer>::interface = &wl_buffer_interface; @@ -62,7 +72,7 @@ const wl_interface* ObjectTraits<wl_data_device>::interface = &wl_data_device_interface; void (*ObjectTraits<wl_data_device>::deleter)(wl_data_device*) = - &wl_data_device_destroy; + &delete_data_device; const wl_interface* ObjectTraits<wl_data_offer>::interface = &wl_data_offer_interface; @@ -117,6 +127,16 @@ const wl_interface* ObjectTraits<wl_touch>::interface = &wl_touch_interface; void (*ObjectTraits<wl_touch>::deleter)(wl_touch*) = &delete_touch; +const wl_interface* ObjectTraits<wp_presentation>::interface = + &wp_presentation_interface; +void (*ObjectTraits<wp_presentation>::deleter)(wp_presentation*) = + &wp_presentation_destroy; + +const wl_interface* ObjectTraits<struct wp_presentation_feedback>::interface = + &wp_presentation_feedback_interface; +void (*ObjectTraits<struct wp_presentation_feedback>::deleter)( + struct wp_presentation_feedback*) = &wp_presentation_feedback_destroy; + const wl_interface* ObjectTraits<xdg_shell>::interface = &xdg_shell_interface; void (*ObjectTraits<xdg_shell>::deleter)(xdg_shell*) = &xdg_shell_destroy;
diff --git a/ui/ozone/platform/wayland/wayland_object.h b/ui/ozone/platform/wayland/wayland_object.h index df3f16e..62890452 100644 --- a/ui/ozone/platform/wayland/wayland_object.h +++ b/ui/ozone/platform/wayland/wayland_object.h
@@ -27,6 +27,8 @@ struct wl_subsurface; struct wl_surface; struct wl_touch; +struct wp_presentation; +struct wp_presentation_feedback; struct xdg_shell; struct xdg_surface; struct xdg_popup; @@ -157,6 +159,18 @@ }; template <> +struct ObjectTraits<wp_presentation> { + static const wl_interface* interface; + static void (*deleter)(wp_presentation*); +}; + +template <> +struct ObjectTraits<wp_presentation_feedback> { + static const wl_interface* interface; + static void (*deleter)(wp_presentation_feedback*); +}; + +template <> struct ObjectTraits<xdg_shell> { static const wl_interface* interface; static void (*deleter)(xdg_shell*);
diff --git a/ui/ozone/platform/wayland/wayland_surface_factory.cc b/ui/ozone/platform/wayland/wayland_surface_factory.cc index 751d7c2..a5b1dec0 100644 --- a/ui/ozone/platform/wayland/wayland_surface_factory.cc +++ b/ui/ozone/platform/wayland/wayland_surface_factory.cc
@@ -231,9 +231,13 @@ return it->second; } -void WaylandSurfaceFactory::ScheduleBufferSwap(gfx::AcceleratedWidget widget, - uint32_t buffer_id) { - connection_->ScheduleBufferSwap(widget, buffer_id); +void WaylandSurfaceFactory::ScheduleBufferSwap( + gfx::AcceleratedWidget widget, + uint32_t buffer_id, + const gfx::Rect& damage_region, + wl::BufferSwapCallback callback) { + connection_->ScheduleBufferSwap(widget, buffer_id, damage_region, + std::move(callback)); } std::unique_ptr<SurfaceOzoneCanvas>
diff --git a/ui/ozone/platform/wayland/wayland_surface_factory.h b/ui/ozone/platform/wayland/wayland_surface_factory.h index 3b569890..d20c3f90 100644 --- a/ui/ozone/platform/wayland/wayland_surface_factory.h +++ b/ui/ozone/platform/wayland/wayland_surface_factory.h
@@ -7,17 +7,21 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" -#include "ui/gl/gl_surface.h" -#include "ui/ozone/public/surface_factory_ozone.h" - #include "base/posix/eintr_wrapper.h" #include "base/single_thread_task_runner.h" #include "base/threading/sequenced_task_runner_handle.h" +#include "ui/gl/gl_surface.h" +#include "ui/ozone/platform/wayland/wayland_util.h" +#include "ui/ozone/public/surface_factory_ozone.h" + +namespace gfx { +class Rect; +} // namespace gfx namespace ui { -class WaylandConnectionProxy; class GbmSurfacelessWayland; +class WaylandConnectionProxy; class WaylandSurfaceFactory : public SurfaceFactoryOzone { public: @@ -25,7 +29,10 @@ ~WaylandSurfaceFactory() override; // These methods are used, when a dmabuf based approach is used. - void ScheduleBufferSwap(gfx::AcceleratedWidget widget, uint32_t buffer_id); + void ScheduleBufferSwap(gfx::AcceleratedWidget widget, + uint32_t buffer_id, + const gfx::Rect& damage_region_, + wl::BufferSwapCallback callback); void RegisterSurface(gfx::AcceleratedWidget widget, GbmSurfacelessWayland* surface); void UnregisterSurface(gfx::AcceleratedWidget widget);
diff --git a/ui/ozone/platform/wayland/wayland_util.h b/ui/ozone/platform/wayland/wayland_util.h index 0b77426..d104fc0 100644 --- a/ui/ozone/platform/wayland/wayland_util.h +++ b/ui/ozone/platform/wayland/wayland_util.h
@@ -7,6 +7,7 @@ #include <wayland-client.h> +#include "base/callback.h" #include "base/macros.h" #include "ui/ozone/platform/wayland/wayland_object.h" @@ -18,10 +19,16 @@ namespace gfx { class Size; +enum class SwapResult; +struct PresentationFeedback; } namespace wl { +// Corresponds to mojom::WaylandConnection::ScheduleBufferSwapCallback. +using BufferSwapCallback = + base::OnceCallback<void(gfx::SwapResult, const gfx::PresentationFeedback&)>; + wl_buffer* CreateSHMBuffer(const gfx::Size& size, base::SharedMemory* shared_memory, wl_shm* shm);
diff --git a/ui/ozone/platform/wayland/wayland_window.cc b/ui/ozone/platform/wayland/wayland_window.cc index c0d8847a0..9b251587 100644 --- a/ui/ozone/platform/wayland/wayland_window.cc +++ b/ui/ozone/platform/wayland/wayland_window.cc
@@ -8,9 +8,11 @@ #include "base/bind.h" #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h" +#include "ui/base/dragdrop/os_exchange_data.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" #include "ui/events/ozone/events_ozone.h" +#include "ui/gfx/geometry/point_f.h" #include "ui/ozone/platform/wayland/wayland_connection.h" #include "ui/ozone/platform/wayland/wayland_pointer.h" #include "ui/ozone/platform/wayland/xdg_popup_wrapper_v5.h" @@ -510,6 +512,31 @@ delegate_->OnCloseRequest(); } +void WaylandWindow::OnDragEnter(const gfx::PointF& point, + std::unique_ptr<OSExchangeData> data, + int operation) { + NOTIMPLEMENTED_LOG_ONCE(); +} + +int WaylandWindow::OnDragMotion(const gfx::PointF& point, + uint32_t time, + int operation) { + NOTIMPLEMENTED_LOG_ONCE(); + return 0; +} + +void WaylandWindow::OnDragDrop(std::unique_ptr<OSExchangeData> data) { + NOTIMPLEMENTED_LOG_ONCE(); +} + +void WaylandWindow::OnDragLeave() { + NOTIMPLEMENTED_LOG_ONCE(); +} + +void WaylandWindow::OnDragSessionClose(uint32_t dnd_action) { + NOTIMPLEMENTED_LOG_ONCE(); +} + bool WaylandWindow::IsMinimized() const { return state_ == PlatformWindowState::PLATFORM_WINDOW_STATE_MINIMIZED; }
diff --git a/ui/ozone/platform/wayland/wayland_window.h b/ui/ozone/platform/wayland/wayland_window.h index d652e896a..4cd69ff 100644 --- a/ui/ozone/platform/wayland/wayland_window.h +++ b/ui/ozone/platform/wayland/wayland_window.h
@@ -13,9 +13,14 @@ #include "ui/platform_window/platform_window.h" #include "ui/platform_window/platform_window_delegate.h" +namespace gfx { +class PointF; +} + namespace ui { class BitmapCursorOzone; +class OSExchangeData; class PlatformWindowDelegate; class WaylandConnection; class XDGPopupWrapper; @@ -101,6 +106,14 @@ void OnCloseRequest(); + void OnDragEnter(const gfx::PointF& point, + std::unique_ptr<OSExchangeData> data, + int operation); + int OnDragMotion(const gfx::PointF& point, uint32_t time, int operation); + void OnDragDrop(std::unique_ptr<OSExchangeData> data); + void OnDragLeave(); + void OnDragSessionClose(uint32_t dnd_action); + private: bool IsMinimized() const; bool IsMaximized() const;
diff --git a/ui/ozone/public/interfaces/wayland/BUILD.gn b/ui/ozone/public/interfaces/wayland/BUILD.gn index c6f2360d..9ee493e 100644 --- a/ui/ozone/public/interfaces/wayland/BUILD.gn +++ b/ui/ozone/public/interfaces/wayland/BUILD.gn
@@ -11,6 +11,7 @@ public_deps = [ "//mojo/public/mojom/base", + "//ui/gfx/geometry/mojo", "//ui/gfx/mojo", ] }
diff --git a/ui/ozone/public/interfaces/wayland/wayland_connection.mojom b/ui/ozone/public/interfaces/wayland/wayland_connection.mojom index fde5b12..a3993ccb 100644 --- a/ui/ozone/public/interfaces/wayland/wayland_connection.mojom +++ b/ui/ozone/public/interfaces/wayland/wayland_connection.mojom
@@ -6,7 +6,10 @@ import "mojo/public/mojom/base/file.mojom"; import "mojo/public/mojom/base/file_path.mojom"; +import "ui/gfx/geometry/mojo/geometry.mojom"; import "ui/gfx/mojo/accelerated_widget.mojom"; +import "ui/gfx/mojo/presentation_feedback.mojom"; +import "ui/gfx/mojo/swap_result.mojom"; // Used by the GPU for communication with a WaylandConnection on the browser // process. @@ -26,7 +29,10 @@ DestroyZwpLinuxDmabuf(uint32 buffer_id); // Swaps wl_buffers for a WaylandWindow with the following |widget|. - ScheduleBufferSwap(gfx.mojom.AcceleratedWidget widget, uint32 buffer_id); + ScheduleBufferSwap(gfx.mojom.AcceleratedWidget widget, uint32 buffer_id, + gfx.mojom.Rect damage_region) + => (gfx.mojom.SwapResult swap_result, + gfx.mojom.PresentationFeedback feedback); }; // Used by the browser process to provide the GPU process with a mojo ptr to a
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.cc b/ui/views/bubble/bubble_dialog_delegate_view.cc index 6349e776..84d9ade9 100644 --- a/ui/views/bubble/bubble_dialog_delegate_view.cc +++ b/ui/views/bubble/bubble_dialog_delegate_view.cc
@@ -202,6 +202,19 @@ return anchor_view_tracker_->view(); } +void BubbleDialogDelegateView::set_arrow(BubbleBorder::Arrow arrow) { + if (arrow_ == arrow) + return; + arrow_ = arrow; + + // If set_arrow() is called before CreateWidget(), there's no need to update + // the BubbleFrameView. + if (GetBubbleFrameView()) { + GetBubbleFrameView()->bubble_border()->set_arrow(arrow); + SizeToContents(); + } +} + gfx::Rect BubbleDialogDelegateView::GetAnchorRect() const { if (!GetAnchorView()) return anchor_rect_;
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.h b/ui/views/bubble/bubble_dialog_delegate_view.h index 8b83610..51a95a0 100644 --- a/ui/views/bubble/bubble_dialog_delegate_view.h +++ b/ui/views/bubble/bubble_dialog_delegate_view.h
@@ -72,7 +72,7 @@ const gfx::Rect& anchor_rect() const { return anchor_rect_; } BubbleBorder::Arrow arrow() const { return arrow_; } - void set_arrow(BubbleBorder::Arrow arrow) { arrow_ = arrow; } + void set_arrow(BubbleBorder::Arrow arrow); void set_mirror_arrow_in_rtl(bool mirror) { mirror_arrow_in_rtl_ = mirror; }
diff --git a/ui/views/cocoa/bridged_native_widget.h b/ui/views/cocoa/bridged_native_widget.h index c5bc55a..f259bb7 100644 --- a/ui/views/cocoa/bridged_native_widget.h +++ b/ui/views/cocoa/bridged_native_widget.h
@@ -67,8 +67,12 @@ static gfx::Size GetWindowSizeForClientSize(NSWindow* window, const gfx::Size& size); + // Retrieve a BridgedNativeWidgetImpl* from its id. + static BridgedNativeWidgetImpl* GetFromId(uint64_t bridged_native_widget_id); + // Creates one side of the bridge. |host| and |parent| must not be NULL. - BridgedNativeWidgetImpl(BridgedNativeWidgetHost* host, + BridgedNativeWidgetImpl(uint64_t bridged_native_widget_id, + BridgedNativeWidgetHost* host, BridgedNativeWidgetHostHelper* host_helper, NativeWidgetMac* parent); ~BridgedNativeWidgetImpl() override; @@ -291,6 +295,7 @@ bool IsVisibleParent() const override; void RemoveChildWindow(BridgedNativeWidgetImpl* child) override; + const uint64_t id_; BridgedNativeWidgetHost* const host_; // Weak. Owns this. BridgedNativeWidgetHostHelper* const host_helper_; // Weak, owned by |host_|. NativeWidgetMac* const native_widget_mac_; // Weak. Owns |host_|.
diff --git a/ui/views/cocoa/bridged_native_widget.mm b/ui/views/cocoa/bridged_native_widget.mm index 8f9dd7c..0d352f6 100644 --- a/ui/views/cocoa/bridged_native_widget.mm +++ b/ui/views/cocoa/bridged_native_widget.mm
@@ -13,6 +13,7 @@ #import "base/mac/foundation_util.h" #include "base/mac/mac_util.h" #import "base/mac/sdk_forward_declarations.h" +#include "base/no_destructor.h" #include "base/single_thread_task_runner.h" #include "base/strings/sys_string_conversions.h" #include "ui/base/cocoa/cocoa_base_utils.h" @@ -137,6 +138,8 @@ } @end +namespace views { + namespace { using RankMap = std::map<NSView*, int>; @@ -178,8 +181,8 @@ return gfx::Size([window contentRectForFrameRect:frame_rect].size); } -void RankNSViews(views::View* view, - const views::BridgedNativeWidgetImpl::AssociatedViews& hosts, +void RankNSViews(View* view, + const BridgedNativeWidgetImpl::AssociatedViews& hosts, RankMap* rank) { auto it = hosts.find(view); if (it != hosts.end()) @@ -223,9 +226,10 @@ return count; } -} // namespace +static base::NoDestructor<std::map<uint64_t, BridgedNativeWidgetImpl*>> + g_id_to_impl_map; -namespace views { +} // namespace // static gfx::Size BridgedNativeWidgetImpl::GetWindowSizeForClientSize( @@ -237,18 +241,36 @@ return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect)); } +// static +BridgedNativeWidgetImpl* BridgedNativeWidgetImpl::GetFromId( + uint64_t bridged_native_widget_id) { + auto found = g_id_to_impl_map.get()->find(bridged_native_widget_id); + if (found == g_id_to_impl_map.get()->end()) + return nullptr; + return found->second; +} + BridgedNativeWidgetImpl::BridgedNativeWidgetImpl( + uint64_t bridged_native_widget_id, BridgedNativeWidgetHost* host, BridgedNativeWidgetHostHelper* host_helper, NativeWidgetMac* parent) - : host_(host), host_helper_(host_helper), native_widget_mac_(parent) { + : id_(bridged_native_widget_id), + host_(host), + host_helper_(host_helper), + native_widget_mac_(parent) { + DCHECK(g_id_to_impl_map.get()->find(id_) == g_id_to_impl_map.get()->end()); + g_id_to_impl_map.get()->insert(std::make_pair(id_, this)); DCHECK(parent); - window_delegate_.reset( - [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]); - ui::CATransactionCoordinator::Get().AddPreCommitObserver(this); } BridgedNativeWidgetImpl::~BridgedNativeWidgetImpl() { + // Ensure that |this| cannot be reached by its id while it is being destroyed. + auto found = g_id_to_impl_map.get()->find(id_); + DCHECK(found != g_id_to_impl_map.get()->end()); + DCHECK_EQ(found->second, this); + g_id_to_impl_map.get()->erase(found); + // The delegate should be cleared already. Note this enforces the precondition // that -[NSWindow close] is invoked on the hosted window before the // destructor is called. @@ -263,9 +285,13 @@ void BridgedNativeWidgetImpl::SetWindow( base::scoped_nsobject<NativeWidgetMacNSWindow> window) { DCHECK(!window_); + window_delegate_.reset( + [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]); window_ = std::move(window); + [window_ setBridgedNativeWidgetId:id_]; [window_ setReleasedWhenClosed:NO]; // Owned by scoped_nsobject. [window_ setDelegate:window_delegate_]; + ui::CATransactionCoordinator::Get().AddPreCommitObserver(this); } void BridgedNativeWidgetImpl::SetParent(NSView* new_parent) { @@ -426,7 +452,7 @@ [window_ setContentView:bridged_view_]; } -void BridgedNativeWidgetImpl::CreateDragDropClient(views::View* view) { +void BridgedNativeWidgetImpl::CreateDragDropClient(View* view) { drag_drop_client_.reset(new DragDropClientMac(this, view)); } @@ -880,14 +906,14 @@ UpdateWindowGeometry(); } -void BridgedNativeWidgetImpl::SetAssociationForView(const views::View* view, +void BridgedNativeWidgetImpl::SetAssociationForView(const View* view, NSView* native_view) { DCHECK_EQ(0u, associated_views_.count(view)); associated_views_[view] = native_view; native_widget_mac_->GetWidget()->ReorderNativeViews(); } -void BridgedNativeWidgetImpl::ClearAssociationForView(const views::View* view) { +void BridgedNativeWidgetImpl::ClearAssociationForView(const View* view) { auto it = associated_views_.find(view); DCHECK(it != associated_views_.end()); associated_views_.erase(it);
diff --git a/ui/views/cocoa/bridged_native_widget_host.h b/ui/views/cocoa/bridged_native_widget_host.h index 83a89158..cdf6536 100644 --- a/ui/views/cocoa/bridged_native_widget_host.h +++ b/ui/views/cocoa/bridged_native_widget_host.h
@@ -48,6 +48,12 @@ bool* found_word, gfx::DecoratedText* decorated_word, gfx::Point* baseline_point) = 0; + + // Returns the vertical position that sheets should be anchored, in pixels + // from the bottom of the window. + // TODO(ccameron): This should be either moved to the mojo interface or + // separated out in such a way as to avoid needing to go through mojo. + virtual double SheetPositionY() = 0; }; } // namespace views
diff --git a/ui/views/cocoa/bridged_native_widget_host_impl.h b/ui/views/cocoa/bridged_native_widget_host_impl.h index a912623..628da32 100644 --- a/ui/views/cocoa/bridged_native_widget_host_impl.h +++ b/ui/views/cocoa/bridged_native_widget_host_impl.h
@@ -46,15 +46,30 @@ public ui::LayerOwner, public ui::AcceleratedWidgetMacNSView { public: + // Unique integer id handles are used to bridge between the + // BridgedNativeWidgetHostImpl in one process and the BridgedNativeWidgetHost + // potentially in another. + static BridgedNativeWidgetHostImpl* GetFromId( + uint64_t bridged_native_widget_id); + uint64_t bridged_native_widget_id() const { return id_; } + // Creates one side of the bridge. |parent| must not be NULL. explicit BridgedNativeWidgetHostImpl(NativeWidgetMac* parent); ~BridgedNativeWidgetHostImpl() override; - // Provide direct access to the BridgedNativeWidgetImpl that this is hosting. + // The NativeWidgetMac that owns |this|. + views::NativeWidgetMac* native_widget_mac() const { + return native_widget_mac_; + } + + // The mojo interface through which to communicate with the underlying + // NSWindow and NSView. + views_bridge_mac::mojom::BridgedNativeWidget* bridge() const; + + // Direct access to the BridgedNativeWidgetImpl that this is hosting. // TODO(ccameron): Remove all accesses to this member, and replace them // with methods that may be sent across processes. BridgedNativeWidgetImpl* bridge_impl() const { return bridge_impl_.get(); } - views_bridge_mac::mojom::BridgedNativeWidget* bridge() const; TooltipManager* tooltip_manager() { return tooltip_manager_.get(); } @@ -132,6 +147,7 @@ bool* found_word, gfx::DecoratedText* decorated_word, gfx::Point* baseline_point) override; + double SheetPositionY() override; // views_bridge_mac::mojom::BridgedNativeWidgetHost: void OnVisibilityChanged(bool visible) override; @@ -174,6 +190,10 @@ bool* is_button_enabled, bool* is_button_default) override; bool GetDoDialogButtonsExist(bool* buttons_exist) override; + bool GetShouldShowWindowTitle(bool* should_show_window_title) override; + bool GetCanWindowBecomeKey(bool* can_window_become_key) override; + bool GetAlwaysRenderWindowAsKey(bool* always_render_as_key) override; + bool GetCanWindowClose(bool* can_window_close) override; // views_bridge_mac::mojom::BridgedNativeWidgetHost, synchronous callbacks: void DispatchKeyEventRemote(std::unique_ptr<ui::Event> event, @@ -194,6 +214,12 @@ GetDialogButtonInfoCallback callback) override; void GetDoDialogButtonsExist( GetDoDialogButtonsExistCallback callback) override; + void GetShouldShowWindowTitle( + GetShouldShowWindowTitleCallback callback) override; + void GetCanWindowBecomeKey(GetCanWindowBecomeKeyCallback callback) override; + void GetAlwaysRenderWindowAsKey( + GetAlwaysRenderWindowAsKeyCallback callback) override; + void GetCanWindowClose(GetCanWindowCloseCallback callback) override; // DialogObserver: void OnDialogModelChanged() override; @@ -213,6 +239,7 @@ // ui::AcceleratedWidgetMacNSView: void AcceleratedWidgetCALayerParamsUpdated() override; + const uint64_t id_; views::NativeWidgetMac* const native_widget_mac_; // Weak. Owns |this_|. Widget::InitParams::Type widget_type_ = Widget::InitParams::TYPE_WINDOW;
diff --git a/ui/views/cocoa/bridged_native_widget_host_impl.mm b/ui/views/cocoa/bridged_native_widget_host_impl.mm index 8a9ffdf3..0f455852 100644 --- a/ui/views/cocoa/bridged_native_widget_host_impl.mm +++ b/ui/views/cocoa/bridged_native_widget_host_impl.mm
@@ -41,14 +41,40 @@ return widget && widget->is_top_level(); } +base::NoDestructor<std::map<uint64_t, BridgedNativeWidgetHostImpl*>> + g_id_to_host_impl_map; + +uint64_t g_last_bridged_native_widget_id = 0; + } // namespace +// static +BridgedNativeWidgetHostImpl* BridgedNativeWidgetHostImpl::GetFromId( + uint64_t bridged_native_widget_id) { + auto found = g_id_to_host_impl_map.get()->find(bridged_native_widget_id); + if (found == g_id_to_host_impl_map.get()->end()) + return nullptr; + return found->second; +} + BridgedNativeWidgetHostImpl::BridgedNativeWidgetHostImpl( NativeWidgetMac* parent) - : native_widget_mac_(parent), - bridge_impl_(new BridgedNativeWidgetImpl(this, this, parent)) {} + : id_(++g_last_bridged_native_widget_id), + native_widget_mac_(parent), + bridge_impl_(new BridgedNativeWidgetImpl(id_, this, this, parent)) { + DCHECK(g_id_to_host_impl_map.get()->find(id_) == + g_id_to_host_impl_map.get()->end()); + g_id_to_host_impl_map.get()->insert(std::make_pair(id_, this)); + DCHECK(parent); +} BridgedNativeWidgetHostImpl::~BridgedNativeWidgetHostImpl() { + // Ensure that |this| cannot be reached by its id while it is being destroyed. + auto found = g_id_to_host_impl_map.get()->find(id_); + DCHECK(found != g_id_to_host_impl_map.get()->end()); + DCHECK_EQ(found->second, this); + g_id_to_host_impl_map.get()->erase(found); + // Destroy the bridge first to prevent any calls back into this during // destruction. // TODO(ccameron): When all communication from |bridge_| to this goes through @@ -292,6 +318,10 @@ return false; } +double BridgedNativeWidgetHostImpl::SheetPositionY() { + return native_widget_mac_->SheetPositionY(); +} + //////////////////////////////////////////////////////////////////////////////// // BridgedNativeWidgetHostImpl, // views_bridge_mac::mojom::BridgedNativeWidgetHost: @@ -583,6 +613,38 @@ return true; } +bool BridgedNativeWidgetHostImpl::GetShouldShowWindowTitle( + bool* should_show_window_title) { + *should_show_window_title = + root_view_ + ? root_view_->GetWidget()->widget_delegate()->ShouldShowWindowTitle() + : true; + return true; +} + +bool BridgedNativeWidgetHostImpl::GetCanWindowBecomeKey( + bool* can_window_become_key) { + *can_window_become_key = + root_view_ ? root_view_->GetWidget()->CanActivate() : false; + return true; +} + +bool BridgedNativeWidgetHostImpl::GetAlwaysRenderWindowAsKey( + bool* always_render_as_key) { + *always_render_as_key = + root_view_ ? root_view_->GetWidget()->IsAlwaysRenderAsActive() : false; + return true; +} + +bool BridgedNativeWidgetHostImpl::GetCanWindowClose(bool* can_window_close) { + *can_window_close = true; + views::NonClientView* non_client_view = + root_view_ ? root_view_->GetWidget()->non_client_view() : nullptr; + if (non_client_view) + *can_window_close = non_client_view->CanClose(); + return true; +} + //////////////////////////////////////////////////////////////////////////////// // BridgedNativeWidgetHostImpl, // views_bridge_mac::mojom::BridgedNativeWidgetHost synchronous callbacks: @@ -658,6 +720,34 @@ std::move(callback).Run(buttons_exist); } +void BridgedNativeWidgetHostImpl::GetShouldShowWindowTitle( + GetShouldShowWindowTitleCallback callback) { + bool should_show_window_title = false; + GetShouldShowWindowTitle(&should_show_window_title); + std::move(callback).Run(should_show_window_title); +} + +void BridgedNativeWidgetHostImpl::GetCanWindowBecomeKey( + GetCanWindowBecomeKeyCallback callback) { + bool can_window_become_key = false; + GetCanWindowBecomeKey(&can_window_become_key); + std::move(callback).Run(can_window_become_key); +} + +void BridgedNativeWidgetHostImpl::GetAlwaysRenderWindowAsKey( + GetAlwaysRenderWindowAsKeyCallback callback) { + bool always_render_as_key = false; + GetAlwaysRenderWindowAsKey(&always_render_as_key); + std::move(callback).Run(always_render_as_key); +} + +void BridgedNativeWidgetHostImpl::GetCanWindowClose( + GetCanWindowCloseCallback callback) { + bool can_window_close = false; + GetCanWindowClose(&can_window_close); + std::move(callback).Run(can_window_close); +} + //////////////////////////////////////////////////////////////////////////////// // BridgedNativeWidgetHostImpl, DialogObserver: @@ -682,7 +772,10 @@ // Sanity check: When focus moves away from the widget (i.e. |focused_now| // is nil), then the textInputClient will be cleared. DCHECK(!!focused_now || !input_client); - bridge_impl_->SetTextInputClient(input_client); + // TODO(ccameron): TextInputClient is not handled across process borders + // yet. + if (bridge_impl_) + bridge_impl_->SetTextInputClient(input_client); } }
diff --git a/ui/views/cocoa/native_widget_mac_nswindow.h b/ui/views/cocoa/native_widget_mac_nswindow.h index 8a0f51b7..8686a4f 100644 --- a/ui/views/cocoa/native_widget_mac_nswindow.h +++ b/ui/views/cocoa/native_widget_mac_nswindow.h
@@ -47,6 +47,10 @@ // create one. - (void)setWindowTouchBarDelegate:(id<WindowTouchBarDelegate>)delegate; +// Identifier for the NativeWidgetMac from which this window was created. This +// may be used to look up the BridgedNativeWidgetHostImpl in the browser process +// or the BridgedNativeWidgetImpl in a display process. +@property(assign, nonatomic) uint64_t bridgedNativeWidgetId; @end #endif // UI_VIEWS_COCOA_NATIVE_WIDGET_MAC_NSWINDOW_H_
diff --git a/ui/views/cocoa/native_widget_mac_nswindow.mm b/ui/views/cocoa/native_widget_mac_nswindow.mm index 9801529..df740b1 100644 --- a/ui/views/cocoa/native_widget_mac_nswindow.mm +++ b/ui/views/cocoa/native_widget_mac_nswindow.mm
@@ -9,11 +9,11 @@ #import "ui/base/cocoa/user_interface_item_command_handler.h" #import "ui/base/cocoa/window_size_constants.h" #import "ui/views/cocoa/bridged_native_widget.h" +#import "ui/views/cocoa/bridged_native_widget_host.h" #import "ui/views/cocoa/views_nswindow_delegate.h" #import "ui/views/cocoa/window_touch_bar_delegate.h" #include "ui/views/controls/menu/menu_controller.h" -#include "ui/views/widget/native_widget_mac.h" -#include "ui/views/widget/widget_delegate.h" +#include "ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom.h" @interface NSWindow (Private) + (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle; @@ -27,13 +27,15 @@ @interface NativeWidgetMacNSWindow () - (ViewsNSWindowDelegate*)viewsNSWindowDelegate; -- (views::Widget*)viewsWidget; - (BOOL)hasViewsMenuActive; - (id)rootAccessibilityObject; // Private API on NSWindow, determines whether the title is drawn on the title // bar. The title is still visible in menus, Expose, etc. - (BOOL)_isTitleHidden; + +// Retrieve the corresponding views::BridgedNativeWidgetImpl in this process. +- (views::BridgedNativeWidgetImpl*)bridgeImpl; @end // Use this category to implement mouseDown: on multiple frame view classes @@ -83,7 +85,9 @@ base::scoped_nsobject<CommandDispatcher> commandDispatcher_; base::scoped_nsprotocol<id<UserInterfaceItemCommandHandler>> commandHandler_; id<WindowTouchBarDelegate> touchBarDelegate_; // Weak. + uint64_t bridgedNativeWidgetId_; } +@synthesize bridgedNativeWidgetId = bridgedNativeWidgetId_; - (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle @@ -132,19 +136,18 @@ return base::mac::ObjCCastStrict<ViewsNSWindowDelegate>([self delegate]); } -- (views::Widget*)viewsWidget { - return [[self viewsNSWindowDelegate] nativeWidgetMac]->GetWidget(); +- (views::BridgedNativeWidgetImpl*)bridgeImpl { + return views::BridgedNativeWidgetImpl::GetFromId(bridgedNativeWidgetId_); } - (BOOL)hasViewsMenuActive { - views::MenuController* menuController = - views::MenuController::GetActiveInstance(); - return menuController && menuController->owner() == [self viewsWidget]; + bool hasMenuController = false; + [self bridgeImpl]->host()->GetHasMenuController(&hasMenuController); + return hasMenuController; } - (id)rootAccessibilityObject { - views::Widget* widget = [self viewsWidget]; - return widget ? widget->GetRootView()->GetNativeViewAccessible() : nil; + return [self bridgeImpl]->host_helper()->GetNativeViewAccessible(); } // NSWindow overrides. @@ -161,10 +164,10 @@ } - (BOOL)_isTitleHidden { - if (![self delegate]) - return NO; - - return ![self viewsWidget]->widget_delegate()->ShouldShowWindowTitle(); + bool shouldShowWindowTitle = YES; + if ([self bridgeImpl]) + [self bridgeImpl]->host()->GetShouldShowWindowTitle(&shouldShowWindowTitle); + return !shouldShowWindowTitle; } // The base implementation returns YES if the window's frame view is a custom @@ -179,23 +182,36 @@ // Note these can be called via -[NSWindow close] while the widget is being torn // down, so check for a delegate. - (BOOL)canBecomeKeyWindow { - return [self delegate] && [self viewsWidget]->CanActivate(); + bool canBecomeKey = NO; + if ([self bridgeImpl]) + [self bridgeImpl]->host()->GetCanWindowBecomeKey(&canBecomeKey); + return canBecomeKey; } - (BOOL)canBecomeMainWindow { - if (![self delegate]) + views::BridgedNativeWidgetImpl* bridgeImpl = [self bridgeImpl]; + if (!bridgeImpl) return NO; // Dialogs and bubbles shouldn't take large shadows away from their parent. - views::Widget* widget = [self viewsWidget]; - return widget->CanActivate() && - !views::NativeWidgetMac::GetBridgeImplForNativeWindow(self)->parent(); + if (bridgeImpl->parent()) + return NO; + + bool canBecomeKey = NO; + if (bridgeImpl) + bridgeImpl->host()->GetCanWindowBecomeKey(&canBecomeKey); + return canBecomeKey; } // Lets the traffic light buttons on the parent window keep their active state. - (BOOL)hasKeyAppearance { - if ([self delegate] && [self viewsWidget]->IsAlwaysRenderAsActive()) - return YES; + views::BridgedNativeWidgetImpl* bridgeImpl = [self bridgeImpl]; + if (bridgeImpl) { + bool isAlwaysRenderWindowAsKey = NO; + bridgeImpl->host()->GetAlwaysRenderWindowAsKey(&isAlwaysRenderWindowAsKey); + if (isAlwaysRenderWindowAsKey) + return YES; + } return [super hasKeyAppearance]; } @@ -312,12 +328,12 @@ // Additionally, if we don't do this, VoiceOver reads out the partial a11y // properties on the NSWindow and repeats them when focusing an item in the // RootView's a11y group. See http://crbug.com/748221. - views::Widget* widget = [self viewsWidget]; id superFocus = [super accessibilityFocusedUIElement]; - if (!widget || superFocus != self) + views::BridgedNativeWidgetImpl* bridgeImpl = [self bridgeImpl]; + if (!bridgeImpl || superFocus != self) return superFocus; - return widget->GetRootView()->GetNativeViewAccessible(); + return bridgeImpl->host_helper()->GetNativeViewAccessible(); } - (id)accessibilityAttributeValue:(NSString*)attribute {
diff --git a/ui/views/cocoa/views_nswindow_delegate.h b/ui/views/cocoa/views_nswindow_delegate.h index 245eb66..98b41f3 100644 --- a/ui/views/cocoa/views_nswindow_delegate.h +++ b/ui/views/cocoa/views_nswindow_delegate.h
@@ -11,7 +11,6 @@ #include "ui/views/views_export.h" namespace views { -class NativeWidgetMac; class BridgedNativeWidgetImpl; } @@ -24,10 +23,6 @@ base::scoped_nsobject<NSCursor> cursor_; } -// The NativeWidgetMac that created the window this is attached to. Returns -// NULL if not created by NativeWidgetMac. -@property(nonatomic, readonly) views::NativeWidgetMac* nativeWidgetMac; - // If set, the cursor set in -[NSResponder updateCursor:] when the window is // reached along the responder chain. @property(retain, nonatomic) NSCursor* cursor;
diff --git a/ui/views/cocoa/views_nswindow_delegate.mm b/ui/views/cocoa/views_nswindow_delegate.mm index b8bd9292..3615c0b 100644 --- a/ui/views/cocoa/views_nswindow_delegate.mm +++ b/ui/views/cocoa/views_nswindow_delegate.mm
@@ -10,7 +10,6 @@ #import "ui/views/cocoa/bridged_content_view.h" #import "ui/views/cocoa/bridged_native_widget.h" #include "ui/views/cocoa/bridged_native_widget_host.h" -#include "ui/views/widget/native_widget_mac.h" #include "ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom.h" @implementation ViewsNSWindowDelegate @@ -23,10 +22,6 @@ return self; } -- (views::NativeWidgetMac*)nativeWidgetMac { - return parent_->native_widget_mac(); -} - - (NSCursor*)cursor { return cursor_.get(); } @@ -124,9 +119,9 @@ } - (BOOL)windowShouldClose:(id)sender { - views::NonClientView* nonClientView = - [self nativeWidgetMac]->GetWidget()->non_client_view(); - return !nonClientView || nonClientView->CanClose(); + bool canWindowClose = true; + parent_->host()->GetCanWindowClose(&canWindowClose); + return canWindowClose; } - (void)windowWillClose:(NSNotification*)notification { @@ -200,12 +195,15 @@ - (NSRect)window:(NSWindow*)window willPositionSheet:(NSWindow*)sheet usingRect:(NSRect)defaultSheetLocation { + // TODO(ccameron): This should go through the BridgedNativeWidgetHost + // interface. + CGFloat sheetPositionY = parent_->host_helper()->SheetPositionY(); + // As per NSWindowDelegate documentation, the origin indicates the top left // point of the host frame in window coordinates. The width changes the // animation from vertical to trapezoid if it is smaller than the width of the // dialog. The height is ignored but should be set to zero. - return NSMakeRect(0, [self nativeWidgetMac]->SheetPositionY(), - NSWidth(defaultSheetLocation), 0); + return NSMakeRect(0, sheetPositionY, NSWidth(defaultSheetLocation), 0); } @end
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm index 952220b..964b018 100644 --- a/ui/views/widget/native_widget_mac.mm +++ b/ui/views/widget/native_widget_mac.mm
@@ -44,17 +44,6 @@ base::LazyInstance<ui::GestureRecognizerImplMac>::Leaky g_gesture_recognizer_instance = LAZY_INSTANCE_INITIALIZER; -NativeWidgetMac* GetNativeWidgetMacForNativeWindow( - gfx::NativeWindow native_window) { - id<NSWindowDelegate> window_delegate = [native_window delegate]; - if ([window_delegate respondsToSelector:@selector(nativeWidgetMac)]) { - ViewsNSWindowDelegate* delegate = - base::mac::ObjCCastStrict<ViewsNSWindowDelegate>(window_delegate); - return [delegate nativeWidgetMac]; - } - return nullptr; // Not created by NativeWidgetMac. -} - NSInteger StyleMaskForParams(const Widget::InitParams& params) { // If the Widget is modal, it will be displayed as a sheet. This works best if // it has NSTitledWindowMask. For example, with NSBorderlessWindowMask, the @@ -95,16 +84,22 @@ // static BridgedNativeWidgetImpl* NativeWidgetMac::GetBridgeImplForNativeWindow( gfx::NativeWindow window) { - if (NativeWidgetMac* widget = GetNativeWidgetMacForNativeWindow(window)) - return widget->bridge_impl(); + if (NativeWidgetMacNSWindow* widget_window = + base::mac::ObjCCast<NativeWidgetMacNSWindow>(window)) { + return BridgedNativeWidgetImpl::GetFromId( + [widget_window bridgedNativeWidgetId]); + } return nullptr; // Not created by NativeWidgetMac. } // static BridgedNativeWidgetHostImpl* NativeWidgetMac::GetBridgeHostImplForNativeWindow( gfx::NativeWindow window) { - if (NativeWidgetMac* widget = GetNativeWidgetMacForNativeWindow(window)) - return widget->bridge_host_.get(); + if (NativeWidgetMacNSWindow* widget_window = + base::mac::ObjCCast<NativeWidgetMacNSWindow>(window)) { + return BridgedNativeWidgetHostImpl::GetFromId( + [widget_window bridgedNativeWidgetId]); + } return nullptr; // Not created by NativeWidgetMac. } @@ -152,7 +147,8 @@ DCHECK(GetWidget()->GetRootView()); bridge_host_->SetRootView(GetWidget()->GetRootView()); bridge()->CreateContentView(GetWidget()->GetRootView()->bounds()); - bridge_impl()->CreateDragDropClient(GetWidget()->GetRootView()); + if (bridge_impl()) + bridge_impl()->CreateDragDropClient(GetWidget()->GetRootView()); if (auto* focus_manager = GetWidget()->GetFocusManager()) { bridge()->MakeFirstResponder(); bridge_host_->SetFocusManager(focus_manager); @@ -697,8 +693,10 @@ // static NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow( gfx::NativeWindow window) { - if (NativeWidgetMac* widget = GetNativeWidgetMacForNativeWindow(window)) - return widget; + if (BridgedNativeWidgetHostImpl* bridge_host_impl = + NativeWidgetMac::GetBridgeHostImplForNativeWindow(window)) { + return bridge_host_impl->native_widget_mac(); + } return nullptr; // Not created by NativeWidgetMac. }
diff --git a/ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom b/ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom index 352e1d8..6434f74 100644 --- a/ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom +++ b/ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom
@@ -134,4 +134,23 @@ // for the current dialog. [Sync] GetDoDialogButtonsExist() => (bool buttons_exist); + + // Synchronously query if the NSWindow should display its title. + [Sync] + GetShouldShowWindowTitle() => (bool should_show_window_title); + + // Synchronously query if the NSWindow can become key (activate, in views + // terminology). + [Sync] + GetCanWindowBecomeKey() => (bool can_window_become_key); + + // Synchronously query if the NSWindow should always render as if it is + // the key window (is active, in views terminology). + [Sync] + GetAlwaysRenderWindowAsKey() => (bool always_render_as_key); + + // Synchronously query if the NSWindow should always render as if it is + // the key window (is active, in views terminology). + [Sync] + GetCanWindowClose() => (bool can_window_close); };
diff --git a/ui/web_dialogs/BUILD.gn b/ui/web_dialogs/BUILD.gn index d5d48e49..d09fa063 100644 --- a/ui/web_dialogs/BUILD.gn +++ b/ui/web_dialogs/BUILD.gn
@@ -33,7 +33,7 @@ } } -static_library("test_support") { +jumbo_static_library("test_support") { sources = [ "test/test_web_contents_handler.cc", "test/test_web_contents_handler.h",
diff --git a/ui/wm/BUILD.gn b/ui/wm/BUILD.gn index e7295a2f..261603db 100644 --- a/ui/wm/BUILD.gn +++ b/ui/wm/BUILD.gn
@@ -98,7 +98,7 @@ } } -static_library("test_support") { +jumbo_static_library("test_support") { testonly = true sources = [ "test/testing_cursor_client_observer.cc",