diff --git a/.gn b/.gn index 3db307e..255c1e9 100644 --- a/.gn +++ b/.gn
@@ -161,6 +161,8 @@ "//third_party/snappy/*", #"//third_party/WebKit/*", # Errors: https://crbug.com/800764 + #"//third_party/webrtc/*", + "//third_party/webrtc_overrides/*", "//tools/*", "//ui/*",
diff --git a/DEPS b/DEPS index 6d9c5bc..9f2abf96 100644 --- a/DEPS +++ b/DEPS
@@ -79,11 +79,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': '4259797efdf354d0ad5b2c67f0027124f4344a33', + 'skia_revision': 'dab15f7a02ddaa2a6977a14d5d04955bd39350ab', # 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': 'b8eac2874af9a0f08d39e5b22f7c15cd8ae0db9f', + 'v8_revision': '2d4815d98a92ce938d575f69a29e4b425a215ebc', # 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. @@ -91,7 +91,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': '8b92c53b8a8c4f9e960f0ae4639e41c05a374136', + 'angle_revision': 'c26214de3f86f9baddcb2f4b97a3dcc5c16498f1', # 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. @@ -320,7 +320,7 @@ # Build tools for Chrome OS. Note: This depends on third_party/pyelftools. 'src/third_party/chromite': { - 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'a1ec7e112c5e7d0d4b24c2b98e0ad49ef1783c07', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '051b1952ca4b29bde5f5b1f07ce0e1fff20d9fce', 'condition': 'checkout_linux', }, @@ -664,7 +664,7 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '3c1cb0203b6cfc10389e85a350b2ea6ca29d01ce', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '3dc0125cf7e8df6e60222fc5b69f08c52486a959', # commit position 21742 + Var('webrtc_git') + '/src.git' + '@' + 'b3179c75ed409fffbaa64ecbee90db243b11efe9', # commit position 21742 'src/third_party/xdg-utils': { 'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
diff --git a/PRESUBMIT.py b/PRESUBMIT.py index 264624f..9aa6800 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py
@@ -454,7 +454,7 @@ ( r'/\bbase::Bind\(', ( - 'Please consider using base::Bind{Once,Repeating} instead ' + 'Please consider using base::Bind{Once,Repeating} instead', 'of base::Bind. (crbug.com/714018)', ), False, @@ -463,7 +463,7 @@ ( r'/\bbase::Callback<', ( - 'Please consider using base::{Once,Repeating}Callback instead ' + 'Please consider using base::{Once,Repeating}Callback instead', 'of base::Callback. (crbug.com/714018)', ), False, @@ -472,13 +472,66 @@ ( r'/\bbase::Closure\b', ( - 'Please consider using base::{Once,Repeating}Closure instead ' + 'Please consider using base::{Once,Repeating}Closure instead', 'of base::Closure. (crbug.com/714018)', ), False, (), ), ( + r'RunMessageLoop', + ( + 'RunMessageLoop is deprecated, use RunLoop instead.', + ), + False, + (), + ), + ( + r'RunThisRunLoop', + ( + 'RunThisRunLoop is deprecated, use RunLoop directly instead.', + ), + False, + (), + ), + ( + r'RunAllPendingInMessageLoop()', + ( + "Prefer RunLoop over RunAllPendingInMessageLoop, please contact gab@", + "if you're convinced you need this.", + ), + False, + (), + ), + ( + r'RunAllPendingInMessageLoop(BrowserThread', + ( + 'RunAllPendingInMessageLoop is deprecated. Use RunLoop for', + 'BrowserThread::UI, TestBrowserThreadBundle::RunIOThreadUntilIdle', + 'for BrowserThread::IO, and prefer RunLoop::QuitClosure to observe', + 'async events instead of flushing threads.', + ), + False, + (), + ), + ( + r'MessageLoopRunner', + ( + 'MessageLoopRunner is deprecated, use RunLoop instead.', + ), + False, + (), + ), + ( + r'GetDeferredQuitTaskForRunLoop', + ( + "GetDeferredQuitTaskForRunLoop shouldn't be needed, please contact", + "gab@ if you found a use case where this is the only solution.", + ), + False, + (), + ), + ( 'sqlite3_initialize', ( 'Instead of sqlite3_initialize, depend on //sql, ',
diff --git a/android_webview/browser/aw_contents.cc b/android_webview/browser/aw_contents.cc index 4e6a494..8367b51 100644 --- a/android_webview/browser/aw_contents.cc +++ b/android_webview/browser/aw_contents.cc
@@ -500,8 +500,8 @@ base::FilePath target_path(ConvertJavaStringToUTF8(env, jpath)); web_contents_->GenerateMHTML( content::MHTMLGenerationParams(target_path), - base::Bind(&GenerateMHTMLCallback, - ScopedJavaGlobalRef<jobject>(env, callback), target_path)); + base::BindOnce(&GenerateMHTMLCallback, + ScopedJavaGlobalRef<jobject>(env, callback), target_path)); } void AwContents::CreatePdfExporter(JNIEnv* env,
diff --git a/android_webview/support_library/BUILD.gn b/android_webview/support_library/BUILD.gn index b46e561..4f3a14c 100644 --- a/android_webview/support_library/BUILD.gn +++ b/android_webview/support_library/BUILD.gn
@@ -8,6 +8,9 @@ android_library("support_lib_glue_java") { java_files = [ "java/src/org/chromium/support_lib_glue/SupportLibReflectionUtil.java", + "java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerClientAdapter.java", + "java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerControllerAdapter.java", + "java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerSettingsAdapter.java", "java/src/org/chromium/support_lib_glue/SupportLibWebSettingsAdapter.java", "java/src/org/chromium/support_lib_glue/SupportLibWebViewChromium.java", "java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java",
diff --git a/android_webview/support_library/boundary_interfaces/BUILD.gn b/android_webview/support_library/boundary_interfaces/BUILD.gn index dda7b79..9b36892 100644 --- a/android_webview/support_library/boundary_interfaces/BUILD.gn +++ b/android_webview/support_library/boundary_interfaces/BUILD.gn
@@ -7,6 +7,9 @@ android_library("boundary_interface_java") { java_files = [ + "src/org/chromium/support_lib_boundary/ServiceWorkerClientBoundaryInterface.java", + "src/org/chromium/support_lib_boundary/ServiceWorkerControllerBoundaryInterface.java", + "src/org/chromium/support_lib_boundary/ServiceWorkerWebSettingsBoundaryInterface.java", "src/org/chromium/support_lib_boundary/StaticsBoundaryInterface.java", "src/org/chromium/support_lib_boundary/SupportLibraryInfoBoundaryInterface.java", "src/org/chromium/support_lib_boundary/VisualStateCallbackBoundaryInterface.java",
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/ServiceWorkerClientBoundaryInterface.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/ServiceWorkerClientBoundaryInterface.java new file mode 100644 index 0000000..36a9cfc --- /dev/null +++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/ServiceWorkerClientBoundaryInterface.java
@@ -0,0 +1,15 @@ +// 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. + +package org.chromium.support_lib_boundary; + +import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; + +/** + * Boundary interface for ServiceWorkerClient. + */ +public interface ServiceWorkerClientBoundaryInterface { + WebResourceResponse shouldInterceptRequest(WebResourceRequest request); +}
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/ServiceWorkerControllerBoundaryInterface.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/ServiceWorkerControllerBoundaryInterface.java new file mode 100644 index 0000000..e25e3ed --- /dev/null +++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/ServiceWorkerControllerBoundaryInterface.java
@@ -0,0 +1,15 @@ +// 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. + +package org.chromium.support_lib_boundary; + +import java.lang.reflect.InvocationHandler; + +/** + * Boundary interface for ServiceWorkerController. + */ +public interface ServiceWorkerControllerBoundaryInterface { + /* ServiceWorkerWebSettings */ InvocationHandler getServiceWorkerWebSettings(); + void setServiceWorkerClient(/* ServiceWorkerClient */ InvocationHandler client); +}
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/ServiceWorkerWebSettingsBoundaryInterface.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/ServiceWorkerWebSettingsBoundaryInterface.java new file mode 100644 index 0000000..dfd61fa --- /dev/null +++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/ServiceWorkerWebSettingsBoundaryInterface.java
@@ -0,0 +1,26 @@ +// 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. + +package org.chromium.support_lib_boundary; + +/** + * Boundary interface for ServiceWorkerWebSettings. + */ +public interface ServiceWorkerWebSettingsBoundaryInterface { + void setCacheMode(int mode); + + int getCacheMode(); + + void setAllowContentAccess(boolean allow); + + boolean getAllowContentAccess(); + + void setAllowFileAccess(boolean allow); + + boolean getAllowFileAccess(); + + void setBlockNetworkLoads(boolean flag); + + boolean getBlockNetworkLoads(); +}
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/WebViewProviderFactoryBoundaryInterface.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/WebViewProviderFactoryBoundaryInterface.java index 094e2cf..380bf42 100644 --- a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/WebViewProviderFactoryBoundaryInterface.java +++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/WebViewProviderFactoryBoundaryInterface.java
@@ -15,4 +15,5 @@ /* SupportLibWebkitToCompatConverter */ InvocationHandler getWebkitToCompatConverter(); /* StaticsAdapter */ InvocationHandler getStatics(); String[] getSupportedFeatures(); + /* SupportLibraryServiceWorkerController */ InvocationHandler getServiceWorkerController(); }
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerClientAdapter.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerClientAdapter.java new file mode 100644 index 0000000..134c2e8 --- /dev/null +++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerClientAdapter.java
@@ -0,0 +1,33 @@ +// 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. + +package org.chromium.support_lib_glue; + +import android.webkit.WebResourceResponse; + +import com.android.webview.chromium.ServiceWorkerClientAdapter; +import com.android.webview.chromium.WebResourceRequestAdapter; + +import org.chromium.android_webview.AwContentsClient.AwWebResourceRequest; +import org.chromium.android_webview.AwServiceWorkerClient; +import org.chromium.android_webview.AwWebResourceResponse; +import org.chromium.support_lib_boundary.ServiceWorkerClientBoundaryInterface; + +/** + * Adapter between ServiceWorkerClientBoundaryInterface and AwServiceWorkerClient. + */ +class SupportLibServiceWorkerClientAdapter extends AwServiceWorkerClient { + ServiceWorkerClientBoundaryInterface mImpl; + + SupportLibServiceWorkerClientAdapter(ServiceWorkerClientBoundaryInterface impl) { + mImpl = impl; + } + + @Override + public AwWebResourceResponse shouldInterceptRequest(AwWebResourceRequest request) { + WebResourceResponse response = + mImpl.shouldInterceptRequest(new WebResourceRequestAdapter(request)); + return ServiceWorkerClientAdapter.fromWebResourceResponse(response); + } +}
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerControllerAdapter.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerControllerAdapter.java new file mode 100644 index 0000000..c4ac302 --- /dev/null +++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerControllerAdapter.java
@@ -0,0 +1,37 @@ +// 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. + +package org.chromium.support_lib_glue; + +import org.chromium.android_webview.AwServiceWorkerController; +import org.chromium.support_lib_boundary.ServiceWorkerClientBoundaryInterface; +import org.chromium.support_lib_boundary.ServiceWorkerControllerBoundaryInterface; +import org.chromium.support_lib_boundary.util.BoundaryInterfaceReflectionUtil; + +import java.lang.reflect.InvocationHandler; + +/** + * Adapter between AwServiceWorkerController and ServiceWorkerControllerBoundaryInterface. + */ +class SupportLibServiceWorkerControllerAdapter implements ServiceWorkerControllerBoundaryInterface { + AwServiceWorkerController mAwServiceWorkerController; + + SupportLibServiceWorkerControllerAdapter(AwServiceWorkerController awServiceController) { + mAwServiceWorkerController = awServiceController; + } + + @Override + public InvocationHandler getServiceWorkerWebSettings() { + return BoundaryInterfaceReflectionUtil.createInvocationHandlerFor( + new SupportLibServiceWorkerSettingsAdapter( + mAwServiceWorkerController.getAwServiceWorkerSettings())); + } + + @Override + public void setServiceWorkerClient(InvocationHandler client) { + mAwServiceWorkerController.setServiceWorkerClient(new SupportLibServiceWorkerClientAdapter( + BoundaryInterfaceReflectionUtil.castToSuppLibClass( + ServiceWorkerClientBoundaryInterface.class, client))); + } +}
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerSettingsAdapter.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerSettingsAdapter.java new file mode 100644 index 0000000..28be7e7 --- /dev/null +++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerSettingsAdapter.java
@@ -0,0 +1,59 @@ +// 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. + +package org.chromium.support_lib_glue; + +import org.chromium.android_webview.AwServiceWorkerSettings; +import org.chromium.support_lib_boundary.ServiceWorkerWebSettingsBoundaryInterface; + +/** + * Adapter between AwServiceWorkerSettings and ServiceWorkerWebSettingsBoundaryInterface. + */ +class SupportLibServiceWorkerSettingsAdapter implements ServiceWorkerWebSettingsBoundaryInterface { + private AwServiceWorkerSettings mAwServiceWorkerSettings; + + SupportLibServiceWorkerSettingsAdapter(AwServiceWorkerSettings settings) { + mAwServiceWorkerSettings = settings; + } + + @Override + public void setCacheMode(int mode) { + mAwServiceWorkerSettings.setCacheMode(mode); + } + + @Override + public int getCacheMode() { + return mAwServiceWorkerSettings.getCacheMode(); + } + + @Override + public void setAllowContentAccess(boolean allow) { + mAwServiceWorkerSettings.setAllowContentAccess(allow); + } + + @Override + public boolean getAllowContentAccess() { + return mAwServiceWorkerSettings.getAllowContentAccess(); + } + + @Override + public void setAllowFileAccess(boolean allow) { + mAwServiceWorkerSettings.setAllowFileAccess(allow); + } + + @Override + public boolean getAllowFileAccess() { + return mAwServiceWorkerSettings.getAllowFileAccess(); + } + + @Override + public void setBlockNetworkLoads(boolean flag) { + mAwServiceWorkerSettings.setBlockNetworkLoads(flag); + } + + @Override + public boolean getBlockNetworkLoads() { + return mAwServiceWorkerSettings.getBlockNetworkLoads(); + } +}
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java index ff175fcf..760ee297 100644 --- a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java +++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java
@@ -36,6 +36,7 @@ // Initialization guarded by mAwInit.getLock() private InvocationHandler mStatics; + private InvocationHandler mServiceWorkerController; public SupportLibWebViewChromiumFactory( /* SupportLibraryInfo */ InvocationHandler supportLibraryInfo) { @@ -101,4 +102,17 @@ public String[] getSupportedFeatures() { return mWebViewSupportedFeatures; } + + @Override + public InvocationHandler getServiceWorkerController() { + synchronized (mAwInit.getLock()) { + if (mServiceWorkerController == null) { + mServiceWorkerController = + BoundaryInterfaceReflectionUtil.createInvocationHandlerFor( + new SupportLibServiceWorkerControllerAdapter( + mAwInit.getServiceWorkerController())); + } + } + return mServiceWorkerController; + } }
diff --git a/ash/frame/caption_buttons/frame_caption_button_container_view.cc b/ash/frame/caption_buttons/frame_caption_button_container_view.cc index 84a093e7..aa7ef2e 100644 --- a/ash/frame/caption_buttons/frame_caption_button_container_view.cc +++ b/ash/frame/caption_buttons/frame_caption_button_container_view.cc
@@ -114,10 +114,7 @@ FrameCaptionButtonContainerView::FrameCaptionButtonContainerView( views::Widget* frame) - : frame_(frame), - minimize_button_(NULL), - size_button_(NULL), - close_button_(NULL) { + : frame_(frame) { constexpr int kTouchOptimizedCaptionButtonsSpacing = 8; auto layout = std::make_unique<views::BoxLayout>( views::BoxLayout::kHorizontal, gfx::Insets(),
diff --git a/ash/frame/caption_buttons/frame_caption_button_container_view.h b/ash/frame/caption_buttons/frame_caption_button_container_view.h index f531e1b5..09757df0 100644 --- a/ash/frame/caption_buttons/frame_caption_button_container_view.h +++ b/ash/frame/caption_buttons/frame_caption_button_container_view.h
@@ -138,9 +138,9 @@ // The buttons. In the normal button style, at most one of |minimize_button_| // and |size_button_| is visible. - FrameCaptionButton* minimize_button_; - FrameCaptionButton* size_button_; - FrameCaptionButton* close_button_; + FrameCaptionButton* minimize_button_ = nullptr; + FrameCaptionButton* size_button_ = nullptr; + FrameCaptionButton* close_button_ = nullptr; // Mapping of the image needed to paint a button for each of the values of // CaptionButtonIcon.
diff --git a/ash/frame/custom_frame_view_ash.cc b/ash/frame/custom_frame_view_ash.cc index c7b434d..61d41348f 100644 --- a/ash/frame/custom_frame_view_ash.cc +++ b/ash/frame/custom_frame_view_ash.cc
@@ -53,9 +53,8 @@ public: CustomFrameViewAshWindowStateDelegate(wm::WindowState* window_state, CustomFrameViewAsh* custom_frame_view, - HeaderView* header_view, bool enable_immersive) - : window_state_(nullptr), header_view_(header_view) { + : window_state_(nullptr) { // Add a window state observer to exit fullscreen properly in case // fullscreen is exited without going through // WindowState::ToggleFullscreen(). This is the case when exiting @@ -130,26 +129,6 @@ window_state_ = nullptr; } - void OnWindowPropertyChanged(aura::Window* window, - const void* key, - intptr_t old) override { - DCHECK_EQ(window_state_->window(), window); - if (key != ash::kFrameActiveColorKey && - key != ash::kFrameInactiveColorKey) { - return; - } - - if (key == ash::kFrameActiveColorKey) { - header_view_->SetFrameColors( - window->GetProperty(ash::kFrameActiveColorKey), - header_view_->GetInactiveFrameColor()); - return; - } - header_view_->SetFrameColors( - header_view_->GetActiveFrameColor(), - window->GetProperty(ash::kFrameInactiveColorKey)); - } - // wm::WindowStateObserver: void OnPostWindowStateTypeChange(wm::WindowState* window_state, mojom::WindowStateType old_type) override { @@ -179,7 +158,6 @@ } wm::WindowState* window_state_; - HeaderView* const header_view_; std::unique_ptr<ImmersiveFullscreenController> immersive_fullscreen_controller_; @@ -342,13 +320,15 @@ header_view_->GetActiveFrameColor()); frame_window->SetProperty(ash::kFrameInactiveColorKey, header_view_->GetInactiveFrameColor()); + frame_window->AddObserver(this); + // A delegate for a more complex way of fullscreening the window may already // be set. This is the case for packaged apps. wm::WindowState* window_state = wm::GetWindowState(frame_window); if (!window_state->HasDelegate()) { window_state->SetDelegate(std::unique_ptr<wm::WindowStateDelegate>( - new CustomFrameViewAshWindowStateDelegate( - window_state, this, header_view_, enable_immersive))); + new CustomFrameViewAshWindowStateDelegate(window_state, this, + enable_immersive))); } Shell::Get()->AddShellObserver(this); Shell::Get()->split_view_controller()->AddObserver(this); @@ -358,6 +338,10 @@ Shell::Get()->RemoveShellObserver(this); if (Shell::Get()->split_view_controller()) Shell::Get()->split_view_controller()->RemoveObserver(this); + if (frame_ && frame_->GetNativeWindow() && + frame_->GetNativeWindow()->HasObserver(this)) { + frame_->GetNativeWindow()->RemoveObserver(this); + } } void CustomFrameViewAsh::InitImmersiveFullscreenControllerForView( @@ -370,12 +354,9 @@ SkColor inactive_frame_color) { header_view_->SetFrameColors(active_frame_color, inactive_frame_color); aura::Window* frame_window = frame_->GetNativeWindow(); - frame_window->SetProperty(aura::client::kTopViewColor, - header_view_->GetInactiveFrameColor()); - frame_window->SetProperty(ash::kFrameActiveColorKey, - header_view_->GetActiveFrameColor()); - frame_window->SetProperty(ash::kFrameInactiveColorKey, - header_view_->GetInactiveFrameColor()); + frame_window->SetProperty(aura::client::kTopViewColor, inactive_frame_color); + frame_window->SetProperty(ash::kFrameActiveColorKey, active_frame_color); + frame_window->SetProperty(ash::kFrameInactiveColorKey, inactive_frame_color); } void CustomFrameViewAsh::SetBackButtonState(FrameBackButtonState state) { @@ -434,7 +415,6 @@ void CustomFrameViewAsh::ActivationChanged(bool active) { // The icons differ between active and inactive. header_view_->SchedulePaint(); - frame_->non_client_view()->Layout(); } @@ -511,6 +491,28 @@ InvalidateLayout(); } +void CustomFrameViewAsh::OnWindowDestroying(aura::Window* window) { + DCHECK_EQ(frame_->GetNativeWindow(), window); + window->RemoveObserver(this); +} + +void CustomFrameViewAsh::OnWindowPropertyChanged(aura::Window* window, + const void* key, + intptr_t old) { + DCHECK_EQ(frame_->GetNativeWindow(), window); + if (key != ash::kFrameActiveColorKey && key != ash::kFrameInactiveColorKey) + return; + + if (key == ash::kFrameActiveColorKey) { + header_view_->SetFrameColors(window->GetProperty(ash::kFrameActiveColorKey), + header_view_->GetInactiveFrameColor()); + return; + } + header_view_->SetFrameColors( + header_view_->GetActiveFrameColor(), + window->GetProperty(ash::kFrameInactiveColorKey)); +} + const views::View* CustomFrameViewAsh::GetAvatarIconViewForTest() const { return header_view_->avatar_icon(); }
diff --git a/ash/frame/custom_frame_view_ash.h b/ash/frame/custom_frame_view_ash.h index 4c9b2a7..4a0d9f69 100644 --- a/ash/frame/custom_frame_view_ash.h +++ b/ash/frame/custom_frame_view_ash.h
@@ -14,6 +14,7 @@ #include "base/macros.h" #include "base/optional.h" #include "third_party/skia/include/core/SkColor.h" +#include "ui/aura/window_observer.h" #include "ui/views/window/non_client_view.h" namespace views { @@ -41,7 +42,8 @@ // BrowserNonClientFrameViewAsh. class ASH_EXPORT CustomFrameViewAsh : public views::NonClientFrameView, public ShellObserver, - public SplitViewController::Observer { + public SplitViewController::Observer, + public aura::WindowObserver { public: // Internal class name. static const char kViewClassName[]; @@ -102,6 +104,12 @@ void SchedulePaintInRect(const gfx::Rect& r) override; void SetVisible(bool visible) override; + // aura::WindowObserver: + void OnWindowDestroying(aura::Window* window) override; + void OnWindowPropertyChanged(aura::Window* window, + const void* key, + intptr_t old) override; + // If |paint| is false, we should not paint the header. Used for overview mode // with OnOverviewModeStarting() and OnOverviewModeEnded() to hide/show the // header of v2 and ARC apps.
diff --git a/ash/frame/custom_frame_view_ash_unittest.cc b/ash/frame/custom_frame_view_ash_unittest.cc index 73d7a85..4a0a4c8 100644 --- a/ash/frame/custom_frame_view_ash_unittest.cc +++ b/ash/frame/custom_frame_view_ash_unittest.cc
@@ -17,6 +17,7 @@ #include "ash/wm/overview/window_selector_controller.h" #include "ash/wm/tablet_mode/tablet_mode_controller.h" #include "ash/wm/window_state.h" +#include "ash/wm/window_state_delegate.h" #include "ash/wm/wm_event.h" #include "services/ui/public/interfaces/window_manager_constants.mojom.h" #include "ui/aura/client/aura_constants.h" @@ -499,10 +500,47 @@ EXPECT_TRUE(widget->non_client_view()->frame_view()->visible()); } +namespace { + +class CustomFrameViewAshFrameColorTest + : public CustomFrameViewAshTest, + public testing::WithParamInterface<bool> { + public: + CustomFrameViewAshFrameColorTest() = default; + ~CustomFrameViewAshFrameColorTest() override = default; + + private: + DISALLOW_COPY_AND_ASSIGN(CustomFrameViewAshFrameColorTest); +}; + +class TestWidgetDelegate : public TestWidgetConstraintsDelegate { + public: + TestWidgetDelegate(bool custom) : custom_(custom) {} + ~TestWidgetDelegate() override = default; + + // views::WidgetDelegate: + views::NonClientFrameView* CreateNonClientFrameView( + views::Widget* widget) override { + if (custom_) { + ash::wm::WindowState* window_state = + ash::wm::GetWindowState(widget->GetNativeWindow()); + window_state->SetDelegate(std::make_unique<wm::WindowStateDelegate>()); + } + return TestWidgetConstraintsDelegate::CreateNonClientFrameView(widget); + } + + private: + bool custom_; + + DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate); +}; + +} // namespace + // Verify that CustomFrameViewAsh updates the active color based on the // ash::kFrameActiveColorKey window property. -TEST_F(CustomFrameViewAshTest, kFrameActiveColorKey) { - TestWidgetConstraintsDelegate* delegate = new TestWidgetConstraintsDelegate; +TEST_P(CustomFrameViewAshFrameColorTest, kFrameActiveColorKey) { + TestWidgetDelegate* delegate = new TestWidgetDelegate(GetParam()); std::unique_ptr<views::Widget> widget(CreateWidget(delegate)); SkColor active_color = @@ -520,8 +558,8 @@ // Verify that CustomFrameViewAsh updates the inactive color based on the // ash::kFrameInactiveColorKey window property. -TEST_F(CustomFrameViewAshTest, KFrameInactiveColor) { - TestWidgetConstraintsDelegate* delegate = new TestWidgetConstraintsDelegate; +TEST_P(CustomFrameViewAshFrameColorTest, KFrameInactiveColor) { + TestWidgetDelegate* delegate = new TestWidgetDelegate(GetParam()); std::unique_ptr<views::Widget> widget(CreateWidget(delegate)); SkColor active_color = @@ -538,4 +576,7 @@ delegate->custom_frame_view()->GetInactiveFrameColorForTest()); } +// Run frame color tests with and without custom wm::WindowStateDelegate. +INSTANTIATE_TEST_CASE_P(, CustomFrameViewAshFrameColorTest, testing::Bool()); + } // namespace ash
diff --git a/ash/frame/default_frame_header.cc b/ash/frame/default_frame_header.cc index 7895e36..94ae882 100644 --- a/ash/frame/default_frame_header.cc +++ b/ash/frame/default_frame_header.cc
@@ -306,6 +306,13 @@ void DefaultFrameHeader::UpdateAllButtonImages() { caption_button_container_->SetUseLightImages(ShouldUseLightImages()); + if (back_button_) { + back_button_->set_use_light_images(ShouldUseLightImages()); + back_button_->SetImage(CAPTION_BUTTON_ICON_BACK, + FrameCaptionButton::ANIMATE_NO, + kWindowControlBackIcon); + } + caption_button_container_->SetButtonImage(CAPTION_BUTTON_ICON_MINIMIZE, kWindowControlMinimizeIcon);
diff --git a/ash/frame/header_view.cc b/ash/frame/header_view.cc index c12340f..c46c339 100644 --- a/ash/frame/header_view.cc +++ b/ash/frame/header_view.cc
@@ -194,6 +194,8 @@ void HeaderView::OnImmersiveRevealStarted() { fullscreen_visible_fraction_ = 0; SetPaintToLayer(); + // The immersive layer should always be top. + layer()->parent()->StackAtTop(layer()); parent()->Layout(); }
diff --git a/base/test/copy_only_int.h b/base/test/copy_only_int.h index 8fb6b37..4e482c9 100644 --- a/base/test/copy_only_int.h +++ b/base/test/copy_only_int.h
@@ -44,7 +44,7 @@ int data() const { return data_; } private: - int data_; + volatile int data_; CopyOnlyInt(CopyOnlyInt&&) = delete; CopyOnlyInt& operator=(CopyOnlyInt&) = delete;
diff --git a/base/test/move_only_int.h b/base/test/move_only_int.h index 71b1e82..6e90983 100644 --- a/base/test/move_only_int.h +++ b/base/test/move_only_int.h
@@ -58,7 +58,7 @@ int data() const { return data_; } private: - int data_; + volatile int data_; DISALLOW_COPY_AND_ASSIGN(MoveOnlyInt); };
diff --git a/base/test/scoped_task_environment.cc b/base/test/scoped_task_environment.cc index 81673b3f..abf4f80c 100644 --- a/base/test/scoped_task_environment.cc +++ b/base/test/scoped_task_environment.cc
@@ -15,6 +15,7 @@ #include "base/task_scheduler/task_scheduler.h" #include "base/task_scheduler/task_scheduler_impl.h" #include "base/test/test_mock_time_task_runner.h" +#include "base/threading/sequence_local_storage_map.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" @@ -95,6 +96,16 @@ ? MakeRefCounted<TestMockTimeTaskRunner>( TestMockTimeTaskRunner::Type::kBoundToThread) : nullptr), + slsm_for_mock_time_( + main_thread_type == MainThreadType::MOCK_TIME + ? std::make_unique<internal::SequenceLocalStorageMap>() + : nullptr), + slsm_registration_for_mock_time_( + main_thread_type == MainThreadType::MOCK_TIME + ? std::make_unique< + internal::ScopedSetSequenceLocalStorageMapForCurrentThread>( + slsm_for_mock_time_.get()) + : nullptr), task_tracker_(new TestTaskTracker()) { CHECK(!TaskScheduler::GetInstance());
diff --git a/base/test/scoped_task_environment.h b/base/test/scoped_task_environment.h index 14f0251..0afb9df 100644 --- a/base/test/scoped_task_environment.h +++ b/base/test/scoped_task_environment.h
@@ -12,6 +12,11 @@ namespace base { +namespace internal { +class ScopedSetSequenceLocalStorageMapForCurrentThread; +class SequenceLocalStorageMap; +} // namespace internal + class MessageLoop; class TaskScheduler; class TestMockTimeTaskRunner; @@ -126,6 +131,14 @@ const std::unique_ptr<MessageLoop> message_loop_; const scoped_refptr<TestMockTimeTaskRunner> mock_time_task_runner_; + // Non-null in MOCK_TIME, where an explicit SequenceLocalStorageMap needs to + // be provided. TODO(gab): This can be removed once mock time support is added + // to MessageLoop directly. + const std::unique_ptr<internal::SequenceLocalStorageMap> slsm_for_mock_time_; + const std::unique_ptr< + internal::ScopedSetSequenceLocalStorageMapForCurrentThread> + slsm_registration_for_mock_time_; + const TaskScheduler* task_scheduler_ = nullptr; // Owned by |task_scheduler_|.
diff --git a/base/test/scoped_task_environment_unittest.cc b/base/test/scoped_task_environment_unittest.cc index 337d182..ec77f6a 100644 --- a/base/test/scoped_task_environment_unittest.cc +++ b/base/test/scoped_task_environment_unittest.cc
@@ -14,6 +14,7 @@ #include "base/task_scheduler/post_task.h" #include "base/test/test_timeouts.h" #include "base/threading/platform_thread.h" +#include "base/threading/sequence_local_storage_slot.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/tick_clock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -230,6 +231,16 @@ } } +// Regression test for https://crbug.com/824770. +TEST_P(ScopedTaskEnvironmentTest, SupportsSequenceLocalStorageOnMainThread) { + ScopedTaskEnvironment scoped_task_environment( + GetParam(), ScopedTaskEnvironment::ExecutionMode::ASYNC); + + SequenceLocalStorageSlot<int> sls_slot; + sls_slot.Set(5); + EXPECT_EQ(5, sls_slot.Get()); +} + // Verify that the TickClock returned by // |ScopedTaskEnvironment::GetMockTickClock| gets updated when the // FastForward(By|UntilNoTasksRemain) functions are called.
diff --git a/base/trace_event/process_memory_dump.cc b/base/trace_event/process_memory_dump.cc index 8bd8973..c36e670 100644 --- a/base/trace_event/process_memory_dump.cc +++ b/base/trace_event/process_memory_dump.cc
@@ -84,7 +84,7 @@ DCHECK_EQ(0u, start_pointer % page_size); size_t offset = 0; - size_t total_resident_size = 0; + size_t total_resident_pages = 0; bool failure = false; // An array as large as number of pages in memory segment needs to be passed @@ -150,16 +150,16 @@ if (failure) break; - total_resident_size += resident_page_count * page_size; + total_resident_pages += resident_page_count * page_size; offset += kMaxChunkSize; } DCHECK(!failure); if (failure) { - total_resident_size = 0; + total_resident_pages = 0; LOG(ERROR) << "CountResidentBytes failed. The resident size is invalid"; } - return total_resident_size; + return total_resident_pages; } // static @@ -180,9 +180,49 @@ return base::Optional<size_t>(); } - size_t resident_size = + size_t resident_pages = info.private_pages_resident + info.shared_pages_resident; - return resident_size * PAGE_SIZE; + + // On macOS, measurements for private memory footprint overcount by + // faulted pages in anonymous shared memory. To discount for this, we touch + // all the resident pages in anonymous shared memory here, thus making them + // faulted as well. This relies on two assumptions: + // + // 1) Consumers use shared memory from front to back. Thus, if there are + // (N) resident pages, those pages represent the first N * PAGE_SIZE bytes in + // the shared memory region. + // + // 2) This logic is run shortly before the logic that calculates + // phys_footprint, thus ensuring that the discrepancy between faulted and + // resident pages is minimal. + // + // The performance penalty is expected to be small. + // + // * Most of the time, we expect the pages to already be resident and faulted, + // thus incurring a cache penalty read hit [since we read from each resident + // page]. + // + // * Rarely, we expect the pages to be resident but not faulted, resulting in + // soft faults + cache penalty. + // + // * If assumption (1) is invalid, this will potentially fault some + // previously non-resident pages, thus increasing memory usage, without fixing + // the accounting. + // + // Sanity check in case the mapped size is less than the total size of the + // region. + size_t pages_to_fault = + std::min(resident_pages, + (shared_memory.mapped_size() + PAGE_SIZE - 1) / PAGE_SIZE); + + volatile char* base_address = static_cast<char*>(shared_memory.memory()); + for (size_t i = 0; i < pages_to_fault; ++i) { + // Reading from a volatile is a visible side-effect for the purposes of + // optimization. This guarantees that the optimizer will not kill this line. + base_address[i * PAGE_SIZE]; + } + + return resident_pages * PAGE_SIZE; #else return CountResidentBytes(shared_memory.memory(), shared_memory.mapped_size());
diff --git a/build/android/gradle/generate_gradle.py b/build/android/gradle/generate_gradle.py index e6a8906..2c581c3 100755 --- a/build/android/gradle/generate_gradle.py +++ b/build/android/gradle/generate_gradle.py
@@ -270,13 +270,13 @@ class _ProjectContextGenerator(object): """Helper class to generate gradle build files""" def __init__(self, project_dir, build_vars, use_gradle_process_resources, - jinja_processor, split_projects, canary): + jinja_processor, split_projects, channel): self.project_dir = project_dir self.build_vars = build_vars self.use_gradle_process_resources = use_gradle_process_resources self.jinja_processor = jinja_processor self.split_projects = split_projects - self.canary = canary + self.channel = channel self.processed_java_dirs = set() self.processed_prebuilts = set() self.processed_res_dirs = set() @@ -529,7 +529,7 @@ 'android-%s' % build_vars['android_sdk_version']) variables['use_gradle_process_resources'] = ( generator.use_gradle_process_resources) - variables['canary'] = generator.canary + variables['channel'] = generator.channel return variables @@ -619,9 +619,9 @@ os.path.join(gradle_output_dir, _MODULE_ALL, _GRADLE_BUILD_FILE), data) -def _GenerateRootGradle(jinja_processor, canary): +def _GenerateRootGradle(jinja_processor, channel): """Returns the data for the root project's build.gradle.""" - return jinja_processor.Render(_TemplatePath('root'), {'canary': canary}) + return jinja_processor.Render(_TemplatePath('root'), {'channel': channel}) def _GenerateSettingsGradle(project_entries, add_all_module): @@ -742,10 +742,15 @@ action='store_true', help='Split projects by their gn deps rather than ' 'combining all the dependencies of each target') - parser.add_argument('--canary', + version_group = parser.add_mutually_exclusive_group() + version_group.add_argument('--beta', action='store_true', help='Generate a project that is compatible with ' - 'Android Studio 3.1 Canary.') + 'Android Studio Beta.') + version_group.add_argument('--canary', + action='store_true', + help='Generate a project that is compatible with ' + 'Android Studio Canary.') sdk_group = parser.add_mutually_exclusive_group() sdk_group.add_argument('--sdk', choices=['AndroidStudioCurrent', @@ -782,9 +787,15 @@ source_properties = _ReadPropertiesFile( _RebasePath(os.path.join(build_vars['android_sdk_build_tools'], 'source.properties'))) + if args.beta: + channel = 'beta' + elif args.canary: + channel = 'canary' + else: + channel = 'stable' generator = _ProjectContextGenerator(_gradle_output_dir, build_vars, args.use_gradle_process_resources, jinja_processor, args.split_projects, - args.canary) + channel) logging.warning('Creating project at: %s', generator.project_dir) # Generate for "all targets" by default when not using --split-projects (too @@ -858,7 +869,7 @@ source_properties, jinja_processor) _WriteFile(os.path.join(generator.project_dir, _GRADLE_BUILD_FILE), - _GenerateRootGradle(jinja_processor, args.canary)) + _GenerateRootGradle(jinja_processor, channel)) _WriteFile(os.path.join(generator.project_dir, 'settings.gradle'), _GenerateSettingsGradle(project_entries, add_all_module)) @@ -882,8 +893,7 @@ _ExtractZips(generator.project_dir, zip_tuples) logging.warning('Project created!') - logging.warning('Generated projects work with Android Studio %s', - '3.1' if args.canary else '3.0') + logging.warning('Generated projects work with Android Studio %s', channel) logging.warning('For more tips: https://chromium.googlesource.com/chromium' '/src.git/+/master/docs/android_studio.md')
diff --git a/build/android/gradle/root.jinja b/build/android/gradle/root.jinja index 178d551..a3c0db9 100644 --- a/build/android/gradle/root.jinja +++ b/build/android/gradle/root.jinja
@@ -11,8 +11,10 @@ } } dependencies { -{% if canary %} - classpath "com.android.tools.build:gradle:3.1.0-beta1" +{% if channel == 'canary' %} + classpath "com.android.tools.build:gradle:3.2.0-alpha04" +{% elif channel == 'beta' %} + classpath "com.android.tools.build:gradle:3.1.0-beta4" {% else %} classpath "com.android.tools.build:gradle:3.0.1" {% endif %}
diff --git a/build/android/gyp/process_resources.py b/build/android/gyp/process_resources.py index a038a60..6a9a941 100755 --- a/build/android/gyp/process_resources.py +++ b/build/android/gyp/process_resources.py
@@ -308,7 +308,7 @@ # changes to the correct package id at runtime. # resource_value is a string with either, a single value '0x12345678', or an # array of values like '{ 0xfedcba98, 0x01234567, 0x56789abc }' - return re.sub(r'0x(?!01)\d\d', r'0x7f', resource_value) + return re.sub(r'0x(?!01)[0-9a-f]{2}', r'0x7f', resource_value) def _CreateRJavaFile(package, resources_by_type, shared_resources, @@ -336,6 +336,16 @@ else: final_resources_by_type = resources_by_type + def _UnrollArray(entry): + res_ids = re.findall(r'0x[0-9a-f]{8}', entry.value) + qualified_array_name = '%s.%s' % (entry.resource_type, entry.name) + unrolled = [] + for i, res_id in enumerate(res_ids): + if res_id.startswith('0x7f'): + unrolled.append("%s[%d] ^= packageIdTransform;" % + (qualified_array_name, i)) + return unrolled + # Keep these assignments all on one line to make diffing against regular # aapt-generated files easier. create_id = ('{{ e.resource_type }}.{{ e.name }} ^= packageIdTransform;') @@ -360,13 +370,6 @@ } {% endfor %} {% if shared_resources %} - public static void transfromArray(int[] array, int packageIdTransform) { - for (int i=0; i < array.length; i++) { - if ((array[i] >>> 24) == 0x7f) { - array[i] ^= packageIdTransform; - } - } - } public static void onResourcesLoaded(int packageId) { assert !sResourcesDidLoad; sResourcesDidLoad = true; @@ -375,7 +378,9 @@ onResourcesLoaded{{ resource_type|title }}(packageIdTransform); {% for e in non_final_resources[resource_type] %} {% if e.java_type == 'int[]' %} - transfromArray({{ e.resource_type }}.{{ e.name }}, packageIdTransform); + {% for line in unrollArray(e) %} + {{ line }} + {% endfor %} {% endif %} {% endfor %} {% endfor %} @@ -398,7 +403,8 @@ resource_types=sorted(resources_by_type), shared_resources=shared_resources, final_resources=final_resources_by_type, - non_final_resources=non_final_resources_by_type) + non_final_resources=non_final_resources_by_type, + unrollArray=_UnrollArray) def _GenerateGlobs(pattern):
diff --git a/build/secondary/third_party/android_tools/BUILD.gn b/build/secondary/third_party/android_tools/BUILD.gn index f0799140..172aeb5 100644 --- a/build/secondary/third_party/android_tools/BUILD.gn +++ b/build/secondary/third_party/android_tools/BUILD.gn
@@ -30,225 +30,227 @@ ] } -template("support_lib_alias") { - java_group(target_name) { - forward_variables_from(invoker, [ "testonly" ]) - deps = [ - "$android_support_library_package:$target_name", - ] +if (enable_java_templates) { + template("support_lib_alias") { + java_group(target_name) { + forward_variables_from(invoker, [ "testonly" ]) + deps = [ + "$android_support_library_package:$target_name", + ] + } } -} -support_lib_alias("android_support_chromium_java") { - testonly = true -} -support_lib_alias("android_gcm_java") { -} -support_lib_alias("emma_device_java") { -} -prebuilt_wrapper("android_arch_lifecycle_common_java") { - fallback_target = "$android_support_library_package:$target_name" -} -prebuilt_wrapper("android_arch_lifecycle_runtime_java") { - fallback_target = "$android_support_library_package:$target_name" -} -prebuilt_wrapper("android_support_multidex_java") { - android_deps_target_name = "com_android_support_multidex_java" - fallback_target = "$android_support_library_package:$target_name" -} -prebuilt_wrapper("android_support_annotations_java") { - android_deps_target_name = "com_android_support_support_annotations_java" - fallback_target = "$android_support_library_package:$target_name" -} -prebuilt_wrapper("android_support_cardview_java") { - android_deps_target_name = "com_android_support_cardview_v7_java" - fallback_target = "$android_support_library_package:$target_name" -} -prebuilt_wrapper("android_support_compat_java") { - android_deps_target_name = "com_android_support_support_compat_java" - fallback_target = "$android_support_library_package:$target_name" -} -prebuilt_wrapper("android_support_core_ui_java") { - android_deps_target_name = "com_android_support_support_core_ui_java" - fallback_target = "$android_support_library_package:$target_name" -} -prebuilt_wrapper("android_support_core_utils_java") { - android_deps_target_name = "com_android_support_support_core_utils_java" - fallback_target = "$android_support_library_package:$target_name" -} -prebuilt_wrapper("android_support_design_java") { - android_deps_target_name = "com_android_support_design_java" - fallback_target = "$android_support_library_package:$target_name" -} -prebuilt_wrapper("android_support_v4_java") { - fallback_target = "$android_support_library_package:$target_name" -} -prebuilt_wrapper("android_support_v7_appcompat_java") { - fallback_target = "$android_support_library_package:$target_name" -} -prebuilt_wrapper("android_support_v7_gridlayout_java") { - android_deps_target_name = "com_android_support_gridlayout_v7_java" - fallback_target = "$android_support_library_package:$target_name" -} -prebuilt_wrapper("android_support_v7_mediarouter_java") { - android_deps_target_name = "com_android_support_mediarouter_v7_java" - fallback_target = "$android_support_library_package:$target_name" -} -prebuilt_wrapper("android_support_v7_recyclerview_java") { - android_deps_target_name = "com_android_support_recyclerview_v7_java" - fallback_target = "$android_support_library_package:$target_name" -} -prebuilt_wrapper("android_support_v13_java") { - android_deps_target_name = "com_android_support_support_v13_java" - fallback_target = "$android_support_library_package:$target_name" -} + support_lib_alias("android_support_chromium_java") { + testonly = true + } + support_lib_alias("android_gcm_java") { + } + support_lib_alias("emma_device_java") { + } + prebuilt_wrapper("android_arch_lifecycle_common_java") { + fallback_target = "$android_support_library_package:$target_name" + } + prebuilt_wrapper("android_arch_lifecycle_runtime_java") { + fallback_target = "$android_support_library_package:$target_name" + } + prebuilt_wrapper("android_support_multidex_java") { + android_deps_target_name = "com_android_support_multidex_java" + fallback_target = "$android_support_library_package:$target_name" + } + prebuilt_wrapper("android_support_annotations_java") { + android_deps_target_name = "com_android_support_support_annotations_java" + fallback_target = "$android_support_library_package:$target_name" + } + prebuilt_wrapper("android_support_cardview_java") { + android_deps_target_name = "com_android_support_cardview_v7_java" + fallback_target = "$android_support_library_package:$target_name" + } + prebuilt_wrapper("android_support_compat_java") { + android_deps_target_name = "com_android_support_support_compat_java" + fallback_target = "$android_support_library_package:$target_name" + } + prebuilt_wrapper("android_support_core_ui_java") { + android_deps_target_name = "com_android_support_support_core_ui_java" + fallback_target = "$android_support_library_package:$target_name" + } + prebuilt_wrapper("android_support_core_utils_java") { + android_deps_target_name = "com_android_support_support_core_utils_java" + fallback_target = "$android_support_library_package:$target_name" + } + prebuilt_wrapper("android_support_design_java") { + android_deps_target_name = "com_android_support_design_java" + fallback_target = "$android_support_library_package:$target_name" + } + prebuilt_wrapper("android_support_v4_java") { + fallback_target = "$android_support_library_package:$target_name" + } + prebuilt_wrapper("android_support_v7_appcompat_java") { + fallback_target = "$android_support_library_package:$target_name" + } + prebuilt_wrapper("android_support_v7_gridlayout_java") { + android_deps_target_name = "com_android_support_gridlayout_v7_java" + fallback_target = "$android_support_library_package:$target_name" + } + prebuilt_wrapper("android_support_v7_mediarouter_java") { + android_deps_target_name = "com_android_support_mediarouter_v7_java" + fallback_target = "$android_support_library_package:$target_name" + } + prebuilt_wrapper("android_support_v7_recyclerview_java") { + android_deps_target_name = "com_android_support_recyclerview_v7_java" + fallback_target = "$android_support_library_package:$target_name" + } + prebuilt_wrapper("android_support_v13_java") { + android_deps_target_name = "com_android_support_support_v13_java" + fallback_target = "$android_support_library_package:$target_name" + } -# TODO(dgn): Use the POM files instead of hardcoding the dependencies. -gms_path = "$default_extras_android_sdk_root/extras/google/m2repository/com/google/android/gms" -gms_version = "11.2.0" + # TODO(dgn): Use the POM files instead of hardcoding the dependencies. + gms_path = "$default_extras_android_sdk_root/extras/google/m2repository/com/google/android/gms" + gms_version = "11.2.0" -android_aar_prebuilt("google_play_services_basement_java") { - deps = [ - ":android_support_v4_java", - ] - _lib_name = "play-services-basement" - aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" - info_path = "//build/secondary/third_party/android_tools/$target_name.info" - input_jars_paths = [ "$android_sdk/optional/org.apache.http.legacy.jar" ] -} + android_aar_prebuilt("google_play_services_basement_java") { + deps = [ + ":android_support_v4_java", + ] + _lib_name = "play-services-basement" + aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" + info_path = "//build/secondary/third_party/android_tools/$target_name.info" + input_jars_paths = [ "$android_sdk/optional/org.apache.http.legacy.jar" ] + } -android_aar_prebuilt("google_play_services_tasks_java") { - deps = [ - ":google_play_services_basement_java", - ] - _lib_name = "play-services-tasks" - aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" - info_path = "//build/secondary/third_party/android_tools/$target_name.info" - proguard_configs = - [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] -} + android_aar_prebuilt("google_play_services_tasks_java") { + deps = [ + ":google_play_services_basement_java", + ] + _lib_name = "play-services-tasks" + aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" + info_path = "//build/secondary/third_party/android_tools/$target_name.info" + proguard_configs = + [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] + } -android_aar_prebuilt("google_play_services_base_java") { - deps = [ - ":android_support_v4_java", - ":google_play_services_basement_java", - ":google_play_services_tasks_java", - ] - _lib_name = "play-services-base" - aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" - info_path = "//build/secondary/third_party/android_tools/$target_name.info" - proguard_configs = - [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] -} + android_aar_prebuilt("google_play_services_base_java") { + deps = [ + ":android_support_v4_java", + ":google_play_services_basement_java", + ":google_play_services_tasks_java", + ] + _lib_name = "play-services-base" + aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" + info_path = "//build/secondary/third_party/android_tools/$target_name.info" + proguard_configs = + [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] + } -android_aar_prebuilt("google_play_services_auth_base_java") { - deps = [ - ":google_play_services_base_java", - ":google_play_services_basement_java", - ] - _lib_name = "play-services-auth-base" - aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" - info_path = "//build/secondary/third_party/android_tools/$target_name.info" - proguard_configs = - [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] -} + android_aar_prebuilt("google_play_services_auth_base_java") { + deps = [ + ":google_play_services_base_java", + ":google_play_services_basement_java", + ] + _lib_name = "play-services-auth-base" + aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" + info_path = "//build/secondary/third_party/android_tools/$target_name.info" + proguard_configs = + [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] + } -android_aar_prebuilt("google_play_services_auth_java") { - deps = [ - ":google_play_services_auth_base_java", - ":google_play_services_base_java", - ":google_play_services_basement_java", - ] - _lib_name = "play-services-auth" - aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" - info_path = "//build/secondary/third_party/android_tools/$target_name.info" - proguard_configs = - [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] -} + android_aar_prebuilt("google_play_services_auth_java") { + deps = [ + ":google_play_services_auth_base_java", + ":google_play_services_base_java", + ":google_play_services_basement_java", + ] + _lib_name = "play-services-auth" + aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" + info_path = "//build/secondary/third_party/android_tools/$target_name.info" + proguard_configs = + [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] + } -android_aar_prebuilt("google_play_services_cast_java") { - deps = [ - ":android_support_v7_mediarouter_java", - ":google_play_services_base_java", - ":google_play_services_basement_java", - ] - _lib_name = "play-services-cast" - aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" - info_path = "//build/secondary/third_party/android_tools/$target_name.info" - proguard_configs = - [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] -} + android_aar_prebuilt("google_play_services_cast_java") { + deps = [ + ":android_support_v7_mediarouter_java", + ":google_play_services_base_java", + ":google_play_services_basement_java", + ] + _lib_name = "play-services-cast" + aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" + info_path = "//build/secondary/third_party/android_tools/$target_name.info" + proguard_configs = + [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] + } -android_aar_prebuilt("google_play_services_iid_java") { - deps = [ - ":google_play_services_base_java", - ":google_play_services_basement_java", - ] - _lib_name = "play-services-iid" - aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" - info_path = "//build/secondary/third_party/android_tools/$target_name.info" - proguard_configs = - [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] -} + android_aar_prebuilt("google_play_services_iid_java") { + deps = [ + ":google_play_services_base_java", + ":google_play_services_basement_java", + ] + _lib_name = "play-services-iid" + aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" + info_path = "//build/secondary/third_party/android_tools/$target_name.info" + proguard_configs = + [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] + } -android_aar_prebuilt("google_play_services_gcm_java") { - deps = [ - ":google_play_services_base_java", - ":google_play_services_basement_java", - ":google_play_services_iid_java", - ] - _lib_name = "play-services-gcm" - aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" - info_path = "//build/secondary/third_party/android_tools/$target_name.info" - proguard_configs = - [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] -} + android_aar_prebuilt("google_play_services_gcm_java") { + deps = [ + ":google_play_services_base_java", + ":google_play_services_basement_java", + ":google_play_services_iid_java", + ] + _lib_name = "play-services-gcm" + aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" + info_path = "//build/secondary/third_party/android_tools/$target_name.info" + proguard_configs = + [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] + } -android_aar_prebuilt("google_play_services_location_java") { - deps = [ - ":google_play_services_base_java", - ":google_play_services_basement_java", - ] - _lib_name = "play-services-location" - aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" - info_path = "//build/secondary/third_party/android_tools/$target_name.info" - proguard_configs = - [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] -} + android_aar_prebuilt("google_play_services_location_java") { + deps = [ + ":google_play_services_base_java", + ":google_play_services_basement_java", + ] + _lib_name = "play-services-location" + aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" + info_path = "//build/secondary/third_party/android_tools/$target_name.info" + proguard_configs = + [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] + } -android_aar_prebuilt("google_play_services_nearby_java") { - deps = [ - ":google_play_services_base_java", - ":google_play_services_basement_java", - ] - _lib_name = "play-services-nearby" - aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" - info_path = "//build/secondary/third_party/android_tools/$target_name.info" - proguard_configs = - [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] -} + android_aar_prebuilt("google_play_services_nearby_java") { + deps = [ + ":google_play_services_base_java", + ":google_play_services_basement_java", + ] + _lib_name = "play-services-nearby" + aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" + info_path = "//build/secondary/third_party/android_tools/$target_name.info" + proguard_configs = + [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] + } -android_aar_prebuilt("google_play_services_vision_java") { - deps = [ - ":google_play_services_base_java", - ":google_play_services_basement_java", - ":google_play_services_vision_common_java", - ] - _lib_name = "play-services-vision" - aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" - info_path = "//build/secondary/third_party/android_tools/$target_name.info" - proguard_configs = - [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] -} + android_aar_prebuilt("google_play_services_vision_java") { + deps = [ + ":google_play_services_base_java", + ":google_play_services_basement_java", + ":google_play_services_vision_common_java", + ] + _lib_name = "play-services-vision" + aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" + info_path = "//build/secondary/third_party/android_tools/$target_name.info" + proguard_configs = + [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] + } -android_aar_prebuilt("google_play_services_vision_common_java") { - deps = [ - ":google_play_services_base_java", - ":google_play_services_basement_java", - ] - _lib_name = "play-services-vision-common" - aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" - info_path = "//build/secondary/third_party/android_tools/$target_name.info" - proguard_configs = - [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] + android_aar_prebuilt("google_play_services_vision_common_java") { + deps = [ + ":google_play_services_base_java", + ":google_play_services_basement_java", + ] + _lib_name = "play-services-vision-common" + aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar" + info_path = "//build/secondary/third_party/android_tools/$target_name.info" + proguard_configs = + [ "${target_gen_dir}/google_play_services_basement_java/proguard.txt" ] + } }
diff --git a/build/secondary/third_party/android_tools/support/BUILD.gn b/build/secondary/third_party/android_tools/support/BUILD.gn index 2d8e676..55609b6 100644 --- a/build/secondary/third_party/android_tools/support/BUILD.gn +++ b/build/secondary/third_party/android_tools/support/BUILD.gn
@@ -4,6 +4,8 @@ import("//build/config/android/rules.gni") +assert(enable_java_templates) + visibility = [ ":*" ] lib_version = "27.0.0"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java index 62175b57..4bc2348 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java
@@ -154,11 +154,9 @@ /** * Parses a list of {@link CustomButtonParams} from the intent sent by clients. * @param intent The intent sent by the client. - * @param isTrustedIntent whether the intent is trusted. * @return A list of parsed {@link CustomButtonParams}. Return an empty list if input is invalid */ - public static List<CustomButtonParams> fromIntent( - Context context, Intent intent, boolean isTrustedIntent) { + public static List<CustomButtonParams> fromIntent(Context context, Intent intent) { List<CustomButtonParams> paramsList = new ArrayList<>(1); if (intent == null) return paramsList;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java index eccce83..7fa0e7c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
@@ -251,21 +251,25 @@ * {@link #mBottombarButtons} and {@link #mToolbarButtons}. */ private void retrieveCustomButtons(Intent intent, Context context) { - mCustomButtonParams = CustomButtonParams.fromIntent(context, intent, isTrustedIntent()); - if (mCustomButtonParams != null) { - for (CustomButtonParams params : mCustomButtonParams) { - if (!params.showOnToolbar()) { - mBottombarButtons.add(params); - } else if (mToolbarButtons.size() < MAX_CUSTOM_TOOLBAR_ITEMS) { - mToolbarButtons.add(params); - } else { - Log.w(TAG, "Only %d items are allowed in the toolbar", - MAX_CUSTOM_TOOLBAR_ITEMS); - } + assert mCustomButtonParams == null; + mCustomButtonParams = CustomButtonParams.fromIntent(context, intent); + for (CustomButtonParams params : mCustomButtonParams) { + if (!params.showOnToolbar()) { + mBottombarButtons.add(params); + } else if (mToolbarButtons.size() < getMaxCustomToolbarItems()) { + mToolbarButtons.add(params); + } else { + Log.w(TAG, "Only %d items are allowed in the toolbar", getMaxCustomToolbarItems()); } } } + private int getMaxCustomToolbarItems() { + if (!isTrustedIntent()) return 1; + + return MAX_CUSTOM_TOOLBAR_ITEMS; + } + /** * Processes the color passed from the client app and updates {@link #mToolbarColor}. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationJobService.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationJobService.java index f3b153b..91b00ff 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationJobService.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationJobService.java
@@ -27,6 +27,8 @@ intent.getStringExtra(NotificationConstants.EXTRA_NOTIFICATION_ID)); bundle.putString(NotificationConstants.EXTRA_NOTIFICATION_INFO_ORIGIN, intent.getStringExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_ORIGIN)); + bundle.putString(NotificationConstants.EXTRA_NOTIFICATION_INFO_SCOPE, + intent.getStringExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_SCOPE)); bundle.putString(NotificationConstants.EXTRA_NOTIFICATION_INFO_PROFILE_ID, intent.getStringExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_PROFILE_ID)); bundle.putBoolean(NotificationConstants.EXTRA_NOTIFICATION_INFO_PROFILE_INCOGNITO,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java index e553ce8..eb24c8d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java
@@ -327,6 +327,9 @@ Uri intentData = makeIntentData(notificationId, origin, actionIndex); Intent intent = new Intent(action, intentData); intent.setClass(context, NotificationService.Receiver.class); + + // Make sure to update NotificationJobService.getJobExtrasFromIntent() when changing any + // of the extras included with the |intent|. intent.putExtra(NotificationConstants.EXTRA_NOTIFICATION_ID, notificationId); intent.putExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_ORIGIN, origin); intent.putExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_SCOPE, scopeUrl);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java index a2e0a52..420b38c 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
@@ -930,6 +930,11 @@ Bitmap expectedIcon1 = createVectorDrawableBitmap(R.drawable.ic_content_copy_black, 48, 48); Bitmap expectedIcon2 = createVectorDrawableBitmap(R.drawable.ic_music_note_36dp, 48, 48); Intent intent = createMinimalCustomTabIntent(); + + // Mark the intent as trusted so it can show more than one action button. + IntentHandler.addTrustedIntentExtras(intent); + Assert.assertTrue(IntentHandler.isIntentChromeOrFirstParty(intent)); + ArrayList<Bundle> toolbarItems = new ArrayList<>(2); final PendingIntent pi1 = PendingIntent.getBroadcast( InstrumentationRegistry.getTargetContext(), 0, new Intent(), 0); @@ -988,6 +993,41 @@ } /** + * Test that additional action buttons are ignored for untrusted intents. + */ + @Test + @SmallTest + @Feature({"UiCatalogue"}) + @RetryOnFailure + public void testMultipleActionButtons_untrusted() + throws InterruptedException, TimeoutException { + Bitmap expectedIcon1 = createVectorDrawableBitmap(R.drawable.ic_content_copy_black, 48, 48); + Bitmap expectedIcon2 = createVectorDrawableBitmap(R.drawable.ic_music_note_36dp, 48, 48); + Intent intent = createMinimalCustomTabIntent(); + + // By default, the intent should not be trusted. + Assert.assertFalse(IntentHandler.isIntentChromeOrFirstParty(intent)); + + ArrayList<Bundle> toolbarItems = new ArrayList<>(2); + final PendingIntent pi = PendingIntent.getBroadcast( + InstrumentationRegistry.getTargetContext(), 0, new Intent(), 0); + toolbarItems.add(makeToolbarItemBundle(expectedIcon1, "Shown", pi)); + toolbarItems.add(makeToolbarItemBundle(expectedIcon2, "Not shown", pi)); + intent.putParcelableArrayListExtra(CustomTabsIntent.EXTRA_TOOLBAR_ITEMS, toolbarItems); + mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent); + + View toolbarView = mCustomTabActivityTestRule.getActivity().findViewById(R.id.toolbar); + Assert.assertTrue( + "A custom tab toolbar is never shown", toolbarView instanceof CustomTabToolbar); + CustomTabToolbar toolbar = (CustomTabToolbar) toolbarView; + final ImageButton actionButton = toolbar.getCustomActionButtonForTest(0); + Assert.assertNotNull("Action button not found", actionButton); + Assert.assertEquals("Shown", actionButton.getContentDescription()); + + Assert.assertNull(toolbar.getCustomActionButtonForTest(1)); + } + + /** * Test the case that the action button should not be shown, given a bitmap with unacceptable * height/width ratio. */
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index ac9b2ae8..6189311 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -7333,9 +7333,6 @@ <message name="IDS_PROFILES_BUBBLE_ACCESSIBLE_DESCRIPTION" desc="The help text for the avatar bubble."> Switch to a different user. </message> - <message name="IDS_PROFILES_NEW_AVATAR_MENU_ACCESSIBLE_NAME" desc="Title of the new avatar bubble menu for accessibility"> - Switch Person - </message> <message name="IDS_PROFILES_CREATE_NEW_PROFILE_OPTION" desc="In Title Case. Create new profile menu item and button title."> Add Person... </message> @@ -10927,11 +10924,11 @@ <message name="IDS_VR_BUTTON_BACK" desc="The label for the back button on the VR controller -- shown when the keyboard is visible, we are in voice search, or we are repositioning."> Back </message> - <message name="IDS_VR_BUTTON_TRACKPAD_REPOSITION" desc="The label for the trackpad while in window repositioning mode."> - Finish repositioning + <message name="IDS_VR_BUTTON_TRACKPAD_REPOSITION" desc="The label for the trackpad while in window repositioning/resizing mode."> + Touch to resize </message> - <message name="IDS_VR_REPOSITION_LABEL" desc="This text appears over a window to explain to the user how to manipulate the window."> - Move the controller to reposition. Click to finish. + <message name="IDS_VR_BUTTON_APP_REPOSITION" desc="This is the label for the app button while in reposition mode"> + Finish </message> <message name="IDS_VR_MENU_NEW_INCOGNITO_TAB" desc="Menu item for opening a new incognito tab that facilitates pseudononymous browsing. [CHAR-LIMIT=27]"> New incognito tab
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 5361985d..0f53056 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -2182,10 +2182,6 @@ flag_descriptions::kMacViewsNativeAppWindowsDescription, kOsMac, ENABLE_DISABLE_VALUE_TYPE(switches::kEnableMacViewsNativeAppWindows, switches::kDisableMacViewsNativeAppWindows)}, - {"mac-views-profile-chooser", - flag_descriptions::kMacViewsProfileChooserName, - flag_descriptions::kMacViewsProfileChooserDescription, kOsMac, - FEATURE_VALUE_TYPE(features::kViewsProfileChooser)}, {"mac-views-task-manager", flag_descriptions::kMacViewsTaskManagerName, flag_descriptions::kMacViewsTaskManagerDescription, kOsMac, FEATURE_VALUE_TYPE(features::kViewsTaskManager)},
diff --git a/chrome/browser/chromeos/file_manager/file_manager_uitest.cc b/chrome/browser/chromeos/file_manager/file_manager_uitest.cc index 4c260d0..83a602e5 100644 --- a/chrome/browser/chromeos/file_manager/file_manager_uitest.cc +++ b/chrome/browser/chromeos/file_manager/file_manager_uitest.cc
@@ -25,9 +25,9 @@ base::FilePath root_path; ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &root_path)); - // Load test/main.html. + // Load test.html. const GURL url = net::FilePathToFileURL(root_path.Append( - FILE_PATH_LITERAL("ui/file_manager/file_manager/test/main.html"))); + FILE_PATH_LITERAL("ui/file_manager/file_manager/test.html"))); content::WebContents* const web_contents = browser()->tab_strip_model()->GetActiveWebContents(); ASSERT_TRUE(web_contents);
diff --git a/chrome/browser/chromeos/login/eula_browsertest.cc b/chrome/browser/chromeos/login/eula_browsertest.cc index c858dc5..b153fe65 100644 --- a/chrome/browser/chromeos/login/eula_browsertest.cc +++ b/chrome/browser/chromeos/login/eula_browsertest.cc
@@ -146,9 +146,10 @@ auto* const owner_contents = GetLoginUI()->GetWebContents(); auto* const manager = guest_view::GuestViewManager::FromBrowserContext( owner_contents->GetBrowserContext()); - manager->ForEachGuest(owner_contents, - base::Bind(&AddNamedWebContentsToSet, &frame_set, - kUniqueEulaWebviewName)); + manager->ForEachGuest( + owner_contents, + base::BindRepeating(&AddNamedWebContentsToSet, &frame_set, + kUniqueEulaWebviewName)); EXPECT_EQ(1u, frame_set.size()); return *frame_set.begin(); }
diff --git a/chrome/browser/chromeos/login/webview_login_browsertest.cc b/chrome/browser/chromeos/login/webview_login_browsertest.cc index 885d468f..3b86725 100644 --- a/chrome/browser/chromeos/login/webview_login_browsertest.cc +++ b/chrome/browser/chromeos/login/webview_login_browsertest.cc
@@ -215,8 +215,9 @@ guest_view::GuestViewManager::FromBrowserContext(browser_context); guest_view_manager->ForEachGuest( web_contents, - base::Bind(&WebviewLoginTest::WebViewVisited, base::Unretained(this), - browser_context, storage_partition, &web_view_found)); + base::BindRepeating(&WebviewLoginTest::WebViewVisited, + base::Unretained(this), browser_context, + storage_partition, &web_view_found)); return web_view_found; }
diff --git a/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc b/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc index e8309a0..228f865 100644 --- a/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc +++ b/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc
@@ -23,7 +23,6 @@ #include "chrome/common/extensions/api/automation_api_constants.h" #include "chrome/common/extensions/api/automation_internal.h" #include "chrome/common/extensions/chrome_extension_messages.h" -#include "chrome/common/extensions/manifest_handlers/automation.h" #include "content/public/browser/ax_event_notification_details.h" #include "content/public/browser/browser_accessibility_state.h" #include "content/public/browser/browser_context.h" @@ -36,6 +35,7 @@ #include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_user_data.h" #include "extensions/common/extension_messages.h" +#include "extensions/common/manifest_handlers/automation.h" #include "extensions/common/permissions/permissions_data.h" #include "ui/accessibility/ax_action_data.h" #include "ui/accessibility/ax_enum_util.h"
diff --git a/chrome/browser/extensions/api/identity/identity_api.cc b/chrome/browser/extensions/api/identity/identity_api.cc index 1838933..29e273d 100644 --- a/chrome/browser/extensions/api/identity/identity_api.cc +++ b/chrome/browser/extensions/api/identity/identity_api.cc
@@ -29,13 +29,9 @@ #include "chrome/browser/signin/account_tracker_service_factory.h" #include "chrome/browser/signin/chrome_signin_client_factory.h" #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" -#include "chrome/browser/signin/signin_manager_factory.h" -#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h" #include "chrome/common/extensions/api/identity.h" #include "chrome/common/url_constants.h" -#include "components/signin/core/browser/account_tracker_service.h" #include "components/signin/core/browser/profile_oauth2_token_service.h" -#include "components/signin/core/browser/signin_manager.h" #include "extensions/browser/extension_function_dispatcher.h" #include "extensions/common/extension.h" #include "extensions/common/extension_l10n_util.h" @@ -103,17 +99,9 @@ } IdentityAPI::IdentityAPI(content::BrowserContext* context) - : browser_context_(context), - profile_identity_provider_( - SigninManagerFactory::GetForProfile( - Profile::FromBrowserContext(context)), - ProfileOAuth2TokenServiceFactory::GetForProfile( - Profile::FromBrowserContext(context)), - LoginUIServiceFactory::GetShowLoginPopupCallbackForProfile( - Profile::FromBrowserContext(context))), - account_tracker_(&profile_identity_provider_, - g_browser_process->system_request_context()) { - account_tracker_.AddObserver(this); + : profile_(Profile::FromBrowserContext(context)) { + AccountTrackerServiceFactory::GetForProfile(profile_)->AddObserver(this); + ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->AddObserver(this); } IdentityAPI::~IdentityAPI() {} @@ -155,8 +143,9 @@ void IdentityAPI::Shutdown() { on_shutdown_callback_list_.Notify(); - account_tracker_.RemoveObserver(this); - account_tracker_.Shutdown(); + AccountTrackerServiceFactory::GetForProfile(profile_)->RemoveObserver(this); + ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->RemoveObserver( + this); } static base::LazyInstance<BrowserContextKeyedAPIFactory<IdentityAPI>>:: @@ -167,31 +156,41 @@ return g_identity_api_factory.Pointer(); } -void IdentityAPI::OnAccountSignInChanged(const gaia::AccountIds& ids, - bool is_signed_in) { - api::identity::AccountInfo account_info; - account_info.id = ids.gaia; +void IdentityAPI::OnRefreshTokenAvailable(const std::string& account_id) { + const AccountInfo& account_info = + AccountTrackerServiceFactory::GetForProfile(profile_)->GetAccountInfo( + account_id); + + // Refresh tokens are sometimes made available in contexts where + // AccountTrackerService is not tracking the account in question (one example + // is SupervisedUserService::InitSync()). Bail out in these cases. + if (account_info.gaia.empty()) + return; + + FireOnAccountSignInChanged(account_info.gaia, true); +} + +void IdentityAPI::OnAccountRemoved(const AccountInfo& account_info) { + DCHECK(!account_info.gaia.empty()); + FireOnAccountSignInChanged(account_info.gaia, false); +} + +void IdentityAPI::FireOnAccountSignInChanged(const std::string& gaia_id, + bool is_signed_in) { + DCHECK(!gaia_id.empty()); + api::identity::AccountInfo api_account_info; + api_account_info.id = gaia_id; std::unique_ptr<base::ListValue> args = - api::identity::OnSignInChanged::Create(account_info, is_signed_in); - std::unique_ptr<Event> event( - new Event(events::IDENTITY_ON_SIGN_IN_CHANGED, - api::identity::OnSignInChanged::kEventName, std::move(args), - browser_context_)); + api::identity::OnSignInChanged::Create(api_account_info, is_signed_in); + std::unique_ptr<Event> event(new Event( + events::IDENTITY_ON_SIGN_IN_CHANGED, + api::identity::OnSignInChanged::kEventName, std::move(args), profile_)); if (on_signin_changed_callback_for_testing_) on_signin_changed_callback_for_testing_.Run(event.get()); - EventRouter::Get(browser_context_)->BroadcastEvent(std::move(event)); -} - -void IdentityAPI::SetAccountStateForTesting(const std::string& account_id, - bool signed_in) { - gaia::AccountIds ids; - ids.account_key = account_id; - ids.email = account_id; - ids.gaia = account_id; - account_tracker_.SetAccountStateForTest(ids, signed_in); + EventRouter::Get(profile_)->BroadcastEvent(std::move(event)); } template <> @@ -199,9 +198,7 @@ DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory()); DependsOn(ChromeSigninClientFactory::GetInstance()); - DependsOn(LoginUIServiceFactory::GetInstance()); DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance()); - DependsOn(SigninManagerFactory::GetInstance()); } } // namespace extensions
diff --git a/chrome/browser/extensions/api/identity/identity_api.h b/chrome/browser/extensions/api/identity/identity_api.h index 8f86f57..5fc41ec 100644 --- a/chrome/browser/extensions/api/identity/identity_api.h +++ b/chrome/browser/extensions/api/identity/identity_api.h
@@ -27,10 +27,9 @@ #include "chrome/browser/extensions/api/identity/identity_remove_cached_auth_token_function.h" #include "chrome/browser/extensions/api/identity/web_auth_flow.h" #include "chrome/browser/extensions/chrome_extension_function.h" -#include "components/signin/core/browser/profile_identity_provider.h" +#include "components/signin/core/browser/account_tracker_service.h" #include "extensions/browser/browser_context_keyed_api_factory.h" #include "extensions/browser/event_router.h" -#include "google_apis/gaia/account_tracker.h" #include "google_apis/gaia/oauth2_mint_token_flow.h" #include "google_apis/gaia/oauth2_token_service.h" @@ -38,6 +37,8 @@ class BrowserContext; } +class Profile; + namespace extensions { class IdentityTokenCacheValue { @@ -72,7 +73,8 @@ }; class IdentityAPI : public BrowserContextKeyedAPI, - public gaia::AccountTracker::Observer { + public AccountTrackerService::Observer, + public OAuth2TokenService::Observer { public: typedef std::map<ExtensionTokenKey, IdentityTokenCacheValue> CachedTokens; @@ -92,24 +94,15 @@ const CachedTokens& GetAllCachedTokens(); - // BrowserContextKeyedAPI implementation. + // BrowserContextKeyedAPI: void Shutdown() override; static BrowserContextKeyedAPIFactory<IdentityAPI>* GetFactoryInstance(); - // gaia::AccountTracker::Observer implementation: - void OnAccountSignInChanged(const gaia::AccountIds& ids, - bool is_signed_in) override; - std::unique_ptr<base::CallbackList<void()>::Subscription> RegisterOnShutdownCallback(const base::Closure& cb) { return on_shutdown_callback_list_.Add(cb); } - // TODO(blundell): Eliminate this method once this class is no longer using - // AccountTracker. - // Makes |account_tracker_| aware of this account. - void SetAccountStateForTesting(const std::string& account_id, bool signed_in); - // Callback that is used in testing contexts to test the implementation of // the chrome.identity.onSignInChanged event. Note that the passed-in Event is // valid only for the duration of the callback. @@ -122,15 +115,31 @@ private: friend class BrowserContextKeyedAPIFactory<IdentityAPI>; - // BrowserContextKeyedAPI implementation. + // BrowserContextKeyedAPI: static const char* service_name() { return "IdentityAPI"; } static const bool kServiceIsNULLWhileTesting = true; - content::BrowserContext* browser_context_; + // OAuth2TokenService::Observer: + void OnRefreshTokenAvailable(const std::string& account_id) override; + + // AccountTrackerService::Observer: + // NOTE: This class listens for signout events via this callback (which itself + // is triggered by O2TS::OnRefreshTokenRevoked()) rather than directly via + // OnRefreshTokenRevoked() in order to obtain the Gaia ID of the signed-out + // account, which is needed to send as input to the + // chrome.identity.onSigninChanged event. That Gaia ID is not guaranteed to be + // available from O2TS::OnRefreshTokenRevoked(). + // TODO(blundell): Eliminate this kludge by porting this class to interact + // with IdentityManager. + void OnAccountRemoved(const AccountInfo& info) override; + + // Fires the chrome.identity.onSignInChanged event. + void FireOnAccountSignInChanged(const std::string& gaia_id, + bool is_signed_in); + + Profile* profile_; IdentityMintRequestQueue mint_queue_; CachedTokens token_cache_; - ProfileIdentityProvider profile_identity_provider_; - gaia::AccountTracker account_tracker_; OnSignInChangedCallback on_signin_changed_callback_for_testing_;
diff --git a/chrome/browser/extensions/api/identity/identity_apitest.cc b/chrome/browser/extensions/api/identity/identity_apitest.cc index 57203683..8dd064b8 100644 --- a/chrome/browser/extensions/api/identity/identity_apitest.cc +++ b/chrome/browser/extensions/api/identity/identity_apitest.cc
@@ -37,6 +37,7 @@ #include "chrome/browser/extensions/extension_function_test_utils.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/signin/account_fetcher_service_factory.h" #include "chrome/browser/signin/account_tracker_service_factory.h" #include "chrome/browser/signin/fake_gaia_cookie_manager_service_builder.h" #include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h" @@ -51,6 +52,7 @@ #include "chrome/test/base/in_process_browser_test.h" #include "components/crx_file/id_util.h" #include "components/guest_view/browser/guest_view_base.h" +#include "components/signin/core/browser/account_fetcher_service.h" #include "components/signin/core/browser/account_tracker_service.h" #include "components/signin/core/browser/fake_gaia_cookie_manager_service.h" #include "components/signin/core/browser/fake_profile_oauth2_token_service.h" @@ -414,14 +416,6 @@ MOCK_METHOD1(StartMintToken, void(IdentityMintRequestQueue::MintType)); }; -gaia::AccountIds CreateIds(const std::string& email, const std::string& obfid) { - gaia::AccountIds ids; - ids.account_key = email; - ids.email = email; - ids.gaia = obfid; - return ids; -} - class IdentityTestWithSignin : public AsyncExtensionBrowserTest { public: void SetUpInProcessBrowserTestFixture() override { @@ -445,6 +439,18 @@ context, &BuildFakeProfileOAuth2TokenService); GaiaCookieManagerServiceFactory::GetInstance()->SetTestingFactory( context, &BuildFakeGaiaCookieManagerService); + + // Ensure that AccountFetcherService is (1) created at all and (2) created + // early enough for it to observe the Profile initialization process and + // loading of tokens by PO2TS. Explicitly forcing this setup (which happens + // naturally in production) is necessary for the flow of + // AccountTrackerService having accounts removed when tokens are revoked + // with PO2TS to work as expected in this testing context. + // TODO(blundell): Change these tests to interact with + // IdentityTestEnvironment once the production code is changed to interact + // with IdentityManager. + AccountFetcherServiceFactory::GetInstance()->GetForProfile( + Profile::FromBrowserContext(context)); } void SetUpOnMainThread() override { @@ -461,6 +467,19 @@ GaiaCookieManagerServiceFactory::GetInstance() ->GetForProfile(profile()) ->Init(); + +#if defined(OS_CHROMEOS) + // On ChromeOS, ProfileOAuth2TokenService does not fire + // OnRefreshTokensLoaded() in text contexts. However, AccountFetcherService + // must receive this call in order to forward later + // OnRefreshToken{Available, Revoked} callbacks on to AccountTrackerService + // as expected. Hence, we make that call explicitly here. + // TODO(blundell): Hide this detail when converting this code to interact + // with IdentityTestEnvironment. + AccountFetcherServiceFactory::GetInstance() + ->GetForProfile(profile()) + ->OnRefreshTokensLoaded(); +#endif } protected: @@ -717,10 +736,6 @@ command_line->AppendSwitch(switches::kExtensionsMultiAccount); } - void IssueLoginRefreshTokenForAccount(const std::string& account_key) { - token_service_->UpdateCredentials(account_key, "refresh_token"); - } - void IssueLoginAccessTokenForAccount(const std::string& account_key) { token_service_->IssueAllTokensForAccount( account_key, @@ -1743,8 +1758,7 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, MultiPrimaryUserManuallyIssueToken) { SignIn("primary@example.com"); - IssueLoginRefreshTokenForAccount("secondary@example.com"); - SeedAccountInfo("secondary@example.com"); + AddAccount("secondary@example.com", "secondary@example.com"); scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction()); scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); @@ -1772,8 +1786,7 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, MultiSecondaryUserManuallyIssueToken) { SignIn("primary@example.com"); - IssueLoginRefreshTokenForAccount("secondary@example.com"); - SeedAccountInfo("secondary@example.com"); + AddAccount("secondary@example.com", "secondary@example.com"); scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction()); scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); @@ -1801,8 +1814,7 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, MultiUnknownUserGetTokenFromTokenServiceFailure) { SignIn("primary@example.com"); - IssueLoginRefreshTokenForAccount("secondary@example.com"); - SeedAccountInfo("secondary@example.com"); + AddAccount("secondary@example.com", "secondary@example.com"); scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction()); scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); @@ -1818,8 +1830,7 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, MultiSecondaryNonInteractiveMintFailure) { SignIn("primary@example.com"); - IssueLoginRefreshTokenForAccount("secondary@example.com"); - SeedAccountInfo("secondary@example.com"); + AddAccount("secondary@example.com", "secondary@example.com"); scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction()); func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); @@ -1836,8 +1847,7 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, MultiSecondaryNonInteractiveLoginAccessTokenFailure) { SignIn("primary@example.com"); - IssueLoginRefreshTokenForAccount("secondary@example.com"); - SeedAccountInfo("secondary@example.com"); + AddAccount("secondary@example.com", "secondary@example.com"); scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction()); func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); @@ -1852,8 +1862,7 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, MultiSecondaryInteractiveApprovalAborted) { SignIn("primary@example.com"); - IssueLoginRefreshTokenForAccount("secondary@example.com"); - SeedAccountInfo("secondary@example.com"); + AddAccount("secondary@example.com", "secondary@example.com"); scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction()); func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); @@ -2222,6 +2231,7 @@ id_api()->set_on_signin_changed_callback_for_testing( base::Bind(&OnSignInChangedEventTest::OnSignInEventChanged, base::Unretained(this))); + IdentityTestWithSignin::SetUpOnMainThread(); } @@ -2245,8 +2255,7 @@ private: void OnSignInEventChanged(Event* event) { - if (!HasExpectedEvent()) - return; + ASSERT_TRUE(HasExpectedEvent()); // Search for |event| in the set of expected events. bool found_event = false; @@ -2283,8 +2292,6 @@ // Test that an event is fired when the primary account signs in. IN_PROC_BROWSER_TEST_F(OnSignInChangedEventTest, FireOnPrimaryAccountSignIn) { - id_api()->SetAccountStateForTesting("primary", false); - api::identity::AccountInfo account_info; account_info.id = "primary"; AddExpectedEvent(api::identity::OnSignInChanged::Create(account_info, true)); @@ -2298,10 +2305,12 @@ #if !defined(OS_CHROMEOS) // Test that an event is fired when the primary account signs out. IN_PROC_BROWSER_TEST_F(OnSignInChangedEventTest, FireOnPrimaryAccountSignOut) { - id_api()->SetAccountStateForTesting("primary", true); - api::identity::AccountInfo account_info; account_info.id = "primary"; + AddExpectedEvent(api::identity::OnSignInChanged::Create(account_info, true)); + + SignIn("primary", "primary"); + AddExpectedEvent(api::identity::OnSignInChanged::Create(account_info, false)); // Sign out and verify that the callback fires. @@ -2315,10 +2324,12 @@ // revoked. IN_PROC_BROWSER_TEST_F(OnSignInChangedEventTest, FireOnPrimaryAccountRefreshTokenRevoked) { - id_api()->SetAccountStateForTesting("primary", true); - api::identity::AccountInfo account_info; account_info.id = "primary"; + AddExpectedEvent(api::identity::OnSignInChanged::Create(account_info, true)); + + SignIn("primary", "primary"); + AddExpectedEvent(api::identity::OnSignInChanged::Create(account_info, false)); // Revoke the refresh token and verify that the callback fires. @@ -2331,38 +2342,39 @@ // newly available. IN_PROC_BROWSER_TEST_F(OnSignInChangedEventTest, FireOnPrimaryAccountRefreshTokenAvailable) { - id_api()->SetAccountStateForTesting("primary", false); - - SignIn("primary", "primary"); - token_service_->RevokeCredentials("primary"); - api::identity::AccountInfo account_info; account_info.id = "primary"; AddExpectedEvent(api::identity::OnSignInChanged::Create(account_info, true)); + SignIn("primary", "primary"); + + AddExpectedEvent(api::identity::OnSignInChanged::Create(account_info, false)); + token_service_->RevokeCredentials("primary"); + + account_info.id = "primary"; + AddExpectedEvent(api::identity::OnSignInChanged::Create(account_info, true)); + // Make the primary account's refresh token available and check that the - // callback fires. - token_service_->UpdateCredentials("primary", "refresh_token"); + // callback fires. Note that we must call AddAccount() here as the account's + // information must be present in the AccountTrackerService as well. + AddAccount("primary", "primary"); EXPECT_FALSE(HasExpectedEvent()); } // Test that an event is fired for changes to a secondary account. IN_PROC_BROWSER_TEST_F(OnSignInChangedEventTest, FireForSecondaryAccount) { - id_api()->SetAccountStateForTesting("primary", false); - id_api()->SetAccountStateForTesting("secondary", false); - - // NOTE: The current implementation requires that the primary account be - // present for an event to fire for secondary accounts. This is not actually - // part of the semantics of chrome.identity.OnSignInEventChanged, however. + api::identity::AccountInfo account_info; + account_info.id = "primary"; + AddExpectedEvent(api::identity::OnSignInChanged::Create(account_info, true)); SignIn("primary", "primary"); - api::identity::AccountInfo account_info; account_info.id = "secondary"; AddExpectedEvent(api::identity::OnSignInChanged::Create(account_info, true)); // Make a secondary account's refresh token available and check that the - // callback fires. - token_service_->UpdateCredentials("secondary", "refresh_token"); + // callback fires. Note that we must call AddAccount() here as the account's + // information must be present in the AccountTrackerService as well. + AddAccount("secondary", "secondary"); EXPECT_FALSE(HasExpectedEvent()); // Revoke the secondary account's refresh token and check that the callback
diff --git a/chrome/browser/extensions/api/page_capture/page_capture_api.cc b/chrome/browser/extensions/api/page_capture/page_capture_api.cc index 9923085..b8b1640d 100644 --- a/chrome/browser/extensions/api/page_capture/page_capture_api.cc +++ b/chrome/browser/extensions/api/page_capture/page_capture_api.cc
@@ -210,7 +210,7 @@ web_contents->GenerateMHTML( content::MHTMLGenerationParams(mhtml_path_), - base::Bind(&PageCaptureSaveAsMHTMLFunction::MHTMLGenerated, this)); + base::BindOnce(&PageCaptureSaveAsMHTMLFunction::MHTMLGenerated, this)); } void PageCaptureSaveAsMHTMLFunction::MHTMLGenerated(int64_t mhtml_file_size) {
diff --git a/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc b/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc index ecf00a02..a57495e 100644 --- a/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc +++ b/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc
@@ -110,8 +110,9 @@ return false; }; guest_view::GuestViewManager::FromBrowserContext(browser_context()) - ->ForEachGuest(GetSenderWebContents(), - base::Bind(get_guest, &guests_found, &target_host)); + ->ForEachGuest( + GetSenderWebContents(), + base::BindRepeating(get_guest, &guests_found, &target_host)); if (!target_host) { SetError("No webview render process found"); return nullptr;
diff --git a/chrome/browser/extensions/extension_fullscreen_apitest.cc b/chrome/browser/extensions/extension_fullscreen_apitest.cc index 57b2d428..6cf1c60 100644 --- a/chrome/browser/extensions/extension_fullscreen_apitest.cc +++ b/chrome/browser/extensions/extension_fullscreen_apitest.cc
@@ -27,8 +27,15 @@ ASSERT_TRUE(RunPlatformAppTest("fullscreen/has_permission")) << message_; } +#if defined(OS_MACOSX) +// Entering fullscreen is flaky on Mac: http://crbug.com/824517 +#define MAYBE_FocusWindowDoesNotExitFullscreen \ + DISABLED_FocusWindowDoesNotExitFullscreen +#else +#define MAYBE_FocusWindowDoesNotExitFullscreen FocusWindowDoesNotExitFullscreen +#endif IN_PROC_BROWSER_TEST_F(ExtensionApiTest, - FocusWindowDoesNotExitFullscreen) { + MAYBE_FocusWindowDoesNotExitFullscreen) { browser()->exclusive_access_manager()->context()->EnterFullscreen( GURL(), EXCLUSIVE_ACCESS_BUBBLE_TYPE_BROWSER_FULLSCREEN_EXIT_INSTRUCTION); ASSERT_TRUE(browser()->window()->IsFullscreen());
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 387e8cc..cc6052d 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -2473,11 +2473,6 @@ const char kMacViewsNativeAppWindowsDescription[] = "Controls whether to use Toolkit-Views based Chrome App windows."; -const char kMacViewsProfileChooserName[] = - "Profile chooser menu with toolkit-views on Mac"; -const char kMacViewsProfileChooserDescription[] = - "Enables profile chooser menu based on toolkit-views, on Mac"; - const char kMacViewsTaskManagerName[] = "Toolkit-Views Task Manager."; const char kMacViewsTaskManagerDescription[] = "Controls whether to use the Toolkit-Views based Task Manager.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index e52b82c..c6cb413b 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -1513,9 +1513,6 @@ extern const char kMacViewsNativeAppWindowsName[]; extern const char kMacViewsNativeAppWindowsDescription[]; -extern const char kMacViewsProfileChooserName[]; -extern const char kMacViewsProfileChooserDescription[]; - extern const char kMacViewsTaskManagerName[]; extern const char kMacViewsTaskManagerDescription[];
diff --git a/chrome/browser/offline_pages/offline_page_mhtml_archiver.cc b/chrome/browser/offline_pages/offline_page_mhtml_archiver.cc index 4f4ce0b..d21eb8d 100644 --- a/chrome/browser/offline_pages/offline_page_mhtml_archiver.cc +++ b/chrome/browser/offline_pages/offline_page_mhtml_archiver.cc
@@ -128,8 +128,8 @@ web_contents_->GenerateMHTML( params, - base::Bind(&OfflinePageMHTMLArchiver::OnGenerateMHTMLDone, - weak_ptr_factory_.GetWeakPtr(), url, file_path, title)); + base::BindOnce(&OfflinePageMHTMLArchiver::OnGenerateMHTMLDone, + weak_ptr_factory_.GetWeakPtr(), url, file_path, title)); } void OfflinePageMHTMLArchiver::OnGenerateMHTMLDone(
diff --git a/chrome/browser/offline_pages/offline_page_mhtml_archiver_unittest.cc b/chrome/browser/offline_pages/offline_page_mhtml_archiver_unittest.cc index c70c2b1..48576df 100644 --- a/chrome/browser/offline_pages/offline_page_mhtml_archiver_unittest.cc +++ b/chrome/browser/offline_pages/offline_page_mhtml_archiver_unittest.cc
@@ -88,9 +88,9 @@ base::FilePath archive_file_path = archives_dir.AppendASCII(url_.ExtractFileName()); base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&TestMHTMLArchiver::OnGenerateMHTMLDone, - base::Unretained(this), url_, archive_file_path, - kTestTitle, kTestFileSize)); + FROM_HERE, base::BindOnce(&TestMHTMLArchiver::OnGenerateMHTMLDone, + base::Unretained(this), url_, archive_file_path, + kTestTitle, kTestFileSize)); } bool TestMHTMLArchiver::HasConnectionSecurityError() {
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc index c8c995d..d99cf7c 100644 --- a/chrome/browser/pdf/pdf_extension_test.cc +++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -264,7 +264,7 @@ web_contents->GetBrowserContext()->GetGuestManager(); WebContents* guest_contents = nullptr; ASSERT_NO_FATAL_FAILURE(guest_manager->ForEachGuest( - web_contents, base::Bind(&GetGuestCallback, &guest_contents))); + web_contents, base::BindRepeating(&GetGuestCallback, &guest_contents))); ASSERT_TRUE(guest_contents); ASSERT_TRUE(content::ExecuteScript( guest_contents,
diff --git a/chrome/browser/printing/print_browsertest.cc b/chrome/browser/printing/print_browsertest.cc index cbafe630..8ead4fd 100644 --- a/chrome/browser/printing/print_browsertest.cc +++ b/chrome/browser/printing/print_browsertest.cc
@@ -5,13 +5,19 @@ #include <memory> #include "base/auto_reset.h" +#include "base/files/file_path.h" +#include "base/path_service.h" #include "base/run_loop.h" +#include "base/threading/thread_restrictions.h" +#include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/printing/print_view_manager_common.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/webui/print_preview/print_preview_ui.h" +#include "chrome/common/chrome_paths.h" #include "chrome/common/pref_names.h" +#include "chrome/common/webui_url_constants.cc" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" #include "components/prefs/pref_service.h" @@ -23,9 +29,14 @@ #include "content/public/browser/render_process_host.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" +#include "extensions/common/extension.h" #include "net/dns/mock_host_resolver.h" #include "net/test/embedded_test_server/embedded_test_server.h" +#if defined(OS_CHROMEOS) +#include "ui/aura/env.h" +#endif + namespace printing { namespace { @@ -231,6 +242,51 @@ constexpr char IsolateOriginsPrintBrowserTest::kIsolatedSite[]; +class PrintExtensionBrowserTest : public ExtensionBrowserTest { + public: + PrintExtensionBrowserTest() {} + ~PrintExtensionBrowserTest() override {} + + void PrintAndWaitUntilPreviewIsReady(bool print_only_selection) { + PrintPreviewObserver print_preview_observer; + + printing::StartPrint(browser()->tab_strip_model()->GetActiveWebContents(), + /*print_preview_disabled=*/false, + print_only_selection); + + print_preview_observer.WaitUntilPreviewIsReady(); + } + + void LoadExtensionAndNavigateToOptionPage() { + const extensions::Extension* extension = nullptr; + { + base::ScopedAllowBlockingForTesting allow_blocking; + base::FilePath test_data_dir; + PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir); + extension = LoadExtension( + test_data_dir.AppendASCII("printing").AppendASCII("test_extension")); + ASSERT_TRUE(extension); + } + + GURL url(chrome::kChromeUIExtensionsURL); + std::string query = + base::StringPrintf("options=%s", extension->id().c_str()); + GURL::Replacements replacements; + replacements.SetQueryStr(query); + url = url.ReplaceComponents(replacements); + ui_test_utils::NavigateToURL(browser(), url); + } +}; + +class SitePerProcessPrintExtensionBrowserTest + : public PrintExtensionBrowserTest { + public: + // content::BrowserTestBase + void SetUpCommandLine(base::CommandLine* command_line) override { + content::IsolateAllSitesForTesting(command_line); + } +}; + // Printing only a selection containing iframes is partially supported. // Iframes aren't currently displayed. This test passes whenever the print // preview is rendered (i.e. no timeout in the test). @@ -493,4 +549,31 @@ EXPECT_TRUE(IsOopifEnabled()); } +// Printing an extension option page. +// The test should not crash or timeout. +IN_PROC_BROWSER_TEST_F(PrintExtensionBrowserTest, PrintOptionPage) { +#if defined(OS_CHROMEOS) + // Mus can not support this test now https://crbug.com/823782. + if (aura::Env::GetInstance()->mode() == aura::Env::Mode::MUS) + return; +#endif + + LoadExtensionAndNavigateToOptionPage(); + PrintAndWaitUntilPreviewIsReady(/*print_only_selection=*/false); +} + +// Printing an extension option page with site per process is enabled. +// The test should not crash or timeout. +IN_PROC_BROWSER_TEST_F(SitePerProcessPrintExtensionBrowserTest, + PrintOptionPage) { +#if defined(OS_CHROMEOS) + // Mus can not support this test now https://crbug.com/823782. + if (aura::Env::GetInstance()->mode() == aura::Env::Mode::MUS) + return; +#endif + + LoadExtensionAndNavigateToOptionPage(); + PrintAndWaitUntilPreviewIsReady(/*print_only_selection=*/false); +} + } // namespace printing
diff --git a/chrome/browser/printing/print_preview_dialog_controller_browsertest.cc b/chrome/browser/printing/print_preview_dialog_controller_browsertest.cc index 9c5cf7b..9315dfa5 100644 --- a/chrome/browser/printing/print_preview_dialog_controller_browsertest.cc +++ b/chrome/browser/printing/print_preview_dialog_controller_browsertest.cc
@@ -7,6 +7,7 @@ #include <memory> #include "base/bind_helpers.h" +#include "base/command_line.h" #include "base/files/file_path.h" #include "base/location.h" #include "base/macros.h" @@ -37,6 +38,7 @@ #include "content/public/test/browser_test_utils.h" #include "ipc/ipc_message_macros.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/ui_base_switches.h" #include "url/gurl.h" #include "url/origin.h" @@ -182,6 +184,11 @@ private: void SetUpOnMainThread() override { +#if defined(OS_MACOSX) + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kDisableModalAnimations); +#endif + WebContents* first_tab = browser()->tab_strip_model()->GetActiveWebContents(); ASSERT_TRUE(first_tab);
diff --git a/chrome/browser/printing/print_view_manager_common.cc b/chrome/browser/printing/print_view_manager_common.cc index 1dae6d3..6301be5 100644 --- a/chrome/browser/printing/print_view_manager_common.cc +++ b/chrome/browser/printing/print_view_manager_common.cc
@@ -49,8 +49,7 @@ contents->GetBrowserContext()); if (guest_view_manager) { guest_view_manager->ForEachGuest( - contents, - base::Bind(&StoreFullPagePlugin, &contents)); + contents, base::BindRepeating(&StoreFullPagePlugin, &contents)); } #endif // BUILDFLAG(ENABLE_EXTENSIONS) return contents;
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc index 55d799b..cd6af5fc 100644 --- a/chrome/browser/profiles/profile_manager.cc +++ b/chrome/browser/profiles/profile_manager.cc
@@ -884,7 +884,6 @@ void ProfileManager::CleanUpEphemeralProfiles() { const std::string last_used_profile = GetLastUsedProfileName(); - bool last_active_profile_deleted = false; base::FilePath new_profile_path; std::vector<base::FilePath> profiles_to_delete; @@ -902,8 +901,11 @@ } } - // If the last active profile was ephemeral, set a new one. - if (last_active_profile_deleted) { + // If the last active profile was ephemeral or all profiles are deleted due to + // ephemeral, set a new one. + if (last_active_profile_deleted || + (entries.size() == profiles_to_delete.size() && + profiles_to_delete.size() > 0)) { if (new_profile_path.empty()) new_profile_path = GenerateNextProfileDirectoryPath();
diff --git a/chrome/browser/profiles/profile_manager_unittest.cc b/chrome/browser/profiles/profile_manager_unittest.cc index 06e957f..f3b12533 100644 --- a/chrome/browser/profiles/profile_manager_unittest.cc +++ b/chrome/browser/profiles/profile_manager_unittest.cc
@@ -1129,6 +1129,32 @@ EXPECT_EQ("Profile 1", local_state->GetString(prefs::kProfileLastUsed)); } +TEST_F(ProfileManagerTest, CleanUpEphemeralProfilesWithGuestLastUsedProfile) { + ProfileManager* profile_manager = g_browser_process->profile_manager(); + ProfileAttributesStorage& storage = + profile_manager->GetProfileAttributesStorage(); + ASSERT_EQ(0u, storage.GetNumberOfProfiles()); + + const std::string profile_name1 = "Homer"; + base::FilePath path1 = + profile_manager->user_data_dir().AppendASCII(profile_name1); + storage.AddProfile(path1, base::UTF8ToUTF16(profile_name1), std::string(), + base::UTF8ToUTF16(profile_name1), 0, std::string()); + storage.GetAllProfilesAttributes()[0u]->SetIsEphemeral(true); + ASSERT_TRUE(base::CreateDirectory(path1)); + ASSERT_EQ(1u, storage.GetNumberOfProfiles()); + + // Set the active profile. + PrefService* local_state = g_browser_process->local_state(); + local_state->SetString(prefs::kProfileLastUsed, std::string("Guest Profile")); + + profile_manager->CleanUpEphemeralProfiles(); + content::RunAllTasksUntilIdle(); + + ASSERT_EQ(0u, storage.GetNumberOfProfiles()); + EXPECT_EQ("Profile 1", local_state->GetString(prefs::kProfileLastUsed)); +} + TEST_F(ProfileManagerTest, ActiveProfileDeleted) { ProfileManager* profile_manager = g_browser_process->profile_manager(); ASSERT_TRUE(profile_manager);
diff --git a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.cc b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.cc index a82bc18..72f2f5f 100644 --- a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.cc +++ b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.cc
@@ -252,6 +252,9 @@ last_auth_error_(GoogleServiceAuthError::NONE) { DCHECK(signin_error_controller_); DCHECK(!account_id_.empty()); +} + +void MutableProfileOAuth2TokenServiceDelegate::AccountStatus::Initialize() { signin_error_controller_->AddProvider(this); } @@ -495,8 +498,7 @@ load_credentials_state_ = LOAD_CREDENTIALS_FINISHED_WITH_NO_TOKEN_FOR_PRIMARY_ACCOUNT; } - refresh_tokens_[loading_primary_account_id_].reset(new AccountStatus( - signin_error_controller_, loading_primary_account_id_, std::string())); + AddAccountStatus(loading_primary_account_id_, std::string()); } // If we don't have a refresh token for a known account, signal an error. @@ -688,8 +690,7 @@ } else { VLOG(1) << "MutablePO2TS::UpdateCredentials; Refresh Token was absent. " << "account_id=" << account_id; - refresh_tokens_[account_id].reset( - new AccountStatus(signin_error_controller_, account_id, refresh_token)); + AddAccountStatus(account_id, refresh_token); } UpdateAuthError(account_id, @@ -719,9 +720,13 @@ VLOG(1) << "MutablePO2TS::RevokeAllCredentials"; CancelWebTokenFetch(); - AccountStatusMap tokens = refresh_tokens_; - for (auto& token : tokens) - RevokeCredentials(token.first); + + // Make a temporary copy of the account ids. + std::vector<std::string> accounts; + for (const auto& token : refresh_tokens_) + accounts.push_back(token.first); + for (const std::string& account : accounts) + RevokeCredentials(account); DCHECK_EQ(0u, refresh_tokens_.size()); @@ -796,3 +801,13 @@ MutableProfileOAuth2TokenServiceDelegate::BackoffEntry() const { return &backoff_entry_; } + +void MutableProfileOAuth2TokenServiceDelegate::AddAccountStatus( + const std::string& account_id, + const std::string& refresh_token) { + DCHECK_EQ(0u, refresh_tokens_.count(account_id)); + AccountStatus* status = + new AccountStatus(signin_error_controller_, account_id, refresh_token); + refresh_tokens_[account_id].reset(status); + status->Initialize(); +}
diff --git a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.h b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.h index 4af0cd2..f6c9c84 100644 --- a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.h +++ b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.h
@@ -92,6 +92,9 @@ const std::string& refresh_token); ~AccountStatus() override; + // Must be called after the account has been added to the AccountStatusMap. + void Initialize(); + const std::string& refresh_token() const { return refresh_token_; } void set_refresh_token(const std::string& token) { refresh_token_ = token; } @@ -166,9 +169,15 @@ std::string GetRefreshToken(const std::string& account_id) const; + // Creates a new AccountStatus and adds it to the AccountStatusMap. + // The account must not be already in the map. + void AddAccountStatus(const std::string& account_id, + const std::string& refresh_token); + // Maps the |account_id| of accounts known to ProfileOAuth2TokenService // to information about the account. - typedef std::map<std::string, linked_ptr<AccountStatus>> AccountStatusMap; + typedef std::map<std::string, std::unique_ptr<AccountStatus>> + AccountStatusMap; // In memory refresh token store mapping account_id to refresh_token. AccountStatusMap refresh_tokens_;
diff --git a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate_unittest.cc b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate_unittest.cc index 6cf87cc..d641892 100644 --- a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate_unittest.cc +++ b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate_unittest.cc
@@ -10,6 +10,7 @@ #include <vector> #include "base/bind.h" +#include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" @@ -1090,3 +1091,41 @@ EXPECT_TRUE( oauth2_service_delegate_->RefreshTokenIsAvailable(secondary_account)); } + +// Regression test for https://crbug.com/823707 +// Checks that OnErrorChanged() is called during UpdateCredentials(), and that +// RefreshTokenIsAvailable() can be used at this time. +TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, OnErrorChanged) { + class ErrorObserver : public SigninErrorController::Observer { + public: + explicit ErrorObserver(MutableProfileOAuth2TokenServiceDelegate* delegate) + : delegate_(delegate) {} + + void OnErrorChanged() override { + error_changed_ = true; + EXPECT_TRUE(delegate_->RefreshTokenIsAvailable("account_id")); + } + + MutableProfileOAuth2TokenServiceDelegate* delegate_; + bool error_changed_ = false; + + DISALLOW_COPY_AND_ASSIGN(ErrorObserver); + }; + + CreateOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled); + + // Start with the SigninErrorController in error state, so that it calls + // OnErrorChanged() from AddProvider(). + oauth2_service_delegate_->UpdateCredentials( + "error_account_id", + MutableProfileOAuth2TokenServiceDelegate::kInvalidRefreshToken); + + ErrorObserver observer(oauth2_service_delegate_.get()); + signin_error_controller_.AddObserver(&observer); + + ASSERT_FALSE(observer.error_changed_); + oauth2_service_delegate_->UpdateCredentials("account_id", "token"); + EXPECT_TRUE(observer.error_changed_); + + signin_error_controller_.RemoveObserver(&observer); +}
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 778b809..b5b537f 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -489,8 +489,6 @@ "cocoa/profiles/avatar_icon_controller.mm", "cocoa/profiles/profile_chooser_bridge_views.h", "cocoa/profiles/profile_chooser_bridge_views.mm", - "cocoa/profiles/profile_chooser_controller.h", - "cocoa/profiles/profile_chooser_controller.mm", "cocoa/profiles/profile_signin_confirmation_dialog_cocoa.h", "cocoa/profiles/profile_signin_confirmation_dialog_cocoa.mm", "cocoa/profiles/profile_signin_confirmation_view_controller.h",
diff --git a/chrome/browser/ui/browser_command_controller_interactive_browsertest.cc b/chrome/browser/ui/browser_command_controller_interactive_browsertest.cc index bdc0ffe..50bde08 100644 --- a/chrome/browser/ui/browser_command_controller_interactive_browsertest.cc +++ b/chrome/browser/ui/browser_command_controller_interactive_browsertest.cc
@@ -495,9 +495,17 @@ ASSERT_NO_FATAL_FAILURE(FinishTestAndVerifyResult()); } +#if defined(OS_MACOSX) +// Triggers a DCHECK in MacViews: http://crbug.com/823478 +#define MAYBE_KeyEventsShouldBeConsumedByWebPageInJsFullscreenExceptForF11 \ + DISABLED_KeyEventsShouldBeConsumedByWebPageInJsFullscreenExceptForF11 +#else +#define MAYBE_KeyEventsShouldBeConsumedByWebPageInJsFullscreenExceptForF11 \ + KeyEventsShouldBeConsumedByWebPageInJsFullscreenExceptForF11 +#endif IN_PROC_BROWSER_TEST_F( BrowserCommandControllerInteractiveTest, - KeyEventsShouldBeConsumedByWebPageInJsFullscreenExceptForF11) { + MAYBE_KeyEventsShouldBeConsumedByWebPageInJsFullscreenExceptForF11) { ASSERT_NO_FATAL_FAILURE(StartFullscreenLockPage()); ASSERT_NO_FATAL_FAILURE(SendJsFullscreenShortcutAndWait());
diff --git a/chrome/browser/ui/cocoa/profiles/avatar_base_controller.h b/chrome/browser/ui/cocoa/profiles/avatar_base_controller.h index 134a19f..5a6f288 100644 --- a/chrome/browser/ui/cocoa/profiles/avatar_base_controller.h +++ b/chrome/browser/ui/cocoa/profiles/avatar_base_controller.h
@@ -15,7 +15,6 @@ #include "chrome/browser/ui/avatar_button_error_controller_delegate.h" #include "chrome/browser/ui/browser_window.h" #import "chrome/browser/ui/cocoa/has_weak_browser_pointer.h" -#include "components/signin/core/browser/signin_header_helper.h" @class BaseBubbleController; class Browser; @@ -34,9 +33,6 @@ // Observer that listens for updates to the ProfileAttributesStorage as well // as AvatarButtonErrorController. std::unique_ptr<ProfileUpdateObserver> profileObserver_; - - // The menu controller, if the menu is open. - BaseBubbleController* menuController_; } // The avatar button view. @@ -64,10 +60,6 @@ @end -@interface AvatarBaseController (ExposedForTesting) -- (BaseBubbleController*)menuController; -@end - class ProfileUpdateObserver : public ProfileAttributesStorage::Observer, public AvatarButtonErrorControllerDelegate { public:
diff --git a/chrome/browser/ui/cocoa/profiles/avatar_base_controller.mm b/chrome/browser/ui/cocoa/profiles/avatar_base_controller.mm index 6750f55..971d176 100644 --- a/chrome/browser/ui/cocoa/profiles/avatar_base_controller.mm +++ b/chrome/browser/ui/cocoa/profiles/avatar_base_controller.mm
@@ -4,41 +4,23 @@ #import "chrome/browser/ui/cocoa/profiles/avatar_base_controller.h" -#include "base/mac/foundation_util.h" #include "base/macros.h" -#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/profile_avatar_icon_util.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_metrics.h" #include "chrome/browser/profiles/profile_window.h" #include "chrome/browser/profiles/profiles_state.h" -#include "chrome/browser/signin/chrome_signin_helper.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/browser_window.h" -#import "chrome/browser/ui/cocoa/base_bubble_controller.h" #import "chrome/browser/ui/cocoa/browser_window_controller.h" -#include "chrome/browser/ui/cocoa/l10n_util.h" #include "chrome/browser/ui/cocoa/profiles/profile_chooser_bridge_views.h" -#import "chrome/browser/ui/cocoa/profiles/profile_chooser_controller.h" -#include "chrome/common/chrome_features.h" -#include "components/signin/core/browser/profile_management_switches.h" -#include "ui/base/cocoa/cocoa_base_utils.h" - -// Space between the avatar icon and the avatar menu bubble. -const CGFloat kMenuYOffsetAdjust = 1.0; -// Offset needed to align the edge of the avatar bubble with the edge of the -// avatar button. -const CGFloat kMenuXOffsetAdjust = 1.0; @interface AvatarBaseController () { std::unique_ptr<ProfileChooserViewBridge> profileChooserViewObserverBridge_; } -// Called when the avatar bubble will close. -- (void)bubbleWillClose:(NSNotification*)notif; - // Updates the profile name displayed by the avatar button. If |layoutParent| is // yes, then the BrowserWindowController is notified to relayout the subviews, // as the button needs to be repositioned. @@ -117,10 +99,6 @@ } - (void)browserWillBeDestroyed { - [[NSNotificationCenter defaultCenter] - removeObserver:self - name:NSWindowWillCloseNotification - object:[menuController_ window]]; profileChooserViewObserverBridge_.reset(); browser_ = nullptr; } @@ -142,86 +120,12 @@ withMode:(BrowserWindow::AvatarBubbleMode)mode withServiceType:(signin::GAIAServiceType)serviceType fromAccessPoint:(signin_metrics::AccessPoint)accessPoint { - if (base::FeatureList::IsEnabled(features::kViewsProfileChooser)) { - [self showViewsAvatarBubbleAnchoredAt:anchor - withMode:mode - withServiceType:serviceType - fromAccessPoint:accessPoint]; - } else { - [self showCocoaAvatarBubbleAnchoredAt:anchor - withMode:mode - withServiceType:serviceType - fromAccessPoint:accessPoint]; - } -} - -- (void)showViewsAvatarBubbleAnchoredAt:(NSView*)anchor - withMode:(BrowserWindow::AvatarBubbleMode)mode - withServiceType:(signin::GAIAServiceType)serviceType - fromAccessPoint: - (signin_metrics::AccessPoint)accessPoint { profiles::BubbleViewMode bubbleViewMode; profiles::BubbleViewModeFromAvatarBubbleMode(mode, &bubbleViewMode); profileChooserViewObserverBridge_ = ShowProfileChooserViews( self, anchor, accessPoint, browser_, bubbleViewMode); } -- (void)showCocoaAvatarBubbleAnchoredAt:(NSView*)anchor - withMode:(BrowserWindow::AvatarBubbleMode)mode - withServiceType:(signin::GAIAServiceType)serviceType - fromAccessPoint: - (signin_metrics::AccessPoint)accessPoint { - if (menuController_) { - profiles::BubbleViewMode viewMode; - profiles::BubbleViewModeFromAvatarBubbleMode(mode, &viewMode); - if (viewMode == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER) { - ProfileChooserController* profileChooserController = - base::mac::ObjCCastStrict<ProfileChooserController>(menuController_); - [profileChooserController showMenuWithViewMode:viewMode]; - } - return; - } - - DCHECK(chrome::IsCommandEnabled(browser_, IDC_SHOW_AVATAR_MENU)); - - BrowserWindowController* bwc = [BrowserWindowController - browserWindowControllerForWindow:browser_->window()->GetNativeWindow()]; - - if (bwc) { - [bwc lockToolbarVisibilityForOwner:self withAnimation:NO]; - } - - // The new avatar bubble does not have an arrow, and it should be anchored - // to the edge of the avatar button. - int anchorX = cocoa_l10n_util::ShouldFlipWindowControlsInRTL() - ? NSMinX([anchor bounds]) + kMenuXOffsetAdjust - : NSMaxX([anchor bounds]) - kMenuXOffsetAdjust; - NSPoint point = NSMakePoint(anchorX, - NSMaxY([anchor bounds]) + kMenuYOffsetAdjust); - point = [anchor convertPoint:point toView:nil]; - point = ui::ConvertPointFromWindowToScreen([anchor window], point); - - // |menuController_| will automatically release itself on close. - profiles::BubbleViewMode viewMode; - profiles::BubbleViewModeFromAvatarBubbleMode(mode, &viewMode); - - menuController_ = - [[ProfileChooserController alloc] initWithBrowser:browser_ - anchoredAt:point - viewMode:viewMode - serviceType:serviceType - accessPoint:accessPoint]; - - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(bubbleWillClose:) - name:NSWindowWillCloseNotification - object:[menuController_ window]]; - [menuController_ showWindow:self]; - - ProfileMetrics::LogProfileOpenMethod(ProfileMetrics::ICON_AVATAR_BUBBLE); -} - // Shows the avatar bubble. - (void)buttonClicked:(id)sender { [self showAvatarBubbleAnchoredAt:button_ @@ -234,15 +138,9 @@ - (void)bubbleWillClose { BrowserWindowController* bwc = [BrowserWindowController browserWindowControllerForWindow:browser_->window()->GetNativeWindow()]; - if (bwc) { + if (bwc) [bwc releaseToolbarVisibilityForOwner:self withAnimation:YES]; - } profileChooserViewObserverBridge_.reset(); - menuController_ = nil; -} - -- (void)bubbleWillClose:(NSNotification*)notif { - [self bubbleWillClose]; } - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent { @@ -253,8 +151,7 @@ } - (BOOL)isMenuOpened { - return profileChooserViewObserverBridge_.get() != nullptr || - [[menuController_ window] isVisible]; + return profileChooserViewObserverBridge_.get() != nullptr; } @end
diff --git a/chrome/browser/ui/cocoa/profiles/profile_chooser_controller.h b/chrome/browser/ui/cocoa/profiles/profile_chooser_controller.h deleted file mode 100644 index bfd0bed..0000000 --- a/chrome/browser/ui/cocoa/profiles/profile_chooser_controller.h +++ /dev/null
@@ -1,139 +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 CHROME_BROWSER_UI_COCOA_PROFILES_PROFILE_CHOOSER_CONTROLLER_H_ -#define CHROME_BROWSER_UI_COCOA_PROFILES_PROFILE_CHOOSER_CONTROLLER_H_ - -#import <Cocoa/Cocoa.h> - -#include <map> -#include <memory> -#include <string> - -#include "chrome/browser/profiles/profile_metrics.h" -#import "chrome/browser/ui/cocoa/base_bubble_controller.h" -#include "chrome/browser/ui/profile_chooser_constants.h" -#include "components/signin/core/browser/signin_header_helper.h" - -class AvatarMenu; -class ActiveProfileObserverBridge; -class Browser; - -namespace content { -class WebContents; -} - -namespace signin_metrics { -enum class AccessPoint; -} - -class GaiaWebContentsDelegate; - -// This window controller manages the bubble that displays a "menu" of profiles. -// It is brought open by clicking on the avatar icon in the window frame. -@interface ProfileChooserController : BaseBubbleController<NSTextViewDelegate> { - @private - // The menu that contains the data from the backend. - std::unique_ptr<AvatarMenu> avatarMenu_; - - // An observer to be notified when the OAuth2 tokens change or the avatar - // menu model updates for the active profile. - std::unique_ptr<ActiveProfileObserverBridge> observer_; - - // The browser that launched the bubble. Not owned. - Browser* browser_; - - // The id for the account that the user has requested to remove from the - // current profile. It is set in |showAccountRemovalView| and used in - // |removeAccount|. - std::string accountIdToRemove_; - NSButton *firstProfileView_; - - // Active view mode. - profiles::BubbleViewMode viewMode_; - - // List of the full, un-elided accounts for the active profile. The keys are - // generated used to tag the UI buttons, and the values are the original - // emails displayed by the buttons. - std::map<int, std::string> currentProfileAccounts_; - - // Web contents used by the inline signin view. - std::unique_ptr<content::WebContents> webContents_; - std::unique_ptr<GaiaWebContentsDelegate> webContentsDelegate_; - - // Whether the bubble is displayed for an active guest profile. - BOOL isGuestSession_; - - // The GAIA service type that caused this menu to open. - signin::GAIAServiceType serviceType_; - - // The current access point of sign in. - signin_metrics::AccessPoint accessPoint_; -} - -- (id)initWithBrowser:(Browser*)browser - anchoredAt:(NSPoint)point - viewMode:(profiles::BubbleViewMode)viewMode - serviceType:(signin::GAIAServiceType)GAIAServiceType - accessPoint:(signin_metrics::AccessPoint)accessPoint; - -// Creates all the subviews of the avatar bubble for |viewToDisplay|, and shows -// the menu. -- (void)showMenuWithViewMode:(profiles::BubbleViewMode)viewToDisplay; - -// Returns the view currently displayed by the bubble. -- (profiles::BubbleViewMode)viewMode; - -// Switches to a given profile. |sender| is an ProfileChooserItemController. -- (IBAction)switchToProfile:(id)sender; - -// Shows the User Manager. -- (IBAction)showUserManager:(id)sender; - -// Closes all guest browsers and shows the User Manager. -- (IBAction)exitGuest:(id)sender; - -// Shows the account management view. -- (IBAction)showAccountManagement:(id)sender; - -// Hides the account management view and shows the default view. -- (IBAction)hideAccountManagement:(id)sender; - -// Locks the active profile. -- (IBAction)lockProfile:(id)sender; - -// Shows the inline signin page. -- (IBAction)showInlineSigninPage:(id)sender; - -// Adds an account to the active profile. -- (IBAction)addAccount:(id)sender; - -// Shows the account removal view to confirm removing the currently selected -// account from the active profile if possible. -- (IBAction)showAccountRemovalView:(id)sender; - -// Shows the account reauthentication view to re-sign in the currently selected -// account from the active profile if possible. -- (IBAction)showAccountReauthenticationView:(id)sender; - -// Removes the current account |accountIdToRemove_|. -- (IBAction)removeAccount:(id)sender; - -// Reset the WebContents used by the Gaia embedded view. -- (void)cleanUpEmbeddedViewContents; - -// Clean-up done after an action was performed in the ProfileChooser. -- (void)postActionPerformed:(ProfileMetrics::ProfileDesktopMenu)action; -@end - -// Testing API ///////////////////////////////////////////////////////////////// - -@interface ProfileChooserController (ExposedForTesting) -- (id)initWithBrowser:(Browser*)browser - anchoredAt:(NSPoint)point - viewMode:(profiles::BubbleViewMode)viewMode - serviceType:(signin::GAIAServiceType)GAIAServiceType; -@end - -#endif // CHROME_BROWSER_UI_COCOA_PROFILES_PROFILE_CHOOSER_CONTROLLER_H_
diff --git a/chrome/browser/ui/cocoa/profiles/profile_chooser_controller.mm b/chrome/browser/ui/cocoa/profiles/profile_chooser_controller.mm deleted file mode 100644 index 23c2dcd..0000000 --- a/chrome/browser/ui/cocoa/profiles/profile_chooser_controller.mm +++ /dev/null
@@ -1,1979 +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. - -#import "chrome/browser/ui/cocoa/profiles/profile_chooser_controller.h" - -#import <Carbon/Carbon.h> // kVK_Return. -#import <Cocoa/Cocoa.h> -#include <stddef.h> - -#include "base/mac/bundle_locations.h" -#include "base/macros.h" -#include "base/metrics/user_metrics.h" -#include "base/scoped_observer.h" -#include "base/strings/string_util.h" -#include "base/strings/sys_string_conversions.h" -#include "base/strings/utf_string_conversions.h" -#include "chrome/app/chrome_command_ids.h" -#include "chrome/app/vector_icons/vector_icons.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/lifetime/application_lifetime.h" -#include "chrome/browser/prefs/incognito_mode_prefs.h" -#include "chrome/browser/profiles/avatar_menu.h" -#include "chrome/browser/profiles/avatar_menu_observer.h" -#include "chrome/browser/profiles/profile_avatar_icon_util.h" -#include "chrome/browser/profiles/profile_manager.h" -#include "chrome/browser/profiles/profile_metrics.h" -#include "chrome/browser/profiles/profile_window.h" -#include "chrome/browser/profiles/profiles_state.h" -#include "chrome/browser/signin/chrome_signin_helper.h" -#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" -#include "chrome/browser/signin/signin_error_controller_factory.h" -#include "chrome/browser/signin/signin_manager_factory.h" -#include "chrome/browser/signin/signin_promo.h" -#include "chrome/browser/signin/signin_ui_util.h" -#include "chrome/browser/sync/profile_sync_service_factory.h" -#include "chrome/browser/sync/sync_ui_util.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_list.h" -#include "chrome/browser/ui/browser_list_observer.h" -#include "chrome/browser/ui/browser_window.h" -#include "chrome/browser/ui/chrome_pages.h" -#import "chrome/browser/ui/cocoa/browser_window_utils.h" -#include "chrome/browser/ui/cocoa/chrome_style.h" -#import "chrome/browser/ui/cocoa/info_bubble_view.h" -#import "chrome/browser/ui/cocoa/info_bubble_window.h" -#include "chrome/browser/ui/cocoa/l10n_util.h" -#include "chrome/browser/ui/cocoa/profiles/signin_view_controller_delegate_mac.h" -#import "chrome/browser/ui/cocoa/profiles/user_manager_mac.h" -#include "chrome/browser/ui/profile_chooser_constants.h" -#include "chrome/browser/ui/singleton_tabs.h" -#include "chrome/browser/ui/user_manager.h" -#include "chrome/browser/ui/webui/signin/login_ui_service.h" -#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h" -#include "chrome/common/pref_names.h" -#include "chrome/common/url_constants.h" -#include "chrome/grit/chromium_strings.h" -#include "chrome/grit/generated_resources.h" -#include "chrome/grit/theme_resources.h" -#include "components/browser_sync/profile_sync_service.h" -#include "components/prefs/pref_service.h" -#include "components/signin/core/browser/profile_management_switches.h" -#include "components/signin/core/browser/profile_oauth2_token_service.h" -#include "components/signin/core/browser/signin_manager.h" -#include "components/signin/core/browser/signin_metrics.h" -#include "components/vector_icons/vector_icons.h" -#include "content/public/browser/native_web_keyboard_event.h" -#include "content/public/browser/render_widget_host_view.h" -#include "content/public/browser/web_contents.h" -#include "google_apis/gaia/oauth2_token_service.h" -#include "skia/ext/skia_utils_mac.h" -#import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h" -#import "ui/base/cocoa/cocoa_base_utils.h" -#import "ui/base/cocoa/controls/blue_label_button.h" -#import "ui/base/cocoa/controls/hyperlink_button_cell.h" -#import "ui/base/cocoa/controls/hyperlink_text_view.h" -#import "ui/base/cocoa/hover_image_button.h" -#include "ui/base/cocoa/window_size_constants.h" -#include "ui/base/l10n/l10n_util.h" -#include "ui/base/l10n/l10n_util_mac.h" -#include "ui/base/resource/resource_bundle.h" -#include "ui/events/keycodes/keyboard_codes.h" -#include "ui/gfx/color_palette.h" -#include "ui/gfx/image/image.h" -#include "ui/gfx/image/image_skia_util_mac.h" -#include "ui/gfx/paint_vector_icon.h" -#include "ui/gfx/text_elider.h" -#include "ui/native_theme/common_theme.h" -#include "ui/native_theme/native_theme.h" - -namespace { - -// Constants taken from the Windows/Views implementation at: -// chrome/browser/ui/views/profile_chooser_view.cc -const int kMdImageSide = 40; - -const CGFloat kFixedMenuWidth = 240.0; -const int kIconImageSide = 18; -const CGFloat kVerticalSpacing = 16.0; -const CGFloat kSmallVerticalSpacing = 10.0; -const CGFloat kRelatedControllVerticalSpacing = 8.0; -const CGFloat kHorizontalSpacing = 16.0; -const CGFloat kTitleFontSize = 15.0; -const CGFloat kTextFontSize = 12.0; -const CGFloat kProfileButtonHeight = 30; -const int kBlueButtonHeight = 30; -const CGFloat kFocusRingLineWidth = 2; - -// Fixed size for embedded sign in pages as defined in Gaia. -const CGFloat kFixedGaiaViewWidth = 360; - -// Fixed size for the account removal view. -const CGFloat kFixedAccountRemovalViewWidth = 280; - -// The tag number for the primary account. -const int kPrimaryProfileTag = -1; - -NSImage* CreateProfileImage(const gfx::Image& icon, - int imageSize, - profiles::AvatarShape shape) { - return (profiles::GetSizedAvatarIcon(icon, true /* image is a square */, - imageSize, imageSize, shape)) - .ToNSImage(); -} - -// Updates the window size and position. -void SetWindowSize(NSWindow* window, NSSize size) { - NSRect frame = [window frame]; - frame.origin.x += frame.size.width - size.width; - frame.origin.y += frame.size.height - size.height; - frame.size = size; - [window setFrame:frame display:YES]; -} - -NSString* ElideEmail(const std::string& email, CGFloat width) { - const base::string16 elidedEmail = - gfx::ElideText(base::UTF8ToUTF16(email), gfx::FontList(), width, - gfx::ELIDE_EMAIL, gfx::Typesetter::BROWSER); - return base::SysUTF16ToNSString(elidedEmail); -} - -NSString* ElideMessage(const base::string16& message, CGFloat width) { - return base::SysUTF16ToNSString(gfx::ElideText(message, gfx::FontList(), - width, gfx::ELIDE_TAIL, - gfx::Typesetter::BROWSER)); -} - -// Builds a label with the given |title| anchored at |frame_origin|. Sets the -// text color to |text_color| if not null. -NSTextField* BuildLabel(NSString* title, - NSPoint frame_origin, - NSColor* text_color) { - base::scoped_nsobject<NSTextField> label( - [[NSTextField alloc] initWithFrame:NSZeroRect]); - [label setStringValue:title]; - [label setEditable:NO]; - [label setAlignment:NSNaturalTextAlignment]; - [label setBezeled:NO]; - [label setFont:[NSFont labelFontOfSize:kTextFontSize]]; - [label setDrawsBackground:NO]; - [label setFrameOrigin:frame_origin]; - [label sizeToFit]; - - if (text_color) - [[label cell] setTextColor:text_color]; - - return label.autorelease(); -} - -// Builds an NSTextView that has the contents set to the specified |message|, -// with a non-underlined |link| inserted at |link_offset|. The view is anchored -// at the specified |frame_origin| and has a fixed |frame_width|. -NSTextView* BuildFixedWidthTextViewWithLink( - id<NSTextViewDelegate> delegate, - NSString* message, - NSString* link, - int link_offset, - NSPoint frame_origin, - CGFloat frame_width) { - base::scoped_nsobject<HyperlinkTextView> text_view( - [[HyperlinkTextView alloc] initWithFrame:NSZeroRect]); - NSColor* link_color = skia::SkColorToCalibratedNSColor( - chrome_style::GetLinkColor()); - NSMutableString* finalMessage = - [NSMutableString stringWithFormat:@"%@\n", message]; - [finalMessage insertString:link atIndex:link_offset]; - // Adds a padding row at the bottom, because |boundingRectWithSize| below cuts - // off the last row sometimes. - [text_view setMessage:finalMessage - withFont:[NSFont labelFontOfSize:kTextFontSize] - messageColor:[NSColor blackColor]]; - [text_view addLinkRange:NSMakeRange(link_offset, [link length]) - withURL:nil - linkColor:link_color]; - - // Removes the underlining from the link. - [text_view setLinkTextAttributes:nil]; - NSTextStorage* text_storage = [text_view textStorage]; - NSRange link_range = NSMakeRange(link_offset, [link length]); - [text_storage addAttribute:NSUnderlineStyleAttributeName - value:[NSNumber numberWithInt:NSUnderlineStyleNone] - range:link_range]; - - NSRect frame = [[text_view attributedString] - boundingRectWithSize:NSMakeSize(frame_width, 0) - options:NSStringDrawingUsesLineFragmentOrigin]; - frame.origin = frame_origin; - [text_view setFrame:frame]; - [text_view setDelegate:delegate]; - return text_view.autorelease(); -} - -// Returns the native dialog background color. -NSColor* GetDialogBackgroundColor() { - return skia::SkColorToCalibratedNSColor( - ui::NativeTheme::GetInstanceForNativeUi()->GetSystemColor( - ui::NativeTheme::kColorId_DialogBackground)); -} - -// Builds a title card with one back button right aligned and one label center -// aligned. -NSView* BuildTitleCard(NSRect frame_rect, - const base::string16& message, - id back_button_target, - SEL back_button_action) { - base::scoped_nsobject<NSView> container( - [[NSView alloc] initWithFrame:frame_rect]); - - base::scoped_nsobject<HoverImageButton> button( - [[HoverImageButton alloc] initWithFrame:frame_rect]); - [button setBordered:NO]; - ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); - [button setDefaultImage:rb->GetNativeImageNamed(IDR_BACK).ToNSImage()]; - [button setHoverImage:rb->GetNativeImageNamed(IDR_BACK_H).ToNSImage()]; - [button setPressedImage:rb->GetNativeImageNamed(IDR_BACK_P).ToNSImage()]; - [button setTarget:back_button_target]; - [button setAction:back_button_action]; - [button setFrameSize:NSMakeSize(kProfileButtonHeight, kProfileButtonHeight)]; - [button setFrameOrigin:NSMakePoint(kHorizontalSpacing, 0)]; - - CGFloat max_label_width = frame_rect.size.width - - (kHorizontalSpacing * 2 + kProfileButtonHeight) * 2; - NSTextField* title_label = BuildLabel( - ElideMessage(message, max_label_width), - NSZeroPoint, nil); - [title_label setAlignment:NSCenterTextAlignment]; - [title_label setFont:[NSFont labelFontOfSize:kTitleFontSize]]; - [title_label sizeToFit]; - CGFloat x_offset = (frame_rect.size.width - NSWidth([title_label frame])) / 2; - CGFloat y_offset = - (NSHeight([button frame]) - NSHeight([title_label frame])) / 2; - [title_label setFrameOrigin:NSMakePoint(x_offset, y_offset)]; - - [container addSubview:button]; - [container addSubview:title_label]; - CGFloat height = std::max(NSMaxY([title_label frame]), - NSMaxY([button frame])) + kVerticalSpacing; - [container setFrameSize:NSMakeSize(NSWidth([container frame]), height)]; - - return container.autorelease(); -} - -bool HasAuthError(Profile* profile) { - const SigninErrorController* error_controller = - SigninErrorControllerFactory::GetForProfile(profile); - return error_controller && error_controller->HasError(); -} - -std::string GetAuthErrorAccountId(Profile* profile) { - const SigninErrorController* error_controller = - SigninErrorControllerFactory::GetForProfile(profile); - if (!error_controller) - return std::string(); - - return error_controller->error_account_id(); -} - -} // namespace - -// Custom WebContentsDelegate that allows handling of hotkeys and suppresses -// the context menu. -class GaiaWebContentsDelegate : public content::WebContentsDelegate { - public: - GaiaWebContentsDelegate() {} - ~GaiaWebContentsDelegate() override {} - - private: - // content::WebContentsDelegate: - bool HandleContextMenu(const content::ContextMenuParams& params) override; - void HandleKeyboardEvent( - content::WebContents* source, - const content::NativeWebKeyboardEvent& event) override; - - DISALLOW_COPY_AND_ASSIGN(GaiaWebContentsDelegate); -}; - -bool GaiaWebContentsDelegate::HandleContextMenu( - const content::ContextMenuParams& params) { - // Suppresses the context menu because some features, such as inspecting - // elements, are not appropriate in a bubble. - return true; -} - -void GaiaWebContentsDelegate::HandleKeyboardEvent( - content::WebContents* source, - const content::NativeWebKeyboardEvent& event) { - if (![BrowserWindowUtils shouldHandleKeyboardEvent:event]) - return; - - int chrome_command_id = [BrowserWindowUtils getCommandId:event]; - - bool is_text_editing_command = [BrowserWindowUtils isTextEditingEvent:event]; - - // TODO(guohui): maybe should add an accelerator for the back button. - if (chrome_command_id == IDC_CLOSE_WINDOW || chrome_command_id == IDC_EXIT || - is_text_editing_command) { - [[NSApp mainMenu] performKeyEquivalent:event.os_event]; - } -} - -// Class that listens to changes to the OAuth2Tokens for the active profile, -// changes to the avatar menu model or browser close notifications. -class ActiveProfileObserverBridge : public AvatarMenuObserver, - public BrowserListObserver, - public OAuth2TokenService::Observer { - public: - ActiveProfileObserverBridge(ProfileChooserController* controller, - Browser* browser) - : controller_(controller), - browser_(browser), - browser_list_observer_(this), - token_observer_registered_(false) { - browser_list_observer_.Add(BrowserList::GetInstance()); - if (!browser_->profile()->IsGuestSession()) - AddTokenServiceObserver(); - } - - ~ActiveProfileObserverBridge() override { RemoveTokenServiceObserver(); } - - private: - void AddTokenServiceObserver() { - ProfileOAuth2TokenService* oauth2_token_service = - ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile()); - DCHECK(oauth2_token_service); - oauth2_token_service->AddObserver(this); - token_observer_registered_ = true; - } - - void RemoveTokenServiceObserver() { - if (!token_observer_registered_) - return; - DCHECK(browser_->profile()); - ProfileOAuth2TokenService* oauth2_token_service = - ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile()); - DCHECK(oauth2_token_service); - oauth2_token_service->RemoveObserver(this); - token_observer_registered_ = false; - } - - // OAuth2TokenService::Observer: - void OnRefreshTokenAvailable(const std::string& account_id) override { - // Tokens can only be added by adding an account through the inline flow, - // which is started from the account management view. Refresh it to show the - // update. - profiles::BubbleViewMode viewMode = [controller_ viewMode]; - if (viewMode == profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT || - viewMode == profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT || - viewMode == profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH) { - [controller_ showMenuWithViewMode: - signin::IsAccountConsistencyMirrorEnabled() - ? profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT - : profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER]; - } - } - - void OnRefreshTokenRevoked(const std::string& account_id) override { - // Tokens can only be removed from the account management view. Refresh it - // to show the update. - if ([controller_ viewMode] == profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT) - [controller_ - showMenuWithViewMode:profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT]; - } - - // AvatarMenuObserver: - void OnAvatarMenuChanged(AvatarMenu* avatar_menu) override { - profiles::BubbleViewMode viewMode = [controller_ viewMode]; - if (viewMode == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER || - viewMode == profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT) { - [controller_ showMenuWithViewMode:viewMode]; - } - } - - // BrowserListObserver: - void OnBrowserClosing(Browser* browser) override { - if (browser_ == browser) { - RemoveTokenServiceObserver(); - // Clean up the bubble's WebContents (used by the Gaia embedded view), to - // make sure the guest profile doesn't have any dangling host renderers. - // This can happen if Chrome is quit using Command-Q while the bubble is - // still open, which won't give the bubble a chance to be closed and - // clean up the WebContents itself. - [controller_ cleanUpEmbeddedViewContents]; - } - } - - ProfileChooserController* controller_; // Weak; owns this. - Browser* browser_; // Weak. - ScopedObserver<BrowserList, BrowserListObserver> browser_list_observer_; - - // The observer can be removed both when closing the browser, and by just - // closing the avatar bubble. However, in the case of closing the browser, - // the avatar bubble will also be closed afterwards, resulting in a second - // attempt to remove the observer. This ensures the observer is only - // removed once. - bool token_observer_registered_; - - DISALLOW_COPY_AND_ASSIGN(ActiveProfileObserverBridge); -}; - -// Custom button cell that adds a leading padding before the button image, and -// a custom spacing between the button image and title. -@interface CustomPaddingImageButtonCell : NSButtonCell { - @private - // Padding added to the leading margin of the button. - int leadingMarginSpacing_; - // Spacing between the cell image and title. - int imageTitleSpacing_; - // Padding added to the traling margin of the button. - int trailingMarginSpacing_; -} - -- (id)initWithLeadingMarginSpacing:(int)leadingMarginSpacing - imageTitleSpacing:(int)imageTitleSpacing; - -- (void)setTrailingMarginSpacing:(int)trailingMarginSpacing; -@end - -@implementation CustomPaddingImageButtonCell -- (id)initWithLeadingMarginSpacing:(int)leadingMarginSpacing - imageTitleSpacing:(int)imageTitleSpacing { - if ((self = [super init])) { - leadingMarginSpacing_ = leadingMarginSpacing; - imageTitleSpacing_ = imageTitleSpacing; - } - return self; -} - -- (void)setTrailingMarginSpacing:(int)trailingMarginSpacing { - trailingMarginSpacing_ = trailingMarginSpacing; -} - -- (NSRect)drawTitle:(NSAttributedString*)title - withFrame:(NSRect)frame - inView:(NSView*)controlView { - NSRect marginRect; - NSDivideRect(frame, &marginRect, &frame, leadingMarginSpacing_, - cocoa_l10n_util::LeadingEdge()); - // The title frame origin isn't aware of the leading margin spacing added - // in -drawImage, so it must be added when drawing the title as well. - NSDivideRect(frame, &marginRect, &frame, imageTitleSpacing_, - cocoa_l10n_util::LeadingEdge()); - NSDivideRect(frame, &marginRect, &frame, trailingMarginSpacing_, - cocoa_l10n_util::TrailingEdge()); - - return [super drawTitle:title withFrame:frame inView:controlView]; -} - -- (void)drawImage:(NSImage*)image - withFrame:(NSRect)frame - inView:(NSView*)controlView { - if (cocoa_l10n_util::ShouldDoExperimentalRTLLayout()) - frame.origin.x -= leadingMarginSpacing_; - else - frame.origin.x = leadingMarginSpacing_; - [super drawImage:image withFrame:frame inView:controlView]; -} - -- (NSSize)cellSize { - NSSize buttonSize = [super cellSize]; - buttonSize.width += leadingMarginSpacing_; - buttonSize.width += imageTitleSpacing_; - return buttonSize; -} - -- (NSFocusRingType)focusRingType { - // This is taken care of by the custom drawing code. - return NSFocusRingTypeNone; -} - -- (void)drawWithFrame:(NSRect)frame inView:(NSView *)controlView { - [super drawInteriorWithFrame:frame inView:controlView]; - - // Focus ring. - if ([self showsFirstResponder]) { - NSRect focusRingRect = - NSInsetRect(frame, kFocusRingLineWidth, kFocusRingLineWidth); - // TODO(noms): When we are targetting 10.7, we should change this to use - // -drawFocusRingMaskWithFrame instead. - [[[NSColor keyboardFocusIndicatorColor] colorWithAlphaComponent:1] set]; - NSBezierPath* path = [NSBezierPath bezierPathWithRect:focusRingRect]; - [path setLineWidth:kFocusRingLineWidth]; - [path stroke]; - } -} - -@end - -// A custom image view that has a transparent backround. -@interface TransparentBackgroundImageView : NSImageView -@end - -@implementation TransparentBackgroundImageView -- (void)drawRect:(NSRect)dirtyRect { - NSColor* backgroundColor = [NSColor colorWithCalibratedWhite:1 alpha:0.6f]; - [backgroundColor setFill]; - NSRectFillUsingOperation(dirtyRect, NSCompositeSourceAtop); - [super drawRect:dirtyRect]; -} -@end - -@interface CustomCircleImageCell : NSButtonCell -@end - -@implementation CustomCircleImageCell -- (void)drawWithFrame:(NSRect)frame inView:(NSView *)controlView { - // Display everything as a circle that spans the entire control. - NSBezierPath* path = [NSBezierPath bezierPathWithOvalInRect:frame]; - [path addClip]; - [super drawImage:[self image] withFrame:frame inView:controlView]; -} -@end - -// A custom view with a filled circular background. -@interface BackgroundCircleView : NSView { - @private - base::scoped_nsobject<NSColor> fillColor_; -} -@end - -@implementation BackgroundCircleView -- (id)initWithFrame:(NSRect)frameRect withFillColor:(NSColor*)fillColor { - if ((self = [super initWithFrame:frameRect])) - fillColor_.reset([fillColor retain]); - return self; -} - -- (void)drawRect:(NSRect)dirtyRect { - [fillColor_ setFill]; - NSBezierPath* circlePath = [NSBezierPath bezierPath]; - [circlePath appendBezierPathWithOvalInRect:[self bounds]]; - [circlePath fill]; - - [super drawRect:dirtyRect]; -} -@end - -// A custom button that allows for setting a background color when hovered over. -@interface BackgroundColorHoverButton : HoverImageButton { - @private - base::scoped_nsobject<NSColor> backgroundColor_; - base::scoped_nsobject<NSColor> hoverColor_; -} - -- (void)setTrailingMarginSpacing:(int)trailingMarginSpacing; -@end - -@implementation BackgroundColorHoverButton - -- (id)initWithFrame:(NSRect)frameRect - imageTitleSpacing:(int)imageTitleSpacing - backgroundColor:(NSColor*)backgroundColor { - if ((self = [super initWithFrame:frameRect])) { - backgroundColor_.reset([backgroundColor retain]); - hoverColor_.reset([skia::SkColorToSRGBNSColor(profiles::kHoverColor) - retain]); - - [self setBordered:NO]; - [self setFont:[NSFont labelFontOfSize:kTextFontSize]]; - [self setButtonType:NSMomentaryChangeButton]; - - base::scoped_nsobject<CustomPaddingImageButtonCell> cell( - [[CustomPaddingImageButtonCell alloc] - initWithLeadingMarginSpacing:kHorizontalSpacing - imageTitleSpacing:imageTitleSpacing]); - [cell setLineBreakMode:NSLineBreakByTruncatingTail]; - [cell setHighlightsBy:NSNoCellMask]; - [self setCell:cell.get()]; - } - return self; -} - -- (void)setTrailingMarginSpacing:(int)trailingMarginSpacing { - [[self cell] setTrailingMarginSpacing:trailingMarginSpacing]; -} - -- (void)drawRect:(NSRect)dirtyRect { - if ([self isEnabled]) { - bool isHighlighted = ([self hoverState] != kHoverStateNone); - NSColor* backgroundColor = isHighlighted ? hoverColor_ : backgroundColor_; - [[self cell] setBackgroundColor:backgroundColor]; - } - [super drawRect:dirtyRect]; -} - --(void)keyDown:(NSEvent*)event { - // Since there is no default button in the bubble, it is safe to activate - // all buttons on Enter as well, and be consistent with the Windows - // implementation. - if ([event keyCode] == kVK_Return || [event keyCode] == kVK_ANSI_KeypadEnter) - [self performClick:self]; - else - [super keyDown:event]; -} - -- (BOOL)canBecomeKeyView { - return [self isEnabled] ? YES : NO; -} - -@end - -// A custom view with the given background color. -@interface BackgroundColorView : NSView { - @private - base::scoped_nsobject<NSColor> backgroundColor_; -} -@end - -@implementation BackgroundColorView -- (id)initWithFrame:(NSRect)frameRect - withColor:(NSColor*)color { - if ((self = [super initWithFrame:frameRect])) - backgroundColor_.reset([color retain]); - return self; -} - -- (void)drawRect:(NSRect)dirtyRect { - [backgroundColor_ setFill]; - NSRectFill(dirtyRect); - [super drawRect:dirtyRect]; -} -@end - -// A custom dummy button that is used to clear focus from the bubble's controls. -@interface DummyWindowFocusButton : NSButton -@end - -@implementation DummyWindowFocusButton -// Ignore accessibility, as this is a placeholder button. -- (BOOL)accessibilityIsIgnored { - return YES; -} - -- (id)accessibilityAttributeValue:(NSString*)attribute { - return nil; -} - -- (BOOL)canBecomeKeyView { - return NO; -} - -@end - -@interface ProfileChooserController () -// Adds an horizontal separator to |container| at |yOffset| and returns the -// yOffset corresponding to after the separator. -- (CGFloat)addSeparatorToContainer:(NSView*)container - atYOffset:(CGFloat)yOffset; - -// Builds the fast user switcher view. This appears as part of the user menu. -// Returns the yOffset corresponding to after the profile switcher buttons. -- (CGFloat)buildFastUserSwitcherViewWithProfiles:(NSArray*)otherProfiles - atYOffset:(CGFloat)yOffset - inContainer:(NSView*)container; - -// Builds the regular profile chooser view. -- (void)buildProfileChooserViewWithProfileView:(NSView*)currentProfileView - syncErrorView:(NSView*)syncErrorView - otherProfiles:(NSArray*)otherProfiles - atYOffset:(CGFloat)yOffset - inContainer:(NSView*)container - showLock:(bool)showLock; - -// Builds the profile chooser view. -- (NSView*)buildProfileChooserView; - -// Builds a header for signin and sync error surfacing on the user menu. -- (NSView*)buildSyncErrorViewIfNeeded; - -// Creates the main profile card for the profile |item| at the top of -// the bubble. -- (NSView*)createCurrentProfileView:(const AvatarMenu::Item&)item; - -// Creates the possible links for the main profile card with profile |item|. -- (NSView*)createCurrentProfileLinksForItem:(const AvatarMenu::Item&)item - rect:(NSRect)rect; - -// Creates the disclaimer text for supervised users, telling them that the -// manager can view their history etc. -- (NSView*)createSupervisedUserDisclaimerView; - -// Creates a main profile card for the guest user. -- (NSView*)createGuestProfileView; - -// Creates an item for the profile |itemIndex| that is used in the fast profile -// switcher view. -- (NSButton*)createOtherProfileView:(int)itemIndex; - -// Creates the following option buttons: lock profile/close all windows, switch -// user/exit guest, and open guest profile. -- (NSView*)createOptionsViewWithFrame:(NSRect)frame showLock:(BOOL)showLock; - -// Creates the account management view for the active profile. -- (NSView*)createCurrentProfileAccountsView:(NSRect)rect; - -// Creates the list of accounts for the active profile. -- (NSView*)createAccountsListWithRect:(NSRect)rect; - -// Creates the Gaia sign-in/add account view. -- (NSView*)buildGaiaEmbeddedView; - -// Creates the account removal view. -- (NSView*)buildAccountRemovalView; - -// Creates a button with |text| and |action|, optionally with an icon given by -// |imageResourceId| or |image|. -- (NSButton*)hoverButtonWithRect:(NSRect)rect - text:(NSString*)text - imageResourceId:(int)imageResourceId - action:(SEL)action; -- (NSButton*)hoverButtonWithRect:(NSRect)rect - text:(NSString*)text - image:(NSImage*)image - action:(SEL)action; -- (BackgroundColorHoverButton*)hoverButtonWithRect:(NSRect)rect - text:(NSString*)text - action:(SEL)action; - -// Creates a generic link button with |title| and an |action| positioned at -// |frameOrigin|. -- (NSButton*)linkButtonWithTitle:(NSString*)title - frameOrigin:(NSPoint)frameOrigin - action:(SEL)action; - -// Creates an email account button with |title| and a remove icon. If -// |reauthRequired| is true, the button also displays a warning icon. |tag| -// indicates which account the button refers to. -- (NSButton*)accountButtonWithRect:(NSRect)rect - accountId:(const std::string&)accountId - tag:(int)tag - reauthRequired:(BOOL)reauthRequired; -@end - -@implementation ProfileChooserController -- (profiles::BubbleViewMode) viewMode { - return viewMode_; -} - -- (void)editProfile:(id)sender { - avatarMenu_->EditProfile(avatarMenu_->GetActiveProfileIndex()); - [self postActionPerformed:ProfileMetrics::PROFILE_DESKTOP_MENU_EDIT_IMAGE]; - [self postActionPerformed:ProfileMetrics::PROFILE_DESKTOP_MENU_EDIT_NAME]; -} - -- (void)switchToProfile:(id)sender { - // Check the event flags to see if a new window should be created. - bool alwaysCreate = - ui::WindowOpenDispositionFromNSEvent([NSApp currentEvent]) == - WindowOpenDisposition::NEW_WINDOW; - avatarMenu_->SwitchToProfile([sender tag], alwaysCreate, - ProfileMetrics::SWITCH_PROFILE_ICON); -} - -- (void)switchToGuest:(id)sender { - PrefService* service = g_browser_process->local_state(); - DCHECK(service); - DCHECK(service->GetBoolean(prefs::kBrowserGuestModeEnabled)); - profiles::SwitchToGuestProfile(ProfileManager::CreateCallback()); -} - -- (void)showUserManager:(id)sender { - UserManager::Show(base::FilePath(), - profiles::USER_MANAGER_SELECT_PROFILE_NO_ACTION); - [self postActionPerformed: - ProfileMetrics::PROFILE_DESKTOP_MENU_OPEN_USER_MANAGER]; -} - -- (void)exitGuest:(id)sender { - DCHECK(browser_->profile()->IsGuestSession()); - UserManager::Show(base::FilePath(), - profiles::USER_MANAGER_SELECT_PROFILE_NO_ACTION); - profiles::CloseGuestProfileWindows(); -} - -- (void)closeAllWindows:(id)sender { - profiles::CloseProfileWindows(browser_->profile()); -} - -- (void)showAccountManagement:(id)sender { - [self showMenuWithViewMode:profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT]; -} - -- (void)hideAccountManagement:(id)sender { - [self showMenuWithViewMode:profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER]; -} - -- (void)lockProfile:(id)sender { - profiles::LockProfile(browser_->profile()); - [self postActionPerformed:ProfileMetrics::PROFILE_DESKTOP_MENU_LOCK]; -} - -- (void)showSigninUIForMode:(profiles::BubbleViewMode)mode { - if (SigninViewController::ShouldShowSigninForMode(mode)) { - browser_->signin_view_controller()->ShowSignin(mode, browser_, - accessPoint_); - } else { - [self showMenuWithViewMode:mode]; - } -} - -- (void)showInlineSigninPage:(id)sender { - [self showSigninUIForMode:profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN]; -} - -- (void)addAccount:(id)sender { - [self showSigninUIForMode:profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT]; - [self postActionPerformed:ProfileMetrics::PROFILE_DESKTOP_MENU_ADD_ACCT]; -} - -- (void)navigateBackFromSigninPage:(id)sender { - std::string primaryAccount = SigninManagerFactory::GetForProfile( - browser_->profile())->GetAuthenticatedAccountId(); - bool hasAccountManagement = - !primaryAccount.empty() && signin::IsAccountConsistencyMirrorEnabled(); - [self showMenuWithViewMode:hasAccountManagement - ? profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT - : profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER]; -} - -- (void)showAccountRemovalView:(id)sender { - DCHECK(!isGuestSession_); - - // Tag is either |kPrimaryProfileTag| for the primary account, or equal to the - // index in |currentProfileAccounts_| for a secondary account. - int tag = [sender tag]; - if (tag == kPrimaryProfileTag) { - accountIdToRemove_ = SigninManagerFactory::GetForProfile( - browser_->profile())->GetAuthenticatedAccountId(); - } else { - DCHECK(base::ContainsKey(currentProfileAccounts_, tag)); - accountIdToRemove_ = currentProfileAccounts_[tag]; - } - - [self showMenuWithViewMode:profiles::BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL]; -} - -- (void)showSignoutView:(id)sender { - chrome::ShowSettingsSubPage(browser_, chrome::kSignOutSubPage); -} - -- (void)showSignoutSigninView:(id)sender { - if (ProfileSyncServiceFactory::GetForProfile(browser_->profile())) - browser_sync::ProfileSyncService::SyncEvent( - browser_sync::ProfileSyncService::STOP_FROM_OPTIONS); - SigninManagerFactory::GetForProfile(browser_->profile()) - ->SignOut(signin_metrics::USER_CLICKED_SIGNOUT_SETTINGS, - signin_metrics::SignoutDelete::IGNORE_METRIC); - [self showSigninUIForMode:profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN]; -} - -- (void)showAccountReauthenticationView:(id)sender { - DCHECK(!isGuestSession_); - [self showSigninUIForMode:profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH]; -} - -- (void)showUpdateChromeView:(id)sender { - chrome::OpenUpdateChromeDialog(browser_); -} - -- (void)showSyncSetupView:(id)sender { - chrome::ShowSettingsSubPage(browser_, chrome::kSyncSetupSubPage); -} - -- (void)removeAccount:(id)sender { - DCHECK(!accountIdToRemove_.empty()); - ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile()) - ->RevokeCredentials(accountIdToRemove_); - [self postActionPerformed:ProfileMetrics::PROFILE_DESKTOP_MENU_REMOVE_ACCT]; - accountIdToRemove_.clear(); - - [self showMenuWithViewMode:profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT]; -} - -- (void)windowWillClose:(NSNotification*)notification { - [super windowWillClose:notification]; -} - -- (void)moveDown:(id)sender { - [[self window] selectNextKeyView:self]; -} - -- (void)moveUp:(id)sender { - [[self window] selectPreviousKeyView:self]; -} - -- (void)cleanUpEmbeddedViewContents { - webContents_.reset(); - webContentsDelegate_.reset(); -} - -- (id)initWithBrowser:(Browser*)browser - anchoredAt:(NSPoint)point - viewMode:(profiles::BubbleViewMode)viewMode - serviceType:(signin::GAIAServiceType)serviceType - accessPoint:(signin_metrics::AccessPoint)accessPoint { - base::scoped_nsobject<InfoBubbleWindow> window([[InfoBubbleWindow alloc] - initWithContentRect:ui::kWindowSizeDeterminedLater - styleMask:NSBorderlessWindowMask - backing:NSBackingStoreBuffered - defer:NO]); - - if ((self = [super initWithWindow:window - parentWindow:browser->window()->GetNativeWindow() - anchoredAt:point])) { - browser_ = browser; - viewMode_ = viewMode; - observer_.reset(new ActiveProfileObserverBridge(self, browser_)); - serviceType_ = serviceType; - accessPoint_ = accessPoint; - - avatarMenu_.reset(new AvatarMenu( - &g_browser_process->profile_manager()->GetProfileAttributesStorage(), - observer_.get(), - browser_)); - avatarMenu_->RebuildMenu(); - - // Guest profiles do not have a token service. - isGuestSession_ = browser_->profile()->IsGuestSession(); - - // If view mode is PROFILE_CHOOSER but there is an auth error, force - // ACCOUNT_MANAGEMENT mode. - if (viewMode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER && - HasAuthError(browser_->profile()) && - signin::IsAccountConsistencyMirrorEnabled() && - avatarMenu_->GetItemAt(avatarMenu_->GetActiveProfileIndex()) - .signed_in) { - viewMode_ = profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT; - } - - [window accessibilitySetOverrideValue: - l10n_util::GetNSString(IDS_PROFILES_NEW_AVATAR_MENU_ACCESSIBLE_NAME) - forAttribute:NSAccessibilityTitleAttribute]; - [window accessibilitySetOverrideValue: - l10n_util::GetNSString(IDS_PROFILES_NEW_AVATAR_MENU_ACCESSIBLE_NAME) - forAttribute:NSAccessibilityHelpAttribute]; - BOOL shouldUseLeadingEdgeForBubble = - cocoa_l10n_util::ShouldDoExperimentalRTLLayout() && - !cocoa_l10n_util::ShouldFlipWindowControlsInRTL(); - [[self bubble] - setAlignment:shouldUseLeadingEdgeForBubble - ? info_bubble::kAlignLeadingEdgeToAnchorEdge - : info_bubble::kAlignTrailingEdgeToAnchorEdge]; - [[self bubble] setArrowLocation:info_bubble::kNoArrow]; - [[self bubble] setBackgroundColor:GetDialogBackgroundColor()]; - [self showMenuWithViewMode:viewMode_]; - } - - return self; -} - -- (void)showMenuWithViewMode:(profiles::BubbleViewMode)viewToDisplay { - if (browser_->profile()->IsSupervised() && - (viewToDisplay == profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT || - viewToDisplay == profiles::BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL)) { - LOG(WARNING) << "Supervised user attempted to add/remove account"; - return; - } - viewMode_ = viewToDisplay; - NSView* contentView = [[self window] contentView]; - [contentView setSubviews:[NSArray array]]; - NSView* subView; - - switch (viewMode_) { - case profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN: - case profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT: - case profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH: - subView = [self buildGaiaEmbeddedView]; - break; - case profiles::BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL: - subView = [self buildAccountRemovalView]; - break; - case profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER: - case profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT: - subView = [self buildProfileChooserView]; - break; - } - - // Add a dummy, empty element so that we don't initially display any - // focus rings. - NSButton* dummyFocusButton = - [[[DummyWindowFocusButton alloc] initWithFrame:NSZeroRect] autorelease]; - [dummyFocusButton setNextKeyView:subView]; - [[self window] makeFirstResponder:dummyFocusButton]; - - [contentView addSubview:subView]; - [contentView addSubview:dummyFocusButton]; - SetWindowSize([self window], - NSMakeSize(NSWidth([subView frame]), NSHeight([subView frame]))); -} - -- (CGFloat)addSeparatorToContainer:(NSView*)container - atYOffset:(CGFloat)yOffset { - NSBox* separator = [self - horizontalSeparatorWithFrame:NSMakeRect(0, yOffset, kFixedMenuWidth, 0)]; - [container addSubview:separator]; - return NSMaxY([separator frame]); -} - -// Builds the fast user switcher view in |container| at |yOffset| and populates -// it with the entries for every profile in |otherProfiles|. Returns the new -// yOffset after adding the elements. -- (CGFloat)buildFastUserSwitcherViewWithProfiles:(NSArray*)otherProfiles - atYOffset:(CGFloat)yOffset - inContainer:(NSView*)container { - // Other profiles switcher. The profiles have already been sorted - // by their y-coordinate, so they can be added in the existing order. - for (NSView* otherProfileView in otherProfiles) { - [otherProfileView setFrameOrigin:NSMakePoint(0, yOffset)]; - [container addSubview:otherProfileView]; - yOffset = NSMaxY([otherProfileView frame]); - } - - [container setFrameSize:NSMakeSize(kFixedMenuWidth, yOffset)]; - return yOffset; -} - -- (void)buildProfileChooserViewWithProfileView:(NSView*)currentProfileView - syncErrorView:(NSView*)syncErrorView - otherProfiles:(NSArray*)otherProfiles - atYOffset:(CGFloat)yOffset - inContainer:(NSView*)container - showLock:(bool)showLock { - yOffset += kRelatedControllVerticalSpacing; - - // Option buttons. - NSRect rect = NSMakeRect(0, yOffset, kFixedMenuWidth, 0); - NSView* optionsView = - [self createOptionsViewWithFrame:rect showLock:showLock]; - [container addSubview:optionsView]; - rect.origin.y = NSMaxY([optionsView frame]); - yOffset = rect.origin.y; - - // Add the fast user switching buttons. - yOffset = [self buildFastUserSwitcherViewWithProfiles:otherProfiles - atYOffset:yOffset - inContainer:container]; - yOffset += kRelatedControllVerticalSpacing; - rect.origin.y = yOffset; - - NSBox* separator = [self horizontalSeparatorWithFrame:rect]; - [container addSubview:separator]; - yOffset = NSMaxY([separator frame]); - - // For supervised users, add the disclaimer text. - if (browser_->profile()->IsSupervised()) { - yOffset += kVerticalSpacing; - NSView* disclaimerContainer = [self createSupervisedUserDisclaimerView]; - [disclaimerContainer setFrameOrigin:NSMakePoint(0, yOffset)]; - [container addSubview:disclaimerContainer]; - yOffset = NSMaxY([disclaimerContainer frame]); - } - - if (viewMode_ == profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT) { - const AvatarMenu::Item& item = - avatarMenu_->GetItemAt(avatarMenu_->GetActiveProfileIndex()); - if (item.signed_in) { - NSView* currentProfileAccountsView = [self - createCurrentProfileAccountsView:NSMakeRect(0, yOffset, - kFixedMenuWidth, 0)]; - [container addSubview:currentProfileAccountsView]; - yOffset = NSMaxY([currentProfileAccountsView frame]); - - yOffset = [self addSeparatorToContainer:container atYOffset:yOffset]; - } else { - // This is the case when the user selects the sign out option in the user - // menu upon encountering unrecoverable errors. Afterwards, the profile - // chooser view is shown instead of the account management view. - viewMode_ = profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER; - } - } - - // Active profile card. - if (currentProfileView) { - const CGFloat verticalSpacing = kRelatedControllVerticalSpacing; - yOffset += verticalSpacing; - [currentProfileView setFrameOrigin:NSMakePoint(0, yOffset)]; - [container addSubview:currentProfileView]; - yOffset = NSMaxY([currentProfileView frame]) + verticalSpacing; - } - - if (syncErrorView) { - yOffset = [self addSeparatorToContainer:container atYOffset:yOffset]; - [syncErrorView setFrameOrigin:NSMakePoint(0, yOffset)]; - [container addSubview:syncErrorView]; - yOffset = NSMaxY([syncErrorView frame]); - } - - [container setFrameSize:NSMakeSize(kFixedMenuWidth, yOffset)]; -} - -- (NSView*)buildProfileChooserView { - base::scoped_nsobject<NSView> container( - [[NSView alloc] initWithFrame:NSZeroRect]); - - NSView* syncErrorView = nil; - NSView* currentProfileView = nil; - base::scoped_nsobject<NSMutableArray> otherProfiles( - [[NSMutableArray alloc] init]); - // Local and guest profiles cannot lock their profile. - bool showLock = false; - - // Loop over the profiles in reverse, so that they are sorted by their - // y-coordinate, and separate them into active and "other" profiles. - for (int i = avatarMenu_->GetNumberOfItems() - 1; i >= 0; --i) { - const AvatarMenu::Item& item = avatarMenu_->GetItemAt(i); - if (item.active) { - syncErrorView = [self buildSyncErrorViewIfNeeded]; - currentProfileView = [self createCurrentProfileView:item]; - showLock = item.signed_in && - profiles::IsLockAvailable(browser_->profile()); - } else { - [otherProfiles addObject:[self createOtherProfileView:i]]; - } - } - firstProfileView_ = [otherProfiles lastObject]; - if (!currentProfileView) // Guest windows don't have an active profile. - currentProfileView = [self createGuestProfileView]; - - // |yOffset| is the next position at which to draw in |container| - // coordinates. Add a pixel offset so that the bottom option buttons don't - // overlap the bubble's rounded corners. - CGFloat yOffset = 1; - - [self buildProfileChooserViewWithProfileView:currentProfileView - syncErrorView:syncErrorView - otherProfiles:otherProfiles.get() - atYOffset:yOffset - inContainer:container - showLock:showLock]; - return container.autorelease(); -} - -- (NSView*)buildSyncErrorViewIfNeeded { - int contentStringId, buttonStringId; - SEL buttonAction; - SigninManagerBase* signinManager = - SigninManagerFactory::GetForProfile(browser_->profile()); - sync_ui_util::AvatarSyncErrorType error = - sync_ui_util::GetMessagesForAvatarSyncError( - browser_->profile(), *signinManager, &contentStringId, - &buttonStringId); - switch (error) { - case sync_ui_util::MANAGED_USER_UNRECOVERABLE_ERROR: - buttonAction = @selector(showSignoutView:); - break; - case sync_ui_util::UNRECOVERABLE_ERROR: - buttonAction = @selector(showSignoutSigninView:); - break; - case sync_ui_util::SUPERVISED_USER_AUTH_ERROR: - buttonAction = nil; - break; - case sync_ui_util::AUTH_ERROR: - buttonAction = @selector(showAccountReauthenticationView:); - break; - case sync_ui_util::UPGRADE_CLIENT_ERROR: - buttonAction = @selector(showUpdateChromeView:); - break; - case sync_ui_util::PASSPHRASE_ERROR: - case sync_ui_util::SETTINGS_UNCONFIRMED_ERROR: - buttonAction = @selector(showSyncSetupView:); - break; - case sync_ui_util::NO_SYNC_ERROR: - return nil; - default: - NOTREACHED(); - } - - base::scoped_nsobject<NSView> container( - [[NSView alloc] initWithFrame:NSMakeRect(0, 0, kFixedMenuWidth, 0)]); - CGFloat iconSize = 20.0; - CGFloat xOffset = kHorizontalSpacing + iconSize + 12.0; - CGFloat availableWidth = kFixedMenuWidth - xOffset - kHorizontalSpacing; - CGFloat yOffset = 16.0; - - // Adds an action button for resolving the error at the bottom. - if (buttonStringId) { - // If the button string is specified, then the button action needs to be - // already initialized for the button to be constructed. - DCHECK(buttonAction); - base::scoped_nsobject<NSButton> resolveErrorButton( - [[BlueLabelButton alloc] initWithFrame:NSZeroRect]); - [resolveErrorButton setTitle:l10n_util::GetNSString(buttonStringId)]; - [resolveErrorButton setTarget:self]; - [resolveErrorButton setAction:buttonAction]; - [resolveErrorButton setAlignment:NSCenterTextAlignment]; - [resolveErrorButton sizeToFit]; - [resolveErrorButton setFrameOrigin:NSMakePoint(xOffset, yOffset + 4.0)]; - [container addSubview:resolveErrorButton]; - yOffset = NSMaxY([resolveErrorButton frame]) + kVerticalSpacing; - } - - // Adds the error message content. - NSTextField* contentLabel = - BuildLabel(l10n_util::GetNSString(contentStringId), - NSMakePoint(xOffset, yOffset), nil); - [contentLabel setFrameSize:NSMakeSize(availableWidth, 0)]; - [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:contentLabel]; - [container addSubview:contentLabel]; - yOffset = NSMaxY([contentLabel frame]) + 4; - - // Adds the title for the error card. - NSTextField* titleLabel = - BuildLabel(l10n_util::GetNSString(IDS_SYNC_ERROR_USER_MENU_TITLE), - NSMakePoint(xOffset, yOffset), - skia::SkColorToCalibratedNSColor(gfx::kGoogleRed700)); - [titleLabel setFrameSize:NSMakeSize(availableWidth, 0)]; - [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:titleLabel]; - [container addSubview:titleLabel]; - yOffset = NSMaxY([titleLabel frame]); - - // Adds the sync problem icon. - base::scoped_nsobject<NSImageView> syncProblemIcon([[NSImageView alloc] - initWithFrame:NSMakeRect(kHorizontalSpacing, yOffset - iconSize, iconSize, - iconSize)]); - [syncProblemIcon - setImage:NSImageFromImageSkia(gfx::CreateVectorIcon( - kSyncProblemIcon, iconSize, gfx::kGoogleRed700))]; - [container addSubview:syncProblemIcon]; - - [container - setFrameSize:NSMakeSize(kFixedMenuWidth, yOffset + kVerticalSpacing)]; - return container.autorelease(); -} - -- (NSView*)createCurrentProfileView:(const AvatarMenu::Item&)item { - base::scoped_nsobject<NSView> container( - [[NSView alloc] initWithFrame:NSZeroRect]); - - BOOL isRTL = cocoa_l10n_util::ShouldDoExperimentalRTLLayout(); - CGFloat xOffset = kHorizontalSpacing; - CGFloat yOffset = 0.0; - CGFloat cardYOffset = kRelatedControllVerticalSpacing; - CGFloat availableTextWidth = - kFixedMenuWidth - 3.0 * kHorizontalSpacing - kMdImageSide; - CGFloat maxAvailableTextWidth = kFixedMenuWidth - kHorizontalSpacing; - - // Profile options. This can be a link to the accounts view, or a "Sign in" - // button for local profiles. - SigninManagerBase* signinManager = SigninManagerFactory::GetForProfile( - browser_->profile()->GetOriginalProfile()); - NSRect profileLinksBound = NSZeroRect; - if (item.signed_in && signin::IsAccountConsistencyMirrorEnabled()) { - profileLinksBound = NSMakeRect(0, 0, kFixedMenuWidth, kVerticalSpacing); - } else if (!item.signed_in && signinManager->IsSigninAllowed()) { - profileLinksBound = NSMakeRect(xOffset, kRelatedControllVerticalSpacing, - maxAvailableTextWidth, kVerticalSpacing); - } - if (!NSIsEmptyRect(profileLinksBound)) { - NSView* linksContainer = - [self createCurrentProfileLinksForItem:item rect:profileLinksBound]; - [container addSubview:linksContainer]; - yOffset = NSMaxY([linksContainer frame]); - } - - // Profile card button that contains the profile icon, name, and username. - const base::string16 profileNameString = - profiles::GetAvatarNameForProfile(browser_->profile()->GetPath()); - NSRect rect = - NSMakeRect(0, yOffset, kFixedMenuWidth, kMdImageSide + kVerticalSpacing); - NSButton* profileCard = - [self hoverButtonWithRect:rect - text:[[NSString alloc] init] - image:CreateProfileImage(item.icon, kMdImageSide, - profiles::SHAPE_CIRCLE) - action:@selector(editProfile:)]; - [[profileCard cell] setImageDimsWhenDisabled:NO]; - if (item.signed_in) { - [[profileCard cell] - accessibilitySetOverrideValue: - l10n_util::GetNSStringF( - IDS_PROFILES_EDIT_SIGNED_IN_PROFILE_ACCESSIBLE_NAME, - profileNameString, item.username) - forAttribute:NSAccessibilityTitleAttribute]; - } else { - [[profileCard cell] - accessibilitySetOverrideValue: - l10n_util::GetNSStringF(IDS_PROFILES_EDIT_PROFILE_ACCESSIBLE_NAME, - profileNameString) - forAttribute:NSAccessibilityTitleAttribute]; - } - [container addSubview:profileCard]; - if (isGuestSession_) - [profileCard setEnabled:NO]; - - // Profile badge for supervised account. - if (browser_->profile()->IsSupervised()) { - // Draw a circle as the background of the badge icon. - constexpr int badgeSize = 24; - constexpr int badgeSpacing = 4; - NSRect badgeIconCircleFrame = - NSMakeRect(xOffset + kMdImageSide - badgeSize + badgeSpacing, - cardYOffset + kMdImageSide - badgeSize + badgeSpacing, - badgeSize, badgeSize); - if (isRTL) { - badgeIconCircleFrame.origin.x = NSWidth([profileCard frame]) - - NSWidth(badgeIconCircleFrame) - - NSMinX(badgeIconCircleFrame); - } - base::scoped_nsobject<BackgroundCircleView> badgeIconWithCircle([ - [BackgroundCircleView alloc] initWithFrame:badgeIconCircleFrame - withFillColor:GetDialogBackgroundColor()]); - // Add the badge icon. - constexpr int borderWidth = 1; - const int badgeIconSize = badgeSize - borderWidth * 2; - base::scoped_nsobject<NSImageView> badgeIconView([[NSImageView alloc] - initWithFrame:NSMakeRect(borderWidth, borderWidth, - badgeIconSize, badgeIconSize)]); - const gfx::VectorIcon& badgeIcon = browser_->profile()->IsChild() - ? kAccountChildCircleIcon - : kSupervisorAccountCircleIcon; - [badgeIconView - setImage:NSImageFromImageSkia(gfx::CreateVectorIcon( - badgeIcon, badgeIconSize, gfx::kChromeIconGrey))]; - [badgeIconWithCircle addSubview:badgeIconView]; - - [profileCard addSubview:badgeIconWithCircle]; - } - if (!isRTL) - xOffset += kMdImageSide + kHorizontalSpacing; - CGFloat fontSize = kTextFontSize + 1.0; - NSString* profileNameNSString = base::SysUTF16ToNSString(profileNameString); - NSTextField* profileName = BuildLabel(profileNameNSString, NSZeroPoint, nil); - [[profileName cell] setLineBreakMode:NSLineBreakByTruncatingTail]; - [profileName setFont:[NSFont labelFontOfSize:fontSize]]; - [profileName sizeToFit]; - const int profileNameYOffset = - cardYOffset + - std::floor((kMdImageSide - NSHeight([profileName frame])) / 2); - if (profileName.frame.size.width > availableTextWidth) { - // Add the tooltip only if the profile name is truncated. This method to - // test if text field is truncated is not ideal (spaces between characters - // may be reduced to avoid truncation). - profileName.toolTip = profileNameNSString; - } - [profileName - setFrame:NSMakeRect(xOffset, profileNameYOffset, availableTextWidth, - NSHeight([profileName frame]))]; - [profileCard addSubview:profileName]; - - // Username, aligned to the leading edge of the profile icon and - // below the profile name. - if (item.signed_in && !signin::IsAccountConsistencyMirrorEnabled()) { - // Adjust the y-position of profile name to leave space for username. - cardYOffset += kMdImageSide / 2 - [profileName frame].size.height; - [profileName setFrameOrigin:NSMakePoint(xOffset, cardYOffset)]; - - NSString* elidedEmail = - ElideEmail(base::UTF16ToUTF8(item.username), availableTextWidth); - NSTextField* username = BuildLabel( - elidedEmail, NSZeroPoint, skia::SkColorToSRGBNSColor(SK_ColorGRAY)); - CGFloat usernameOffset = - isRTL ? NSMaxX([profileName frame]) - NSWidth([username frame]) - : xOffset; - [username setFrameOrigin:NSMakePoint(usernameOffset, - NSMaxY([profileName frame]))]; - NSString* usernameNSString = base::SysUTF16ToNSString(item.username); - if (![elidedEmail isEqualToString:usernameNSString]) { - // Add the tooltip only if the user name is truncated. - username.toolTip = usernameNSString; - } - [profileCard addSubview:username]; - } - - yOffset = NSMaxY([profileCard frame]); - [container setFrameSize:NSMakeSize(kFixedMenuWidth, yOffset)]; - return container.autorelease(); -} - -- (NSView*)createCurrentProfileLinksForItem:(const AvatarMenu::Item&)item - rect:(NSRect)rect { - // The branch is empty in non-account-consistency mode, because in that case, - // the username would appear in the profile card instead of as a separate link - // here. - SigninManagerBase* signinManager = SigninManagerFactory::GetForProfile( - browser_->profile()->GetOriginalProfile()); - DCHECK((item.signed_in && signin::IsAccountConsistencyMirrorEnabled()) || - (!item.signed_in && signinManager->IsSigninAllowed())); - - base::scoped_nsobject<NSView> container([[NSView alloc] initWithFrame:rect]); - - // Don't double-apply the left margin to the sub-views. - rect.origin.x = 0; - - // Adds right padding. - const CGFloat kRightPadding = kHorizontalSpacing; - rect.size.width -= kRightPadding; - - // The available links depend on the type of profile that is active. - if (item.signed_in) { - NSButton* link = nil; - if (signin::IsAccountConsistencyMirrorEnabled()) { - NSString* linkTitle = l10n_util::GetNSString( - viewMode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER ? - IDS_PROFILES_PROFILE_MANAGE_ACCOUNTS_BUTTON : - IDS_PROFILES_PROFILE_HIDE_MANAGE_ACCOUNTS_BUTTON); - SEL linkSelector = - (viewMode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER) ? - @selector(showAccountManagement:) : @selector(hideAccountManagement:); - rect.size.width += kRightPadding; // Spans the width of the entire menu. - link = [self hoverButtonWithRect:NSMakeRect(0, 0, rect.size.width, - kBlueButtonHeight) - text:linkTitle - action:linkSelector]; - } - if (link) { - // -linkButtonWithTitle sizeToFit's the link. We can use the height, but - // need to re-stretch the width so that the link can be centered correctly - // in the view. - rect.size.height = [link frame].size.height; - [link setFrame:rect]; - [container addSubview:link]; - [container setFrameSize:rect.size]; - } - } else { - rect.size.height = kBlueButtonHeight; - NSButton* signinButton = [[BlueLabelButton alloc] initWithFrame:rect]; - - // Manually elide the button text so that the contents fit inside the bubble - // This is needed because the BlueLabelButton cell resets the style on - // every call to -cellSize, which prevents setting a custom lineBreakMode. - NSString* elidedButtonText = base::SysUTF16ToNSString( - gfx::ElideText(l10n_util::GetStringFUTF16( - IDS_SYNC_START_SYNC_BUTTON_LABEL, - l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)), - gfx::FontList(), rect.size.width, gfx::ELIDE_TAIL, - gfx::Typesetter::BROWSER)); - - [signinButton setTitle:elidedButtonText]; - [signinButton sizeToFit]; - [signinButton setTarget:self]; - [signinButton setAction:@selector(showInlineSigninPage:)]; - if (cocoa_l10n_util::ShouldDoExperimentalRTLLayout()) { - NSRect buttonFrame = [signinButton frame]; - buttonFrame.origin.x = NSWidth(rect) - NSWidth(buttonFrame); - [signinButton setFrame:buttonFrame]; - } - [container addSubview:signinButton]; - - // Sign-in promo text. - NSTextField* promo = BuildLabel( - l10n_util::GetNSString(IDS_PROFILES_SIGNIN_PROMO), - NSMakePoint(0, NSMaxY([signinButton frame]) + kVerticalSpacing), - nil); - if (kRightPadding >= 8 && - !cocoa_l10n_util::ShouldDoExperimentalRTLLayout()) { - rect.size.width += 8; // Re-stretch a little bit to fit promo text. - } - DCHECK(kRightPadding >= 8); - [promo setFrameSize:NSMakeSize(rect.size.width, 0)]; - [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:promo]; - [container addSubview:promo]; - - [container setFrameSize:NSMakeSize(rect.size.width, - NSMaxY([promo frame]) + - kRelatedControllVerticalSpacing)]; - base::RecordAction( - base::UserMetricsAction("Signin_Impression_FromAvatarBubbleSignin")); - } - - return container.autorelease(); -} - -- (NSView*)createSupervisedUserDisclaimerView { - base::scoped_nsobject<NSView> container( - [[NSView alloc] initWithFrame:NSZeroRect]); - - int yOffset = 0; - int availableTextWidth = kFixedMenuWidth - 2 * kHorizontalSpacing; - - NSTextField* disclaimer = BuildLabel( - base::SysUTF16ToNSString(avatarMenu_->GetSupervisedUserInformation()), - NSMakePoint(kHorizontalSpacing, yOffset), nil); - [disclaimer setFrameSize:NSMakeSize(availableTextWidth, 0)]; - [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:disclaimer]; - yOffset = NSMaxY([disclaimer frame]); - - [container addSubview:disclaimer]; - [container setFrameSize:NSMakeSize(kFixedMenuWidth, yOffset)]; - return container.autorelease(); -} - -- (NSView*)createGuestProfileView { - gfx::Image guestIcon = - ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed( - profiles::GetPlaceholderAvatarIconResourceID()); - AvatarMenu::Item guestItem(std::string::npos, /* menu_index, not used */ - base::FilePath(), /* profile_path, not used */ - guestIcon); - guestItem.active = true; - guestItem.name = base::SysNSStringToUTF16( - l10n_util::GetNSString(IDS_PROFILES_GUEST_PROFILE_NAME)); - - return [self createCurrentProfileView:guestItem]; -} - -- (NSButton*)createOtherProfileView:(int)itemIndex { - const AvatarMenu::Item& item = avatarMenu_->GetItemAt(itemIndex); - - NSRect rect = NSMakeRect(0, 0, kFixedMenuWidth, - kBlueButtonHeight + kSmallVerticalSpacing); - const int imageTitleSpacing = kHorizontalSpacing; - base::scoped_nsobject<BackgroundColorHoverButton> profileButton( - [[BackgroundColorHoverButton alloc] - initWithFrame:rect - imageTitleSpacing:imageTitleSpacing - backgroundColor:GetDialogBackgroundColor()]); - [profileButton setTrailingMarginSpacing:kHorizontalSpacing]; - - NSString* title = base::SysUTF16ToNSString( - profiles::GetProfileSwitcherTextForItem(item)); - [profileButton setTitle:title]; - - CGFloat availableWidth; - [profileButton setDefaultImage:CreateProfileImage(item.icon, kIconImageSide, - profiles::SHAPE_CIRCLE)]; - availableWidth = rect.size.width - kIconImageSide - imageTitleSpacing - - 2 * kHorizontalSpacing; - - [profileButton setImagePosition:cocoa_l10n_util::LeadingCellImagePosition()]; - [profileButton setAlignment:NSNaturalTextAlignment]; - [profileButton setBordered:NO]; - [profileButton setTag:itemIndex]; - [profileButton setTarget:self]; - [profileButton setAction:@selector(switchToProfile:)]; - - return profileButton.autorelease(); -} - -- (NSView*)createCurrentProfileAccountsView:(NSRect)rect { - const CGFloat kAccountButtonHeight = 34; - - const AvatarMenu::Item& item = - avatarMenu_->GetItemAt(avatarMenu_->GetActiveProfileIndex()); - DCHECK(item.signed_in); - - NSColor* backgroundColor = skia::SkColorToCalibratedNSColor( - profiles::kAvatarBubbleAccountsBackgroundColor); - base::scoped_nsobject<NSView> container([[BackgroundColorView alloc] - initWithFrame:rect - withColor:backgroundColor]); - - rect.origin.y = 0; - if (!browser_->profile()->IsSupervised()) { - // Manually elide the button text so the contents fit inside the bubble. - // This is needed because the BlueLabelButton cell resets the style on - // every call to -cellSize, which prevents setting a custom lineBreakMode. - NSString* elidedButtonText = base::SysUTF16ToNSString( - gfx::ElideText(l10n_util::GetStringFUTF16( - IDS_PROFILES_PROFILE_ADD_ACCOUNT_BUTTON, item.name), - gfx::FontList(), rect.size.width, gfx::ELIDE_TAIL, - gfx::Typesetter::BROWSER)); - - NSButton* addAccountsButton = - [self linkButtonWithTitle:elidedButtonText - frameOrigin:NSMakePoint( - kHorizontalSpacing, kSmallVerticalSpacing) - action:@selector(addAccount:)]; - if (cocoa_l10n_util::ShouldDoExperimentalRTLLayout()) { - CGRect addAccountsButtonFrame = [addAccountsButton frame]; - addAccountsButtonFrame.origin.x = NSMaxX(rect) - - NSWidth(addAccountsButtonFrame) - - NSMinX(addAccountsButtonFrame); - [addAccountsButton setFrame:addAccountsButtonFrame]; - } - - [container addSubview:addAccountsButton]; - rect.origin.y += kAccountButtonHeight; - } - - NSView* accountEmails = [self createAccountsListWithRect:NSMakeRect( - 0, rect.origin.y, rect.size.width, kAccountButtonHeight)]; - [container addSubview:accountEmails]; - - [container setFrameSize:NSMakeSize(rect.size.width, - NSMaxY([accountEmails frame]))]; - return container.autorelease(); -} - -- (NSView*)createOptionsViewWithFrame:(NSRect)rect showLock:(BOOL)showLock { - NSRect viewRect = NSMakeRect(0, 0, rect.size.width, - kBlueButtonHeight + kSmallVerticalSpacing); - base::scoped_nsobject<NSView> container([[NSView alloc] initWithFrame:rect]); - const int icon_size = 20; - - // Create a lock profile button when supervised users exist; otherwise, create - // a button that closes all of the current profile's windows if more than one - // is open. - if (showLock) { - NSButton* lockButton = - [self hoverButtonWithRect:viewRect - text:l10n_util::GetNSString( - IDS_PROFILES_PROFILE_SIGNOUT_BUTTON) - image:NSImageFromImageSkia(gfx::CreateVectorIcon( - vector_icons::kLockIcon, icon_size, - gfx::kChromeIconGrey)) - action:@selector(lockProfile:)]; - [container addSubview:lockButton]; - viewRect.origin.y = NSMaxY([lockButton frame]); - } else if (!isGuestSession_) { - NSButton* closeAllWindowsButton = [self - hoverButtonWithRect:viewRect - text:l10n_util::GetNSString( - IDS_PROFILES_CLOSE_ALL_WINDOWS_BUTTON) - image:NSImageFromImageSkia(gfx::CreateVectorIcon( - kCloseAllIcon, icon_size, gfx::kChromeIconGrey)) - action:@selector(closeAllWindows:)]; - [container addSubview:closeAllWindowsButton]; - viewRect.origin.y = NSMaxY([closeAllWindowsButton frame]); - } - - // Create a manage users/exit guest button. - NSString* text = - isGuestSession_ - ? l10n_util::GetNSString(IDS_PROFILES_EXIT_GUEST) - : l10n_util::GetNSString(IDS_PROFILES_MANAGE_USERS_BUTTON); - NSImage* icon = NSImageFromImageSkia( - gfx::CreateVectorIcon(isGuestSession_ ? kCloseAllIcon : kSettingsIcon, - icon_size, gfx::kChromeIconGrey)); - SEL action = - isGuestSession_ ? @selector(exitGuest:) : @selector(showUserManager:); - NSButton* manageUsersButton = - [self hoverButtonWithRect:viewRect text:text image:icon action:action]; - viewRect.origin.y = NSMaxY([manageUsersButton frame]); - [container addSubview:manageUsersButton]; - - // Create a guest profile button. - if (!isGuestSession_ && !browser_->profile()->IsSupervised()) { - PrefService* service = g_browser_process->local_state(); - DCHECK(service); - if (service->GetBoolean(prefs::kBrowserGuestModeEnabled)) { - NSButton* guestProfileButton = - [self hoverButtonWithRect:viewRect - text:l10n_util::GetNSString( - IDS_PROFILES_GUEST_PROFILE_NAME) - image:NSImageFromImageSkia(gfx::CreateVectorIcon( - kAccountCircleIcon, icon_size, - gfx::kChromeIconGrey)) - action:@selector(switchToGuest:)]; - viewRect.origin.y = NSMaxY([guestProfileButton frame]); - [container addSubview:guestProfileButton]; - } - } - - [container setFrameSize:NSMakeSize(rect.size.width, viewRect.origin.y)]; - return container.autorelease(); -} - -- (NSView*)createAccountsListWithRect:(NSRect)rect { - base::scoped_nsobject<NSView> container([[NSView alloc] initWithFrame:rect]); - currentProfileAccounts_.clear(); - - Profile* profile = browser_->profile(); - std::string primaryAccount = - SigninManagerFactory::GetForProfile(profile)->GetAuthenticatedAccountId(); - DCHECK(!primaryAccount.empty()); - std::vector<std::string>accounts = - profiles::GetSecondaryAccountsForProfile(profile, primaryAccount); - - // If there is an account with an authentication error, it needs to be - // badged with a warning icon. - std::string errorAccountId = GetAuthErrorAccountId(profile); - - rect.origin.y = 0; - for (size_t i = 0; i < accounts.size(); ++i) { - // Save the original email address, as the button text could be elided. - currentProfileAccounts_[i] = accounts[i]; - NSButton* accountButton = - [self accountButtonWithRect:rect - accountId:accounts[i] - tag:i - reauthRequired:errorAccountId == accounts[i]]; - [container addSubview:accountButton]; - rect.origin.y = NSMaxY([accountButton frame]); - } - - // The primary account should always be listed first. - NSButton* accountButton = - [self accountButtonWithRect:rect - accountId:primaryAccount - tag:kPrimaryProfileTag - reauthRequired:errorAccountId == primaryAccount]; - [container addSubview:accountButton]; - [container setFrameSize:NSMakeSize(NSWidth([container frame]), - NSMaxY([accountButton frame]))]; - return container.autorelease(); -} - -- (NSView*)buildGaiaEmbeddedView { - base::scoped_nsobject<NSView> container( - [[NSView alloc] initWithFrame:NSZeroRect]); - CGFloat yOffset = 0; - - int messageId = -1; - switch (viewMode_) { - case profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN: - messageId = IDS_PROFILES_GAIA_SIGNIN_TITLE; - break; - case profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT: - messageId = IDS_PROFILES_GAIA_ADD_ACCOUNT_TITLE; - break; - case profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH: - DCHECK(HasAuthError(browser_->profile())); - messageId = IDS_PROFILES_GAIA_REAUTH_TITLE; - break; - default: - NOTREACHED() << "Called with invalid mode=" << viewMode_; - break; - } - - webContentsDelegate_.reset(new GaiaWebContentsDelegate()); - webContents_ = SigninViewControllerDelegateMac::CreateGaiaWebContents( - webContentsDelegate_.get(), viewMode_, browser_->profile(), accessPoint_); - - NSView* webview = webContents_->GetNativeView(); - - [container addSubview:webview]; - yOffset = NSMaxY([webview frame]); - - // Adds the title card. - NSBox* separator = [self horizontalSeparatorWithFrame: - NSMakeRect(0, yOffset, kFixedGaiaViewWidth, 0)]; - [container addSubview:separator]; - yOffset = NSMaxY([separator frame]) + kVerticalSpacing; - - NSView* titleView = BuildTitleCard( - NSMakeRect(0, yOffset, kFixedGaiaViewWidth, 0), - l10n_util::GetStringUTF16(messageId), - self /* backButtonTarget*/, - @selector(navigateBackFromSigninPage:) /* backButtonAction */); - [container addSubview:titleView]; - yOffset = NSMaxY([titleView frame]); - - [container setFrameSize:NSMakeSize(kFixedGaiaViewWidth, yOffset)]; - return container.autorelease(); -} - -- (NSView*)buildAccountRemovalView { - DCHECK(!accountIdToRemove_.empty()); - - base::scoped_nsobject<NSView> container( - [[NSView alloc] initWithFrame:NSZeroRect]); - CGFloat availableWidth = - kFixedAccountRemovalViewWidth - 2 * kHorizontalSpacing; - CGFloat yOffset = kVerticalSpacing; - - const std::string& primaryAccount = SigninManagerFactory::GetForProfile( - browser_->profile())->GetAuthenticatedAccountId(); - bool isPrimaryAccount = primaryAccount == accountIdToRemove_; - - // Adds "remove account" button at the bottom if needed. - if (!isPrimaryAccount) { - base::scoped_nsobject<NSButton> removeAccountButton( - [[BlueLabelButton alloc] initWithFrame:NSZeroRect]); - [removeAccountButton setTitle:l10n_util::GetNSString( - IDS_PROFILES_ACCOUNT_REMOVAL_BUTTON)]; - [removeAccountButton setTarget:self]; - [removeAccountButton setAction:@selector(removeAccount:)]; - [removeAccountButton sizeToFit]; - [removeAccountButton setAlignment:NSCenterTextAlignment]; - CGFloat xOffset = (kFixedAccountRemovalViewWidth - - NSWidth([removeAccountButton frame])) / 2; - [removeAccountButton setFrameOrigin:NSMakePoint(xOffset, yOffset)]; - [container addSubview:removeAccountButton]; - - yOffset = NSMaxY([removeAccountButton frame]) + kVerticalSpacing; - } - - NSView* contentView; - NSPoint contentFrameOrigin = NSMakePoint(kHorizontalSpacing, yOffset); - if (isPrimaryAccount) { - std::string email = signin_ui_util::GetDisplayEmail(browser_->profile(), - accountIdToRemove_); - std::vector<size_t> offsets; - NSString* contentStr = l10n_util::GetNSStringF( - IDS_PROFILES_PRIMARY_ACCOUNT_REMOVAL_TEXT, - base::UTF8ToUTF16(email), base::string16(), &offsets); - NSString* linkStr = l10n_util::GetNSString(IDS_PROFILES_SETTINGS_LINK); - contentView = BuildFixedWidthTextViewWithLink(self, contentStr, linkStr, - offsets[1], contentFrameOrigin, availableWidth); - } else { - NSString* contentStr = - l10n_util::GetNSString(IDS_PROFILES_ACCOUNT_REMOVAL_TEXT); - NSTextField* contentLabel = BuildLabel(contentStr, contentFrameOrigin, nil); - [contentLabel setFrameSize:NSMakeSize(availableWidth, 0)]; - [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:contentLabel]; - contentView = contentLabel; - } - [container addSubview:contentView]; - yOffset = NSMaxY([contentView frame]) + kVerticalSpacing; - - // Adds the title card. - NSBox* separator = [self horizontalSeparatorWithFrame: - NSMakeRect(0, yOffset, kFixedAccountRemovalViewWidth, 0)]; - [container addSubview:separator]; - yOffset = NSMaxY([separator frame]) + kVerticalSpacing; - - NSView* titleView = BuildTitleCard( - NSMakeRect(0, yOffset, kFixedAccountRemovalViewWidth,0), - l10n_util::GetStringUTF16(IDS_PROFILES_ACCOUNT_REMOVAL_TITLE), - self /* backButtonTarget*/, - @selector(showAccountManagement:) /* backButtonAction */); - [container addSubview:titleView]; - yOffset = NSMaxY([titleView frame]); - - [container setFrameSize:NSMakeSize(kFixedAccountRemovalViewWidth, yOffset)]; - return container.autorelease(); -} - -// Called when clicked on the settings link. -- (BOOL)textView:(NSTextView*)textView - clickedOnLink:(id)link - atIndex:(NSUInteger)charIndex { - chrome::ShowSettings(browser_); - return YES; -} - -- (NSButton*)hoverButtonWithRect:(NSRect)rect - text:(NSString*)text - imageResourceId:(int)imageResourceId - action:(SEL)action { - ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - NSImage* image = rb.GetNativeImageNamed(imageResourceId).ToNSImage(); - return [self hoverButtonWithRect:rect text:text image:image action:action]; -} - -- (NSButton*)hoverButtonWithRect:(NSRect)rect - text:(NSString*)text - image:(NSImage*)image - action:(SEL)action { - BackgroundColorHoverButton* button = - [self hoverButtonWithRect:rect text:text action:action]; - [button setDefaultImage:image]; - [button setHoverImage:image]; - [button setPressedImage:image]; - [button setImagePosition:cocoa_l10n_util::LeadingCellImagePosition()]; - - return button; -} - -- (BackgroundColorHoverButton*)hoverButtonWithRect:(NSRect)rect - text:(NSString*)text - action:(SEL)action { - // The vector icons in hover buttons have small embeded paddings and are - // therefore given an extra 2px in size to have a consistent look as the - // profile icons; hence the -2.0 here to left align the hover button texts - // with those of profile buttons. - const int image_title_spacing = kHorizontalSpacing - 2.0; - - base::scoped_nsobject<BackgroundColorHoverButton> button( - [[BackgroundColorHoverButton alloc] - initWithFrame:rect - imageTitleSpacing:image_title_spacing - backgroundColor:GetDialogBackgroundColor()]); - - [button setTitle:text]; - [button setAlignment:NSNaturalTextAlignment]; - [button setBordered:NO]; - [button setTarget:self]; - [button setAction:action]; - - return button.autorelease(); -} - -- (NSButton*)linkButtonWithTitle:(NSString*)title - frameOrigin:(NSPoint)frameOrigin - action:(SEL)action { - base::scoped_nsobject<NSButton> link( - [[HyperlinkButtonCell buttonWithString:title] retain]); - - [[link cell] setTextColor:skia::SkColorToCalibratedNSColor( - chrome_style::GetLinkColor())]; - [link setTitle:title]; - [link setBordered:NO]; - [link setFont:[NSFont labelFontOfSize:kTextFontSize]]; - [link setTarget:self]; - [link setAction:action]; - [link setFrameOrigin:frameOrigin]; - [link sizeToFit]; - - return link.autorelease(); -} - -- (NSButton*)accountButtonWithRect:(NSRect)rect - accountId:(const std::string&)accountId - tag:(int)tag - reauthRequired:(BOOL)reauthRequired { - // Get display email address for account. - std::string email = signin_ui_util::GetDisplayEmail(browser_->profile(), - accountId); - - ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); - NSImage* deleteImage = rb->GetNativeImageNamed(IDR_CLOSE_1).ToNSImage(); - CGFloat deleteImageWidth = [deleteImage size].width; - NSImage* warningImage = reauthRequired ? rb->GetNativeImageNamed( - IDR_ICON_PROFILES_ACCOUNT_BUTTON_ERROR).ToNSImage() : nil; - CGFloat warningImageWidth = [warningImage size].width; - - CGFloat availableTextWidth = rect.size.width - kHorizontalSpacing - - warningImageWidth - deleteImageWidth; - if (warningImage) - availableTextWidth -= kHorizontalSpacing; - - NSColor* backgroundColor = skia::SkColorToCalibratedNSColor( - profiles::kAvatarBubbleAccountsBackgroundColor); - base::scoped_nsobject<BackgroundColorHoverButton> button( - [[BackgroundColorHoverButton alloc] initWithFrame:rect - imageTitleSpacing:0 - backgroundColor:backgroundColor]); - [button setTitle:ElideEmail(email, availableTextWidth)]; - [button setAlignment:NSNaturalTextAlignment]; - [button setBordered:NO]; - if (reauthRequired) { - [button setDefaultImage:warningImage]; - [button setImagePosition:cocoa_l10n_util::LeadingCellImagePosition()]; - [button setTarget:self]; - [button setAction:@selector(showAccountReauthenticationView:)]; - [button setTag:tag]; - } - - // Delete button. - if (!browser_->profile()->IsSupervised()) { - NSRect buttonRect; - NSDivideRect(rect, &buttonRect, &rect, - deleteImageWidth + kHorizontalSpacing, - cocoa_l10n_util::TrailingEdge()); - buttonRect.origin.y = 0; - - base::scoped_nsobject<HoverImageButton> deleteButton( - [[HoverImageButton alloc] initWithFrame:buttonRect]); - [deleteButton setBordered:NO]; - [deleteButton setDefaultImage:deleteImage]; - [deleteButton setHoverImage:rb->GetNativeImageNamed( - IDR_CLOSE_1_H).ToNSImage()]; - [deleteButton setPressedImage:rb->GetNativeImageNamed( - IDR_CLOSE_1_P).ToNSImage()]; - [deleteButton setTarget:self]; - [deleteButton setAction:@selector(showAccountRemovalView:)]; - [deleteButton setTag:tag]; - - [button addSubview:deleteButton]; - } - - return button.autorelease(); -} - -- (void)postActionPerformed:(ProfileMetrics::ProfileDesktopMenu)action { - ProfileMetrics::LogProfileDesktopMenu(action, serviceType_); - serviceType_ = signin::GAIA_SERVICE_TYPE_NONE; -} - -- (void)showWindow:(id)sender { - [super showWindow:sender]; - NSEvent *event = [[NSApplication sharedApplication] currentEvent]; - if (firstProfileView_ && [event type] == NSKeyDown) { - [[self window] makeFirstResponder:firstProfileView_]; - } -} - -@end
diff --git a/chrome/browser/ui/cocoa/profiles/profile_chooser_controller_unittest.mm b/chrome/browser/ui/cocoa/profiles/profile_chooser_controller_unittest.mm deleted file mode 100644 index c79d595..0000000 --- a/chrome/browser/ui/cocoa/profiles/profile_chooser_controller_unittest.mm +++ /dev/null
@@ -1,491 +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. - -#import "chrome/browser/ui/cocoa/profiles/profile_chooser_controller.h" - -#include <memory> - -#include "base/command_line.h" -#import "base/mac/foundation_util.h" -#include "base/mac/scoped_nsobject.h" -#include "base/macros.h" -#include "base/strings/sys_string_conversions.h" -#include "base/strings/utf_string_conversions.h" -#include "chrome/browser/gcm/fake_gcm_profile_service.h" -#include "chrome/browser/gcm/gcm_profile_service_factory.h" -#include "chrome/browser/profiles/avatar_menu.h" -#include "chrome/browser/profiles/profile_attributes_entry.h" -#include "chrome/browser/profiles/profile_attributes_storage.h" -#include "chrome/browser/signin/account_fetcher_service_factory.h" -#include "chrome/browser/signin/account_tracker_service_factory.h" -#include "chrome/browser/signin/chrome_signin_helper.h" -#include "chrome/browser/signin/fake_account_fetcher_service_builder.h" -#include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h" -#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" -#include "chrome/browser/signin/signin_manager_factory.h" -#include "chrome/browser/sync/profile_sync_service_factory.h" -#include "chrome/browser/sync/profile_sync_test_util.h" -#include "chrome/browser/ui/browser.h" -#import "chrome/browser/ui/cocoa/info_bubble_view.h" -#include "chrome/browser/ui/cocoa/l10n_util.h" -#include "chrome/browser/ui/cocoa/test/cocoa_profile_test.h" -#include "chrome/browser/ui/cocoa/test/scoped_force_rtl_mac.h" -#include "chrome/common/chrome_features.h" -#include "chrome/common/chrome_switches.h" -#include "chrome/common/pref_names.h" -#include "components/signin/core/browser/fake_account_fetcher_service.h" -#include "components/signin/core/browser/fake_profile_oauth2_token_service.h" -#include "components/signin/core/browser/profile_management_switches.h" -#include "components/signin/core/browser/profile_oauth2_token_service.h" -#include "components/signin/core/browser/scoped_account_consistency.h" -#include "components/signin/core/browser/signin_manager.h" -#include "components/signin/core/browser/signin_pref_names.h" -#include "components/sync_preferences/pref_service_syncable.h" - -using ::testing::Return; - -const std::string kGaiaId = "gaiaid-user@gmail.com"; -const std::string kEmail = "user@gmail.com"; -const std::string kSecondaryEmail = "user2@gmail.com"; -const std::string kSecondaryGaiaId = "gaiaid-user2@gmail.com"; -const std::string kLoginToken = "oauth2_login_token"; - -class ProfileChooserControllerTest : public CocoaProfileTest { - public: - ProfileChooserControllerTest() { - // This file only tests Cocoa UI and can be deleted when - // kViewsProfileChooser is removed. - scoped_feature_list_.InitAndDisableFeature(features::kViewsProfileChooser); - - TestingProfile::TestingFactories factories; - factories.push_back( - std::make_pair(ProfileOAuth2TokenServiceFactory::GetInstance(), - BuildFakeProfileOAuth2TokenService)); - factories.push_back( - std::make_pair(AccountFetcherServiceFactory::GetInstance(), - FakeAccountFetcherServiceBuilder::BuildForTests)); - factories.push_back(std::make_pair(ProfileSyncServiceFactory::GetInstance(), - BuildMockProfileSyncService)); - AddTestingFactories(factories); - } - - void SetUp() override { - CocoaProfileTest::SetUp(); - - ASSERT_TRUE(browser()->profile()); - - gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactory( - browser()->profile(), gcm::FakeGCMProfileService::Build); - - testing_profile_manager()->CreateTestingProfile( - "test1", std::unique_ptr<sync_preferences::PrefServiceSyncable>(), - base::ASCIIToUTF16("Test 1"), 0, std::string(), testing_factories()); - testing_profile_manager()->CreateTestingProfile( - "test2", std::unique_ptr<sync_preferences::PrefServiceSyncable>(), - base::ASCIIToUTF16("Test 2"), 1, std::string(), - TestingProfile::TestingFactories()); - - mock_sync_service_ = static_cast<browser_sync::ProfileSyncServiceMock*>( - ProfileSyncServiceFactory::GetInstance()->GetForProfile( - browser()->profile())); - - menu_ = new AvatarMenu( - testing_profile_manager()->profile_attributes_storage(), NULL, NULL); - menu_->RebuildMenu(); - - // There should be the default profile + two profiles we created. - EXPECT_EQ(3U, menu_->GetNumberOfItems()); - } - - void TearDown() override { - [controller() close]; - controller_.reset(); - CocoaProfileTest::TearDown(); - } - - void StartProfileChooserController() { - NSRect frame = [test_window() frame]; - NSPoint point = NSMakePoint(NSMidX(frame), NSMidY(frame)); - controller_.reset([[ProfileChooserController alloc] - initWithBrowser:browser() - anchoredAt:point - viewMode:profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER - serviceType:signin::GAIA_SERVICE_TYPE_NONE - accessPoint:signin_metrics::AccessPoint:: - ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN]); - [controller_ showWindow:nil]; - } - - void SignInFirstProfile() { - std::vector<ProfileAttributesEntry*> entries = - testing_profile_manager() - ->profile_attributes_storage() - ->GetAllProfilesAttributesSortedByName(); - ASSERT_LE(1U, entries.size()); - ProfileAttributesEntry* entry = entries.front(); - entry->SetAuthInfo(kGaiaId, base::ASCIIToUTF16(kEmail)); - } - - void SuppressSyncConfirmationError() { - EXPECT_CALL(*mock_sync_service_, IsFirstSetupComplete()) - .WillRepeatedly(Return(false)); - EXPECT_CALL(*mock_sync_service_, IsFirstSetupInProgress()) - .WillRepeatedly(Return(true)); - EXPECT_CALL(*mock_sync_service_, IsSyncConfirmationNeeded()) - .WillRepeatedly(Return(false)); - } - - ProfileChooserController* controller() { return controller_; } - AvatarMenu* menu() { return menu_; } - - void ExpectGuestButton(NSButton* guest_button) { - ASSERT_TRUE(guest_button); - EXPECT_EQ(@selector(switchToGuest:), [guest_button action]); - EXPECT_EQ(controller(), [guest_button target]); - EXPECT_TRUE([guest_button isEnabled]); - } - - void ExpectManagePeopleButton(NSButton* manage_people_button) { - ASSERT_TRUE(manage_people_button); - EXPECT_EQ(@selector(showUserManager:), [manage_people_button action]); - EXPECT_EQ(controller(), [manage_people_button target]); - EXPECT_TRUE([manage_people_button isEnabled]); - } - - void ExpectCloseAllWindowsButton(NSButton* close_all_windows_button) { - ASSERT_TRUE(close_all_windows_button); - EXPECT_EQ(@selector(closeAllWindows:), [close_all_windows_button action]); - EXPECT_EQ(controller(), [close_all_windows_button target]); - EXPECT_TRUE([close_all_windows_button isEnabled]); - } - - void ExpectRegularProfileButtons(NSArray* buttonSubviews) { - // There are 3 buttons for a regular profile: "Guest", "Manage People" and - // "Close all your windows". - ASSERT_EQ(3U, [buttonSubviews count]); - - // There should be a "Close all your windows" button. - NSButton* close_all_windows_button = - base::mac::ObjCCast<NSButton>([buttonSubviews objectAtIndex:0]); - ExpectCloseAllWindowsButton(close_all_windows_button); - // There should be a "Manage People" button. - NSButton* manage_people_button = - base::mac::ObjCCast<NSButton>([buttonSubviews objectAtIndex:1]); - ExpectManagePeopleButton(manage_people_button); - // There should be a "Guest" button. - NSButton* guest_button = - base::mac::ObjCCast<NSButton>([buttonSubviews objectAtIndex:2]); - ExpectGuestButton(guest_button); - } - - private: - base::test::ScopedFeatureList scoped_feature_list_; - base::scoped_nsobject<ProfileChooserController> controller_; - browser_sync::ProfileSyncServiceMock* mock_sync_service_ = nullptr; - - // Weak; owned by |controller_|. - AvatarMenu* menu_; - - DISALLOW_COPY_AND_ASSIGN(ProfileChooserControllerTest); -}; - -TEST_F(ProfileChooserControllerTest, InitialLayoutWithNewMenu) { - StartProfileChooserController(); - - NSArray* subviews = [[[controller() window] contentView] subviews]; - ASSERT_EQ(2U, [subviews count]); - subviews = [[subviews objectAtIndex:0] subviews]; - - // Three profiles means we should have one active card, one separator and - // one option buttons view. We also have an update promo for the new avatar - // menu. - // TODO(noms): Enforcing 4U fails on the waterfall debug bots, but it's not - // reproducible anywhere else. - ASSERT_GE([subviews count], 3U); - - // There should be one button in the option buttons view. - NSArray* buttonSubviews = [[subviews objectAtIndex:0] subviews]; - ExpectRegularProfileButtons(buttonSubviews); - - NSUInteger lastSubviewIndex = 4; - NSArray* activeCardSubviews = - [[subviews objectAtIndex:lastSubviewIndex] subviews]; - - // There should be the profile avatar, name and links container in the active - // card view. The links displayed in the container are checked separately. - // In the MD user menu, the profile avatar and name are in the same subview. - ASSERT_EQ(2U, [activeCardSubviews count]); - // Profile links. This is a local profile, so there should be a signin button - // and a signin promo. - NSArray* linksSubviews = [[activeCardSubviews objectAtIndex:0] subviews]; - ASSERT_EQ(2U, [linksSubviews count]); - NSButton* link = base::mac::ObjCCast<NSButton>( - [linksSubviews objectAtIndex:0]); - EXPECT_EQ(@selector(showInlineSigninPage:), [link action]); - EXPECT_EQ(controller(), [link target]); - - NSTextField* promo = base::mac::ObjCCast<NSTextField>( - [linksSubviews objectAtIndex:1]); - EXPECT_GT([[promo stringValue] length], 0U); -} - -// Check to see if the bubble is aligned properly in LTR and RTL format. -TEST_F(ProfileChooserControllerTest, BubbleAlignment) { - // Test the LTR format. - StartProfileChooserController(); - EXPECT_EQ(info_bubble::kAlignTrailingEdgeToAnchorEdge, - [[controller() bubble] alignment]); - [controller() close]; - - // Force to RTL format - cocoa_l10n_util::ScopedForceRTLMac rtl; - StartProfileChooserController(); - info_bubble::BubbleAlignment expected_alignment = - cocoa_l10n_util::ShouldFlipWindowControlsInRTL() - ? info_bubble::kAlignTrailingEdgeToAnchorEdge - : info_bubble::kAlignLeadingEdgeToAnchorEdge; - EXPECT_EQ(expected_alignment, [[controller() bubble] alignment]); - [controller() close]; -} - -TEST_F(ProfileChooserControllerTest, - LocalProfileActiveCardLinksWithNewMenu) { - StartProfileChooserController(); - NSArray* subviews = [[[controller() window] contentView] subviews]; - ASSERT_EQ(2U, [subviews count]); - subviews = [[subviews objectAtIndex:0] subviews]; - // The active card is the last subview and the MD User Menu has 2 extra - // buttons. - NSUInteger lastSubviewIndex = 4; - NSArray* activeCardSubviews = - [[subviews objectAtIndex:lastSubviewIndex] subviews]; - NSArray* activeCardLinks = [[activeCardSubviews objectAtIndex:0] subviews]; - - ASSERT_EQ(2U, [activeCardLinks count]); - - // There should be a sign in button. - NSButton* link = base::mac::ObjCCast<NSButton>( - [activeCardLinks objectAtIndex:0]); - EXPECT_EQ(@selector(showInlineSigninPage:), [link action]); - EXPECT_EQ(controller(), [link target]); - - // Local profiles have a signin promo. - NSTextField* promo = base::mac::ObjCCast<NSTextField>( - [activeCardLinks objectAtIndex:1]); - EXPECT_GT([[promo stringValue] length], 0U); -} - -TEST_F(ProfileChooserControllerTest, - SignedInProfileActiveCardLinksWithAccountConsistency) { - signin::ScopedAccountConsistencyMirror scoped_mirror; - - SignInFirstProfile(); - - StartProfileChooserController(); - NSArray* subviews = [[[controller() window] contentView] subviews]; - ASSERT_EQ(2U, [subviews count]); - subviews = [[subviews objectAtIndex:0] subviews]; - // The active card is the last subview and the MD User Menu has 2 extra - // buttons. - NSUInteger lastSubviewIndex = 4; - NSArray* activeCardSubviews = - [[subviews objectAtIndex:lastSubviewIndex] subviews]; - NSArray* activeCardLinks = [[activeCardSubviews objectAtIndex:0] subviews]; - - // There is one link: manage accounts. - ASSERT_EQ(1U, [activeCardLinks count]); - NSButton* manageAccountsLink = - base::mac::ObjCCast<NSButton>([activeCardLinks objectAtIndex:0]); - EXPECT_EQ(@selector(showAccountManagement:), [manageAccountsLink action]); - EXPECT_EQ(controller(), [manageAccountsLink target]); -} - -TEST_F(ProfileChooserControllerTest, - SignedInProfileActiveCardLinksWithNewMenu) { - SignInFirstProfile(); - - StartProfileChooserController(); - NSArray* subviews = [[[controller() window] contentView] subviews]; - ASSERT_EQ(2U, [subviews count]); - subviews = [[subviews objectAtIndex:0] subviews]; - // The active card is the last subview and the MD User Menu has 2 extra - // buttons. - NSUInteger lastSubviewIndex = 4; - NSArray* activeCardSubviews = - [[subviews objectAtIndex:lastSubviewIndex] subviews]; - NSArray* activeCardLinks = [[activeCardSubviews objectAtIndex:0] subviews]; - - // There is the profile avatar and the profile name. - ASSERT_EQ(2U, [activeCardLinks count]); -} - -TEST_F(ProfileChooserControllerTest, AccountManagementLayout) { - signin::ScopedAccountConsistencyMirror scoped_mirror; - - SignInFirstProfile(); - - // Mark that we are using the profile name on purpose, so that we don't - // fallback to testing the algorithm that chooses which default name - // should be used. - ProfileAttributesEntry* entry = testing_profile_manager()-> - profile_attributes_storage()->GetAllProfilesAttributes().front(); - entry->SetIsUsingDefaultName(false); - - // Set up the AccountTrackerService, signin manager and the OAuth2Tokens. - Profile* profile = browser()->profile(); - AccountTrackerServiceFactory::GetForProfile(profile) - ->SeedAccountInfo(kGaiaId, kEmail); - AccountTrackerServiceFactory::GetForProfile(profile) - ->SeedAccountInfo(kSecondaryGaiaId, kSecondaryEmail); - SigninManagerFactory::GetForProfile(profile) - ->SetAuthenticatedAccountInfo(kGaiaId, kEmail); - std::string account_id = - SigninManagerFactory::GetForProfile(profile)->GetAuthenticatedAccountId(); - ProfileOAuth2TokenServiceFactory::GetForProfile(profile) - ->UpdateCredentials(account_id, kLoginToken); - account_id = AccountTrackerServiceFactory::GetForProfile(profile) - ->PickAccountIdForAccount(kSecondaryGaiaId, kSecondaryEmail); - ProfileOAuth2TokenServiceFactory::GetForProfile(profile) - ->UpdateCredentials(account_id, kLoginToken); - - SuppressSyncConfirmationError(); - - StartProfileChooserController(); - [controller() - showMenuWithViewMode:profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT]; - - NSArray* subviews = [[[controller() window] contentView] subviews]; - ASSERT_EQ(2U, [subviews count]); - subviews = [[subviews objectAtIndex:0] subviews]; - - // There should be one active card, one accounts container, two separators - // and one option buttons view. In the MD User Menu, there are 2 more buttons. - NSUInteger viewsCount = 7; - ASSERT_EQ(viewsCount, [subviews count]); - - NSArray* buttonSubviews = [[subviews objectAtIndex:0] subviews]; - ExpectRegularProfileButtons(buttonSubviews); - - NSUInteger accountsViewIndex = 4; - // In the accounts view, there should be the account list container - // accounts and one "add accounts" button. - NSArray* accountsSubviews = - [[subviews objectAtIndex:accountsViewIndex] subviews]; - ASSERT_EQ(2U, [accountsSubviews count]); - - NSButton* addAccountsButton = - base::mac::ObjCCast<NSButton>([accountsSubviews objectAtIndex:0]); - EXPECT_EQ(@selector(addAccount:), [addAccountsButton action]); - EXPECT_EQ(controller(), [addAccountsButton target]); - - // There should be two accounts in the account list container. - NSArray* accountsListSubviews = [[accountsSubviews objectAtIndex:1] subviews]; - ASSERT_EQ(2U, [accountsListSubviews count]); - - NSButton* genericAccount = - base::mac::ObjCCast<NSButton>([accountsListSubviews objectAtIndex:0]); - NSButton* genericAccountDelete = base::mac::ObjCCast<NSButton>( - [[genericAccount subviews] objectAtIndex:0]); - EXPECT_EQ(@selector(showAccountRemovalView:), [genericAccountDelete action]); - EXPECT_EQ(controller(), [genericAccountDelete target]); - EXPECT_NE(-1, [genericAccountDelete tag]); - - // Primary accounts are always last. - NSButton* primaryAccount = - base::mac::ObjCCast<NSButton>([accountsListSubviews objectAtIndex:1]); - NSButton* primaryAccountDelete = base::mac::ObjCCast<NSButton>( - [[primaryAccount subviews] objectAtIndex:0]); - EXPECT_EQ(@selector(showAccountRemovalView:), [primaryAccountDelete action]); - EXPECT_EQ(controller(), [primaryAccountDelete target]); - EXPECT_EQ(-1, [primaryAccountDelete tag]); - - // There should be another separator. - EXPECT_TRUE([[subviews objectAtIndex:3] isKindOfClass:[NSBox class]]); - - // There should be the profile avatar, name and a "hide accounts" link - // container in the active card view. - NSArray* activeCardSubviews = [[subviews objectAtIndex:4] subviews]; - // In the MD user menu, the profile name and avatar are in the same subview. - ASSERT_EQ(2U, [activeCardSubviews count]); -} - -TEST_F(ProfileChooserControllerTest, SignedInProfileLockDisabled) { - SignInFirstProfile(); - - // The preference, not the email, determines whether the profile can lock. - browser()->profile()->GetPrefs()->SetString( - prefs::kGoogleServicesHostedDomain, "chromium.org"); - - StartProfileChooserController(); - NSArray* subviews = [[[controller() window] contentView] subviews]; - ASSERT_EQ(2U, [subviews count]); - subviews = [[subviews objectAtIndex:0] subviews]; - - NSArray* buttonSubviews = [[subviews objectAtIndex:0] subviews]; - ExpectRegularProfileButtons(buttonSubviews); -} - -TEST_F(ProfileChooserControllerTest, SignedInProfileLockEnabled) { - SignInFirstProfile(); - - // The preference, not the email, determines whether the profile can lock. - browser()->profile()->GetPrefs()->SetString( - prefs::kGoogleServicesHostedDomain, "google.com"); - // Lock is only available where a supervised user is present. - ProfileAttributesEntry* entry = testing_profile_manager()-> - profile_attributes_storage()->GetAllProfilesAttributes().front(); - entry->SetSupervisedUserId(kEmail); - - StartProfileChooserController(); - NSArray* subviews = [[[controller() window] contentView] subviews]; - ASSERT_EQ(2U, [subviews count]); - subviews = [[subviews objectAtIndex:0] subviews]; - - NSArray* buttonSubviews = [[subviews objectAtIndex:0] subviews]; - // There are 3 buttons for a lockable profile: "Guest", "Manage People" and - // "Close all your windows". - ASSERT_EQ(3U, [buttonSubviews count]); - - // There should be a lock button. - NSButton* lockButton = - base::mac::ObjCCast<NSButton>([buttonSubviews objectAtIndex:0]); - ASSERT_TRUE(lockButton); - EXPECT_EQ(@selector(lockProfile:), [lockButton action]); - EXPECT_EQ(controller(), [lockButton target]); - EXPECT_TRUE([lockButton isEnabled]); -} - -TEST_F(ProfileChooserControllerTest, RegularProfileWithManagePeopleAndGuest) { - StartProfileChooserController(); - NSArray* subviews = [[[controller() window] contentView] subviews]; - ASSERT_EQ(2U, [subviews count]); - subviews = [[subviews objectAtIndex:0] subviews]; - - NSArray* buttonSubviews = [[subviews objectAtIndex:0] subviews]; - ExpectRegularProfileButtons(buttonSubviews); -} - -TEST_F(ProfileChooserControllerTest, SupervisedProfileWithManagePeopleOnly) { - TestingProfile* test = static_cast<TestingProfile*>(browser()->profile()); - test->SetSupervisedUserId(kSecondaryGaiaId); - - StartProfileChooserController(); - NSArray* subviews = [[[controller() window] contentView] subviews]; - ASSERT_EQ(2U, [subviews count]); - subviews = [[subviews objectAtIndex:0] subviews]; - - NSArray* buttonSubviews = [[subviews objectAtIndex:0] subviews]; - // There are 3 buttons for a lockable profile: "Guest", "Manage People" and - // "Close all your windows". - ASSERT_EQ(2U, [buttonSubviews count]); - - // There should be a "Close all your windows" button. - NSButton* close_all_windows_button = - base::mac::ObjCCast<NSButton>([buttonSubviews objectAtIndex:0]); - ExpectCloseAllWindowsButton(close_all_windows_button); - // There should be a "Manage People" button. - NSButton* manage_people_button = - base::mac::ObjCCast<NSButton>([buttonSubviews objectAtIndex:1]); - ExpectManagePeopleButton(manage_people_button); -}
diff --git a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc index 8524a9d..1f09733 100644 --- a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc +++ b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
@@ -1504,6 +1504,10 @@ #if defined(OS_LINUX) #define MAYBE_PersistKeywordModeOnTabSwitch \ DISABLED_PersistKeywordModeOnTabSwitch +#elif defined(OS_MACOSX) +// Getting text from textfields doesn't always work: https://crbug.com/823532 +#define MAYBE_PersistKeywordModeOnTabSwitch \ + DISABLED_PersistKeywordModeOnTabSwitch #else #define MAYBE_PersistKeywordModeOnTabSwitch PersistKeywordModeOnTabSwitch #endif
diff --git a/chrome/browser/ui/tab_contents/core_tab_helper.cc b/chrome/browser/ui/tab_contents/core_tab_helper.cc index 2072833..e5030a17 100644 --- a/chrome/browser/ui/tab_contents/core_tab_helper.cc +++ b/chrome/browser/ui/tab_contents/core_tab_helper.cc
@@ -139,8 +139,8 @@ if (!guest_manager) return false; return guest_manager->ForEachGuest( - source, base::Bind(&CoreTabHelper::GetStatusTextForWebContents, - status_text)); + source, base::BindRepeating(&CoreTabHelper::GetStatusTextForWebContents, + status_text)); #else // !BUILDFLAG(ENABLE_EXTENSIONS) return false; #endif // BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/ui/views/constrained_window_views_browsertest.cc b/chrome/browser/ui/views/constrained_window_views_browsertest.cc index f9e79285..24e20a4a 100644 --- a/chrome/browser/ui/views/constrained_window_views_browsertest.cc +++ b/chrome/browser/ui/views/constrained_window_views_browsertest.cc
@@ -66,10 +66,16 @@ } // namespace +#if defined(OS_MACOSX) +// Unexpected multiple focus managers on MacViews: http://crbug.com/824551 +#define MAYBE_FocusTest DISABLED_FocusTest +#else +#define MAYBE_FocusTest FocusTest +#endif // Tests the intial focus of tab-modal dialogs, the restoration of focus to the // browser when they close, and that queued dialogs don't register themselves as // accelerator targets until they are displayed. -IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, FocusTest) { +IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, MAYBE_FocusTest) { content::WebContents* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); EXPECT_TRUE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_OMNIBOX));
diff --git a/chrome/browser/ui/views/extensions/extension_dialog_interactive_uitest.cc b/chrome/browser/ui/views/extensions/extension_dialog_interactive_uitest.cc index adb55eb..6ae5fab 100644 --- a/chrome/browser/ui/views/extensions/extension_dialog_interactive_uitest.cc +++ b/chrome/browser/ui/views/extensions/extension_dialog_interactive_uitest.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ui/views/extensions/extension_dialog.h" +#include "build/build_config.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/extensions/extension_view_host.h" #include "chrome/browser/ui/browser_window.h" @@ -27,7 +28,13 @@ } // namespace -IN_PROC_BROWSER_TEST_F(ExtensionDialogUiTest, TabFocusLoop) { +#if defined(OS_MACOSX) +// Focusing or input is not completely working on Mac: http://crbug.com/824418 +#define MAYBE_TabFocusLoop DISABLED_TabFocusLoop +#else +#define MAYBE_TabFocusLoop TabFocusLoop +#endif +IN_PROC_BROWSER_TEST_F(ExtensionDialogUiTest, MAYBE_TabFocusLoop) { ExtensionTestMessageListener init_listener("ready", false /* will_reply */); ExtensionTestMessageListener button1_focus_listener("button1-focused", false); ExtensionTestMessageListener button2_focus_listener("button2-focused", false);
diff --git a/chrome/browser/ui/views/find_bar_views_interactive_uitest.cc b/chrome/browser/ui/views/find_bar_views_interactive_uitest.cc index 5187060..271bd75 100644 --- a/chrome/browser/ui/views/find_bar_views_interactive_uitest.cc +++ b/chrome/browser/ui/views/find_bar_views_interactive_uitest.cc
@@ -274,7 +274,13 @@ EXPECT_TRUE(IsViewFocused(browser(), VIEW_ID_OMNIBOX)); } -IN_PROC_BROWSER_TEST_F(FindInPageTest, SelectionRestoreOnTabSwitch) { +#if defined(OS_MACOSX) +// Getting text from textfields doesn't always work: https://crbug.com/823532 +#define MAYBE_SelectionRestoreOnTabSwitch DISABLED_SelectionRestoreOnTabSwitch +#else +#define MAYBE_SelectionRestoreOnTabSwitch SelectionRestoreOnTabSwitch +#endif +IN_PROC_BROWSER_TEST_F(FindInPageTest, MAYBE_SelectionRestoreOnTabSwitch) { ASSERT_TRUE(embedded_test_server()->Start()); // Make sure Chrome is in the foreground, otherwise sending input @@ -343,7 +349,13 @@ EXPECT_EQ(ASCIIToUTF16("de"), GetFindBarSelectedText()); } -IN_PROC_BROWSER_TEST_F(FindInPageTest, FocusRestoreOnTabSwitch) { +#if defined(OS_MACOSX) +// Getting text from textfields doesn't always work: https://crbug.com/823532 +#define MAYBE_FocusRestoreOnTabSwitch DISABLED_FocusRestoreOnTabSwitch +#else +#define MAYBE_FocusRestoreOnTabSwitch FocusRestoreOnTabSwitch +#endif +IN_PROC_BROWSER_TEST_F(FindInPageTest, MAYBE_FocusRestoreOnTabSwitch) { ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); ASSERT_TRUE(embedded_test_server()->Start());
diff --git a/chrome/browser/ui/views/fullscreen_control/fullscreen_control_view_interactive_uitest.cc b/chrome/browser/ui/views/fullscreen_control/fullscreen_control_view_interactive_uitest.cc index ae741b6..5e266eb 100644 --- a/chrome/browser/ui/views/fullscreen_control/fullscreen_control_view_interactive_uitest.cc +++ b/chrome/browser/ui/views/fullscreen_control/fullscreen_control_view_interactive_uitest.cc
@@ -4,6 +4,7 @@ #include "base/command_line.h" #include "base/macros.h" +#include "build/build_config.h" #include "chrome/browser/ui/exclusive_access/exclusive_access_bubble_type.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/fullscreen_control/fullscreen_control_host.h" @@ -44,7 +45,13 @@ DISALLOW_COPY_AND_ASSIGN(FullscreenControlViewTest); }; -IN_PROC_BROWSER_TEST_F(FullscreenControlViewTest, MouseExitFullscreen) { +#if defined(OS_MACOSX) +// Entering fullscreen is flaky on Mac: http://crbug.com/824517 +#define MAYBE_MouseExitFullscreen DISABLED_MouseExitFullscreen +#else +#define MAYBE_MouseExitFullscreen MouseExitFullscreen +#endif +IN_PROC_BROWSER_TEST_F(FullscreenControlViewTest, MAYBE_MouseExitFullscreen) { GURL blank_url("about:blank"); ui_test_utils::NavigateToURL(browser(), blank_url); BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
diff --git a/chrome/browser/ui/views/keyboard_access_browsertest.cc b/chrome/browser/ui/views/keyboard_access_browsertest.cc index fd6da7ca..0b9778e 100644 --- a/chrome/browser/ui/views/keyboard_access_browsertest.cc +++ b/chrome/browser/ui/views/keyboard_access_browsertest.cc
@@ -391,12 +391,18 @@ } #endif +#if defined(OS_MACOSX) +// Focusing or input is not completely working on Mac: http://crbug.com/824418 +#define MAYBE_ReserveKeyboardAccelerators DISABLED_ReserveKeyboardAccelerators +#else +#define MAYBE_ReserveKeyboardAccelerators ReserveKeyboardAccelerators +#endif // Test that JavaScript cannot intercept reserved keyboard accelerators like // ctrl-t to open a new tab or ctrl-f4 to close a tab. // TODO(isherman): This test times out on ChromeOS. We should merge it with // BrowserKeyEventsTest.ReservedAccelerators, but just disable for now. // If this flakes, use http://crbug.com/62311. -IN_PROC_BROWSER_TEST_F(KeyboardAccessTest, ReserveKeyboardAccelerators) { +IN_PROC_BROWSER_TEST_F(KeyboardAccessTest, MAYBE_ReserveKeyboardAccelerators) { const std::string kBadPage = "<html><script>" "document.onkeydown = function() {"
diff --git a/chrome/browser/ui/views/location_bar/location_icon_view_interactive_uitest.cc b/chrome/browser/ui/views/location_bar/location_icon_view_interactive_uitest.cc index b2f0071..e44c8572 100644 --- a/chrome/browser/ui/views/location_bar/location_icon_view_interactive_uitest.cc +++ b/chrome/browser/ui/views/location_bar/location_icon_view_interactive_uitest.cc
@@ -27,8 +27,14 @@ DISALLOW_COPY_AND_ASSIGN(LocationIconViewTest); }; +#if defined(OS_MACOSX) +// Focusing or input is not completely working on Mac: http://crbug.com/824418 +#define MAYBE_HideOnSecondClick DISABLED_HideOnSecondClick +#else +#define MAYBE_HideOnSecondClick HideOnSecondClick +#endif // Verify that clicking the location icon a second time hides the bubble. -IN_PROC_BROWSER_TEST_F(LocationIconViewTest, HideOnSecondClick) { +IN_PROC_BROWSER_TEST_F(LocationIconViewTest, MAYBE_HideOnSecondClick) { BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser()); views::View* location_icon_view = browser_view->toolbar()->location_bar()->location_icon_view();
diff --git a/chrome/browser/ui/views/location_bar/star_view_browsertest.cc b/chrome/browser/ui/views/location_bar/star_view_browsertest.cc index 483e975d..c51d56e 100644 --- a/chrome/browser/ui/views/location_bar/star_view_browsertest.cc +++ b/chrome/browser/ui/views/location_bar/star_view_browsertest.cc
@@ -47,6 +47,9 @@ // bubble. #if defined(OS_LINUX) && defined(USE_AURA) && !defined(OS_CHROMEOS) #define MAYBE_HideOnSecondClick DISABLED_HideOnSecondClick +#elif defined(OS_MACOSX) +// Focusing or input is not completely working on Mac: http://crbug.com/824418 +#define MAYBE_HideOnSecondClick DISABLED_HideOnSecondClick #else #define MAYBE_HideOnSecondClick HideOnSecondClick #endif
diff --git a/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc b/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc index 9bdfc4a..689e17a0 100644 --- a/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc +++ b/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
@@ -49,6 +49,9 @@ // TODO(linux_aura) http://crbug.com/163931 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA) #define MAYBE_NonImmersiveFullscreen DISABLED_NonImmersiveFullscreen +#elif defined(OS_MACOSX) +// Trips AppKit assert on Mac: https://crbug.com/824757 +#define MAYBE_NonImmersiveFullscreen DISABLED_NonImmersiveFullscreen #else #define MAYBE_NonImmersiveFullscreen NonImmersiveFullscreen #endif
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc index 3141d083..37679be 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc
@@ -332,7 +332,14 @@ EXPECT_TRUE(omnibox_view->IsSelectAll()); } -IN_PROC_BROWSER_TEST_F(OmniboxViewViewsTest, CloseOmniboxPopupOnTextDrag) { +#if defined(OS_MACOSX) +// Focusing or input is not completely working on Mac: http://crbug.com/824418 +#define MAYBE_CloseOmniboxPopupOnTextDrag DISABLED_CloseOmniboxPopupOnTextDrag +#else +#define MAYBE_CloseOmniboxPopupOnTextDrag CloseOmniboxPopupOnTextDrag +#endif +IN_PROC_BROWSER_TEST_F(OmniboxViewViewsTest, + MAYBE_CloseOmniboxPopupOnTextDrag) { OmniboxView* omnibox_view = nullptr; ASSERT_NO_FATAL_FAILURE(GetOmniboxViewForBrowser(browser(), &omnibox_view)); OmniboxViewViews* omnibox_view_views =
diff --git a/chrome/browser/ui/views/sad_tab_view_interactive_uitest.cc b/chrome/browser/ui/views/sad_tab_view_interactive_uitest.cc index ec1caa65..4693e640 100644 --- a/chrome/browser/ui/views/sad_tab_view_interactive_uitest.cc +++ b/chrome/browser/ui/views/sad_tab_view_interactive_uitest.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ui/views/sad_tab_view.h" +#include "build/build_config.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/sad_tab.h" @@ -132,8 +133,14 @@ DISALLOW_COPY_AND_ASSIGN(SadTabViewInteractiveUITest); }; +#if defined(OS_MACOSX) +// Focusing or input is not completely working on Mac: http://crbug.com/824418 +#define MAYBE_SadTabKeyboardAccessibility DISABLED_SadTabKeyboardAccessibility +#else +#define MAYBE_SadTabKeyboardAccessibility SadTabKeyboardAccessibility +#endif IN_PROC_BROWSER_TEST_F(SadTabViewInteractiveUITest, - SadTabKeyboardAccessibility) { + MAYBE_SadTabKeyboardAccessibility) { ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/links.html")); ui_test_utils::NavigateToURL(browser(), url); @@ -167,7 +174,14 @@ ASSERT_TRUE(IsFocusedViewInsideBrowserToolbar()); } -IN_PROC_BROWSER_TEST_F(SadTabViewInteractiveUITest, ReloadMultipleSadTabs) { +#if defined(OS_MACOSX) +// Focusing or input is not completely working on Mac: http://crbug.com/824418 +#define MAYBE_ReloadMultipleSadTabs DISABLED_ReloadMultipleSadTabs +#else +#define MAYBE_ReloadMultipleSadTabs ReloadMultipleSadTabs +#endif +IN_PROC_BROWSER_TEST_F(SadTabViewInteractiveUITest, + MAYBE_ReloadMultipleSadTabs) { ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/links.html")); ui_test_utils::NavigateToURL(browser(), url);
diff --git a/chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc b/chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc index eb5d4284..d0620c03 100644 --- a/chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc +++ b/chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc
@@ -317,7 +317,13 @@ // Let the mock get checked on destruction. } -IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorTest, Escape) { +#if defined(OS_MACOSX) +// Focusing or input is not completely working on Mac: http://crbug.com/824418 +#define MAYBE_Escape DISABLED_Escape +#else +#define MAYBE_Escape Escape +#endif +IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorTest, MAYBE_Escape) { EXPECT_CALL(*auth_requestor_.get(), CertificateSelected(nullptr, nullptr)); EXPECT_TRUE(ui_test_utils::SendKeyPressSync( @@ -337,7 +343,7 @@ Mock::VerifyAndClear(auth_requestor_.get()); } -IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiTabTest, Escape) { +IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiTabTest, MAYBE_Escape) { // auth_requestor_1_ should get selected automatically by the // SSLClientAuthObserver when selector_2_ is accepted, since both 1 & 2 have // the same host:port.
diff --git a/chrome/browser/ui/views/toolbar/browser_actions_container_browsertest.cc b/chrome/browser/ui/views/toolbar/browser_actions_container_browsertest.cc index 78dc260..4f57c08f 100644 --- a/chrome/browser/ui/views/toolbar/browser_actions_container_browsertest.cc +++ b/chrome/browser/ui/views/toolbar/browser_actions_container_browsertest.cc
@@ -277,6 +277,8 @@ void SetUpOnMainThread() override; void TearDownOnMainThread() override; + test::ScopedMacViewsBrowserMode views_mode_{true}; + // The main BrowserActionsContainer (owned by the browser view). BrowserActionsContainer* main_bar_; @@ -397,14 +399,8 @@ EXPECT_TRUE(VerifyVisibleCount(1u)); } -class BrowserActionsContainerOverflowViewsTest - : public BrowserActionsContainerOverflowTest { - private: - test::ScopedMacViewsBrowserMode views_mode_{true}; -}; - // Test drag and drop between the overflow container and the main container. -IN_PROC_BROWSER_TEST_F(BrowserActionsContainerOverflowViewsTest, +IN_PROC_BROWSER_TEST_F(BrowserActionsContainerOverflowTest, TestOverflowDragging) { LoadExtensions();
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view_interactive_uitest.cc b/chrome/browser/ui/views/toolbar/toolbar_action_view_interactive_uitest.cc index 9f015618..26d5472 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_action_view_interactive_uitest.cc +++ b/chrome/browser/ui/views/toolbar/toolbar_action_view_interactive_uitest.cc
@@ -280,6 +280,10 @@ // Fails on Win debug; see https;//crbug.com/788112. #define MAYBE_DoubleClickToolbarActionToClose \ DISABLED_DoubleClickToolbarActionToClose +#elif defined(OS_MACOSX) +// Focusing or input is not completely working on Mac: http://crbug.com/824418 +#define MAYBE_DoubleClickToolbarActionToClose \ + DISABLED_DoubleClickToolbarActionToClose #else #define MAYBE_DoubleClickToolbarActionToClose DoubleClickToolbarActionToClose #endif
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc b/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc index b5bd828..cb9ee57 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc +++ b/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc
@@ -36,15 +36,6 @@ #include "ui/views/view.h" #include "ui/views/widget/widget.h" -// Borrowed from chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc, -// since these are also disabled on Linux for drag and drop. -// TODO(erg): Fix DND tests on linux_aura. crbug.com/163931 -#if defined(OS_LINUX) && defined(USE_AURA) -#define MAYBE(x) DISABLED_##x -#else -#define MAYBE(x) x -#endif - using bookmarks::BookmarkModel; class ToolbarViewInteractiveUITest : public ExtensionBrowserTest { @@ -162,8 +153,19 @@ AppMenuButton::g_open_app_immediately_for_testing = false; } +// Borrowed from chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc, +// since these are also disabled on Linux for drag and drop. +// TODO(erg): Fix DND tests on linux_aura. crbug.com/163931 +#if defined(OS_LINUX) && defined(USE_AURA) +#define MAYBE_TestAppMenuOpensOnDrag DISABLED_TestAppMenuOpensOnDrag +#elif defined(OS_MACOSX) +// Illegal thread join on the UI thread, may fix above: http://crbug.com/824570 +#define MAYBE_TestAppMenuOpensOnDrag DISABLED_TestAppMenuOpensOnDrag +#else +#define MAYBE_TestAppMenuOpensOnDrag TestAppMenuOpensOnDrag +#endif IN_PROC_BROWSER_TEST_F(ToolbarViewInteractiveUITest, - MAYBE(TestAppMenuOpensOnDrag)) { + MAYBE_TestAppMenuOpensOnDrag) { // Load an extension that has a browser action. ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") .AppendASCII("browser_action")
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 4911e866..0a14718 100644 --- a/chrome/browser/ui/webui/signin/inline_login_ui_browsertest.cc +++ b/chrome/browser/ui/webui/signin/inline_login_ui_browsertest.cc
@@ -340,7 +340,7 @@ std::set<content::WebContents*> set; GuestViewManager* manager = GuestViewManager::FromBrowserContext( info.contents->GetBrowserContext()); - manager->ForEachGuest(info.contents, base::Bind(&AddToSet, &set)); + manager->ForEachGuest(info.contents, base::BindRepeating(&AddToSet, &set)); ASSERT_EQ(1u, set.size()); content::WebContents* webview_contents = *set.begin(); content::RenderProcessHost* process =
diff --git a/chrome/browser/ui/webui/signin/signin_utils.cc b/chrome/browser/ui/webui/signin/signin_utils.cc index d6d78cd20..fe68910 100644 --- a/chrome/browser/ui/webui/signin/signin_utils.cc +++ b/chrome/browser/ui/webui/signin/signin_utils.cc
@@ -43,9 +43,9 @@ auto* manager = guest_view::GuestViewManager::FromBrowserContext( web_contents->GetBrowserContext()); if (manager) { - manager->ForEachGuest( - web_contents, - base::Bind(&AddWebContentsToSet, &frame_set, parent_frame_name)); + manager->ForEachGuest(web_contents, + base::BindRepeating(&AddWebContentsToSet, &frame_set, + parent_frame_name)); } DCHECK_GE(1U, frame_set.size()); if (!frame_set.empty())
diff --git a/chrome/browser/ui/webui/web_ui_test_handler.cc b/chrome/browser/ui/webui/web_ui_test_handler.cc index 8ae8cd3..be8e805 100644 --- a/chrome/browser/ui/webui/web_ui_test_handler.cc +++ b/chrome/browser/ui/webui/web_ui_test_handler.cc
@@ -29,10 +29,11 @@ : test_done_(false), test_succeeded_(false), run_test_done_(false), - run_test_succeeded_(false), - is_waiting_(false) { + run_test_succeeded_(false) { } +WebUITestHandler::~WebUITestHandler() = default; + void WebUITestHandler::PreloadJavaScript(const base::string16& js_text, RenderViewHost* preload_host) { DCHECK(preload_host); @@ -64,10 +65,8 @@ } void WebUITestHandler::HandleTestResult(const base::ListValue* test_result) { - // Quit the message loop if |is_waiting_| so waiting process can get result or - // error. To ensure this gets done, do this before ASSERT* calls. - if (is_waiting_) - base::RunLoop::QuitCurrentWhenIdleDeprecated(); + // To ensure this gets done, do this before ASSERT* calls. + quit_closure_.Run(); SCOPED_TRACE("WebUITestHandler::HandleTestResult"); @@ -84,10 +83,8 @@ } void WebUITestHandler::JavaScriptComplete(const base::Value* result) { - // Quit the message loop if |is_waiting_| so waiting process can get result or - // error. To ensure this gets done, do this before ASSERT* calls. - if (is_waiting_) - base::RunLoop::QuitCurrentWhenIdleDeprecated(); + // To ensure this gets done, do this before ASSERT* calls. + quit_closure_.Run(); SCOPED_TRACE("WebUITestHandler::JavaScriptComplete"); @@ -102,21 +99,24 @@ SCOPED_TRACE("WebUITestHandler::WaitForResult"); test_done_ = false; run_test_done_ = false; - is_waiting_ = true; // Either sync test completion or the testDone() will cause message loop // to quit. - content::RunMessageLoop(); + { + base::RunLoop run_loop; + quit_closure_ = run_loop.QuitWhenIdleClosure(); + content::RunThisRunLoop(&run_loop); + } // Run a second message loop when not |run_test_done_| so that the sync test // completes, or |run_test_succeeded_| but not |test_done_| so async tests // complete. if (!run_test_done_ || (run_test_succeeded_ && !test_done_)) { - content::RunMessageLoop(); + base::RunLoop run_loop; + quit_closure_ = run_loop.QuitWhenIdleClosure(); + content::RunThisRunLoop(&run_loop); } - is_waiting_ = false; - // To succeed the test must execute as well as pass the test. return run_test_succeeded_ && test_succeeded_; }
diff --git a/chrome/browser/ui/webui/web_ui_test_handler.h b/chrome/browser/ui/webui/web_ui_test_handler.h index cb95c2f..cba7ccf 100644 --- a/chrome/browser/ui/webui/web_ui_test_handler.h +++ b/chrome/browser/ui/webui/web_ui_test_handler.h
@@ -23,6 +23,7 @@ class WebUITestHandler : public content::WebUIMessageHandler { public: WebUITestHandler(); + ~WebUITestHandler() override; // Sends a message through |preload_host| with the |js_text| to preload at the // appropriate time before the onload call is made. @@ -66,8 +67,8 @@ // pass/fail. bool run_test_succeeded_; - // Waiting for a test to finish. - bool is_waiting_; + // Quits the currently running RunLoop. + base::Closure quit_closure_; DISALLOW_COPY_AND_ASSIGN(WebUITestHandler); };
diff --git a/chrome/browser/vr/elements/ui_element_name.cc b/chrome/browser/vr/elements/ui_element_name.cc index 0c687d6..d089e60 100644 --- a/chrome/browser/vr/elements/ui_element_name.cc +++ b/chrome/browser/vr/elements/ui_element_name.cc
@@ -143,6 +143,7 @@ "kControllerTrackpadRepositionLabel", "kControllerExitButtonLabel", "kControllerBackButtonLabel", + "kControllerRepositionFinishLabel", "kControllerTouchpadButton", "kControllerAppButton", "kControllerHomeButton",
diff --git a/chrome/browser/vr/elements/ui_element_name.h b/chrome/browser/vr/elements/ui_element_name.h index 1548b8b..7921f93 100644 --- a/chrome/browser/vr/elements/ui_element_name.h +++ b/chrome/browser/vr/elements/ui_element_name.h
@@ -142,6 +142,7 @@ kControllerTrackpadRepositionLabel, kControllerExitButtonLabel, kControllerBackButtonLabel, + kControllerRepositionFinishLabel, kControllerTouchpadButton, kControllerAppButton, kControllerHomeButton,
diff --git a/chrome/browser/vr/ui_scene_creator.cc b/chrome/browser/vr/ui_scene_creator.cc index 68a1cfb..8fc02c4 100644 --- a/chrome/browser/vr/ui_scene_creator.cc +++ b/chrome/browser/vr/ui_scene_creator.cc
@@ -1646,10 +1646,16 @@ kControllerBackButtonLabel, kControllerBackButtonOffset, l10n_util::GetStringUTF16(IDS_VR_BUTTON_BACK), model_); VR_BIND_VISIBILITY(back_button_label, model->omnibox_editing_enabled() || - model->voice_search_enabled() || - model->reposition_window_enabled()); + model->voice_search_enabled()); callout_group->AddChild(std::move(back_button_label)); + auto reposition_finish_button = CreateControllerLabel( + kControllerRepositionFinishLabel, kControllerBackButtonOffset, + l10n_util::GetStringUTF16(IDS_VR_BUTTON_APP_REPOSITION), model_); + VR_BIND_VISIBILITY(reposition_finish_button, + model->reposition_window_enabled()); + callout_group->AddChild(std::move(reposition_finish_button)); + controller->AddChild(std::move(callout_group)); scene_->AddUiElement(kControllerGroup, std::move(controller));
diff --git a/chrome/browser/vr/ui_unittest.cc b/chrome/browser/vr/ui_unittest.cc index abd493d0..6ab4fa3 100644 --- a/chrome/browser/vr/ui_unittest.cc +++ b/chrome/browser/vr/ui_unittest.cc
@@ -1193,7 +1193,7 @@ EXPECT_FALSE(IsVisible(kControllerTrackpadLabel)); EXPECT_TRUE(IsVisible(kControllerTrackpadRepositionLabel)); EXPECT_FALSE(IsVisible(kControllerExitButtonLabel)); - EXPECT_TRUE(IsVisible(kControllerBackButtonLabel)); + EXPECT_TRUE(IsVisible(kControllerRepositionFinishLabel)); model_->controller.resting_in_viewport = false; EXPECT_FALSE(IsVisible(kControllerTrackpadRepositionLabel));
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn index 0a7569b..dc302a1 100644 --- a/chrome/common/BUILD.gn +++ b/chrome/common/BUILD.gn
@@ -339,8 +339,6 @@ "extensions/manifest_handlers/app_launch_info.h", "extensions/manifest_handlers/app_theme_color_info.cc", "extensions/manifest_handlers/app_theme_color_info.h", - "extensions/manifest_handlers/automation.cc", - "extensions/manifest_handlers/automation.h", "extensions/manifest_handlers/extension_action_handler.cc", "extensions/manifest_handlers/extension_action_handler.h", "extensions/manifest_handlers/linked_app_icons.cc",
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc index 66135ea..2faf3c1 100644 --- a/chrome/common/chrome_features.cc +++ b/chrome/common/chrome_features.cc
@@ -37,10 +37,6 @@ const base::Feature kFullscreenToolbarReveal{"FullscreenToolbarReveal", base::FEATURE_ENABLED_BY_DEFAULT}; -// Use toolkit-views for profile chooser menu. -const base::Feature kViewsProfileChooser{"ViewsProfileChooser", - base::FEATURE_ENABLED_BY_DEFAULT}; - // Use the Toolkit-Views Task Manager window. const base::Feature kViewsTaskManager{"ViewsTaskManager", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h index f86990b..561702fb 100644 --- a/chrome/common/chrome_features.h +++ b/chrome/common/chrome_features.h
@@ -32,7 +32,6 @@ #if defined(OS_MACOSX) extern const base::Feature kAppleScriptExecuteJavaScriptMenuItem; extern const base::Feature kShow10_9ObsoleteInfobar; -extern const base::Feature kViewsProfileChooser; extern const base::Feature kViewsTaskManager; #endif // defined(OS_MACOSX)
diff --git a/chrome/common/extensions/api/manifest_types.json b/chrome/common/extensions/api/manifest_types.json index ed2f1315..667967fd 100644 --- a/chrome/common/extensions/api/manifest_types.json +++ b/chrome/common/extensions/api/manifest_types.json
@@ -130,34 +130,6 @@ } }, { - "id": "automation", - "description": "This API provides programmatic access to the user interface elements of Chrome. This includes everything in the web view, and optionally Chrome's full user interface.", - "choices": [ - { "type": "boolean", - "description": "If true, enables non-interactive access to the automation tree only for the sites for which the extension has a <a href='https://developer.chrome.com/extensions/declare_permissions#host-permissions'>host permission</a> or <a href='https://developer.chrome.com/extensions/declare_permissions#activeTab'>activeTab permission</a>)." }, - { "type": "object", - "properties": { - "desktop": { - "description": "Whether to request permission to the whole ChromeOS desktop. If granted, this gives the extension access to every aspect of the desktop, and every site and app. If this permission is requested, all other permissions are implicitly included and do not need to be requested separately.", - "optional": true, - "type": "boolean" - }, - "matches": { - "description": "A list of URL patterns for which this extension may request an automation tree. If not specified, automation permission will be granted for the sites for which the extension has a <a href='https://developer.chrome.com/extensions/declare_permissions#host-permissions'>host permission</a> or <a href='https://developer.chrome.com/extensions/declare_permissions#activeTab'>activeTab permission</a>).", - "optional": true, - "type": "array", - "items": { "type": "string" } - }, - "interact": { - "description": "Whether the extension is allowed interactive access (true) or read-only access (false; default) to the automation tree.", - "optional": true, - "type": "boolean" - } - } - } - ] - }, - { "id": "FileSystemProviderSource", "type": "string", "description": "For <code>\"file\"</code> the source is a file passed via <code>onLaunched</code> event. For <code>\"device\"</code> contents are fetched from an external device (eg. plugged via USB), without using <code>file_handlers</code>. Finally, for <code>\"network\"</code> source, contents should be fetched via network.",
diff --git a/chrome/common/extensions/chrome_manifest_handlers.cc b/chrome/common/extensions/chrome_manifest_handlers.cc index a64f7a5b..64965ac9 100644 --- a/chrome/common/extensions/chrome_manifest_handlers.cc +++ b/chrome/common/extensions/chrome_manifest_handlers.cc
@@ -16,7 +16,6 @@ #include "chrome/common/extensions/manifest_handlers/app_icon_color_info.h" #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" #include "chrome/common/extensions/manifest_handlers/app_theme_color_info.h" -#include "chrome/common/extensions/manifest_handlers/automation.h" #include "chrome/common/extensions/manifest_handlers/extension_action_handler.h" #include "chrome/common/extensions/manifest_handlers/linked_app_icons.h" #include "chrome/common/extensions/manifest_handlers/minimum_chrome_version_checker.h" @@ -24,6 +23,7 @@ #include "chrome/common/extensions/manifest_handlers/theme_handler.h" #include "chrome/common/extensions/manifest_handlers/ui_overrides_handler.h" #include "extensions/common/manifest_handlers/app_isolation_info.h" +#include "extensions/common/manifest_handlers/automation.h" #include "extensions/common/manifest_handlers/content_scripts_handler.h" #include "extensions/common/manifest_handlers/options_page_info.h" #include "extensions/common/manifest_url_handlers.h"
diff --git a/chrome/common/extensions/docs/static/images/get_started/click_detials.png b/chrome/common/extensions/docs/static/images/get_started/click_detials.png new file mode 100644 index 0000000..b09213a --- /dev/null +++ b/chrome/common/extensions/docs/static/images/get_started/click_detials.png Binary files differ
diff --git a/chrome/common/extensions/docs/static/images/get_started/load_extension.png b/chrome/common/extensions/docs/static/images/get_started/load_extension.png index 97c542f5..cd59caa 100644 --- a/chrome/common/extensions/docs/static/images/get_started/load_extension.png +++ b/chrome/common/extensions/docs/static/images/get_started/load_extension.png Binary files differ
diff --git a/chrome/common/extensions/docs/static/images/get_started/options.png b/chrome/common/extensions/docs/static/images/get_started/options.png index 40d44fdf..2ad17d77 100644 --- a/chrome/common/extensions/docs/static/images/get_started/options.png +++ b/chrome/common/extensions/docs/static/images/get_started/options.png Binary files differ
diff --git a/chrome/common/extensions/docs/static/images/get_started/view_background.png b/chrome/common/extensions/docs/static/images/get_started/view_background.png index 8c196085..9034e983 100644 --- a/chrome/common/extensions/docs/static/images/get_started/view_background.png +++ b/chrome/common/extensions/docs/static/images/get_started/view_background.png Binary files differ
diff --git a/chrome/common/extensions/docs/templates/articles/getstarted.html b/chrome/common/extensions/docs/templates/articles/getstarted.html index 55af3c4..d71b0fe 100644 --- a/chrome/common/extensions/docs/templates/articles/getstarted.html +++ b/chrome/common/extensions/docs/templates/articles/getstarted.html
@@ -64,15 +64,16 @@ </ul> </li> <li> - Enable Developer Mode by checking the box next to <strong>Developer mode</strong>. + Enable Developer Mode by clicking the toggle switch + next to <strong>Developer mode</strong>. </li> <li> - Click the <strong>Load unpacked extension...</strong> button + Click the <strong>LOAD UNPACKED</strong> button and select the extension directory. </li> </ol> <img src="{{static}}/images/get_started/load_extension.png" - height="200" + height="300" alt="Load Extension" /> <p> Ta-da! @@ -161,7 +162,7 @@ with a blue link, <strong>background page</strong>. </p> <img src="{{static}}/images/get_started/view_background.png" - height="200" + height="300" alt="Inspect Views" /> <p> Click the link to view the background script's console log, @@ -467,11 +468,18 @@ } </pre> <p> - Reload the extension then select the new options link to view the page, + Reload the extension and click <strong>DETIALS</strong>. +</p> +<img src="{{static}}/images/get_started/click_detials.png" + height="300" + alt="Inspect Views" /> +<p> + Scroll down the detials page and select <strong>Extension options</strong> + to view the options page, although it will currently appear blank. </p> <img src="{{static}}/images/get_started/options.png" - height="200" + height="500" alt="Inspect Views" /> <p> Last step is to add the options logic.
diff --git a/chrome/common/extensions/manifest_handlers/automation_unittest.cc b/chrome/common/extensions/manifest_handlers/automation_unittest.cc index 2a3650b..a8a4460 100644 --- a/chrome/common/extensions/manifest_handlers/automation_unittest.cc +++ b/chrome/common/extensions/manifest_handlers/automation_unittest.cc
@@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/common/extensions/manifest_handlers/automation.h" #include "chrome/common/extensions/manifest_tests/chrome_manifest_test.h" #include "chrome/grit/generated_resources.h" #include "components/version_info/version_info.h" #include "extensions/common/error_utils.h" #include "extensions/common/features/feature_channel.h" #include "extensions/common/manifest_constants.h" +#include "extensions/common/manifest_handlers/automation.h" #include "extensions/common/permissions/permission_message_test_util.h" #include "extensions/common/permissions/permissions_data.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/renderer/extensions/automation_internal_custom_bindings.cc b/chrome/renderer/extensions/automation_internal_custom_bindings.cc index 83c9a76..3bd89498 100644 --- a/chrome/renderer/extensions/automation_internal_custom_bindings.cc +++ b/chrome/renderer/extensions/automation_internal_custom_bindings.cc
@@ -15,12 +15,12 @@ #include "base/values.h" #include "chrome/common/extensions/api/automation_api_constants.h" #include "chrome/common/extensions/chrome_extension_messages.h" -#include "chrome/common/extensions/manifest_handlers/automation.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_view.h" #include "extensions/common/extension.h" #include "extensions/common/manifest.h" +#include "extensions/common/manifest_handlers/automation.h" #include "extensions/common/manifest_handlers/background_info.h" #include "extensions/renderer/extension_bindings_system.h" #include "extensions/renderer/script_context.h"
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index b6f073f..186dc4c 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -4025,7 +4025,6 @@ "../browser/ui/cocoa/profiles/avatar_button_controller_unittest.mm", "../browser/ui/cocoa/profiles/avatar_button_unittest.mm", "../browser/ui/cocoa/profiles/avatar_icon_controller_unittest.mm", - "../browser/ui/cocoa/profiles/profile_chooser_controller_unittest.mm", "../browser/ui/cocoa/profiles/profile_menu_controller_unittest.mm", "../browser/ui/cocoa/profiles/user_manager_mac_unittest.mm", "../browser/ui/cocoa/scoped_menu_bar_lock_unittest.mm",
diff --git a/chrome/test/data/printing/test_extension/manifest.json b/chrome/test/data/printing/test_extension/manifest.json new file mode 100644 index 0000000..78b73230 --- /dev/null +++ b/chrome/test/data/printing/test_extension/manifest.json
@@ -0,0 +1,13 @@ +{ + "name": "Test Extensions", + "description" : "Base Level Extension", + "version": "1.0", + "manifest_version": 2, + "browser_action": { + "default_popup": "test_extension.html" + }, + "options_ui": { + "page": "options.html", + "chrome_style": true + } +}
diff --git a/chrome/test/data/printing/test_extension/options.html b/chrome/test/data/printing/test_extension/options.html new file mode 100644 index 0000000..7866c31 --- /dev/null +++ b/chrome/test/data/printing/test_extension/options.html
@@ -0,0 +1,6 @@ +<html> + <body> + <h2>Test Extension's Option Page</h2> + <p>No content here, for test only.</p> + </body> +</html>
diff --git a/chrome/test/data/printing/test_extension/test_extension.html b/chrome/test/data/printing/test_extension/test_extension.html new file mode 100644 index 0000000..c138e43 --- /dev/null +++ b/chrome/test/data/printing/test_extension/test_extension.html
@@ -0,0 +1,5 @@ +<html> + <body> + <h1>Test Extensions</h1> + </body> +</html>
diff --git a/components/pdf/DEPS b/components/pdf/DEPS index ffff8fe..4cc7efa 100644 --- a/components/pdf/DEPS +++ b/components/pdf/DEPS
@@ -1,11 +1,5 @@ include_rules = [ - "+content/public", + "+content/public/common", "+ipc", - "+ppapi", - "+third_party/skia/include", - "+third_party/WebKit/public", - "+ui/base", "+ui/gfx", - "+ui/strings", - "+ui/touch_selection", ]
diff --git a/components/pdf/browser/DEPS b/components/pdf/browser/DEPS new file mode 100644 index 0000000..1b80679 --- /dev/null +++ b/components/pdf/browser/DEPS
@@ -0,0 +1,5 @@ +include_rules = [ + "+content/public/browser", + "+ui/touch_selection", + "+ui/strings", +]
diff --git a/components/pdf/renderer/DEPS b/components/pdf/renderer/DEPS index 9401253..fc10b69 100644 --- a/components/pdf/renderer/DEPS +++ b/components/pdf/renderer/DEPS
@@ -1,8 +1,10 @@ include_rules = [ + "+content/public/renderer", "+components/strings/grit/components_strings.h", - "+gin", "+mojo/public/cpp/bindings", + "+ppapi", "+skia/ext", + "+third_party/WebKit/public", "+ui/accessibility", - "+ui/gfx", + "+ui/base", ]
diff --git a/components/printing/browser/print_composite_client.cc b/components/printing/browser/print_composite_client.cc index b9ed9c2..45c358b0 100644 --- a/components/printing/browser/print_composite_client.cc +++ b/components/printing/browser/print_composite_client.cc
@@ -68,6 +68,21 @@ content::RenderFrameHost* render_frame_host, int document_cookie, const PrintHostMsg_DidPrintContent_Params& params) { + auto* outer_contents = web_contents()->GetOuterWebContents(); + if (outer_contents) { + // When the printed content belongs to an extension or app page, the print + // composition needs to be handled by its outer content. + // TODO(weili): so far, we don't have printable web contents nested in more + // than one level. In the future, especially after PDF plugin is moved to + // OOPIF-based webview, we should check whether we need to handle web + // contents nested in multiple layers. + auto* outer_client = PrintCompositeClient::FromWebContents(outer_contents); + DCHECK(outer_client); + outer_client->OnDidPrintFrameContent(render_frame_host, document_cookie, + params); + return; + } + // Content in |params| is sent from untrusted source; only minimal processing // is done here. Most of it will be directly forwarded to pdf compositor // service.
diff --git a/components/signin/core/browser/account_fetcher_service.h b/components/signin/core/browser/account_fetcher_service.h index 01ef1ef..993fa67 100644 --- a/components/signin/core/browser/account_fetcher_service.h +++ b/components/signin/core/browser/account_fetcher_service.h
@@ -23,6 +23,10 @@ class OAuth2TokenService; class SigninClient; +namespace base { +class DictionaryValue; +} + namespace image_fetcher { struct RequestMetadata; class ImageDecoder;
diff --git a/components/suggestions/suggestions_service_impl.cc b/components/suggestions/suggestions_service_impl.cc index 31e0d8d..81dab47 100644 --- a/components/suggestions/suggestions_service_impl.cc +++ b/components/suggestions/suggestions_service_impl.cc
@@ -127,7 +127,7 @@ : identity_manager_(identity_manager), sync_service_(sync_service), sync_service_observer_(this), - sync_state_(INITIALIZED_ENABLED_HISTORY), + history_sync_state_(syncer::UploadState::INITIALIZING), url_request_context_(url_request_context), suggestions_store_(std::move(suggestions_store)), thumbnail_manager_(std::move(thumbnail_manager)), @@ -152,7 +152,7 @@ bool SuggestionsServiceImpl::FetchSuggestionsData() { DCHECK(thread_checker_.CalledOnValidThread()); // If sync state allows, issue a network request to refresh the suggestions. - if (sync_state_ != INITIALIZED_ENABLED_HISTORY) { + if (history_sync_state_ != syncer::UploadState::ACTIVE) { return false; } IssueRequestIfNoneOngoing(BuildSuggestionsURL()); @@ -192,7 +192,7 @@ bool SuggestionsServiceImpl::BlacklistURL(const GURL& candidate_url) { DCHECK(thread_checker_.CalledOnValidThread()); - // TODO(treib): Do we need to check |sync_state_| here? + // TODO(treib): Do we need to check |history_sync_state_| here? if (!blacklist_store_->BlacklistUrl(candidate_url)) return false; @@ -211,7 +211,7 @@ bool SuggestionsServiceImpl::UndoBlacklistURL(const GURL& url) { DCHECK(thread_checker_.CalledOnValidThread()); - // TODO(treib): Do we need to check |sync_state_| here? + // TODO(treib): Do we need to check |history_sync_state_| here? TimeDelta time_delta; if (blacklist_store_->GetTimeUntilURLReadyForUpload(url, &time_delta) && @@ -229,7 +229,7 @@ void SuggestionsServiceImpl::ClearBlacklist() { DCHECK(thread_checker_.CalledOnValidThread()); - // TODO(treib): Do we need to check |sync_state_| here? + // TODO(treib): Do we need to check |history_sync_state_| here? blacklist_store_->ClearBlacklist(); callback_list_.Notify( @@ -308,55 +308,41 @@ kDeviceType)); } -SuggestionsServiceImpl::SyncState SuggestionsServiceImpl::ComputeSyncState() - const { - if (!sync_service_ || !sync_service_->CanSyncStart() || - sync_service_->IsLocalSyncEnabled() || - sync_service_->GetAuthError().IsPersistentError()) { - return SYNC_OR_HISTORY_SYNC_DISABLED; - } - if (!sync_service_->IsSyncActive() || !sync_service_->ConfigurationDone()) { - return NOT_INITIALIZED_ENABLED; - } - return sync_service_->GetActiveDataTypes().Has( - syncer::HISTORY_DELETE_DIRECTIVES) - ? INITIALIZED_ENABLED_HISTORY - : SYNC_OR_HISTORY_SYNC_DISABLED; -} - SuggestionsServiceImpl::RefreshAction -SuggestionsServiceImpl::RefreshSyncState() { - SyncState new_sync_state = ComputeSyncState(); - if (sync_state_ == new_sync_state) { +SuggestionsServiceImpl::RefreshHistorySyncState() { + syncer::UploadState new_sync_state = syncer::GetUploadToGoogleState( + sync_service_, syncer::HISTORY_DELETE_DIRECTIVES); + if (history_sync_state_ == new_sync_state) return NO_ACTION; - } - SyncState old_sync_state = sync_state_; - sync_state_ = new_sync_state; + syncer::UploadState old_sync_state = history_sync_state_; + history_sync_state_ = new_sync_state; switch (new_sync_state) { - case NOT_INITIALIZED_ENABLED: - break; - case INITIALIZED_ENABLED_HISTORY: - // If the user just signed in, we fetch suggestions, so that hopefully the - // next NTP will already get them. - if (old_sync_state == SYNC_OR_HISTORY_SYNC_DISABLED) { + case syncer::UploadState::INITIALIZING: + // In this state, we do not issue server requests, but we will serve from + // cache if available -> no action required. + return NO_ACTION; + case syncer::UploadState::ACTIVE: + // If history sync was just enabled, immediately fetch suggestions, so + // that hopefully the next NTP will already get them. + if (old_sync_state == syncer::UploadState::NOT_ACTIVE) return FETCH_SUGGESTIONS; - } - break; - case SYNC_OR_HISTORY_SYNC_DISABLED: + // Otherwise, this just means sync initialization finished. + return NO_ACTION; + case syncer::UploadState::NOT_ACTIVE: // If the user signed out (or disabled history sync), we have to clear // everything. return CLEAR_SUGGESTIONS; } - // Otherwise, there's nothing to do. + NOTREACHED(); return NO_ACTION; } void SuggestionsServiceImpl::OnStateChanged(syncer::SyncService* sync) { DCHECK(sync_service_ == sync); - switch (RefreshSyncState()) { + switch (RefreshHistorySyncState()) { case NO_ACTION: break; case CLEAR_SUGGESTIONS:
diff --git a/components/suggestions/suggestions_service_impl.h b/components/suggestions/suggestions_service_impl.h index cc20f12f..5a84eef5 100644 --- a/components/suggestions/suggestions_service_impl.h +++ b/components/suggestions/suggestions_service_impl.h
@@ -24,6 +24,7 @@ #include "components/suggestions/proto/suggestions.pb.h" #include "components/suggestions/suggestions_service.h" #include "components/sync/driver/sync_service_observer.h" +#include "components/sync/driver/sync_service_utils.h" #include "google_apis/gaia/google_service_auth_error.h" #include "net/base/backoff_entry.h" #include "net/url_request/url_fetcher_delegate.h" @@ -111,7 +112,8 @@ SYNC_OR_HISTORY_SYNC_DISABLED, }; - // The action that should be taken as the result of a RefreshSyncState call. + // The action that should be taken as the result of a RefreshHistorySyncState + // call. enum RefreshAction { NO_ACTION, FETCH_SUGGESTIONS, CLEAR_SUGGESTIONS }; // Helpers to build the various suggestions URLs. These are static members @@ -122,12 +124,9 @@ static GURL BuildSuggestionsBlacklistURL(const GURL& candidate_url); static GURL BuildSuggestionsBlacklistClearURL(); - // Computes the appropriate SyncState from |sync_service_|. - SyncState ComputeSyncState() const; - - // Re-computes |sync_state_| from the sync service. Returns the action that - // should be taken in response. - RefreshAction RefreshSyncState() WARN_UNUSED_RESULT; + // Re-computes |history_sync_state_| from the sync service. Returns the action + // that should be taken in response. + RefreshAction RefreshHistorySyncState() WARN_UNUSED_RESULT; // syncer::SyncServiceObserver implementation. void OnStateChanged(syncer::SyncService* sync) override; @@ -182,7 +181,8 @@ ScopedObserver<syncer::SyncService, syncer::SyncServiceObserver> sync_service_observer_; - SyncState sync_state_; + // The state of history sync, i.e. are we uploading history data to Google? + syncer::UploadState history_sync_state_; net::URLRequestContextGetter* url_request_context_;
diff --git a/components/suggestions/suggestions_service_impl_unittest.cc b/components/suggestions/suggestions_service_impl_unittest.cc index 75fc3f27..c170e43d 100644 --- a/components/suggestions/suggestions_service_impl_unittest.cc +++ b/components/suggestions/suggestions_service_impl_unittest.cc
@@ -87,6 +87,9 @@ MOCK_CONST_METHOD0(CanSyncStart, bool()); MOCK_CONST_METHOD0(IsSyncActive, bool()); MOCK_CONST_METHOD0(ConfigurationDone, bool()); + MOCK_CONST_METHOD0(IsLocalSyncEnabled, bool()); + MOCK_CONST_METHOD0(IsUsingSecondaryPassphrase, bool()); + MOCK_CONST_METHOD0(GetPreferredDataTypes, syncer::ModelTypeSet()); MOCK_CONST_METHOD0(GetActiveDataTypes, syncer::ModelTypeSet()); }; @@ -157,6 +160,16 @@ EXPECT_CALL(*sync_service(), ConfigurationDone()) .Times(AnyNumber()) .WillRepeatedly(Return(true)); + EXPECT_CALL(*sync_service(), IsLocalSyncEnabled()) + .Times(AnyNumber()) + .WillRepeatedly(Return(false)); + EXPECT_CALL(*sync_service(), IsUsingSecondaryPassphrase()) + .Times(AnyNumber()) + .WillRepeatedly(Return(false)); + EXPECT_CALL(*sync_service(), GetPreferredDataTypes()) + .Times(AnyNumber()) + .WillRepeatedly( + Return(syncer::ModelTypeSet(syncer::HISTORY_DELETE_DIRECTIVES))); EXPECT_CALL(*sync_service(), GetActiveDataTypes()) .Times(AnyNumber()) .WillRepeatedly(
diff --git a/components/sync/driver/sync_service_utils.cc b/components/sync/driver/sync_service_utils.cc index 9e6ab81..bdef1f0 100644 --- a/components/sync/driver/sync_service_utils.cc +++ b/components/sync/driver/sync_service_utils.cc
@@ -23,7 +23,8 @@ // Note: Before configuration is done, GetPreferredDataTypes returns // "everything" (i.e. the default setting). If a data type is missing there, // it must be because the user explicitly disabled it. - if (!sync_service->CanSyncStart() || sync_service->IsLocalSyncEnabled() || + if (!sync_service || !sync_service->CanSyncStart() || + sync_service->IsLocalSyncEnabled() || !sync_service->GetPreferredDataTypes().Has(type) || sync_service->GetAuthError().IsPersistentError() || sync_service->IsUsingSecondaryPassphrase()) {
diff --git a/content/browser/android/overscroll_controller_android.cc b/content/browser/android/overscroll_controller_android.cc index 912c9813..f2b1e44b 100644 --- a/content/browser/android/overscroll_controller_android.cc +++ b/content/browser/android/overscroll_controller_android.cc
@@ -33,18 +33,17 @@ namespace content { namespace { -// Used for conditional creation of EdgeEffect types for the overscroll glow. -const int kAndroidLSDKVersion = 21; - // If the glow effect alpha is greater than this value, the refresh effect will // be suppressed. This value was experimentally determined to provide a // reasonable balance between avoiding accidental refresh activation and // minimizing the wait required to refresh after the glow has been triggered. const float kMinGlowAlphaToDisableRefreshOnL = 0.085f; +// Used for conditional creation of EdgeEffect types for the overscroll glow. bool IsAndroidLOrNewer() { static bool android_l_or_newer = - base::android::BuildInfo::GetInstance()->sdk_int() >= kAndroidLSDKVersion; + base::android::BuildInfo::GetInstance()->sdk_int() >= + base::android::SDK_VERSION_LOLLIPOP; return android_l_or_newer; }
diff --git a/content/browser/browser_plugin/browser_plugin_embedder.cc b/content/browser/browser_plugin/browser_plugin_embedder.cc index d3bf567..c3e3bbd 100644 --- a/content/browser/browser_plugin/browser_plugin_embedder.cc +++ b/content/browser/browser_plugin/browser_plugin_embedder.cc
@@ -60,7 +60,8 @@ return; GetBrowserPluginGuestManager()->ForEachGuest( - web_contents(), base::Bind(&BrowserPluginEmbedder::CancelDialogs)); + web_contents(), + base::BindRepeating(&BrowserPluginEmbedder::CancelDialogs)); } void BrowserPluginEmbedder::StartDrag(BrowserPluginGuest* guest) { @@ -101,7 +102,7 @@ GetBrowserPluginGuestManager()->ForEachGuest( web_contents(), - base::Bind(&BrowserPluginEmbedder::DidSendScreenRectsCallback)); + base::BindRepeating(&BrowserPluginEmbedder::DidSendScreenRectsCallback)); } bool BrowserPluginEmbedder::OnMessageReceived( @@ -179,8 +180,9 @@ bool event_consumed = false; GetBrowserPluginGuestManager()->ForEachGuest( web_contents(), - base::Bind(&BrowserPluginEmbedder::UnlockMouseIfNecessaryCallback, - &event_consumed)); + base::BindRepeating( + &BrowserPluginEmbedder::UnlockMouseIfNecessaryCallback, + &event_consumed)); return event_consumed; } @@ -204,7 +206,8 @@ return GetBrowserPluginGuestManager()->ForEachGuest( web_contents(), - base::Bind(&BrowserPluginEmbedder::GuestRecentlyAudibleCallback)); + base::BindRepeating( + &BrowserPluginEmbedder::GuestRecentlyAudibleCallback)); } // static
diff --git a/content/browser/download/mhtml_generation_browsertest.cc b/content/browser/download/mhtml_generation_browsertest.cc index 38e8ca67..a9c682d261 100644 --- a/content/browser/download/mhtml_generation_browsertest.cc +++ b/content/browser/download/mhtml_generation_browsertest.cc
@@ -120,9 +120,8 @@ histogram_tester_.reset(new base::HistogramTester()); shell()->web_contents()->GenerateMHTML( - params, base::Bind(&MHTMLGenerationTest::MHTMLGenerated, - base::Unretained(this), - run_loop.QuitClosure())); + params, base::BindOnce(&MHTMLGenerationTest::MHTMLGenerated, + base::Unretained(this), run_loop.QuitClosure())); // Block until the MHTML is generated. run_loop.Run();
diff --git a/content/browser/download/mhtml_generation_manager.cc b/content/browser/download/mhtml_generation_manager.cc index 22eaed1c..102ce72 100644 --- a/content/browser/download/mhtml_generation_manager.cc +++ b/content/browser/download/mhtml_generation_manager.cc
@@ -51,14 +51,14 @@ Job(int job_id, WebContents* web_contents, const MHTMLGenerationParams& params, - const GenerateMHTMLCallback& callback); + GenerateMHTMLCallback callback); ~Job() override; int id() const { return job_id_; } void set_browser_file(base::File file) { browser_file_ = std::move(file); } base::TimeTicks creation_time() const { return creation_time_; } - const GenerateMHTMLCallback& callback() const { return callback_; } + GenerateMHTMLCallback callback() { return std::move(callback_); } // Indicates whether we expect a message from the |sender| at this time. // We expect only one message per frame - therefore calling this method @@ -88,7 +88,7 @@ // back on the UI thread with the updated status and file size (which will be // negative in case of errors). void CloseFile( - base::Callback<void(const std::tuple<MhtmlSaveStatus, int64_t>&)> + base::OnceCallback<void(const std::tuple<MhtmlSaveStatus, int64_t>&)> callback, MhtmlSaveStatus save_status); @@ -175,7 +175,7 @@ std::string salt_; // The callback to call once generation is complete. - const GenerateMHTMLCallback callback_; + GenerateMHTMLCallback callback_; // Whether the job is finished (set to true only for the short duration of // time between MHTMLGenerationManager::JobFinished is called and the job is @@ -195,14 +195,14 @@ MHTMLGenerationManager::Job::Job(int job_id, WebContents* web_contents, const MHTMLGenerationParams& params, - const GenerateMHTMLCallback& callback) + GenerateMHTMLCallback callback) : job_id_(job_id), creation_time_(base::TimeTicks::Now()), params_(params), frame_tree_node_id_of_busy_frame_(FrameTreeNode::kFrameTreeNodeInvalidId), mhtml_boundary_marker_(net::GenerateMimeMultipartBoundary()), salt_(base::GenerateGUID()), - callback_(callback), + callback_(std::move(callback)), is_finished_(false), observed_renderer_process_host_(this) { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -371,7 +371,8 @@ } void MHTMLGenerationManager::Job::CloseFile( - base::Callback<void(const std::tuple<MhtmlSaveStatus, int64_t>&)> callback, + base::OnceCallback<void(const std::tuple<MhtmlSaveStatus, int64_t>&)> + callback, MhtmlSaveStatus save_status) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!mhtml_boundary_marker_.empty()); @@ -387,7 +388,7 @@ // If no previous error occurred the boundary should be sent. base::PostTaskAndReplyWithResult( download::GetDownloadTaskRunner().get(), FROM_HERE, - base::Bind( + base::BindOnce( &MHTMLGenerationManager::Job::FinalizeAndCloseFileOnFileThread, save_status, (save_status == MhtmlSaveStatus::SUCCESS ? mhtml_boundary_marker_ @@ -542,10 +543,10 @@ void MHTMLGenerationManager::SaveMHTML(WebContents* web_contents, const MHTMLGenerationParams& params, - const GenerateMHTMLCallback& callback) { + GenerateMHTMLCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - Job* job = NewJob(web_contents, params, callback); + Job* job = NewJob(web_contents, params, std::move(callback)); TRACE_EVENT_NESTABLE_ASYNC_BEGIN2( "page-serialization", "SavingMhtmlJob", job, "url", web_contents->GetLastCommittedURL().possibly_invalid_spec(), @@ -644,9 +645,9 @@ DCHECK(job); job->MarkAsFinished(); job->CloseFile( - base::Bind(&MHTMLGenerationManager::OnFileClosed, - base::Unretained(this), // Safe b/c |this| is a singleton. - job->id()), + base::BindOnce(&MHTMLGenerationManager::OnFileClosed, + base::Unretained(this), // Safe b/c |this| is a singleton. + job->id()), save_status); } @@ -667,17 +668,18 @@ UMA_HISTOGRAM_ENUMERATION("PageSerialization.MhtmlGeneration.FinalSaveStatus", static_cast<int>(save_status), static_cast<int>(MhtmlSaveStatus::LAST)); - job->callback().Run(save_status == MhtmlSaveStatus::SUCCESS ? file_size : -1); + std::move(job->callback()) + .Run(save_status == MhtmlSaveStatus::SUCCESS ? file_size : -1); id_to_job_.erase(job_id); } MHTMLGenerationManager::Job* MHTMLGenerationManager::NewJob( WebContents* web_contents, const MHTMLGenerationParams& params, - const GenerateMHTMLCallback& callback) { + GenerateMHTMLCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - Job* job = new Job(++next_job_id_, web_contents, params, callback); + Job* job = new Job(++next_job_id_, web_contents, params, std::move(callback)); id_to_job_[job->id()] = base::WrapUnique(job); return job; }
diff --git a/content/browser/download/mhtml_generation_manager.h b/content/browser/download/mhtml_generation_manager.h index e3000153..57a1c7e 100644 --- a/content/browser/download/mhtml_generation_manager.h +++ b/content/browser/download/mhtml_generation_manager.h
@@ -41,13 +41,13 @@ // GenerateMHTMLCallback is called to report completion and status of MHTML // generation. On success |file_size| indicates the size of the // generated file. On failure |file_size| is -1. - typedef base::Callback<void(int64_t file_size)> GenerateMHTMLCallback; + using GenerateMHTMLCallback = base::OnceCallback<void(int64_t file_size)>; // Instructs the RenderFrames in |web_contents| to generate a MHTML // representation of the current page. void SaveMHTML(WebContents* web_contents, const MHTMLGenerationParams& params, - const GenerateMHTMLCallback& callback); + GenerateMHTMLCallback callback); // Handler for FrameHostMsg_SerializeAsMHTMLResponse (a notification from the // renderer that the MHTML generation finished for a single frame). @@ -83,7 +83,7 @@ // Creates and registers a new job. Job* NewJob(WebContents* web_contents, const MHTMLGenerationParams& params, - const GenerateMHTMLCallback& callback); + GenerateMHTMLCallback callback); // Finds job by id. Returns nullptr if no job with a given id was found. Job* FindJob(int job_id);
diff --git a/content/browser/download/save_package.cc b/content/browser/download/save_package.cc index a7d9f445..3c82319 100644 --- a/content/browser/download/save_package.cc +++ b/content/browser/download/save_package.cc
@@ -314,7 +314,7 @@ MHTMLGenerationParams mhtml_generation_params(saved_main_file_path_); web_contents()->GenerateMHTML( mhtml_generation_params, - base::Bind(&SavePackage::OnMHTMLGenerated, this)); + base::BindOnce(&SavePackage::OnMHTMLGenerated, this)); } else { DCHECK_EQ(SAVE_PAGE_TYPE_AS_ONLY_HTML, save_type_); wait_state_ = NET_FILES;
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index 7b509ff..53540189 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -3158,7 +3158,7 @@ GetProcess()->GetID(), GetRoutingID())); #endif // BUILDFLAG(ENABLE_MEDIA_REMOTING) - registry_->AddInterface(base::Bind( + registry_->AddInterface(base::BindRepeating( &KeyboardLockServiceImpl::CreateMojoService, base::Unretained(this))); registry_->AddInterface(base::Bind(&ImageCaptureImpl::Create));
diff --git a/content/browser/keyboard_lock/keyboard_lock_service_impl.cc b/content/browser/keyboard_lock/keyboard_lock_service_impl.cc index a5f583a3..14d29ee 100644 --- a/content/browser/keyboard_lock/keyboard_lock_service_impl.cc +++ b/content/browser/keyboard_lock/keyboard_lock_service_impl.cc
@@ -7,10 +7,18 @@ #include <memory> #include <utility> -#include "base/memory/ptr_util.h" +#include "base/bind.h" +#include "base/callback.h" +#include "base/containers/flat_set.h" +#include "base/logging.h" #include "base/metrics/histogram_macros.h" +#include "base/optional.h" +#include "content/browser/frame_host/render_frame_host_impl.h" +#include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/public/browser/render_frame_host.h" -#include "content/public/browser/web_contents.h" +#include "content/public/browser/render_widget_host.h" +#include "content/public/common/content_features.h" +#include "ui/events/keycodes/dom/keycode_converter.h" namespace content { @@ -36,8 +44,8 @@ KeyboardLockServiceImpl::KeyboardLockServiceImpl( RenderFrameHost* render_frame_host) - : web_contents_(WebContents::FromRenderFrameHost(render_frame_host)) { - DCHECK(web_contents_); + : render_frame_host_(static_cast<RenderFrameHostImpl*>(render_frame_host)) { + DCHECK(render_frame_host_); } KeyboardLockServiceImpl::~KeyboardLockServiceImpl() = default; @@ -59,14 +67,51 @@ else LogKeyboardLockMethodCalled(KeyboardLockMethods::kRequestSomeKeys); - // TODO(joedow): Implementation required. + if (!base::FeatureList::IsEnabled(features::kKeyboardLockAPI)) { + std::move(callback).Run(blink::mojom::KeyboardLockRequestResult::SUCCESS); + return; + } + + if (!render_frame_host_->IsCurrent() || render_frame_host_->GetParent()) { + // TODO(joedow): Return an error code here. + std::move(callback).Run(blink::mojom::KeyboardLockRequestResult::SUCCESS); + return; + } + + // Per base::flat_set usage notes, the proper way to init a flat_set is + // inserting into a vector and using that to init the flat_set. + std::vector<int> native_key_codes; + const int invalid_key_code = ui::KeycodeConverter::InvalidNativeKeycode(); + for (const std::string& code : key_codes) { + int native_key_code = ui::KeycodeConverter::CodeStringToNativeKeycode(code); + if (native_key_code != invalid_key_code) + native_key_codes.push_back(native_key_code); + } + + // If we are provided with a vector containing only invalid keycodes, then + // exit without enabling keyboard lock. An empty vector is treated as + // 'capture all keys' which is not what the caller intended. + if (!key_codes.empty() && native_key_codes.empty()) { + // TODO(joedow): Return an error code here. + std::move(callback).Run(blink::mojom::KeyboardLockRequestResult::SUCCESS); + return; + } + + base::Optional<base::flat_set<int>> key_code_set; + if (!native_key_codes.empty()) + key_code_set = std::move(native_key_codes); + + render_frame_host_->GetRenderWidgetHost()->RequestKeyboardLock( + std::move(key_code_set)); + std::move(callback).Run(blink::mojom::KeyboardLockRequestResult::SUCCESS); } void KeyboardLockServiceImpl::CancelKeyboardLock() { LogKeyboardLockMethodCalled(KeyboardLockMethods::kCancelLock); - // TODO(joedow): Implementation required. + if (base::FeatureList::IsEnabled(features::kKeyboardLockAPI)) + render_frame_host_->GetRenderWidgetHost()->CancelKeyboardLock(); } } // namespace content
diff --git a/content/browser/keyboard_lock/keyboard_lock_service_impl.h b/content/browser/keyboard_lock/keyboard_lock_service_impl.h index e217d71c..f227b578 100644 --- a/content/browser/keyboard_lock/keyboard_lock_service_impl.h +++ b/content/browser/keyboard_lock/keyboard_lock_service_impl.h
@@ -15,7 +15,7 @@ namespace content { class RenderFrameHost; -class WebContents; +class RenderFrameHostImpl; class CONTENT_EXPORT KeyboardLockServiceImpl : public blink::mojom::KeyboardLockService { @@ -27,15 +27,15 @@ RenderFrameHost* render_frame_host, blink::mojom::KeyboardLockServiceRequest request); - // blink::mojom::KeyboardLockService implementations. + // blink::mojom::KeyboardLockService implementation. void RequestKeyboardLock(const std::vector<std::string>& key_codes, RequestKeyboardLockCallback callback) override; void CancelKeyboardLock() override; private: - WebContents* const web_contents_; + RenderFrameHostImpl* const render_frame_host_; }; -} // namespace +} // namespace content #endif // CONTENT_BROWSER_KEYBOARD_LOCK_KEYBOARD_LOCK_SERVICE_IMPL_H_
diff --git a/content/browser/keyboard_lock_browsertest.cc b/content/browser/keyboard_lock_browsertest.cc new file mode 100644 index 0000000..d9afa32 --- /dev/null +++ b/content/browser/keyboard_lock_browsertest.cc
@@ -0,0 +1,580 @@ +// 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 <vector> + +#include "base/test/scoped_feature_list.h" +#include "build/build_config.h" +#include "content/browser/renderer_host/render_widget_host_impl.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/common/content_features.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/content_browser_test.h" +#include "content/public/test/content_browser_test_utils.h" +#include "content/public/test/test_utils.h" +#include "content/shell/browser/shell.h" +#include "content/test/content_browser_test_utils_internal.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "ui/gfx/native_widget_types.h" + +#ifdef USE_AURA +#include "content/browser/renderer_host/render_widget_host_view_aura.h" +#include "content/browser/web_contents/web_contents_view_aura.h" +#endif // USE_AURA + +namespace content { + +namespace { + +// TODO(joedow): Enable tests on additional platforms as they are implemented. +#if defined(OS_WIN) +#define MAYBE_RUN(test_name) test_name +#else +#define MAYBE_RUN(test_name) DISABLED_##test_name +#endif + +constexpr char kFullscreenFrameName[] = "/fullscreen_frame.html"; + +constexpr char kKeyboardLockMethodExistanceCheck[] = + "window.domAutomationController.send(" + " (navigator.keyboard != undefined) &&" + " (navigator.keyboard.lock != undefined));"; + +constexpr char kKeyboardLockMethodCallWithAllKeys[] = + "navigator.keyboard.lock().then(" + " () => { window.domAutomationController.send(true); }," + " () => { window.domAutomationController.send(false); }," + ");"; + +constexpr char kKeyboardLockMethodCallWithSomeKeys[] = + "navigator.keyboard.lock(['MetaLeft', 'Tab', 'AltLeft']).then(" + " () => { window.domAutomationController.send(true); }," + " () => { window.domAutomationController.send(false); }," + ");"; + +constexpr char kKeyboardLockMethodCallWithAllInvalidKeys[] = + "navigator.keyboard.lock(['BlerghLeft', 'BlargRight']).then(" + " () => { window.domAutomationController.send(true); }," + " () => { window.domAutomationController.send(false); }," + ");"; + +constexpr char kKeyboardLockMethodCallWithSomeInvalidKeys[] = + "navigator.keyboard.lock(['Tab', 'BlarghTab', 'Space', 'BlerghLeft']).then(" + " () => { window.domAutomationController.send(true); }," + " () => { window.domAutomationController.send(false); }," + ");"; + +constexpr char kKeyboardUnlockMethodCall[] = "navigator.keyboard.unlock()"; + +bool g_window_has_focus = false; + +#if defined(USE_AURA) +class TestRenderWidgetHostView : public RenderWidgetHostViewAura { + public: + TestRenderWidgetHostView(RenderWidgetHost* host, bool is_guest_view_hack) + : RenderWidgetHostViewAura(host, + is_guest_view_hack, + false /* is_mus_browser_plugin_guest */) {} + ~TestRenderWidgetHostView() override {} + + bool HasFocus() const override { return g_window_has_focus; } + + void OnWindowFocused(aura::Window* gained_focus, + aura::Window* lost_focus) override { + // Ignore all focus events. + } +}; + +void InstallCreateHooksForKeyboardLockBrowserTests() { + WebContentsViewAura::InstallCreateHookForTests( + [](RenderWidgetHost* host, + bool is_guest_view_hack) -> RenderWidgetHostViewAura* { + return new TestRenderWidgetHostView(host, is_guest_view_hack); + }); +} +#endif // USE_AURA + +class FakeKeyboardLockWebContentsDelegate : public WebContentsDelegate { + public: + FakeKeyboardLockWebContentsDelegate() {} + ~FakeKeyboardLockWebContentsDelegate() override {} + + // WebContentsDelegate overrides. + void EnterFullscreenModeForTab(WebContents* web_contents, + const GURL& origin) override; + void ExitFullscreenModeForTab(WebContents* web_contents) override; + bool IsFullscreenForTabOrPending( + const WebContents* web_contents) const override; + + private: + bool is_fullscreen_ = false; +}; + +void FakeKeyboardLockWebContentsDelegate::EnterFullscreenModeForTab( + WebContents* web_contents, + const GURL& origin) { + is_fullscreen_ = true; +} + +void FakeKeyboardLockWebContentsDelegate::ExitFullscreenModeForTab( + WebContents* web_contents) { + is_fullscreen_ = false; +} + +bool FakeKeyboardLockWebContentsDelegate::IsFullscreenForTabOrPending( + const WebContents* web_contents) const { + return is_fullscreen_; +} + +} // namespace + +class KeyboardLockBrowserTest : public ContentBrowserTest { + public: + KeyboardLockBrowserTest(); + ~KeyboardLockBrowserTest() override; + + protected: + // ContentBrowserTest overrides. + void SetUp() override; + void SetUpCommandLine(base::CommandLine* command_line) override; + void SetUpOnMainThread() override; + + virtual void SetUpFeatureList(); + + // Helper methods for common tasks. + bool KeyboardLockApiExists(); + void NavigateToTestURL(); + void RequestKeyboardLock(const base::Location& from_here, + bool lock_all_keys = true); + void CancelKeyboardLock(const base::Location& from_here); + void EnterFullscreen(const base::Location& from_here); + void ExitFullscreen(const base::Location& from_here); + void FocusContent(const base::Location& from_here); + void BlurContent(const base::Location& from_here); + void VerifyKeyboardLockState(const base::Location& from_here); + + WebContentsImpl* web_contents() const { + return static_cast<WebContentsImpl*>(shell()->web_contents()); + } + + net::EmbeddedTestServer* https_test_server() { return &https_test_server_; } + + GURL https_fullscreen_frame() { + return https_test_server()->GetURL(kFullscreenFrameName); + } + + base::test::ScopedFeatureList* feature_list() { + return &scoped_feature_list_; + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; + net::EmbeddedTestServer https_test_server_; + FakeKeyboardLockWebContentsDelegate web_contents_delegate_; +}; + +KeyboardLockBrowserTest::KeyboardLockBrowserTest() + : https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {} + +KeyboardLockBrowserTest::~KeyboardLockBrowserTest() = default; + +void KeyboardLockBrowserTest::SetUp() { + // Assume we have focus to start with. + g_window_has_focus = true; +#if defined(USE_AURA) + InstallCreateHooksForKeyboardLockBrowserTests(); +#endif + SetUpFeatureList(); + ContentBrowserTest::SetUp(); +} + +void KeyboardLockBrowserTest::SetUpCommandLine( + base::CommandLine* command_line) { + IsolateAllSitesForTesting(command_line); +} + +void KeyboardLockBrowserTest::SetUpOnMainThread() { + web_contents()->SetDelegate(&web_contents_delegate_); + + // KeyboardLock requires a secure context (HTTPS). + https_test_server()->AddDefaultHandlers( + base::FilePath(FILE_PATH_LITERAL("content/test/data"))); + ASSERT_TRUE(https_test_server()->Start()); +} + +void KeyboardLockBrowserTest::SetUpFeatureList() { + feature_list()->InitAndEnableFeature(features::kKeyboardLockAPI); +} + +bool KeyboardLockBrowserTest::KeyboardLockApiExists() { + bool api_exists = false; + EXPECT_TRUE(ExecuteScriptAndExtractBool( + web_contents(), kKeyboardLockMethodExistanceCheck, &api_exists)); + return api_exists; +} + +void KeyboardLockBrowserTest::NavigateToTestURL() { + ASSERT_TRUE(NavigateToURL(shell(), https_fullscreen_frame())); + + ASSERT_TRUE(KeyboardLockApiExists()); + + // Ensure the window has focus and is in windowed mode after the navigation. + FocusContent(FROM_HERE); + ExitFullscreen(FROM_HERE); +} + +void KeyboardLockBrowserTest::RequestKeyboardLock( + const base::Location& from_here, + bool lock_all_keys /*=true*/) { + bool result; + // keyboardLock() is an async call which requires a promise handling dance. + ASSERT_TRUE(ExecuteScriptAndExtractBool( + web_contents(), + lock_all_keys ? kKeyboardLockMethodCallWithAllKeys + : kKeyboardLockMethodCallWithSomeKeys, + &result)) + << "Location: " << from_here.ToString(); + + ASSERT_TRUE(result) << "Location: " << from_here.ToString(); + + ASSERT_EQ(result, web_contents()->GetKeyboardLockWidget() != nullptr) + << "Location: " << from_here.ToString(); + + VerifyKeyboardLockState(from_here); +} + +void KeyboardLockBrowserTest::CancelKeyboardLock( + const base::Location& from_here) { + // keyboardUnlock() is a synchronous call. + ASSERT_TRUE(ExecuteScript(web_contents(), kKeyboardUnlockMethodCall)); + + ASSERT_EQ(nullptr, web_contents()->GetKeyboardLockWidget()) + << "Location: " << from_here.ToString(); + + VerifyKeyboardLockState(from_here); +} + +void KeyboardLockBrowserTest::EnterFullscreen(const base::Location& from_here) { + web_contents()->EnterFullscreenMode(https_fullscreen_frame()); + + ASSERT_TRUE(web_contents()->IsFullscreenForCurrentTab()) + << "Location: " << from_here.ToString(); + + VerifyKeyboardLockState(from_here); +} + +void KeyboardLockBrowserTest::ExitFullscreen(const base::Location& from_here) { + web_contents()->ExitFullscreenMode(/*should_resize=*/true); + + ASSERT_FALSE(web_contents()->IsFullscreenForCurrentTab()) + << "Location: " << from_here.ToString(); + + VerifyKeyboardLockState(from_here); +} + +void KeyboardLockBrowserTest::FocusContent(const base::Location& from_here) { + g_window_has_focus = true; + RenderWidgetHostImpl* host = RenderWidgetHostImpl::From( + web_contents()->GetRenderWidgetHostView()->GetRenderWidgetHost()); + host->GotFocus(); + host->SetActive(true); + + ASSERT_TRUE(web_contents()->GetRenderWidgetHostView()->HasFocus()) + << "Location: " << from_here.ToString(); + + VerifyKeyboardLockState(from_here); +} + +void KeyboardLockBrowserTest::BlurContent(const base::Location& from_here) { + g_window_has_focus = false; + RenderWidgetHostImpl* host = RenderWidgetHostImpl::From( + web_contents()->GetRenderWidgetHostView()->GetRenderWidgetHost()); + host->SetActive(false); + host->LostFocus(); + + ASSERT_FALSE(web_contents()->GetRenderWidgetHostView()->HasFocus()) + << "Location: " << from_here.ToString(); + + VerifyKeyboardLockState(from_here); +} + +void KeyboardLockBrowserTest::VerifyKeyboardLockState( + const base::Location& from_here) { + bool keyboard_lock_requested = !!web_contents()->GetKeyboardLockWidget(); + + bool ux_conditions_satisfied = + web_contents()->GetRenderWidgetHostView()->HasFocus() && + web_contents()->IsFullscreenForCurrentTab(); + + bool keyboard_lock_active = + web_contents()->GetRenderWidgetHostView()->IsKeyboardLocked(); + + // Keyboard lock only active when requested and the UX is in the right state. + ASSERT_EQ(keyboard_lock_active, + ux_conditions_satisfied && keyboard_lock_requested) + << "Location: " << from_here.ToString(); +} + +class KeyboardLockDisabledBrowserTest : public KeyboardLockBrowserTest { + public: + KeyboardLockDisabledBrowserTest() = default; + ~KeyboardLockDisabledBrowserTest() override = default; + + protected: + // KeyboardLockBrowserTest override. + void SetUpFeatureList() override; +}; + +void KeyboardLockDisabledBrowserTest::SetUpFeatureList() { + feature_list()->InitAndDisableFeature(features::kKeyboardLockAPI); +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, MAYBE_RUN(SingleLockCall)) { + NavigateToTestURL(); + RequestKeyboardLock(FROM_HERE); + // Don't explicitly call CancelKeyboardLock(). +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, + MAYBE_RUN(SingleLockCallForSomeKeys)) { + NavigateToTestURL(); + RequestKeyboardLock(FROM_HERE, /*lock_all_keys=*/false); + // Don't explicitly call CancelKeyboardLock(). +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, + MAYBE_RUN(SingleLockWithCancelCall)) { + NavigateToTestURL(); + RequestKeyboardLock(FROM_HERE); + CancelKeyboardLock(FROM_HERE); +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, + MAYBE_RUN(LockCalledBeforeFullscreen)) { + NavigateToTestURL(); + RequestKeyboardLock(FROM_HERE); + EnterFullscreen(FROM_HERE); +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, + MAYBE_RUN(LockCalledAfterFullscreen)) { + NavigateToTestURL(); + EnterFullscreen(FROM_HERE); + RequestKeyboardLock(FROM_HERE); +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, + MAYBE_RUN(LockAndCancelCyclingNoActivation)) { + NavigateToTestURL(); + + RequestKeyboardLock(FROM_HERE); + CancelKeyboardLock(FROM_HERE); + RequestKeyboardLock(FROM_HERE, /*lock_all_keys=*/false); + CancelKeyboardLock(FROM_HERE); + RequestKeyboardLock(FROM_HERE); + CancelKeyboardLock(FROM_HERE); + RequestKeyboardLock(FROM_HERE); + CancelKeyboardLock(FROM_HERE); +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, + MAYBE_RUN(LockAndCancelCyclingInFullscreen)) { + NavigateToTestURL(); + + EnterFullscreen(FROM_HERE); + + RequestKeyboardLock(FROM_HERE); + CancelKeyboardLock(FROM_HERE); + RequestKeyboardLock(FROM_HERE, /*lock_all_keys=*/false); + CancelKeyboardLock(FROM_HERE); + RequestKeyboardLock(FROM_HERE, /*lock_all_keys=*/false); + CancelKeyboardLock(FROM_HERE); + RequestKeyboardLock(FROM_HERE); + CancelKeyboardLock(FROM_HERE); + RequestKeyboardLock(FROM_HERE); + CancelKeyboardLock(FROM_HERE); +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, MAYBE_RUN(CancelInFullscreen)) { + NavigateToTestURL(); + + RequestKeyboardLock(FROM_HERE); + EnterFullscreen(FROM_HERE); + CancelKeyboardLock(FROM_HERE); + ExitFullscreen(FROM_HERE); +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, + MAYBE_RUN(EnterAndExitFullscreenCycling)) { + NavigateToTestURL(); + + RequestKeyboardLock(FROM_HERE); + + EnterFullscreen(FROM_HERE); + ExitFullscreen(FROM_HERE); + EnterFullscreen(FROM_HERE); + ExitFullscreen(FROM_HERE); + EnterFullscreen(FROM_HERE); + ExitFullscreen(FROM_HERE); + EnterFullscreen(FROM_HERE); + ExitFullscreen(FROM_HERE); +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, + MAYBE_RUN(GainAndLoseFocusInWindowMode)) { + NavigateToTestURL(); + + RequestKeyboardLock(FROM_HERE); + + FocusContent(FROM_HERE); + BlurContent(FROM_HERE); + FocusContent(FROM_HERE); + BlurContent(FROM_HERE); +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, + MAYBE_RUN(EnterFullscreenWithoutFocus)) { + NavigateToTestURL(); + + RequestKeyboardLock(FROM_HERE); + + BlurContent(FROM_HERE); + EnterFullscreen(FROM_HERE); + ExitFullscreen(FROM_HERE); + + EnterFullscreen(FROM_HERE); + FocusContent(FROM_HERE); +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, + MAYBE_RUN(GainAndLoseFocusCyclingInFullscreen)) { + NavigateToTestURL(); + + RequestKeyboardLock(FROM_HERE); + + BlurContent(FROM_HERE); + EnterFullscreen(FROM_HERE); + + FocusContent(FROM_HERE); + BlurContent(FROM_HERE); + FocusContent(FROM_HERE); + BlurContent(FROM_HERE); + FocusContent(FROM_HERE); + BlurContent(FROM_HERE); + FocusContent(FROM_HERE); + BlurContent(FROM_HERE); + + ExitFullscreen(FROM_HERE); +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, MAYBE_RUN(CancelWithoutLock)) { + NavigateToTestURL(); + CancelKeyboardLock(FROM_HERE); + CancelKeyboardLock(FROM_HERE); +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, MAYBE_RUN(MultipleLockCalls)) { + NavigateToTestURL(); + + RequestKeyboardLock(FROM_HERE); + RequestKeyboardLock(FROM_HERE); + RequestKeyboardLock(FROM_HERE); +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, + MAYBE_RUN(MultipleCancelCalls)) { + NavigateToTestURL(); + + RequestKeyboardLock(FROM_HERE); + + CancelKeyboardLock(FROM_HERE); + CancelKeyboardLock(FROM_HERE); + CancelKeyboardLock(FROM_HERE); +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, + MAYBE_RUN(LockCallWithAllInvalidKeys)) { + NavigateToTestURL(); + + bool result; + ASSERT_TRUE(ExecuteScriptAndExtractBool( + web_contents(), kKeyboardLockMethodCallWithAllInvalidKeys, &result)); + ASSERT_TRUE(result); + + // If no valid Keys are passed in, then KeyboardLock will not be requested. + ASSERT_EQ(nullptr, web_contents()->GetKeyboardLockWidget()); + + EnterFullscreen(FROM_HERE); +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, + MAYBE_RUN(LockCallWithSomeInvalidKeys)) { + NavigateToTestURL(); + + bool result; + ASSERT_TRUE(ExecuteScriptAndExtractBool( + web_contents(), kKeyboardLockMethodCallWithSomeInvalidKeys, &result)); + ASSERT_TRUE(result); + + // If some valid Keys are passed in, then KeyboardLock will be requested. + ASSERT_NE(nullptr, web_contents()->GetKeyboardLockWidget()); + + EnterFullscreen(FROM_HERE); +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockDisabledBrowserTest, + MAYBE_RUN(NoKeyboardLockWhenDisabled)) { + ASSERT_TRUE(NavigateToURL(shell(), https_fullscreen_frame())); + ASSERT_FALSE(KeyboardLockApiExists()); +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, + MAYBE_RUN(KeyboardLockNotAllowedForIFrame)) { + // TODO(joedow): IMPLEMENT. +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, + MAYBE_RUN(KeyboardUnlockedWhenNavigatingAway)) { + // TODO(joedow): IMPLEMENT. +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, + MAYBE_RUN(CrossOriginIFrameDoesNotReceiveInput)) { + // TODO(joedow): Added per code review feedback. + // Steps: Main frame initiates keyboard lock and goes fullscreen. Is input + // delivered to cross-origin iFrame? +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, + MAYBE_RUN(CrossOriginIFrameRequestsFullscreen)) { + // TODO(joedow): Added per code review feedback. + // Steps: Main frame requests keyboard lock, cross-origin iFrame goes + // fullscreen. Should KeyboardLock be triggered in that case (presumably no). +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, + MAYBE_RUN(LockRequestWhileIFrameIsFullscreen)) { + // TODO(joedow): Added per code review feedback. + // Steps: 1. Load a page with a cross-site iframe: call main frame "A" and the + // subframe "B" + // 2. B goes fullscreen. + // 3. A initiates keyboard lock. +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, + MAYBE_RUN(LockRequestFailsFromInnerWebContents)) { + // TODO(joedow): Added per code review feedback. + // Steps: Try requesting KeyboardLock from with an inner WebContents context. + // See: CreateAndAttachInnerContents() helper method in + // https://cs.chromium.org/chromium/src/content/public/test/test_utils.h +} + +IN_PROC_BROWSER_TEST_F(KeyboardLockBrowserTest, MAYBE_RUN(HistogramTest)) { + // TODO(joedow): Added per code review feedback. + // Steps: Call the API methods and verify the histogram data is accurate using + // base::HistogramTester. Alternatively, this could be integrated with the + // test fixture as well. window_open_apitest.cc (line 317()) is an example. +} + +} // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_delegate.cc b/content/browser/renderer_host/render_widget_host_delegate.cc index 23afbb78..386ea30 100644 --- a/content/browser/renderer_host/render_widget_host_delegate.cc +++ b/content/browser/renderer_host/render_widget_host_delegate.cc
@@ -74,6 +74,14 @@ return nullptr; } +bool RenderWidgetHostDelegate::RequestKeyboardLock(RenderWidgetHostImpl* host) { + return false; +} + +RenderWidgetHostImpl* RenderWidgetHostDelegate::GetKeyboardLockWidget() { + return nullptr; +} + TextInputManager* RenderWidgetHostDelegate::GetTextInputManager() { return nullptr; }
diff --git a/content/browser/renderer_host/render_widget_host_delegate.h b/content/browser/renderer_host/render_widget_host_delegate.h index 4f6068ea..b202c89d 100644 --- a/content/browser/renderer_host/render_widget_host_delegate.h +++ b/content/browser/renderer_host/render_widget_host_delegate.h
@@ -193,6 +193,17 @@ // locked. virtual RenderWidgetHostImpl* GetMouseLockWidget(); + // Requests to lock the keyboard. Once the request is approved or rejected, + // GotResponseToKeyboardLockRequest() will be called on the requesting render + // widget host. + virtual bool RequestKeyboardLock(RenderWidgetHostImpl* render_widget_host); + + // Cancels a previous keyboard lock request. + virtual void CancelKeyboardLock(RenderWidgetHostImpl* render_widget_host) {} + + // Returns the widget that holds the keyboard lock or nullptr if not locked. + virtual RenderWidgetHostImpl* GetKeyboardLockWidget(); + // Called when the visibility of the RenderFrameProxyHost in outer // WebContents changes. This method is only called on an inner WebContents and // will eventually notify all the RenderWidgetHostViews belonging to that
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc index 8571ab6..afee464 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -567,6 +567,7 @@ } void RenderWidgetHostImpl::ShutdownAndDestroyWidget(bool also_delete) { + CancelKeyboardLock(); RejectMouseLockOrUnlockIfNecessary(); if (process_->HasConnection()) { @@ -909,8 +910,13 @@ if (IsMouseLocked()) view_->UnlockMouse(); + if (IsKeyboardLocked()) + UnlockKeyboard(); + if (touch_emulator_) touch_emulator_->CancelTouch(); + } else if (keyboard_lock_allowed_) { + LockKeyboard(); } GetWidgetInputHandler()->SetFocus(focused); @@ -945,6 +951,7 @@ } void RenderWidgetHostImpl::ViewDestroyed() { + CancelKeyboardLock(); RejectMouseLockOrUnlockIfNecessary(); // TODO(evanm): tracking this may no longer be necessary; @@ -1868,6 +1875,10 @@ } } +bool RenderWidgetHostImpl::IsKeyboardLocked() const { + return view_ ? view_->IsKeyboardLocked() : false; +} + bool RenderWidgetHostImpl::IsMouseLocked() const { return view_ ? view_->IsMouseLocked() : false; } @@ -2264,6 +2275,36 @@ is_last_unlocked_by_target_ = true; } +void RenderWidgetHostImpl::RequestKeyboardLock( + base::Optional<base::flat_set<int>> keys_to_lock) { + if (!delegate_) { + CancelKeyboardLock(); + return; + } + + // If keyboard lock is already active, then skip the request and just update + // the set of keys used for the lock. Otherwise call through the delegate to + // request keyboard lock. Cancel the request if we don't have a delegate. + DCHECK(!keys_to_lock.has_value() || !keys_to_lock.value().empty()); + keyboard_keys_to_lock_ = std::move(keys_to_lock); + keyboard_lock_requested_ = true; + if (IsKeyboardLocked()) + LockKeyboard(); + else if (!delegate_->RequestKeyboardLock(this)) + CancelKeyboardLock(); +} + +void RenderWidgetHostImpl::CancelKeyboardLock() { + if (delegate_) + delegate_->CancelKeyboardLock(this); + + UnlockKeyboard(); + + keyboard_lock_allowed_ = false; + keyboard_lock_requested_ = false; + keyboard_keys_to_lock_.reset(); +} + void RenderWidgetHostImpl::OnShowDisambiguationPopup( const gfx::Rect& rect_pixels, const gfx::Size& size, @@ -2522,6 +2563,16 @@ return true; } +void RenderWidgetHostImpl::GotResponseToKeyboardLockRequest(bool allowed) { + DCHECK(keyboard_lock_requested_); + keyboard_lock_allowed_ = allowed; + + if (keyboard_lock_allowed_) + LockKeyboard(); + else + UnlockKeyboard(); +} + void RenderWidgetHostImpl::DelayedAutoResized() { gfx::Size new_size = new_auto_size_; // Clear the new_auto_size_ since the empty value is used as a flag to @@ -3106,4 +3157,20 @@ return first != second; } +bool RenderWidgetHostImpl::LockKeyboard() { + if (!keyboard_lock_allowed_ || !is_focused_ || !view_) + return false; + + // KeyboardLock can be activated and deactivated several times per request, + // for example when a fullscreen tab loses and gains focus multiple times, + // so we need to retain a copy of the keys requested. + base::Optional<base::flat_set<int>> copy_of_keys = keyboard_keys_to_lock_; + return view_->LockKeyboard(std::move(copy_of_keys)); +} + +void RenderWidgetHostImpl::UnlockKeyboard() { + if (IsKeyboardLocked()) + view_->UnlockKeyboard(); +} + } // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h index 9ef34d3c..d803c85 100644 --- a/content/browser/renderer_host/render_widget_host_impl.h +++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -16,12 +16,14 @@ #include <vector> #include "base/callback.h" +#include "base/containers/flat_set.h" #include "base/containers/queue.h" #include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/memory/shared_memory_handle.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" +#include "base/optional.h" #include "base/process/kill.h" #include "base/strings/string16.h" #include "base/time/tick_clock.h" @@ -486,6 +488,11 @@ allow_privileged_mouse_lock_ = allow; } + // Called when the response to a pending keyboard lock request has arrived. + // |allowed| should be true if the current tab is in tab initiated fullscreen + // mode. + void GotResponseToKeyboardLockRequest(bool allowed); + // Resets state variables related to tracking pending size and painting. // // We need to reset these flags when we want to repaint the contents of @@ -678,6 +685,17 @@ void SetScreenOrientationForTesting(uint16_t angle, ScreenOrientationValues type); + // Requests Keyboard lock. Note: the lock may not take effect until later. + // If |keys_to_lock| has no value then all keys will be locked, otherwise only + // the keys specified will be intercepted and routed to the web page. + void RequestKeyboardLock(base::Optional<base::flat_set<int>> keys_to_lock); + + // Cancels a previous keyboard lock request. + void CancelKeyboardLock(); + + // Indicates whether keyboard lock is active. + bool IsKeyboardLocked() const; + protected: // --------------------------------------------------------------------------- // The following method is overridden by RenderViewHost to send upwards to @@ -850,6 +868,12 @@ const RenderWidgetSurfaceProperties& first, const RenderWidgetSurfaceProperties& second) const; + // Start intercepting system keyboard events. + bool LockKeyboard(); + + // Stop intercepting system keyboard events. + void UnlockKeyboard(); + #if defined(OS_MACOSX) device::mojom::WakeLock* GetWakeLock(); #endif @@ -980,6 +1004,11 @@ bool pending_mouse_lock_request_; bool allow_privileged_mouse_lock_; + // Stores the keyboard keys to lock while waiting for a pending lock request. + base::Optional<base::flat_set<int>> keyboard_keys_to_lock_; + bool keyboard_lock_requested_ = false; + bool keyboard_lock_allowed_ = false; + // Used when locking to indicate when a target application has voluntarily // unlocked and desires to relock the mouse. If the mouse is unlocked due // to ESC being pressed by the user, this will be false.
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc index 1c7bcaef..289b80b 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -1148,6 +1148,19 @@ event_handler_->UnlockMouse(); } +bool RenderWidgetHostViewAura::LockKeyboard( + base::Optional<base::flat_set<int>> keys) { + return event_handler_->LockKeyboard(std::move(keys)); +} + +void RenderWidgetHostViewAura::UnlockKeyboard() { + event_handler_->UnlockKeyboard(); +} + +bool RenderWidgetHostViewAura::IsKeyboardLocked() { + return event_handler_->IsKeyboardLocked(); +} + //////////////////////////////////////////////////////////////////////////////// // RenderWidgetHostViewAura, ui::TextInputClient implementation: void RenderWidgetHostViewAura::SetCompositionText(
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h index f54822b..6d5843b8 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.h +++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -167,6 +167,9 @@ void SetMainFrameAXTreeID(ui::AXTreeIDRegistry::AXTreeID id) override; bool LockMouse() override; void UnlockMouse() override; + bool LockKeyboard(base::Optional<base::flat_set<int>> keys) override; + void UnlockKeyboard() override; + bool IsKeyboardLocked() override; void DidCreateNewRendererCompositorFrameSink( viz::mojom::CompositorFrameSinkClient* renderer_compositor_frame_sink) override;
diff --git a/content/browser/renderer_host/render_widget_host_view_base.cc b/content/browser/renderer_host/render_widget_host_view_base.cc index dd77d08..b77b7e4 100644 --- a/content/browser/renderer_host/render_widget_host_view_base.cc +++ b/content/browser/renderer_host/render_widget_host_view_base.cc
@@ -43,7 +43,6 @@ : host_(RenderWidgetHostImpl::From(host)), is_fullscreen_(false), popup_type_(blink::kWebPopupTypeNone), - mouse_locked_(false), current_device_scale_factor_(0), current_display_rotation_(display::Display::ROTATE_0), text_input_manager_(nullptr), @@ -57,6 +56,7 @@ } RenderWidgetHostViewBase::~RenderWidgetHostViewBase() { + DCHECK(!keyboard_locked_); DCHECK(!mouse_locked_); // We call this here to guarantee that observers are notified before we go // away. However, some subclasses may wish to call this earlier in their @@ -198,6 +198,20 @@ return mouse_locked_; } +bool RenderWidgetHostViewBase::LockKeyboard( + base::Optional<base::flat_set<int>> keys) { + NOTIMPLEMENTED(); + return false; +} + +void RenderWidgetHostViewBase::UnlockKeyboard() { + NOTIMPLEMENTED(); +} + +bool RenderWidgetHostViewBase::IsKeyboardLocked() { + return keyboard_locked_; +} + InputEventAckState RenderWidgetHostViewBase::FilterInputEvent( const blink::WebInputEvent& input_event) { // By default, input events are simply forwarded to the renderer.
diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h index 6b98a54aa..cadf54c5 100644 --- a/content/browser/renderer_host/render_widget_host_view_base.h +++ b/content/browser/renderer_host/render_widget_host_view_base.h
@@ -9,7 +9,6 @@ #include <stdint.h> #include <memory> -#include <string> #include <vector> #include "base/callback_forward.h" @@ -115,6 +114,9 @@ void SetIsInVR(bool is_in_vr) override; base::string16 GetSelectedText() override; bool IsMouseLocked() override; + bool LockKeyboard(base::Optional<base::flat_set<int>> keys) override; + void UnlockKeyboard() override; + bool IsKeyboardLocked() override; gfx::Size GetVisibleViewportSize() const override; void SetInsets(const gfx::Insets& insets) override; bool IsSurfaceAvailableForCopy() const override; @@ -548,12 +550,15 @@ // autofill...). blink::WebPopupType popup_type_; + // Indicates whether keyboard lock is active for this view. + bool keyboard_locked_ = false; + // While the mouse is locked, the cursor is hidden from the user. Mouse events // are still generated. However, the position they report is the last known // mouse position just as mouse lock was entered; the movement they report // indicates what the change in position of the mouse would be had it not been // locked. - bool mouse_locked_; + bool mouse_locked_ = false; // The scale factor of the display the renderer is currently on. float current_device_scale_factor_;
diff --git a/content/browser/renderer_host/render_widget_host_view_event_handler.cc b/content/browser/renderer_host/render_widget_host_view_event_handler.cc index c2b6f75..b480943 100644 --- a/content/browser/renderer_host/render_widget_host_view_event_handler.cc +++ b/content/browser/renderer_host/render_widget_host_view_event_handler.cc
@@ -21,6 +21,7 @@ #include "ui/aura/client/cursor_client.h" #include "ui/aura/client/focus_client.h" #include "ui/aura/client/screen_position_client.h" +#include "ui/aura/scoped_keyboard_hook.h" #include "ui/aura/window.h" #include "ui/aura/window_delegate.h" #include "ui/base/ime/text_input_client.h" @@ -240,6 +241,27 @@ host_->LostMouseLock(); } +bool RenderWidgetHostViewEventHandler::LockKeyboard( + base::Optional<base::flat_set<int>> keys) { + aura::Window* root_window = window_->GetRootWindow(); + if (!root_window) + return false; + + // Remove existing hook, if registered. + UnlockKeyboard(); + scoped_keyboard_hook_ = root_window->CaptureSystemKeyEvents(std::move(keys)); + + return IsKeyboardLocked(); +} + +void RenderWidgetHostViewEventHandler::UnlockKeyboard() { + scoped_keyboard_hook_.reset(); +} + +bool RenderWidgetHostViewEventHandler::IsKeyboardLocked() const { + return scoped_keyboard_hook_ != nullptr; +} + void RenderWidgetHostViewEventHandler::OnKeyEvent(ui::KeyEvent* event) { TRACE_EVENT0("input", "RenderWidgetHostViewBase::OnKeyEvent");
diff --git a/content/browser/renderer_host/render_widget_host_view_event_handler.h b/content/browser/renderer_host/render_widget_host_view_event_handler.h index 5bdd7a8..c06981f 100644 --- a/content/browser/renderer_host/render_widget_host_view_event_handler.h +++ b/content/browser/renderer_host/render_widget_host_view_event_handler.h
@@ -7,7 +7,9 @@ #include <memory> +#include "base/containers/flat_set.h" #include "base/macros.h" +#include "base/optional.h" #include "content/browser/renderer_host/input/mouse_wheel_phase_handler.h" #include "content/common/content_export.h" #include "content/public/browser/native_web_keyboard_event.h" @@ -17,6 +19,7 @@ #include "ui/latency/latency_info.h" namespace aura { +class ScopedKeyboardHook; class Window; } // namespace aura @@ -140,6 +143,11 @@ bool LockMouse(); void UnlockMouse(); + // Start/Stop processing of future system keyboard events. + bool LockKeyboard(base::Optional<base::flat_set<int>> keys); + void UnlockKeyboard(); + bool IsKeyboardLocked() const; + // ui::EventHandler: void OnKeyEvent(ui::KeyEvent* event) override; void OnMouseEvent(ui::MouseEvent* event) override; @@ -210,6 +218,9 @@ // RenderWidgetHostInputEventRouter. bool disable_input_event_router_for_testing_; + // Deactivates keyboard lock when destroyed. + std::unique_ptr<aura::ScopedKeyboardHook> scoped_keyboard_hook_; + // While the mouse is locked, the cursor is hidden from the user. Mouse events // are still generated. However, the position they report is the last known // mouse position just as mouse lock was entered; the movement they report
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index ce2f473..6cb03828 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc
@@ -520,7 +520,6 @@ #if !defined(OS_ANDROID) page_scale_factor_is_one_(true), #endif // !defined(OS_ANDROID) - mouse_lock_widget_(nullptr), is_overlay_content_(false), showing_context_menu_(false), loading_weak_factory_(this), @@ -1160,7 +1159,7 @@ if (browser_plugin_embedder_) { std::vector<WebContentsImpl*> inner_contents; GetBrowserContext()->GetGuestManager()->ForEachGuest( - this, base::Bind(&GetInnerWebContentsHelper, &inner_contents)); + this, base::BindRepeating(&GetInnerWebContentsHelper, &inner_contents)); return inner_contents; } @@ -1957,6 +1956,8 @@ if (render_widget_host == mouse_lock_widget_) LostMouseLock(mouse_lock_widget_); + + CancelKeyboardLock(keyboard_lock_widget_); } void WebContentsImpl::RenderWidgetGotFocus( @@ -2124,6 +2125,9 @@ ->ShutdownAndDestroyWidget(true); } + if (keyboard_lock_widget_) + keyboard_lock_widget_->GotResponseToKeyboardLockRequest(true); + if (delegate_) delegate_->EnterFullscreenModeForTab(this, origin); @@ -2146,6 +2150,9 @@ video_view->ExitFullscreen(); #endif + if (keyboard_lock_widget_) + keyboard_lock_widget_->GotResponseToKeyboardLockRequest(false); + if (delegate_) delegate_->ExitFullscreenModeForTab(this); @@ -2261,6 +2268,45 @@ return nullptr; } +bool WebContentsImpl::RequestKeyboardLock( + RenderWidgetHostImpl* render_widget_host) { + DCHECK(render_widget_host); + if (render_widget_host == keyboard_lock_widget_) + return true; + + if (render_widget_host->delegate()->GetAsWebContents() != this) { + NOTREACHED(); + return false; + } + + // KeyboardLock is only supported when called by the top-level browsing + // context and is not supported in embedded content scenarios. + if (GetOuterWebContents()) + return false; + + keyboard_lock_widget_ = render_widget_host; + + if (IsFullscreen()) + render_widget_host->GotResponseToKeyboardLockRequest(true); + + return true; +} + +void WebContentsImpl::CancelKeyboardLock( + RenderWidgetHostImpl* render_widget_host) { + if (!keyboard_lock_widget_ || render_widget_host != keyboard_lock_widget_) + return; + + RenderWidgetHostImpl* old_keyboard_lock_widget = keyboard_lock_widget_; + keyboard_lock_widget_ = nullptr; + + old_keyboard_lock_widget->CancelKeyboardLock(); +} + +RenderWidgetHostImpl* WebContentsImpl::GetKeyboardLockWidget() { + return keyboard_lock_widget_; +} + void WebContentsImpl::OnRenderFrameProxyVisibilityChanged(bool visible) { if (visible && !GetOuterWebContents()->IsHidden()) WasShown(); @@ -3411,8 +3457,9 @@ void WebContentsImpl::GenerateMHTML( const MHTMLGenerationParams& params, - const base::Callback<void(int64_t)>& callback) { - MHTMLGenerationManager::GetInstance()->SaveMHTML(this, params, callback); + base::OnceCallback<void(int64_t)> callback) { + MHTMLGenerationManager::GetInstance()->SaveMHTML(this, params, + std::move(callback)); } const std::string& WebContentsImpl::GetContentsMimeType() const { @@ -4068,6 +4115,16 @@ const gfx::Rect& rect, int document_cookie, RenderFrameHost* subframe_host) { + auto* outer_contents = GetOuterWebContents(); + if (outer_contents) { + // When an extension or app page is printed, the content should be + // composited with outer content, so the outer contents should handle the + // print request. + outer_contents->PrintCrossProcessSubframe(rect, document_cookie, + subframe_host); + return; + } + // If there is no delegate such as in tests or during deletion, do nothing. if (!delegate_) return;
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h index 97527f1..04446a61 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h
@@ -170,11 +170,6 @@ // TODO(creis): Remove this now that we can get to it via FrameTreeNode. RenderFrameHostManager* GetRenderManagerForTesting(); - // Returns the outer WebContents of this WebContents if any. - // Note that without --site-per-process, this will return the WebContents - // of the BrowserPluginEmbedder, if |this| is a guest. - WebContentsImpl* GetOuterWebContents(); - // Returns guest browser plugin object, or NULL if this WebContents is not a // guest. BrowserPluginGuest* GetBrowserPluginGuest() const; @@ -373,7 +368,9 @@ void AttachToOuterWebContentsFrame( WebContents* outer_web_contents, RenderFrameHost* outer_contents_frame) override; + WebContentsImpl* GetOuterWebContents() override; void DidChangeVisibleSecurityState() override; + void Stop() override; void FreezePage() override; WebContents* Clone() override; @@ -421,7 +418,7 @@ const std::string& headers, const base::string16& suggested_filename) override; void GenerateMHTML(const MHTMLGenerationParams& params, - const base::Callback<void(int64_t)>& callback) override; + base::OnceCallback<void(int64_t)> callback) override; const std::string& GetContentsMimeType() const override; bool WillNotifyDisconnection() const override; RendererPreferences* GetMutableRendererPrefs() override; @@ -725,6 +722,9 @@ bool user_gesture, bool last_unlocked_by_target, bool privileged) override; + bool RequestKeyboardLock(RenderWidgetHostImpl* render_widget_host) override; + void CancelKeyboardLock(RenderWidgetHostImpl* render_widget_host) override; + RenderWidgetHostImpl* GetKeyboardLockWidget() override; // The following function is already listed under WebContents overrides: // bool IsFullscreenForCurrentTab() const override; blink::WebDisplayMode GetDisplayMode( @@ -1675,7 +1675,11 @@ // Stores the RenderWidgetHost that currently holds a mouse lock or nullptr if // there's no RenderWidgetHost holding a lock. - RenderWidgetHostImpl* mouse_lock_widget_; + RenderWidgetHostImpl* mouse_lock_widget_ = nullptr; + + // Stores the RenderWidgetHost that currently holds a keyboard lock or nullptr + // if no RenderWidgetHost has the keyboard locked. + RenderWidgetHostImpl* keyboard_lock_widget_ = nullptr; #if defined(OS_ANDROID) std::unique_ptr<service_manager::InterfaceProvider> java_interfaces_;
diff --git a/content/browser/web_contents/web_contents_view_android.cc b/content/browser/web_contents/web_contents_view_android.cc index 4a46d79..e9c134f 100644 --- a/content/browser/web_contents/web_contents_view_android.cc +++ b/content/browser/web_contents/web_contents_view_android.cc
@@ -44,8 +44,6 @@ namespace { -const int kAndroidLSDKVersion = 21; - // True if we want to disable Android native event batching and use // compositor event queue. bool ShouldRequestUnbufferedDispatch() { @@ -53,7 +51,7 @@ base::FeatureList::IsEnabled( content::android::kRequestUnbufferedDispatch) && base::android::BuildInfo::GetInstance()->sdk_int() >= - kAndroidLSDKVersion && + base::android::SDK_VERSION_LOLLIPOP && !content::GetContentClient()->UsingSynchronousCompositing(); return should_request_unbuffered_dispatch; }
diff --git a/content/public/browser/browser_plugin_guest_manager.cc b/content/public/browser/browser_plugin_guest_manager.cc index 6db8329..81a843b 100644 --- a/content/public/browser/browser_plugin_guest_manager.cc +++ b/content/public/browser/browser_plugin_guest_manager.cc
@@ -12,9 +12,8 @@ return nullptr; } -bool BrowserPluginGuestManager::ForEachGuest( - WebContents* embedder_web_contents, - const GuestCallback& callback) { +bool BrowserPluginGuestManager::ForEachGuest(WebContents* embedder_web_contents, + const GuestCallback& callback) { return false; }
diff --git a/content/public/browser/browser_plugin_guest_manager.h b/content/public/browser/browser_plugin_guest_manager.h index c809c0ff..4e1019c 100644 --- a/content/public/browser/browser_plugin_guest_manager.h +++ b/content/public/browser/browser_plugin_guest_manager.h
@@ -26,7 +26,7 @@ // Iterates over all WebContents belonging to a given |embedder_web_contents|, // calling |callback| for each. If one of the callbacks returns true, then // the iteration exits early. - typedef base::Callback<bool(WebContents*)> GuestCallback; + using GuestCallback = base::RepeatingCallback<bool(WebContents*)>; virtual bool ForEachGuest(WebContents* embedder_web_contents, const GuestCallback& callback);
diff --git a/content/public/browser/render_widget_host_view.h b/content/public/browser/render_widget_host_view.h index e629961..7a179405 100644 --- a/content/public/browser/render_widget_host_view.h +++ b/content/public/browser/render_widget_host_view.h
@@ -5,8 +5,8 @@ #ifndef CONTENT_PUBLIC_BROWSER_RENDER_WIDGET_HOST_VIEW_H_ #define CONTENT_PUBLIC_BROWSER_RENDER_WIDGET_HOST_VIEW_H_ -#include <memory> - +#include "base/containers/flat_set.h" +#include "base/optional.h" #include "base/strings/string16.h" #include "build/build_config.h" #include "content/common/content_export.h" @@ -163,6 +163,12 @@ // Returns true if the mouse pointer is currently locked. virtual bool IsMouseLocked() = 0; + // Start/Stop intercepting future system keyboard events. + virtual bool LockKeyboard(base::Optional<base::flat_set<int>> keys) = 0; + virtual void UnlockKeyboard() = 0; + // Returns true if keyboard lock is active. + virtual bool IsKeyboardLocked() = 0; + // Retrives the size of the viewport for the visible region. May be smaller // than the view size if a portion of the view is obstructed (e.g. by a // virtual keyboard).
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h index 1df8201e..e392d557 100644 --- a/content/public/browser/web_contents.h +++ b/content/public/browser/web_contents.h
@@ -474,6 +474,10 @@ WebContents* outer_web_contents, RenderFrameHost* outer_contents_frame) = 0; + // Returns the outer WebContents of this WebContents if any. + // Otherwise, return nullptr. + virtual WebContents* GetOuterWebContents() = 0; + // Invoked when visible security state changes. virtual void DidChangeVisibleSecurityState() = 0; @@ -624,7 +628,7 @@ // not the recommended encoding for shareable content. virtual void GenerateMHTML( const MHTMLGenerationParams& params, - const base::Callback<void(int64_t /* size of the file */)>& callback) = 0; + base::OnceCallback<void(int64_t /* size of the file */)> callback) = 0; // Returns the contents MIME type after a navigation. virtual const std::string& GetContentsMimeType() const = 0;
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc index 9de46c5..b634d4a2 100644 --- a/content/public/test/browser_test_utils.cc +++ b/content/public/test/browser_test_utils.cc
@@ -1402,8 +1402,8 @@ web_contents_impl->GetBrowserContext()->GetGuestManager(); if (guest_manager) { guest_manager->ForEachGuest(web_contents_impl, - base::Bind(&ListenToGuestWebContents, - &accessibility_waiter)); + base::BindRepeating(&ListenToGuestWebContents, + &accessibility_waiter)); } accessibility_waiter.WaitForNotification();
diff --git a/content/public/test/layouttest_support.h b/content/public/test/layouttest_support.h index fe538d8a..acce77f 100644 --- a/content/public/test/layouttest_support.h +++ b/content/public/test/layouttest_support.h
@@ -153,8 +153,12 @@ void SetDeviceColorSpace(RenderView* render_view, const gfx::ColorSpace& color_space); -// Sets the scan duration to 0. -void SetTestBluetoothScanDuration(); +// Sets the scan duration to reflect the given setting. +enum class BluetoothTestScanDurationSetting { + kImmediateTimeout, // Set the scan duration to 0 seconds. + kNeverTimeout, // Set the scan duration to base::TimeDelta::Max() seconds. +}; +void SetTestBluetoothScanDuration(BluetoothTestScanDurationSetting setting); // Enables or disables synchronous resize mode. When enabled, all window-sizing // machinery is short-circuited inside the renderer. This mode is necessary for
diff --git a/content/public/test/test_browser_thread_bundle.cc b/content/public/test/test_browser_thread_bundle.cc index b6c045d3..4b3f779 100644 --- a/content/public/test/test_browser_thread_bundle.cc +++ b/content/public/test/test_browser_thread_bundle.cc
@@ -132,4 +132,24 @@ scoped_task_environment_->RunUntilIdle(); } +void TestBrowserThreadBundle::RunIOThreadUntilIdle() { + // Use a RunLoop to run until idle if already on BrowserThread::IO (which is + // the main thread unless using Options::REAL_IO_THREAD). + DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO)); + + base::WaitableEvent io_thread_idle( + base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce( + [](base::WaitableEvent* io_thread_idle) { + base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed) + .RunUntilIdle(); + io_thread_idle->Signal(); + }, + Unretained(&io_thread_idle))); + io_thread_idle.Wait(); +} + } // namespace content
diff --git a/content/public/test/test_browser_thread_bundle.h b/content/public/test/test_browser_thread_bundle.h index 88411d9..96a7457 100644 --- a/content/public/test/test_browser_thread_bundle.h +++ b/content/public/test/test_browser_thread_bundle.h
@@ -123,13 +123,28 @@ // DONT_CREATE_BROWSER_THREADS option was used when the bundle was created. void CreateBrowserThreads(); - // Runs all tasks posted to TaskScheduler and main thread until idle. Note: at - // the momment, this will not process BrowserThread::IO if this - // TestBrowserThreadBundle is using a REAL_IO_THREAD. TODO(robliao): fix this - // by making TaskScheduler aware of all MessageLoops. - // Prefer this to the equivalent content::RunAllTasksUntilIdle(). + // Runs all tasks posted to TaskScheduler and main thread until idle. + // Note: At the moment, this will not process BrowserThread::IO if this + // TestBrowserThreadBundle is using a REAL_IO_THREAD. + // TODO(robliao): fix this by making TaskScheduler aware of all MessageLoops. + // + // Note that this is not the cleanest way to run until idle as it will return + // early if it depends on an async condition that isn't guaranteed to have + // occurred yet. The best way to run until a condition is met is with RunLoop: + // + // void KickoffAsyncFoo(base::OnceClosure on_done); + // + // base::RunLoop run_loop; + // KickoffAsyncFoo(run_loop.QuitClosure()); + // run_loop.Run(); + // void RunUntilIdle(); + // Flush the IO thread. Replacement for RunLoop::RunUntilIdle() for tests that + // have a REAL_IO_THREAD. As with RunUntilIdle() above, prefer using + // RunLoop+QuitClosure() to await an async condition. + void RunIOThreadUntilIdle(); + ~TestBrowserThreadBundle(); private:
diff --git a/content/public/test/test_browser_thread_bundle_unittest.cc b/content/public/test/test_browser_thread_bundle_unittest.cc index f810fa8..2636939 100644 --- a/content/public/test/test_browser_thread_bundle_unittest.cc +++ b/content/public/test/test_browser_thread_bundle_unittest.cc
@@ -100,6 +100,38 @@ EXPECT_EQ(kNumTasks * kNumHops, base::subtle::NoBarrier_Load(&tasks_run)); } +namespace { + +void PostRecurringTaskToIOThread(int iteration, int* tasks_run) { + // All iterations but the first come from a task that was posted. + if (iteration > 0) + (*tasks_run)++; + + if (iteration == kNumHops) + return; + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce(&PostRecurringTaskToIOThread, iteration + 1, tasks_run)); +} + +} // namespace + +TEST(TestBrowserThreadBundleTest, RunIOThreadUntilIdle) { + TestBrowserThreadBundle test_browser_thread_bundle( + TestBrowserThreadBundle::Options::REAL_IO_THREAD); + + int tasks_run = 0; + + for (int i = 0; i < kNumTasks; ++i) { + PostRecurringTaskToIOThread(0, &tasks_run); + } + + test_browser_thread_bundle.RunIOThreadUntilIdle(); + + EXPECT_EQ(kNumTasks * kNumHops, tasks_run); +} + TEST(TestBrowserThreadBundleTest, MessageLoopTypeMismatch) { base::MessageLoopForUI message_loop;
diff --git a/content/public/test/test_utils.h b/content/public/test/test_utils.h index 659e685..55845e6 100644 --- a/content/public/test/test_utils.h +++ b/content/public/test/test_utils.h
@@ -39,34 +39,36 @@ class RenderFrameHost; class TestServiceManagerContext; -// Turns on nestable tasks, runs the message loop, then resets nestable tasks -// to what they were originally. Prefer this over MessageLoop::Run for in -// process browser tests that need to block until a condition is met. +// Deprecated: Use RunLoop::Run(). Use RunLoop::Type::kNestableTasksAllowed to +// force nesting in browser tests. void RunMessageLoop(); -// Variant of RunMessageLoop that takes RunLoop. +// Deprecated: Invoke |run_loop->Run()| directly. void RunThisRunLoop(base::RunLoop* run_loop); -// Turns on nestable tasks, runs all pending tasks in the message loop, -// then resets nestable tasks to what they were originally. Prefer this -// over MessageLoop::RunAllPending for in process browser tests to run -// all pending tasks. Can only be called from the UI thread. +// Turns on nestable tasks, runs all pending tasks in the message loop, then +// resets nestable tasks to what they were originally. Can only be called from +// the UI thread. Only use this instead of RunLoop::RunUntilIdle() to work +// around cases where a task keeps reposting itself and prevents the loop from +// going idle. +// TODO(gab): Assess whether this API is really needed. If you find yourself +// needing this, post a comment on https://crbug.com/824431. void RunAllPendingInMessageLoop(); -// Blocks the current thread until all the pending messages in the loop of the -// thread |thread_id| have been processed. Can only be called from the UI -// thread. +// Deprecated: For BrowserThread::IO use +// TestBrowserThreadBundle::RunIOThreadUntilIdle. For the main thread use +// RunLoop. In non-unit-tests use RunLoop::QuitClosure to observe async events +// rather than flushing entire threads. void RunAllPendingInMessageLoop(BrowserThread::ID thread_id); -// Runs until the task scheduler and the current message loop are all empty -// (have no more immediate tasks, delayed tasks may still exist). Tasks may -// still be running from sources outside of the task scheduler and the current -// message loop. -// Prefer TestBrowserThreadBundle::RunUntilIdle() over this static method. +// Deprecated: Use TestBrowserThreadBundle::RunUntilIdle(). void RunAllTasksUntilIdle(); // Get task to quit the given RunLoop. It allows a few generations of pending // tasks to run as opposed to run_loop->QuitClosure(). +// Prefer RunLoop::RunUntilIdle() to this. +// TODO(gab): Assess the need for this API (see comment on +// RunAllPendingInMessageLoop() above). base::Closure GetDeferredQuitTaskForRunLoop(base::RunLoop* run_loop); // Executes the specified JavaScript in the specified frame, and runs a nested @@ -119,9 +121,8 @@ // // DEPRECATED. Consider using base::RunLoop, in most cases MessageLoopRunner is // not needed. If you need to defer quitting the loop, use -// GetDeferredQuitTaskForRunLoop directly. -// If you found a case where base::RunLoop is inconvenient or can not be used at -// all, please post details in a comment on https://crbug.com/668707. +// RunLoop::RunUntilIdle() and if you really think you need deferred quit (can't +// reach idle, please post details in a comment on https://crbug.com/668707). class MessageLoopRunner : public base::RefCountedThreadSafe<MessageLoopRunner> { public: enum class QuitMode {
diff --git a/content/shell/browser/layout_test/fake_bluetooth_chooser.cc b/content/shell/browser/layout_test/fake_bluetooth_chooser.cc index 963e038e..b1070662 100644 --- a/content/shell/browser/layout_test/fake_bluetooth_chooser.cc +++ b/content/shell/browser/layout_test/fake_bluetooth_chooser.cc
@@ -8,15 +8,20 @@ #include <utility> #include "content/public/browser/bluetooth_chooser.h" +#include "content/public/test/layouttest_support.h" #include "content/shell/common/layout_test/fake_bluetooth_chooser.mojom.h" namespace content { -FakeBluetoothChooser::~FakeBluetoothChooser() = default; +FakeBluetoothChooser::~FakeBluetoothChooser() { + SetTestBluetoothScanDuration( + BluetoothTestScanDurationSetting::kImmediateTimeout); +} // static std::unique_ptr<FakeBluetoothChooser> FakeBluetoothChooser::Create( mojom::FakeBluetoothChooserRequest request) { + SetTestBluetoothScanDuration(BluetoothTestScanDurationSetting::kNeverTimeout); return std::unique_ptr<FakeBluetoothChooser>( new FakeBluetoothChooser(std::move(request))); }
diff --git a/content/shell/browser/layout_test/fake_bluetooth_chooser.h b/content/shell/browser/layout_test/fake_bluetooth_chooser.h index 867490e..e83cc14 100644 --- a/content/shell/browser/layout_test/fake_bluetooth_chooser.h +++ b/content/shell/browser/layout_test/fake_bluetooth_chooser.h
@@ -28,6 +28,7 @@ class FakeBluetoothChooser : public mojom::FakeBluetoothChooser, public BluetoothChooser { public: + // Resets the test scan duration to timeout immediately. ~FakeBluetoothChooser() override; // LayoutTestContentBrowserClient will create an instance of this class when a
diff --git a/content/shell/browser/layout_test/layout_test_bluetooth_fake_adapter_setter_impl.cc b/content/shell/browser/layout_test/layout_test_bluetooth_fake_adapter_setter_impl.cc index f06a649..7bc8019e 100644 --- a/content/shell/browser/layout_test/layout_test_bluetooth_fake_adapter_setter_impl.cc +++ b/content/shell/browser/layout_test/layout_test_bluetooth_fake_adapter_setter_impl.cc
@@ -32,7 +32,8 @@ void LayoutTestBluetoothFakeAdapterSetterImpl::Set( const std::string& adapter_name, SetCallback callback) { - SetTestBluetoothScanDuration(); + SetTestBluetoothScanDuration( + BluetoothTestScanDurationSetting::kImmediateTimeout); device::BluetoothAdapterFactoryWrapper::Get().SetBluetoothAdapterForTesting( LayoutTestBluetoothAdapterProvider::GetBluetoothAdapter(adapter_name));
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index d0d17863..c697db7c 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -747,6 +747,7 @@ "../browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc", "../browser/indexed_db/mock_browsertest_indexed_db_class_factory.h", "../browser/isolated_origin_browsertest.cc", + "../browser/keyboard_lock_browsertest.cc", "../browser/loader/cross_site_document_blocking_browsertest.cc", "../browser/loader/prefetch_browsertest.cc", "../browser/loader/reload_cache_control_browsertest.cc", @@ -1023,6 +1024,7 @@ "../shell/android/browsertests_apk/content_browser_tests_jni_onload.cc", ] sources -= [ + "../browser/keyboard_lock_browsertest.cc", "../browser/media/session/audio_focus_delegate_default_browsertest.cc", "../browser/network_service_restart_browsertest.cc", "../browser/pointer_lock_browsertest.cc",
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py index 00269dc..f80c963 100644 --- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py +++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -236,6 +236,8 @@ # Recent AMD drivers seem to have a regression with 3D textures. self.Fail('conformance2/textures/canvas_sub_rectangle/tex-3d-*', ['win', 'amd', 'd3d11'], bug=2424) # ANGLE bug ID + self.Fail('conformance2/textures/image/tex-3d-*', + ['win', 'amd', 'd3d11'], bug=2424) # ANGLE bug ID self.Fail('conformance2/textures/image_data/tex-3d-*', ['win', 'amd', 'd3d11'], bug=2424) # ANGLE bug ID self.Fail('conformance2/textures/misc/tex-unpack-params.html', @@ -250,8 +252,14 @@ 'basic_teximage3d_3d_*', ['win', 'amd', 'd3d11'], bug=2424) # ANGLE bug ID self.Fail('deqp/functional/gles3/texturespecification/' + + 'basic_texsubimage3d_*', + ['win', 'amd', 'd3d11'], bug=2424) # ANGLE bug ID + self.Fail('deqp/functional/gles3/texturespecification/' + 'teximage3d_pbo_3d*', ['win', 'amd', 'd3d11'], bug=2424) # ANGLE bug ID + self.Fail('deqp/functional/gles3/texturespecification/' + + 'texsubimage3d_unpack_params.html', + ['win', 'amd', 'd3d11'], bug=2424) # ANGLE bug ID # Have seen this time out. Think it may be because it's currently # the first test that runs in the shard, and the browser might not
diff --git a/content/test/layouttest_support.cc b/content/test/layouttest_support.cc index c10abdd9..a23315f8 100644 --- a/content/test/layouttest_support.cc +++ b/content/test/layouttest_support.cc
@@ -507,8 +507,19 @@ ->SetDeviceColorSpaceForTesting(color_space); } -void SetTestBluetoothScanDuration() { - BluetoothDeviceChooserController::SetTestScanDurationForTesting(); +void SetTestBluetoothScanDuration(BluetoothTestScanDurationSetting setting) { + switch (setting) { + case BluetoothTestScanDurationSetting::kImmediateTimeout: + BluetoothDeviceChooserController::SetTestScanDurationForTesting( + BluetoothDeviceChooserController::TestScanDurationSetting:: + IMMEDIATE_TIMEOUT); + break; + case BluetoothTestScanDurationSetting::kNeverTimeout: + BluetoothDeviceChooserController::SetTestScanDurationForTesting( + BluetoothDeviceChooserController::TestScanDurationSetting:: + NEVER_TIMEOUT); + break; + } } void UseSynchronousResizeMode(RenderView* render_view, bool enable) {
diff --git a/device/BUILD.gn b/device/BUILD.gn index 04148494..159adca 100644 --- a/device/BUILD.gn +++ b/device/BUILD.gn
@@ -73,6 +73,7 @@ "fido/fido_discovery_unittest.cc", "fido/fido_hid_message_unittest.cc", "fido/test_callback_receiver_unittest.cc", + "fido/u2f_parsing_utils_unittest.cc", "fido/u2f_register_unittest.cc", "fido/u2f_request_unittest.cc", "fido/u2f_sign_unittest.cc",
diff --git a/device/bluetooth/bluetooth_device.h b/device/bluetooth/bluetooth_device.h index 13a8758..69efd03 100644 --- a/device/bluetooth/bluetooth_device.h +++ b/device/bluetooth/bluetooth_device.h
@@ -580,7 +580,7 @@ // Helper class to easily update the sets of UUIDs and keep them in sync with // the set of all the device's UUIDs. - class DEVICE_BLUETOOTH_EXPORT DeviceUUIDs { + class DeviceUUIDs { public: DeviceUUIDs(); ~DeviceUUIDs();
diff --git a/device/bluetooth/test/fake_peripheral.cc b/device/bluetooth/test/fake_peripheral.cc index 203ceee..bf790f55 100644 --- a/device/bluetooth/test/fake_peripheral.cc +++ b/device/bluetooth/test/fake_peripheral.cc
@@ -8,7 +8,6 @@ #include "base/memory/ptr_util.h" #include "base/memory/weak_ptr.h" -#include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "device/bluetooth/bluetooth_uuid.h" #include "device/bluetooth/test/fake_remote_gatt_service.h" @@ -36,20 +35,7 @@ } void FakePeripheral::SetServiceUUIDs(UUIDSet service_uuids) { - device::BluetoothDevice::GattServiceMap services_map; - bool inserted; - - // Create a temporary map of services, because ReplaceServiceUUids expects a - // GattServiceMap even though it only uses the UUIDs. - int count = 0; - for (const auto& uuid : service_uuids) { - std::string id = base::IntToString(count++); - std::tie(std::ignore, inserted) = - services_map.emplace(id, std::make_unique<FakeRemoteGattService>( - id, uuid, true /* is_primary */, this)); - DCHECK(inserted); - } - device_uuids_.ReplaceServiceUUIDs(services_map); + service_uuids_ = std::move(service_uuids); } void FakePeripheral::SetNextGATTConnectionResponse(uint16_t code) { @@ -80,7 +66,6 @@ // for more details. system_connected_ = false; gatt_connected_ = false; - device_uuids_.ClearServiceUUIDs(); SetGattServicesDiscoveryComplete(false); DidDisconnectGatt(); } @@ -195,6 +180,10 @@ return false; } +device::BluetoothDevice::UUIDSet FakePeripheral::GetUUIDs() const { + return service_uuids_; +} + bool FakePeripheral::ExpectingPinCode() const { NOTREACHED(); return false; @@ -339,7 +328,6 @@ pending_gatt_discovery_ = false; if (code == mojom::kHCISuccess) { - device_uuids_.ReplaceServiceUUIDs(gatt_services_); SetGattServicesDiscoveryComplete(true); GetAdapter()->NotifyGattServicesDiscovered(this); } else {
diff --git a/device/bluetooth/test/fake_peripheral.h b/device/bluetooth/test/fake_peripheral.h index 5006f4c..e9879063 100644 --- a/device/bluetooth/test/fake_peripheral.h +++ b/device/bluetooth/test/fake_peripheral.h
@@ -81,6 +81,7 @@ bool IsGattConnected() const override; bool IsConnectable() const override; bool IsConnecting() const override; + UUIDSet GetUUIDs() const override; bool ExpectingPinCode() const override; bool ExpectingPasskey() const override; bool ExpectingConfirmation() const override;
diff --git a/device/fido/u2f_parsing_utils.cc b/device/fido/u2f_parsing_utils.cc index c0278956..ab2cfcb 100644 --- a/device/fido/u2f_parsing_utils.cc +++ b/device/fido/u2f_parsing_utils.cc
@@ -13,19 +13,37 @@ const uint32_t kU2fResponseKeyHandleStartPos = 67u; const char kEs256[] = "ES256"; +std::vector<uint8_t> Materialize(base::span<const uint8_t> span) { + return std::vector<uint8_t>(span.begin(), span.end()); +} + void Append(std::vector<uint8_t>* target, base::span<const uint8_t> in_values) { target->insert(target->end(), in_values.begin(), in_values.end()); } -std::vector<uint8_t> Extract(base::span<const uint8_t> source, +std::vector<uint8_t> Extract(base::span<const uint8_t> span, size_t pos, size_t length) { - if (!(pos <= source.size() && length <= source.size() - pos)) { - return std::vector<uint8_t>(); - } + return Materialize(ExtractSpan(span, pos, length)); +} - return std::vector<uint8_t>(source.begin() + pos, - source.begin() + pos + length); +base::span<const uint8_t> ExtractSpan(base::span<const uint8_t> span, + size_t pos, + size_t length) { + if (!(pos <= span.size() && length <= span.size() - pos)) + return base::span<const uint8_t>(); + return span.subspan(pos, length); +} + +std::vector<uint8_t> ExtractSuffix(base::span<const uint8_t> span, size_t pos) { + return Materialize(ExtractSuffixSpan(span, pos)); +} + +base::span<const uint8_t> ExtractSuffixSpan(base::span<const uint8_t> span, + size_t pos) { + if (pos > span.size()) + return std::vector<uint8_t>(); + return span.subspan(pos); } } // namespace u2f_parsing_utils
diff --git a/device/fido/u2f_parsing_utils.h b/device/fido/u2f_parsing_utils.h index 6a02424..b13ed95 100644 --- a/device/fido/u2f_parsing_utils.h +++ b/device/fido/u2f_parsing_utils.h
@@ -23,14 +23,36 @@ extern const uint32_t kU2fResponseKeyHandleStartPos; COMPONENT_EXPORT(DEVICE_FIDO) extern const char kEs256[]; +// Returns copy of |span|, that is, a vector with the same elements. +COMPONENT_EXPORT(DEVICE_FIDO) +std::vector<uint8_t> Materialize(base::span<const uint8_t> span); + +// Appends |in_values| to the end of |target|. The underlying container for +// |in_values| should *not* be |target|. COMPONENT_EXPORT(DEVICE_FIDO) void Append(std::vector<uint8_t>* target, base::span<const uint8_t> in_values); -// Parses out a sub-vector after verifying no out-of-bound reads. +// Safely extracts, with bound checking, a contiguous subsequence of |span| of +// the given |length| and starting at |pos|. Returns an empty vector/span if the +// requested range is out-of-bound. COMPONENT_EXPORT(DEVICE_FIDO) -std::vector<uint8_t> Extract(base::span<const uint8_t> source, +std::vector<uint8_t> Extract(base::span<const uint8_t> span, size_t pos, size_t length); +COMPONENT_EXPORT(DEVICE_FIDO) +base::span<const uint8_t> ExtractSpan(base::span<const uint8_t> span, + size_t pos, + size_t length); + +// Safely extracts, with bound checking, the suffix of the given |span| starting +// at the given position |pos|. Returns an empty vector/span if the requested +// starting position is out-of-bound. +COMPONENT_EXPORT(DEVICE_FIDO) +std::vector<uint8_t> ExtractSuffix(base::span<const uint8_t> span, size_t pos); + +COMPONENT_EXPORT(DEVICE_FIDO) +base::span<const uint8_t> ExtractSuffixSpan(base::span<const uint8_t> span, + size_t pos); } // namespace u2f_parsing_utils } // namespace device
diff --git a/device/fido/u2f_parsing_utils_unittest.cc b/device/fido/u2f_parsing_utils_unittest.cc new file mode 100644 index 0000000..d739455 --- /dev/null +++ b/device/fido/u2f_parsing_utils_unittest.cc
@@ -0,0 +1,113 @@ +// 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 "device/fido/u2f_parsing_utils.h" + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace device { +namespace u2f_parsing_utils { + +namespace { +constexpr uint8_t kOne[] = {0x01}; +constexpr uint8_t kTwoThree[] = {0x02, 0x03}; +constexpr uint8_t kThree[] = {0x03}; +constexpr uint8_t kOneTwoThree[] = {0x01, 0x02, 0x03}; +} // namespace + +TEST(U2fParsingUtils, Materialize) { + const std::vector<uint8_t> empty; + EXPECT_THAT(Materialize(empty), ::testing::IsEmpty()); + EXPECT_THAT(Materialize(base::span<const uint8_t>()), ::testing::IsEmpty()); + + EXPECT_THAT(Materialize(kOne), ::testing::ElementsAreArray(kOne)); + EXPECT_THAT(Materialize(kOneTwoThree), + ::testing::ElementsAreArray(kOneTwoThree)); +} + +TEST(U2fParsingUtils, Append) { + std::vector<uint8_t> target; + + Append(&target, base::span<const uint8_t>()); + EXPECT_THAT(target, ::testing::IsEmpty()); + + // Should be idempotent, try twice for good measure. + Append(&target, base::span<const uint8_t>()); + EXPECT_THAT(target, ::testing::IsEmpty()); + + const std::vector<uint8_t> one(std::begin(kOne), std::end(kOne)); + Append(&target, one); + EXPECT_THAT(target, ::testing::ElementsAreArray(kOne)); + + Append(&target, kTwoThree); + EXPECT_THAT(target, ::testing::ElementsAreArray(kOneTwoThree)); +} + +// ExtractSpan and ExtractSuffixSpan are implicitly tested as they used by +// the Extract and ExtractSuffix implementations. + +TEST(U2fParsingUtils, ExtractEmpty) { + const std::vector<uint8_t> empty; + EXPECT_THAT(Extract(empty, 0, 0), ::testing::IsEmpty()); + + EXPECT_THAT(Extract(kOne, 0, 0), ::testing::IsEmpty()); + EXPECT_THAT(Extract(kOne, 1, 0), ::testing::IsEmpty()); + + EXPECT_THAT(Extract(kOneTwoThree, 0, 0), ::testing::IsEmpty()); + EXPECT_THAT(Extract(kOneTwoThree, 1, 0), ::testing::IsEmpty()); + EXPECT_THAT(Extract(kOneTwoThree, 2, 0), ::testing::IsEmpty()); + EXPECT_THAT(Extract(kOneTwoThree, 3, 0), ::testing::IsEmpty()); +} + +TEST(U2fParsingUtils, ExtractInBounds) { + EXPECT_THAT(Extract(kOne, 0, 1), ::testing::ElementsAreArray(kOne)); + EXPECT_THAT(Extract(kOneTwoThree, 0, 1), ::testing::ElementsAreArray(kOne)); + EXPECT_THAT(Extract(kOneTwoThree, 2, 1), ::testing::ElementsAreArray(kThree)); + EXPECT_THAT(Extract(kOneTwoThree, 1, 2), + ::testing::ElementsAreArray(kTwoThree)); + EXPECT_THAT(Extract(kOneTwoThree, 0, 3), + ::testing::ElementsAreArray(kOneTwoThree)); +} + +TEST(U2fParsingUtils, ExtractOutOfBounds) { + const std::vector<uint8_t> empty; + EXPECT_THAT(Extract(empty, 0, 1), ::testing::IsEmpty()); + EXPECT_THAT(Extract(empty, 1, 0), ::testing::IsEmpty()); + + EXPECT_THAT(Extract(kOne, 0, 2), ::testing::IsEmpty()); + EXPECT_THAT(Extract(kOne, 1, 1), ::testing::IsEmpty()); + EXPECT_THAT(Extract(kOne, 2, 0), ::testing::IsEmpty()); + + EXPECT_THAT(Extract(kOneTwoThree, 0, 4), ::testing::IsEmpty()); + EXPECT_THAT(Extract(kOneTwoThree, 1, 3), ::testing::IsEmpty()); + EXPECT_THAT(Extract(kOneTwoThree, 2, 2), ::testing::IsEmpty()); + EXPECT_THAT(Extract(kOneTwoThree, 3, 1), ::testing::IsEmpty()); + EXPECT_THAT(Extract(kOneTwoThree, 4, 0), ::testing::IsEmpty()); +} + +TEST(U2fParsingUtils, ExtractSuffixEmpty) { + const std::vector<uint8_t> empty; + EXPECT_THAT(ExtractSuffix(empty, 0), ::testing::IsEmpty()); + EXPECT_THAT(ExtractSuffix(kOne, 1), ::testing::IsEmpty()); + EXPECT_THAT(ExtractSuffix(kOneTwoThree, 3), ::testing::IsEmpty()); +} + +TEST(U2fParsingUtils, ExtractSuffixInBounds) { + EXPECT_THAT(ExtractSuffix(kOne, 0), ::testing::ElementsAreArray(kOne)); + EXPECT_THAT(ExtractSuffix(kOneTwoThree, 1), + ::testing::ElementsAreArray(kTwoThree)); + EXPECT_THAT(ExtractSuffix(kOneTwoThree, 2), + ::testing::ElementsAreArray(kThree)); +} + +TEST(U2fParsingUtils, ExtractSuffixOutOfBounds) { + const std::vector<uint8_t> empty; + EXPECT_THAT(ExtractSuffix(empty, 1), ::testing::IsEmpty()); + EXPECT_THAT(ExtractSuffix(kOne, 2), ::testing::IsEmpty()); + EXPECT_THAT(ExtractSuffix(kOneTwoThree, 4), ::testing::IsEmpty()); +} + +} // namespace u2f_parsing_utils +} // namespace device
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn index 1bf7841..ed05c37b 100644 --- a/extensions/common/BUILD.gn +++ b/extensions/common/BUILD.gn
@@ -179,6 +179,8 @@ "manifest_handler_helpers.h", "manifest_handlers/app_isolation_info.cc", "manifest_handlers/app_isolation_info.h", + "manifest_handlers/automation.cc", + "manifest_handlers/automation.h", "manifest_handlers/background_info.cc", "manifest_handlers/background_info.h", "manifest_handlers/content_capabilities_handler.cc",
diff --git a/extensions/common/api/extensions_manifest_types.json b/extensions/common/api/extensions_manifest_types.json index ea9eaf08..0c2f12dd 100644 --- a/extensions/common/api/extensions_manifest_types.json +++ b/extensions/common/api/extensions_manifest_types.json
@@ -11,6 +11,34 @@ "compiler_options": { "generate_error_messages": true }, "types": [ { + "id": "automation", + "description": "This API provides programmatic access to the user interface elements of Chrome. This includes everything in the web view, and optionally Chrome's full user interface.", + "choices": [ + { "type": "boolean", + "description": "If true, enables non-interactive access to the automation tree only for the sites for which the extension has a <a href='https://developer.chrome.com/extensions/declare_permissions#host-permissions'>host permission</a> or <a href='https://developer.chrome.com/extensions/declare_permissions#activeTab'>activeTab permission</a>)." }, + { "type": "object", + "properties": { + "desktop": { + "description": "Whether to request permission to the whole ChromeOS desktop. If granted, this gives the extension access to every aspect of the desktop, and every site and app. If this permission is requested, all other permissions are implicitly included and do not need to be requested separately.", + "optional": true, + "type": "boolean" + }, + "matches": { + "description": "A list of URL patterns for which this extension may request an automation tree. If not specified, automation permission will be granted for the sites for which the extension has a <a href='https://developer.chrome.com/extensions/declare_permissions#host-permissions'>host permission</a> or <a href='https://developer.chrome.com/extensions/declare_permissions#activeTab'>activeTab permission</a>).", + "optional": true, + "type": "array", + "items": { "type": "string" } + }, + "interact": { + "description": "Whether the extension is allowed interactive access (true) or read-only access (false; default) to the automation tree.", + "optional": true, + "type": "boolean" + } + } + } + ] + }, + { "id": "ContentCapabilities", "type": "object", "description": "The <code>content_capabilities</code> manifest entry allows an extension to grant certain additional capabilities to web contents whose locations match a given set of URL patterns.",
diff --git a/chrome/common/extensions/manifest_handlers/automation.cc b/extensions/common/manifest_handlers/automation.cc similarity index 92% rename from chrome/common/extensions/manifest_handlers/automation.cc rename to extensions/common/manifest_handlers/automation.cc index b8db260..ef7c022 100644 --- a/chrome/common/extensions/manifest_handlers/automation.cc +++ b/extensions/common/manifest_handlers/automation.cc
@@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/common/extensions/manifest_handlers/automation.h" +#include "extensions/common/manifest_handlers/automation.h" #include <memory> #include <utility> #include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" -#include "chrome/common/extensions/api/manifest_types.h" +#include "extensions/common/api/extensions_manifest_types.h" #include "extensions/common/error_utils.h" #include "extensions/common/extensions_client.h" #include "extensions/common/manifest_constants.h" @@ -32,11 +32,11 @@ "matches will be ignored."; const char kErrorInvalidMatch[] = "Invalid match pattern '*': *"; const char kErrorNoMatchesProvided[] = "No valid match patterns provided."; -} +} // namespace automation_errors namespace errors = manifest_errors; namespace keys = extensions::manifest_keys; -using api::manifest_types::Automation; +using api::extensions_manifest_types::Automation; class AutomationManifestPermission : public ManifestPermission { public: @@ -102,9 +102,9 @@ bool AutomationManifestPermission::FromValue(const base::Value* value) { base::string16 error; - automation_info_.reset(AutomationInfo::FromValue(*value, - NULL /* install_warnings */, - &error).release()); + automation_info_.reset( + AutomationInfo::FromValue(*value, NULL /* install_warnings */, &error) + .release()); return error.empty(); } @@ -154,11 +154,9 @@ base::WrapUnique(new const AutomationInfo(desktop, matches, interact))); } -AutomationHandler::AutomationHandler() { -} +AutomationHandler::AutomationHandler() {} -AutomationHandler::~AutomationHandler() { -} +AutomationHandler::~AutomationHandler() {} bool AutomationHandler::Parse(Extension* extension, base::string16* error) { const base::Value* automation = NULL; @@ -211,12 +209,12 @@ base::string16* error) { std::unique_ptr<Automation> automation = Automation::FromValue(value, error); if (!automation) - return std::unique_ptr<AutomationInfo>(); + return nullptr; if (automation->as_boolean) { if (*automation->as_boolean) return base::WrapUnique(new AutomationInfo()); - return std::unique_ptr<AutomationInfo>(); + return nullptr; } const Automation::Object& automation_object = *automation->as_object; @@ -245,8 +243,7 @@ for (std::vector<std::string>::iterator it = automation_object.matches->begin(); - it != automation_object.matches->end(); - ++it) { + it != automation_object.matches->end(); ++it) { // TODO(aboxhall): Refactor common logic from content_scripts_handler, // manifest_url_handler and user_script.cc into a single location and // re-use here. @@ -257,8 +254,7 @@ if (parse_result != URLPattern::PARSE_SUCCESS) { install_warnings->push_back( InstallWarning(ErrorUtils::FormatErrorMessage( - automation_errors::kErrorInvalidMatch, - *it, + automation_errors::kErrorInvalidMatch, *it, URLPattern::GetParseResultString(parse_result)))); continue; } @@ -299,15 +295,13 @@ return automation; } -AutomationInfo::AutomationInfo() : desktop(false), interact(false) { -} +AutomationInfo::AutomationInfo() : desktop(false), interact(false) {} AutomationInfo::AutomationInfo(bool desktop, const URLPatternSet& matches, bool interact) : desktop(desktop), matches(matches), interact(interact) {} -AutomationInfo::~AutomationInfo() { -} +AutomationInfo::~AutomationInfo() {} } // namespace extensions
diff --git a/chrome/common/extensions/manifest_handlers/automation.h b/extensions/common/manifest_handlers/automation.h similarity index 86% rename from chrome/common/extensions/manifest_handlers/automation.h rename to extensions/common/manifest_handlers/automation.h index eb1c1dc..f78220a1c 100644 --- a/chrome/common/extensions/manifest_handlers/automation.h +++ b/extensions/common/manifest_handlers/automation.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_COMMON_EXTENSIONS_MANIFEST_HANDLERS_AUTOMATION_H_ -#define CHROME_COMMON_EXTENSIONS_MANIFEST_HANDLERS_AUTOMATION_H_ +#ifndef EXTENSIONS_COMMON_MANIFEST_HANDLERS_AUTOMATION_H_ +#define EXTENSIONS_COMMON_MANIFEST_HANDLERS_AUTOMATION_H_ #include <memory> #include <string> @@ -17,10 +17,10 @@ namespace extensions { namespace api { -namespace manifest_types { +namespace extensions_manifest_types { struct Automation; } -} +} // namespace api class URLPatternSet; class AutomationManifestPermission; @@ -32,7 +32,7 @@ extern const char kErrorURLMalformed[]; extern const char kErrorInvalidMatch[]; extern const char kErrorNoMatchesProvided[]; -} +} // namespace automation_errors // The parsed form of the automation manifest entry. struct AutomationInfo : public Extension::ManifestData { @@ -61,8 +61,8 @@ AutomationInfo(); AutomationInfo(bool desktop, const URLPatternSet& matches, bool interact); - static std::unique_ptr<api::manifest_types::Automation> AsManifestType( - const AutomationInfo& info); + static std::unique_ptr<api::extensions_manifest_types::Automation> + AsManifestType(const AutomationInfo& info); DISALLOW_COPY_AND_ASSIGN(AutomationInfo); friend class AutomationManifestPermission; @@ -89,4 +89,4 @@ } // namespace extensions -#endif // CHROME_COMMON_EXTENSIONS_MANIFEST_HANDLERS_AUTOMATION_H_ +#endif // EXTENSIONS_COMMON_MANIFEST_HANDLERS_AUTOMATION_H_
diff --git a/google_apis/gaia/google_service_auth_error.cc b/google_apis/gaia/google_service_auth_error.cc index 79f445c..d28bc25 100644 --- a/google_apis/gaia/google_service_auth_error.cc +++ b/google_apis/gaia/google_service_auth_error.cc
@@ -8,11 +8,8 @@ #include <string> #include <utility> -#include "base/json/json_reader.h" #include "base/logging.h" -#include "base/strings/string_util.h" #include "base/strings/stringprintf.h" -#include "base/values.h" #include "net/base/net_errors.h" GoogleServiceAuthError::Captcha::Captcha() : image_width(0), image_height(0) { @@ -77,23 +74,17 @@ } GoogleServiceAuthError::GoogleServiceAuthError() - : state_(NONE), network_error_(0) {} + : GoogleServiceAuthError(NONE) {} GoogleServiceAuthError::GoogleServiceAuthError(State s) - : state_(s), - network_error_(0) { - // If the caller has no idea, then we just set it to a generic failure. - if (s == CONNECTION_FAILED) { - network_error_ = net::ERR_FAILED; - } -} + : GoogleServiceAuthError(s, std::string()) {} -GoogleServiceAuthError::GoogleServiceAuthError( - State state, - const std::string& error_message) - : state_(state), - network_error_(0), - error_message_(error_message) { +GoogleServiceAuthError::GoogleServiceAuthError(State state, + const std::string& error_message) + : GoogleServiceAuthError( + state, + (state == CONNECTION_FAILED) ? net::ERR_FAILED : 0) { + error_message_ = error_message; } GoogleServiceAuthError::GoogleServiceAuthError( @@ -106,6 +97,14 @@ } // static +GoogleServiceAuthError GoogleServiceAuthError::FromInvalidGaiaCredentialsReason( + InvalidGaiaCredentialsReason reason) { + GoogleServiceAuthError error(INVALID_GAIA_CREDENTIALS); + error.invalid_gaia_credentials_reason_ = reason; + return error; +} + +// static GoogleServiceAuthError GoogleServiceAuthError::FromClientLoginCaptchaChallenge( const std::string& captcha_token, const GURL& captcha_image_url, @@ -171,53 +170,10 @@ return error_message_; } -base::DictionaryValue* GoogleServiceAuthError::ToValue() const { - base::DictionaryValue* value = new base::DictionaryValue(); - std::string state_str; - switch (state_) { -#define STATE_CASE(x) case x: state_str = #x; break - STATE_CASE(NONE); - STATE_CASE(INVALID_GAIA_CREDENTIALS); - STATE_CASE(USER_NOT_SIGNED_UP); - STATE_CASE(CONNECTION_FAILED); - STATE_CASE(CAPTCHA_REQUIRED); - STATE_CASE(ACCOUNT_DELETED); - STATE_CASE(ACCOUNT_DISABLED); - STATE_CASE(SERVICE_UNAVAILABLE); - STATE_CASE(TWO_FACTOR); - STATE_CASE(REQUEST_CANCELED); - STATE_CASE(UNEXPECTED_SERVICE_RESPONSE); - STATE_CASE(SERVICE_ERROR); - STATE_CASE(WEB_LOGIN_REQUIRED); -#undef STATE_CASE - default: - NOTREACHED(); - break; - } - value->SetString("state", state_str); - if (!error_message_.empty()) { - value->SetString("errorMessage", error_message_); - } - if (state_ == CAPTCHA_REQUIRED) { - auto captcha_value = std::make_unique<base::DictionaryValue>(); - captcha_value->SetString("token", captcha_.token); - captcha_value->SetString("audioUrl", captcha_.audio_url.spec()); - captcha_value->SetString("imageUrl", captcha_.image_url.spec()); - captcha_value->SetString("unlockUrl", captcha_.unlock_url.spec()); - captcha_value->SetInteger("imageWidth", captcha_.image_width); - captcha_value->SetInteger("imageHeight", captcha_.image_height); - value->Set("captcha", std::move(captcha_value)); - } else if (state_ == CONNECTION_FAILED) { - value->SetString("networkError", net::ErrorToString(network_error_)); - } else if (state_ == TWO_FACTOR) { - auto two_factor_value = std::make_unique<base::DictionaryValue>(); - two_factor_value->SetString("token", second_factor_.token); - two_factor_value->SetString("promptText", second_factor_.prompt_text); - two_factor_value->SetString("alternateText", second_factor_.alternate_text); - two_factor_value->SetInteger("fieldLength", second_factor_.field_length); - value->Set("two_factor", std::move(two_factor_value)); - } - return value; +GoogleServiceAuthError::InvalidGaiaCredentialsReason +GoogleServiceAuthError::GetInvalidGaiaCredentialsReason() const { + DCHECK_EQ(INVALID_GAIA_CREDENTIALS, state()); + return invalid_gaia_credentials_reason_; } std::string GoogleServiceAuthError::ToString() const { @@ -225,7 +181,9 @@ case NONE: return std::string(); case INVALID_GAIA_CREDENTIALS: - return "Invalid credentials."; + return base::StringPrintf( + "Invalid credentials (%d).", + static_cast<int>(invalid_gaia_credentials_reason_)); case USER_NOT_SIGNED_UP: return "Not authorized."; case CONNECTION_FAILED: @@ -280,19 +238,22 @@ GoogleServiceAuthError::GoogleServiceAuthError(State s, int error) : state_(s), - network_error_(error) { -} + network_error_(error), + invalid_gaia_credentials_reason_(InvalidGaiaCredentialsReason::UNKNOWN) {} -GoogleServiceAuthError::GoogleServiceAuthError( - State s, - const std::string& captcha_token, - const GURL& captcha_audio_url, - const GURL& captcha_image_url, - const GURL& captcha_unlock_url, - int image_width, - int image_height) +GoogleServiceAuthError::GoogleServiceAuthError(State s, + const std::string& captcha_token, + const GURL& captcha_audio_url, + const GURL& captcha_image_url, + const GURL& captcha_unlock_url, + int image_width, + int image_height) : state_(s), - captcha_(captcha_token, captcha_audio_url, captcha_image_url, - captcha_unlock_url, image_width, image_height), - network_error_(0) { -} + captcha_(captcha_token, + captcha_audio_url, + captcha_image_url, + captcha_unlock_url, + image_width, + image_height), + network_error_((state_ == CONNECTION_FAILED) ? net::ERR_FAILED : net::OK), + invalid_gaia_credentials_reason_(InvalidGaiaCredentialsReason::UNKNOWN) {}
diff --git a/google_apis/gaia/google_service_auth_error.h b/google_apis/gaia/google_service_auth_error.h index 5e84599..8ed19a9 100644 --- a/google_apis/gaia/google_service_auth_error.h +++ b/google_apis/gaia/google_service_auth_error.h
@@ -8,17 +8,6 @@ // Accounts (e.g expired credentials). It may contain additional data such as // captcha or OTP challenges. -// A GoogleServiceAuthError without additional data is just a State, defined -// below. A case could be made to have this relation implicit, to allow raising -// error events concisely by doing OnAuthError(GoogleServiceAuthError::NONE), -// for example. But the truth is this class is ever so slightly more than a -// transparent wrapper around 'State' due to additional Captcha data -// (e.g consider operator=), and this would violate the style guide. Thus, -// you must explicitly use the constructor when all you have is a State. -// The good news is the implementation nests the enum inside a class, so you -// may forward declare and typedef GoogleServiceAuthError to something shorter -// in the comfort of your own translation unit. - #ifndef GOOGLE_APIS_GAIA_GOOGLE_SERVICE_AUTH_ERROR_H_ #define GOOGLE_APIS_GAIA_GOOGLE_SERVICE_AUTH_ERROR_H_ @@ -34,10 +23,6 @@ } } -namespace base { -class DictionaryValue; -} - class GoogleServiceAuthError { public: // @@ -101,6 +86,23 @@ NUM_STATES = 14, }; + // Error reason for invalid credentials. Only used when the error is + // INVALID_GAIA_CREDENTIALS. + // Used by UMA histograms: do not remove or reorder values, add new values at + // the end. + enum class InvalidGaiaCredentialsReason { + // The error was not specified. + UNKNOWN = 0, + // Credentials were rejectedby the Gaia server. + CREDENTIALS_REJECTED_BY_SERVER, + // Credentials were invalidated locally by Chrome. + CREDENTIALS_REJECTED_BY_CLIENT, + // Credentials are missing (e.g. could not be loaded from disk). + CREDENTIALS_MISSING, + + NUM_REASONS + }; + // Additional data for CAPTCHA_REQUIRED errors. struct Captcha { Captcha(); @@ -164,6 +166,9 @@ // It will be created with CONNECTION_FAILED set. static GoogleServiceAuthError FromConnectionError(int error); + static GoogleServiceAuthError FromInvalidGaiaCredentialsReason( + InvalidGaiaCredentialsReason reason); + // Construct a CAPTCHA_REQUIRED error with CAPTCHA challenge data from the // the ClientLogin endpoint. // TODO(rogerta): once ClientLogin is no longer used, may be able to get @@ -198,9 +203,8 @@ const std::string& token() const; const std::string& error_message() const; - // Returns info about this object in a dictionary. Caller takes - // ownership of returned dictionary. - base::DictionaryValue* ToValue() const; + // Should only be used when the error state is INVALID_GAIA_CREDENTIALS. + InvalidGaiaCredentialsReason GetInvalidGaiaCredentialsReason() const; // Returns a message describing the error. std::string ToString() const; @@ -234,6 +238,7 @@ SecondFactor second_factor_; int network_error_; std::string error_message_; + InvalidGaiaCredentialsReason invalid_gaia_credentials_reason_; }; #endif // GOOGLE_APIS_GAIA_GOOGLE_SERVICE_AUTH_ERROR_H_
diff --git a/google_apis/gaia/google_service_auth_error_unittest.cc b/google_apis/gaia/google_service_auth_error_unittest.cc index 2fec3ea..4035256 100644 --- a/google_apis/gaia/google_service_auth_error_unittest.cc +++ b/google_apis/gaia/google_service_auth_error_unittest.cc
@@ -7,49 +7,73 @@ #include <memory> #include <string> -#include "base/test/values_test_util.h" -#include "base/values.h" #include "net/base/net_errors.h" #include "testing/gtest/include/gtest/gtest.h" namespace { -using base::ExpectDictStringValue; +TEST(GoogleServiceAuthErrorTest, State) { + for (GoogleServiceAuthError::State i = GoogleServiceAuthError::NONE; + i < GoogleServiceAuthError::NUM_STATES; + i = GoogleServiceAuthError::State(i + 1)) { + GoogleServiceAuthError error(i); + EXPECT_EQ(i, error.state()); + EXPECT_TRUE(error.error_message().empty()); -class GoogleServiceAuthErrorTest : public testing::Test {}; + if (i == GoogleServiceAuthError::CONNECTION_FAILED) + EXPECT_EQ(net::ERR_FAILED, error.network_error()); + else + EXPECT_EQ(net::OK, error.network_error()); -void TestSimpleState(GoogleServiceAuthError::State state) { - GoogleServiceAuthError error(state); - std::unique_ptr<base::DictionaryValue> value(error.ToValue()); - EXPECT_EQ(1u, value->size()); - std::string state_str; - EXPECT_TRUE(value->GetString("state", &state_str)); - EXPECT_FALSE(state_str.empty()); - EXPECT_NE("CONNECTION_FAILED", state_str); - EXPECT_NE("CAPTCHA_REQUIRED", state_str); -} + if (i == GoogleServiceAuthError::NONE) { + EXPECT_FALSE(error.IsTransientError()); + EXPECT_FALSE(error.IsPersistentError()); + } else if ((i == GoogleServiceAuthError::CONNECTION_FAILED) || + (i == GoogleServiceAuthError::SERVICE_UNAVAILABLE) || + (i == GoogleServiceAuthError::REQUEST_CANCELED)) { + EXPECT_TRUE(error.IsTransientError()); + EXPECT_FALSE(error.IsPersistentError()); + } else { + EXPECT_FALSE(error.IsTransientError()); + EXPECT_TRUE(error.IsPersistentError()); + } -TEST_F(GoogleServiceAuthErrorTest, SimpleToValue) { - for (int i = GoogleServiceAuthError::NONE; - i <= GoogleServiceAuthError::USER_NOT_SIGNED_UP; ++i) { - TestSimpleState(static_cast<GoogleServiceAuthError::State>(i)); + if (i == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS) { + EXPECT_EQ(GoogleServiceAuthError::InvalidGaiaCredentialsReason::UNKNOWN, + error.GetInvalidGaiaCredentialsReason()); + } } } -TEST_F(GoogleServiceAuthErrorTest, None) { - GoogleServiceAuthError error(GoogleServiceAuthError::AuthErrorNone()); - std::unique_ptr<base::DictionaryValue> value(error.ToValue()); - EXPECT_EQ(1u, value->size()); - ExpectDictStringValue("NONE", *value, "state"); +TEST(GoogleServiceAuthErrorTest, FromConnectionError) { + GoogleServiceAuthError error = + GoogleServiceAuthError::FromConnectionError(net::ERR_TIMED_OUT); + EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error.state()); + EXPECT_EQ(net::ERR_TIMED_OUT, error.network_error()); } -TEST_F(GoogleServiceAuthErrorTest, ConnectionFailed) { - GoogleServiceAuthError error( - GoogleServiceAuthError::FromConnectionError(net::OK)); - std::unique_ptr<base::DictionaryValue> value(error.ToValue()); - EXPECT_EQ(2u, value->size()); - ExpectDictStringValue("CONNECTION_FAILED", *value, "state"); - ExpectDictStringValue("net::OK", *value, "networkError"); +TEST(GoogleServiceAuthErrorTest, FromServiceError) { + GoogleServiceAuthError error = + GoogleServiceAuthError::FromServiceError("Foo"); + EXPECT_EQ(GoogleServiceAuthError::SERVICE_ERROR, error.state()); + EXPECT_EQ("Foo", error.error_message()); +} + +TEST(GoogleServiceAuthErrorTest, FromInvalidGaiaCredentialsReason) { + GoogleServiceAuthError error = + GoogleServiceAuthError::FromInvalidGaiaCredentialsReason( + GoogleServiceAuthError::InvalidGaiaCredentialsReason:: + CREDENTIALS_REJECTED_BY_SERVER); + EXPECT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, error.state()); + EXPECT_EQ(GoogleServiceAuthError::InvalidGaiaCredentialsReason:: + CREDENTIALS_REJECTED_BY_SERVER, + error.GetInvalidGaiaCredentialsReason()); + EXPECT_EQ("Invalid credentials (1).", error.ToString()); +} + +TEST(GoogleServiceAuthErrorTest, AuthErrorNone) { + EXPECT_EQ(GoogleServiceAuthError(GoogleServiceAuthError::NONE), + GoogleServiceAuthError::AuthErrorNone()); } } // namespace
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg index 9c7f8b1..35f409c 100644 --- a/infra/config/global/cr-buildbucket.cfg +++ b/infra/config/global/cr-buildbucket.cfg
@@ -473,6 +473,7 @@ builders { name: "Linux Builder" mixins: "linux-ci" + dimensions: "cores:32" } builders {
diff --git a/ios/chrome/browser/download/download_manager_metric_names.h b/ios/chrome/browser/download/download_manager_metric_names.h index 6f7d1c1..03b9cf9 100644 --- a/ios/chrome/browser/download/download_manager_metric_names.h +++ b/ios/chrome/browser/download/download_manager_metric_names.h
@@ -22,4 +22,19 @@ Count, }; +// Values of the UMA Download.IOSDownloadFileResult histogram. This action is +// reported only for started downloads. +enum class DownloadFileResult { + // Download has successfully completed. + Completed = 0, + // In progress download was cancelled by the user. + Cancelled = 1, + // Download has completed with error. + Failure = 2, + // In progress download did no finish because the tab was closed or user has + // quit the app. + Other = 3, + Count +}; + #endif // IOS_CHROME_BROWSER_DOWNLOAD_DOWNLOAD_MANAGER_METRIC_NAMES_H_
diff --git a/ios/chrome/browser/metrics/new_tab_page_uma.h b/ios/chrome/browser/metrics/new_tab_page_uma.h index 7db52e4f..627ec18 100644 --- a/ios/chrome/browser/metrics/new_tab_page_uma.h +++ b/ios/chrome/browser/metrics/new_tab_page_uma.h
@@ -30,13 +30,15 @@ ACTION_OPENED_LEARN_MORE, ACTION_OPENED_PROMO, ACTION_OPENED_HISTORY_ENTRY, + ACTION_NAVIGATED_USING_VOICE_SEARCH, NUM_ACTION_TYPES, }; void RecordAction(ios::ChromeBrowserState* browserState, ActionType action); void RecordActionFromOmnibox(ios::ChromeBrowserState* browserState, const GURL& url, - ui::PageTransition transition); + ui::PageTransition transition, + bool isExpectingVoiceSearch); } // namespace new_tab_page_uma #endif // IOS_CHROME_BROWSER_METRICS_NEW_TAB_PAGE_UMA_H_
diff --git a/ios/chrome/browser/metrics/new_tab_page_uma.mm b/ios/chrome/browser/metrics/new_tab_page_uma.mm index 59c0709..77b54e8 100644 --- a/ios/chrome/browser/metrics/new_tab_page_uma.mm +++ b/ios/chrome/browser/metrics/new_tab_page_uma.mm
@@ -39,7 +39,12 @@ void RecordActionFromOmnibox(ios::ChromeBrowserState* browserState, const GURL& url, - ui::PageTransition transition) { + ui::PageTransition transition, + bool isExpectingVoiceSearch) { + if (isExpectingVoiceSearch) { + RecordAction(browserState, ACTION_NAVIGATED_USING_VOICE_SEARCH); + return; + } ui::PageTransition coreTransition = static_cast<ui::PageTransition>( transition & ui::PAGE_TRANSITION_CORE_MASK); if (PageTransitionCoreTypeIs(coreTransition, ui::PAGE_TRANSITION_GENERATED)) {
diff --git a/ios/chrome/browser/ui/BUILD.gn b/ios/chrome/browser/ui/BUILD.gn index 03cc0d1..d27a67c6 100644 --- a/ios/chrome/browser/ui/BUILD.gn +++ b/ios/chrome/browser/ui/BUILD.gn
@@ -417,6 +417,7 @@ "//ios/chrome/browser/ui/util", "//ios/chrome/browser/ui/voice", "//ios/chrome/browser/upgrade", + "//ios/chrome/browser/voice:voice", "//ios/chrome/browser/web", "//ios/chrome/browser/web:tab_helper_delegates", "//ios/chrome/browser/web:web_internal",
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm index 2c9eeb4..37cada0 100644 --- a/ios/chrome/browser/ui/browser_view_controller.mm +++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -226,6 +226,7 @@ #import "ios/chrome/browser/ui/util/pasteboard_util.h" #import "ios/chrome/browser/ui/voice/text_to_speech_player.h" #include "ios/chrome/browser/upgrade/upgrade_center.h" +#import "ios/chrome/browser/voice/voice_search_navigations_tab_helper.h" #import "ios/chrome/browser/web/blocked_popup_tab_helper.h" #import "ios/chrome/browser/web/error_page_content.h" #import "ios/chrome/browser/web/external_apps_launch_policy_decider.h" @@ -4131,7 +4132,15 @@ [_bookmarkInteractionController dismissBookmarkModalControllerAnimated:YES]; if (transition & ui::PAGE_TRANSITION_FROM_ADDRESS_BAR) { - new_tab_page_uma::RecordActionFromOmnibox(_browserState, url, transition); + BOOL isExpectingVoiceSearch = NO; + web::WebState* webState = [_model currentTab].webState; + if (webState) { + isExpectingVoiceSearch = + VoiceSearchNavigationTabHelper::FromWebState(webState) + ->IsExpectingVoiceSearch(); + } + new_tab_page_uma::RecordActionFromOmnibox(_browserState, url, transition, + isExpectingVoiceSearch); } // NOTE: This check for the Crash Host URL is here to avoid the URL from
diff --git a/ios/chrome/browser/ui/download/download_manager_coordinator.mm b/ios/chrome/browser/ui/download/download_manager_coordinator.mm index 5e1da9c5..10fc99a 100644 --- a/ios/chrome/browser/ui/download/download_manager_coordinator.mm +++ b/ios/chrome/browser/ui/download/download_manager_coordinator.mm
@@ -17,6 +17,7 @@ #import "ios/chrome/browser/download/google_drive_app_util.h" #import "ios/chrome/browser/installation_notifier.h" #import "ios/chrome/browser/store_kit/store_kit_coordinator.h" +#import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h" #import "ios/chrome/browser/ui/download/download_manager_mediator.h" #import "ios/chrome/browser/ui/download/download_manager_view_controller.h" #import "ios/chrome/browser/ui/presenters/contained_presenter.h" @@ -41,10 +42,25 @@ // Stops tracking this download task. void Remove(web::DownloadTask* task) { task->RemoveObserver(this); } // DownloadTaskObserver overrides: + void OnDownloadUpdated(web::DownloadTask* task) override { + if (task->IsDone()) { + UMA_HISTOGRAM_ENUMERATION("Download.IOSDownloadFileResult", + task->GetErrorCode() + ? DownloadFileResult::Failure + : DownloadFileResult::Completed, + DownloadFileResult::Count); + } + } void OnDownloadDestroyed(web::DownloadTask* task) override { // This download task was never open by the user. task->RemoveObserver(this); + if (task->GetState() == web::DownloadTask::State::kInProgress) { + UMA_HISTOGRAM_ENUMERATION("Download.IOSDownloadFileResult", + DownloadFileResult::Other, + DownloadFileResult::Count); + } + if (task->IsDone() && task->GetErrorCode() == net::OK) { UMA_HISTOGRAM_ENUMERATION("Download.IOSDownloadedFileAction", DownloadedFileAction::NoAction, @@ -66,6 +82,10 @@ UIDocumentInteractionController* _openInController; DownloadManagerMediator _mediator; StoreKitCoordinator* _storeKitCoordinator; + // Coordinator for displaying the alert informing the user that no application + // on the device can open the file. The alert offers the user to install + // Google Drive app. + AlertCoordinator* _installDriveAlertCoordinator; UnopenedDownloadsTracker _unopenedDownloads; } @end @@ -109,6 +129,8 @@ [_storeKitCoordinator stop]; _storeKitCoordinator = nil; + [_installDriveAlertCoordinator stop]; + _installDriveAlertCoordinator = nil; } - (UIViewController*)viewController { @@ -206,6 +228,11 @@ message:nil completionHandler:^(BOOL confirmed) { if (confirmed) { + UMA_HISTOGRAM_ENUMERATION( + "Download.IOSDownloadFileResult", + DownloadFileResult::Cancelled, + DownloadFileResult::Count); + [weakSelf cancelDownload]; } }]; @@ -213,19 +240,7 @@ - (void)installDriveForDownloadManagerViewController: (DownloadManagerViewController*)controller { - if (!_storeKitCoordinator) { - _storeKitCoordinator = [[StoreKitCoordinator alloc] - initWithBaseViewController:self.baseViewController]; - _storeKitCoordinator.iTunesItemIdentifier = - kGoogleDriveITunesItemIdentifier; - } - [_storeKitCoordinator start]; - [controller setInstallDriveButtonVisible:NO animated:YES]; - - [[InstallationNotifier sharedInstance] - registerForInstallationNotifications:self - withSelector:@selector(didInstallGoogleDriveApp) - forScheme:kGoogleDriveAppURLScheme]; + [self presentStoreKitForGoogleDriveApp]; } - (void)downloadManagerViewControllerDidStartDownload: @@ -249,7 +264,12 @@ [_openInController presentOpenInMenuFromRect:layoutGuide.layoutFrame inView:layoutGuide.owningView animated:YES]; - DCHECK(menuShown); + + // No application on this device can open the file. Typically happens on + // iOS 10, where Files app does not exist. + if (!menuShown) { + [self didFailOpenInMenuPresentation]; + } } #pragma mark - Private @@ -301,4 +321,52 @@ base::UserMetricsAction(kDownloadManagerGoogleDriveInstalled)); } +// Called when Open In... menu was not presented. This method shows the alert +// which offers the user to install Google Drive app. +- (void)didFailOpenInMenuPresentation { + NSString* title = + l10n_util::GetNSString(IDS_IOS_DOWNLOAD_MANAGER_UNABLE_TO_OPEN_FILE); + NSString* message = + l10n_util::GetNSString(IDS_IOS_DOWNLOAD_MANAGER_NO_APP_MESSAGE); + + _installDriveAlertCoordinator = [[AlertCoordinator alloc] + initWithBaseViewController:self.baseViewController + title:title + message:message]; + + NSString* googleDriveButtonTitle = + l10n_util::GetNSString(IDS_IOS_DOWNLOAD_MANAGER_UPLOAD_TO_GOOGLE_DRIVE); + __weak DownloadManagerCoordinator* weakSelf = self; + [_installDriveAlertCoordinator + addItemWithTitle:googleDriveButtonTitle + action:^{ + [weakSelf presentStoreKitForGoogleDriveApp]; + } + style:UIAlertActionStyleDefault]; + + [_installDriveAlertCoordinator + addItemWithTitle:l10n_util::GetNSString(IDS_CANCEL) + action:nil + style:UIAlertActionStyleCancel]; + + [_installDriveAlertCoordinator start]; +} + +// Presents StoreKit dialog for Google Drive application. +- (void)presentStoreKitForGoogleDriveApp { + if (!_storeKitCoordinator) { + _storeKitCoordinator = [[StoreKitCoordinator alloc] + initWithBaseViewController:self.baseViewController]; + _storeKitCoordinator.iTunesItemIdentifier = + kGoogleDriveITunesItemIdentifier; + } + [_storeKitCoordinator start]; + [_viewController setInstallDriveButtonVisible:NO animated:YES]; + + [[InstallationNotifier sharedInstance] + registerForInstallationNotifications:self + withSelector:@selector(didInstallGoogleDriveApp) + forScheme:kGoogleDriveAppURLScheme]; +} + @end
diff --git a/ios/chrome/browser/ui/download/download_manager_coordinator_unittest.mm b/ios/chrome/browser/ui/download/download_manager_coordinator_unittest.mm index cbebe6f3..d0bffa8 100644 --- a/ios/chrome/browser/ui/download/download_manager_coordinator_unittest.mm +++ b/ios/chrome/browser/ui/download/download_manager_coordinator_unittest.mm
@@ -404,11 +404,40 @@ // Download task is destroyed without opening the file. task = nullptr; histogram_tester_.ExpectUniqueSample( + "Download.IOSDownloadFileResult", + static_cast<base::HistogramBase::Sample>(DownloadFileResult::Completed), + 1); + histogram_tester_.ExpectUniqueSample( "Download.IOSDownloadedFileAction", static_cast<base::HistogramBase::Sample>(DownloadedFileAction::NoAction), 1); } +// Tests destroying download task for in progress download. +TEST_F(DownloadManagerCoordinatorTest, DestroyInProgressDownload) { + auto task = CreateTestTask(); + coordinator_.downloadTask = task.get(); + [coordinator_ start]; + + EXPECT_EQ(1U, base_view_controller_.childViewControllers.count); + DownloadManagerViewController* viewController = + base_view_controller_.childViewControllers.firstObject; + ASSERT_EQ([DownloadManagerViewController class], [viewController class]); + + // Start and the download. + base::FilePath path; + ASSERT_TRUE(base::GetTempDir(&path)); + task->Start(std::make_unique<net::URLFetcherFileWriter>( + base::ThreadTaskRunnerHandle::Get(), path)); + + // Download task is destroyed before the download is complete. + task = nullptr; + histogram_tester_.ExpectTotalCount("Download.IOSDownloadedFileAction", 0); + histogram_tester_.ExpectUniqueSample( + "Download.IOSDownloadFileResult", + static_cast<base::HistogramBase::Sample>(DownloadFileResult::Other), 1); +} + // Tests opening the download in Google Drive app. TEST_F(DownloadManagerCoordinatorTest, OpenInDrive) { web::FakeDownloadTask task(GURL(kTestUrl), kTestMimeType); @@ -451,6 +480,7 @@ willBeginSendingToApplication:kGoogleDriveAppBundleID]; } + histogram_tester_.ExpectTotalCount("Download.IOSDownloadFileResult", 0); histogram_tester_.ExpectUniqueSample("Download.IOSDownloadedFileAction", static_cast<base::HistogramBase::Sample>( DownloadedFileAction::OpenedInDrive), @@ -499,6 +529,7 @@ willBeginSendingToApplication:@"foo-app-id"]; } + histogram_tester_.ExpectTotalCount("Download.IOSDownloadFileResult", 0); histogram_tester_.ExpectUniqueSample( "Download.IOSDownloadedFileAction", static_cast<base::HistogramBase::Sample>( @@ -506,6 +537,52 @@ 1); } +// Tests the failure to present Open In... menu. Typically happens on iOS 10 +// where Files app is not installed. +TEST_F(DownloadManagerCoordinatorTest, OpenInFailure) { + web::FakeDownloadTask task(GURL(kTestUrl), kTestMimeType); + task.SetSuggestedFilename(base::SysNSStringToUTF16(kTestSuggestedFileName)); + coordinator_.downloadTask = &task; + [coordinator_ start]; + + EXPECT_EQ(1U, base_view_controller_.childViewControllers.count); + DownloadManagerViewController* viewController = + base_view_controller_.childViewControllers.firstObject; + ASSERT_EQ([DownloadManagerViewController class], [viewController class]); + + // Start and complete the download. + base::FilePath path; + ASSERT_TRUE(base::GetTempDir(&path)); + task.Start(std::make_unique<net::URLFetcherFileWriter>( + base::ThreadTaskRunnerHandle::Get(), path)); + + // Stub UIDocumentInteractionController. + id document_interaction_controller = + [[FakeDocumentInteractionController alloc] init]; + [document_interaction_controller setPresentsOpenInMenu:NO]; + OCMStub([document_interaction_controller_class_ + interactionControllerWithURL:[OCMArg any]]) + .andReturn(document_interaction_controller); + + // Attempt to present Open In... menu. + ASSERT_FALSE([document_interaction_controller presentedOpenInMenu]); + @autoreleasepool { + // This call will retain coordinator, which should outlive thread bundle. + [viewController.delegate downloadManagerViewController:viewController + presentOpenInMenuWithLayoutGuide:nil]; + } + ASSERT_FALSE([document_interaction_controller presentedOpenInMenu]); + + // Verify that UIAlert is presented. + ASSERT_TRUE([base_view_controller_.presentedViewController + isKindOfClass:[UIAlertController class]]); + UIAlertController* alert = base::mac::ObjCCast<UIAlertController>( + base_view_controller_.presentedViewController); + EXPECT_NSEQ(@"Unable to Open File", alert.title); + EXPECT_NSEQ(@"No application on this device can open the file.", + alert.message); +} + // Tests closing view controller while the download is in progress. Coordinator // should present the confirmation dialog. TEST_F(DownloadManagerCoordinatorTest, CloseInProgressDownload) { @@ -613,10 +690,11 @@ TEST_F(DownloadManagerCoordinatorTest, RetryingDownload) { web::FakeDownloadTask task(GURL(kTestUrl), kTestMimeType); task.SetSuggestedFilename(base::SysNSStringToUTF16(kTestSuggestedFileName)); - task.SetErrorCode(net::ERR_INTERNET_DISCONNECTED); web::DownloadTask* task_ptr = &task; coordinator_.downloadTask = &task; [coordinator_ start]; + task.SetErrorCode(net::ERR_INTERNET_DISCONNECTED); + task.SetDone(true); DownloadManagerViewController* viewController = base_view_controller_.childViewControllers.firstObject; @@ -633,6 +711,9 @@ return task_ptr->GetState() == web::DownloadTask::State::kInProgress; })); + histogram_tester_.ExpectUniqueSample( + "Download.IOSDownloadFileResult", + static_cast<base::HistogramBase::Sample>(DownloadFileResult::Failure), 1); ASSERT_EQ(1, user_action_tester_.GetActionCount("MobileDownloadRetryDownload")); }
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm b/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm index bc52bdf..09647ac 100644 --- a/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm +++ b/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm
@@ -704,7 +704,7 @@ - (NSArray<UIKeyCommand*>*)keyCommands { NSMutableArray<UIKeyCommand*>* commands = [[self upDownCommands] mutableCopy]; - if ([self isPreEditing]) { + if ([self isPreEditing] || [self hasAutocompleteText]) { [commands addObjectsFromArray:[self leftRightCommands]]; } @@ -719,10 +719,12 @@ [self.suggestionCommandsEndpoint highlightPreviousSuggestion]; } -#pragma mark preedit-only key commands +#pragma mark preedit and inline autocomplete key commands // React to left and right keys when in preedit state to exit preedit and put -// cursor to the beginning/end of the textfield. +// cursor to the beginning/end of the textfield; or if there is inline +// suggestion displayed, accept it and put the cursor before/after the +// suggested text. - (NSArray<UIKeyCommand*>*)leftRightCommands { UIKeyCommand* commandLeft = [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow @@ -737,22 +739,57 @@ } - (void)keyCommandLeft { - [self exitPreEditState]; - UITextPosition* beginning = self.beginningOfDocument; - UITextRange* textRange = - [self textRangeFromPosition:beginning toPosition:beginning]; + DCHECK([self isPreEditing] || [self hasAutocompleteText]); + // Cursor offset. + NSInteger offset = 0; + if ([self isPreEditing]) { + [self exitPreEditState]; + } + + if ([self hasAutocompleteText]) { + // The cursor should stay in the end of the user input. + offset = self.text.length; + + // Accept autocomplete suggestion. + [self acceptAutocompleteText]; + } + + UITextPosition* beginning = self.beginningOfDocument; + UITextPosition* cursorPosition = + [self positionFromPosition:beginning offset:offset]; + UITextRange* textRange = + [self textRangeFromPosition:cursorPosition toPosition:cursorPosition]; self.selectedTextRange = textRange; } - (void)keyCommandRight { - [self exitPreEditState]; + DCHECK([self isPreEditing] || [self hasAutocompleteText]); + + if ([self isPreEditing]) { + [self exitPreEditState]; + } + + if ([self hasAutocompleteText]) { + [self acceptAutocompleteText]; + } + + // Put the cursor to the end of the input. UITextPosition* end = self.endOfDocument; UITextRange* textRange = [self textRangeFromPosition:end toPosition:end]; self.selectedTextRange = textRange; } +// A helper to accept the current autocomplete text. +- (void)acceptAutocompleteText { + DCHECK([self hasAutocompleteText]); + // Strip attributes and set text as if the user typed it. + NSAttributedString* string = + [[NSAttributedString alloc] initWithString:_selection.text]; + [self setText:string userTextLength:string.length]; +} + #pragma mark - helpers // Gets the bounds of the rect covering the URL.
diff --git a/ios/chrome/browser/ui/tab_grid/grid/BUILD.gn b/ios/chrome/browser/ui/tab_grid/grid/BUILD.gn index cfd8120..c638041 100644 --- a/ios/chrome/browser/ui/tab_grid/grid/BUILD.gn +++ b/ios/chrome/browser/ui/tab_grid/grid/BUILD.gn
@@ -37,3 +37,20 @@ "//ui/base", ] } + +source_set("unit_tests") { + testonly = true + + sources = [ + "grid_view_controller_unittest.mm", + ] + + configs += [ "//build/config/compiler:enable_arc" ] + + deps = [ + ":grid_ui", + "//base/test:test_support", + "//testing/gtest", + "//third_party/ocmock", + ] +}
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 1c266e31..9f1c258d 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
@@ -76,7 +76,7 @@ - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self.collectionView reloadData]; - self.collectionView.backgroundView.hidden = (self.items.count > 0); + self.emptyStateView.hidden = (self.items.count > 0); // Selection is invalid if there are no items. if (self.items.count == 0) return; @@ -198,12 +198,14 @@ selectedIndex:(NSUInteger)selectedIndex { self.items = [items mutableCopy]; self.selectedIndex = selectedIndex; - if (![self isViewVisible]) - return; - [self.collectionView reloadData]; - [self.collectionView selectItemAtIndexPath:CreateIndexPath(selectedIndex) - animated:YES - scrollPosition:UICollectionViewScrollPositionTop]; + if ([self isViewVisible]) { + [self.collectionView reloadData]; + [self.collectionView + selectItemAtIndexPath:CreateIndexPath(selectedIndex) + animated:YES + scrollPosition:UICollectionViewScrollPositionTop]; + } + // Whether the view is visible or not, the delegate must be updated. [self.delegate gridViewController:self didChangeItemCount:self.items.count]; } @@ -221,7 +223,7 @@ } auto performAllUpdates = ^{ performDataSourceUpdates(); - self.collectionView.backgroundView.hidden = YES; + self.emptyStateView.hidden = YES; [self.collectionView insertItemsAtIndexPaths:@[ CreateIndexPath(index) ]]; }; auto completion = ^(BOOL finished) { @@ -249,6 +251,9 @@ auto performAllUpdates = ^{ performDataSourceUpdates(); [self.collectionView deleteItemsAtIndexPaths:@[ CreateIndexPath(index) ]]; + if (self.items.count == 0) { + [self animateEmptyStateIntoView]; + } }; auto completion = ^(BOOL finished) { if (self.items.count > 0) { @@ -256,8 +261,6 @@ selectItemAtIndexPath:CreateIndexPath(selectedIndex) animated:YES scrollPosition:UICollectionViewScrollPositionNone]; - } else { - self.collectionView.backgroundView.hidden = NO; } [self.delegate gridViewController:self didChangeItemCount:self.items.count]; }; @@ -319,4 +322,26 @@ return (self.isViewLoaded && self.view.window); } +// Animates the empty state into view. +- (void)animateEmptyStateIntoView { + // TODO(crbug.com/820410) : Polish the animation, and put constants where they + // belong. + CGFloat scale = 0.8f; + CGAffineTransform originalTransform = self.emptyStateView.transform; + self.emptyStateView.transform = + CGAffineTransformScale(self.emptyStateView.transform, scale, scale); + self.emptyStateView.alpha = 0.0f; + self.emptyStateView.hidden = NO; + [UIView animateWithDuration:1.0f + delay:0.0f + usingSpringWithDamping:1.0f + initialSpringVelocity:0.7f + options:UIViewAnimationCurveEaseOut + animations:^{ + self.emptyStateView.alpha = 1.0f; + self.emptyStateView.transform = originalTransform; + } + completion:nil]; +} + @end
diff --git a/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller_unittest.mm b/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller_unittest.mm new file mode 100644 index 0000000..159c9c55 --- /dev/null +++ b/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller_unittest.mm
@@ -0,0 +1,132 @@ +// 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. + +#import "ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.h" +#import "base/mac/foundation_util.h" +#import "ios/chrome/browser/ui/tab_grid/grid/grid_item.h" +#include "testing/gtest/include/gtest/gtest.h" +#import "testing/gtest_mac.h" +#include "testing/platform_test.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +// Test object that exposes the inner state for test verification. +@interface TestGridViewController : GridViewController +@property(nonatomic, readonly) NSMutableArray<GridItem*>* items; +@property(nonatomic, readonly) NSUInteger selectedIndex; +@property(nonatomic, readonly) UICollectionView* collectionView; +@end +@implementation TestGridViewController +@dynamic items; +@dynamic selectedIndex; +@dynamic collectionView; +@end + +// Fake object that conforms to GridViewControllerDelegate. +@interface FakeGridViewControllerDelegate : NSObject<GridViewControllerDelegate> +@property(nonatomic, assign) NSUInteger itemCount; +@end +@implementation FakeGridViewControllerDelegate +@synthesize itemCount = _itemCount; +- (void)gridViewController:(GridViewController*)gridViewController + didChangeItemCount:(NSUInteger)count { + self.itemCount = count; +} +- (void)gridViewController:(GridViewController*)gridViewController + didSelectItemAtIndex:(NSUInteger)index { + // No-op for unittests. This is only called when a user taps on a cell, not + // generically when selectedIndex is updated. +} +- (void)gridViewController:(GridViewController*)gridViewController + didCloseItemAtIndex:(NSUInteger)index { + // No-op for unittests. This is only called when a user taps to close a cell, + // not generically when items are removed from the data source. +} +@end + +class GridViewControllerTest : public PlatformTest { + public: + GridViewControllerTest() { + view_controller_ = [[TestGridViewController alloc] init]; + [view_controller_ + populateItems:@[ [[GridItem alloc] init], [[GridItem alloc] init] ] + selectedIndex:0]; + delegate_ = [[FakeGridViewControllerDelegate alloc] init]; + delegate_.itemCount = 2; + view_controller_.delegate = delegate_; + } + + protected: + TestGridViewController* view_controller_; + FakeGridViewControllerDelegate* delegate_; +}; + +// Tests that items are initialized and delegate is updated with a new +// itemCount. +TEST_F(GridViewControllerTest, InitializeItems) { + // Previously: The grid had 2 items and selectedIndex was 0. The delegate had + // an itemCount of 2. + GridItem* item = [[GridItem alloc] init]; + item.identifier = @"NEW-ITEM"; + [view_controller_ populateItems:@[ item ] selectedIndex:0]; + EXPECT_NSEQ(@"NEW-ITEM", view_controller_.items[0].identifier); + EXPECT_EQ(1U, view_controller_.items.count); + EXPECT_EQ(0U, view_controller_.selectedIndex); + EXPECT_EQ(1U, delegate_.itemCount); +} + +// Tests that an item is inserted and delegate is updated with a new itemCount. +TEST_F(GridViewControllerTest, InsertItem) { + // Previously: The grid had 2 items and selectedIndex was 0. The delegate had + // an itemCount of 2. + [view_controller_ insertItem:[[GridItem alloc] init] + atIndex:0 + selectedIndex:2]; + EXPECT_EQ(3U, view_controller_.items.count); + EXPECT_EQ(2U, view_controller_.selectedIndex); + EXPECT_EQ(3U, delegate_.itemCount); +} + +// Tests that an item is removed and delegate is updated with a new itemCount. +TEST_F(GridViewControllerTest, RemoveItem) { + // Previously: The grid had 2 items and selectedIndex was 0. The delegate had + // an itemCount of 2. + [view_controller_ removeItemAtIndex:0 selectedIndex:1]; + EXPECT_EQ(1U, view_controller_.items.count); + EXPECT_EQ(1U, view_controller_.selectedIndex); + EXPECT_EQ(1U, delegate_.itemCount); +} + +// Tests that an item is selected. +TEST_F(GridViewControllerTest, SelectItem) { + // Previously: The grid had 2 items and selectedIndex was 0. The delegate had + // an itemCount of 2. + [view_controller_ selectItemAtIndex:1]; + EXPECT_EQ(1U, view_controller_.selectedIndex); + EXPECT_EQ(2U, delegate_.itemCount); +} + +// Tests that an item is replaced. +TEST_F(GridViewControllerTest, ReplaceItem) { + // Previously: The grid had 2 items and selectedIndex was 0. The delegate had + // an itemCount of 2. + GridItem* item = [[GridItem alloc] init]; + item.identifier = @"NEW-ITEM"; + [view_controller_ replaceItemAtIndex:0 withItem:item]; + EXPECT_NSEQ(@"NEW-ITEM", view_controller_.items[0].identifier); + EXPECT_EQ(2U, delegate_.itemCount); +} + +// Tests that an item is moved. +TEST_F(GridViewControllerTest, MoveItem) { + // Previously: The grid had 2 items and selectedIndex was 0. The delegate had + // an itemCount of 2. + view_controller_.items[0].identifier = @"ITEM-0"; + [view_controller_ moveItemFromIndex:0 toIndex:1 selectedIndex:1]; + EXPECT_NSEQ(@"ITEM-0", view_controller_.items[1].identifier); + EXPECT_EQ(1U, view_controller_.selectedIndex); + EXPECT_EQ(2U, delegate_.itemCount); +}
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 f593242f..03c32fde 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
@@ -103,6 +103,7 @@ // Call the current page setter to sync the scroll view offset to the current // page value. self.currentPage = _currentPage; + [self.topToolbar.pageControl setSelectedPage:self.currentPage animated:YES]; [self configureViewControllerForCurrentSizeClassesAndPage]; if (animated && self.transitionCoordinator) { [self animateToolbarsForAppearance]; @@ -155,16 +156,17 @@ self.scrollView.contentSize.width - self.scrollView.frame.size.width; CGFloat offset = scrollView.contentOffset.x / offsetWidth; self.topToolbar.pageControl.sliderPosition = offset; - } -} -- (void)scrollViewDidEndDecelerating:(UIScrollView*)scrollView { - // Bookkeeping for the current page. - CGFloat pageWidth = scrollView.frame.size.width; - float fractionalPage = scrollView.contentOffset.x / pageWidth; - NSUInteger page = lround(fractionalPage); - _currentPage = static_cast<TabGridPage>(page); - [self configureButtonsForCurrentPage]; + // Bookkeeping for the current page. + // TODO(crbug.com/822328) : Fix for RTL. + CGFloat pageWidth = scrollView.frame.size.width; + float fractionalPage = scrollView.contentOffset.x / pageWidth; + NSUInteger page = lround(fractionalPage); + if (page != self.currentPage) { + _currentPage = static_cast<TabGridPage>(page); + [self configureButtonsForCurrentPage]; + } + } } #pragma mark - UIScrollViewAccessibilityDelegate @@ -553,7 +555,6 @@ } - (void)configureButtonsForCurrentPage { - [self.topToolbar.pageControl setSelectedPage:self.currentPage animated:YES]; self.newTabButton.page = self.currentPage; switch (self.currentPage) { case TabGridPageIncognitoTabs:
diff --git a/ios/chrome/browser/ui/tab_grid/transitions/BUILD.gn b/ios/chrome/browser/ui/tab_grid/transitions/BUILD.gn index 52ed4990a..7337c0f 100644 --- a/ios/chrome/browser/ui/tab_grid/transitions/BUILD.gn +++ b/ios/chrome/browser/ui/tab_grid/transitions/BUILD.gn
@@ -10,6 +10,8 @@ "grid_to_hidden_tab_animator.mm", "grid_to_visible_tab_animator.h", "grid_to_visible_tab_animator.mm", + "grid_transition_animation.h", + "grid_transition_animation.mm", "grid_transition_layout.h", "grid_transition_layout.mm", "grid_transition_state_providing.h",
diff --git a/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.h b/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.h new file mode 100644 index 0000000..04d9148d --- /dev/null +++ b/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.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 IOS_CHROME_BROWSER_UI_TAB_GRID_TRANSITIONS_GRID_TRANSITION_ANIMATION_H_ +#define IOS_CHROME_BROWSER_UI_TAB_GRID_TRANSITIONS_GRID_TRANSITION_ANIMATION_H_ + +#import <UIKit/UIKit.h> + +@class GridTransitionLayout; + +// Delegate for this animation, to be informed about animation events. +@protocol GridTransitionAnimationDelegate +// Tell the delegate thet the animation completed. If |finished| is YES, then +// the animation was able to run its full duration. +- (void)gridTransitionAnimationDidFinish:(BOOL)finished; +@end + +// A view that encapsulates an animation used to transition into a grid. +// A transition animator should place this view at the appropriate place in the +// view hierarchy and then call -animateWithDuration: to trigger the animations. +// TODO(crbug.com/804539): Update this class to be an Orchestrator object +// that the present and dismiss animations can both use. +@interface GridTransitionAnimation : UIView + +// Designated initializer. |layout| is a GridTransitionLayout object defining +// the layout the animation should animate to. |delegate| is an object that will +// be informed about events in this object's animation. +- (instancetype)initWithLayout:(GridTransitionLayout*)layout + delegate:(id<GridTransitionAnimationDelegate>)delegate + NS_DESIGNATED_INITIALIZER; + +- (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE; +- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE; + +// Runs the animation for this object with the passed duration. +// It's an error to call this more than once on any instance of this object. +- (void)animateWithDuration:(NSTimeInterval)duration; + +@end + +#endif // IOS_CHROME_BROWSER_UI_TAB_GRID_TRANSITIONS_GRID_TRANSITION_ANIMATION_H_
diff --git a/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.mm b/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.mm new file mode 100644 index 0000000..82801c4 --- /dev/null +++ b/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.mm
@@ -0,0 +1,238 @@ +// 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. + +#import "ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.h" + +#import "ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +@interface GridTransitionAnimation () +@property(nonatomic, strong) GridTransitionLayout* layout; +@property(nonatomic, weak) id<GridTransitionAnimationDelegate> delegate; +@property(nonatomic, assign) CGFloat xScale; +@property(nonatomic, assign) CGFloat yScale; +@property(nonatomic, readonly) CGSize selectedSize; +@property(nonatomic, readonly) CGPoint selectedCenter; +@property(nonatomic, assign) CGFloat finalSelectedCellCornerRadius; +@end + +@implementation GridTransitionAnimation +@synthesize delegate = _delegate; +@synthesize layout = _layout; +@synthesize xScale = _xScale; +@synthesize yScale = _yScale; +@synthesize finalSelectedCellCornerRadius = _finalSelectedCellCornerRadius; + +- (instancetype)initWithLayout:(GridTransitionLayout*)layout + delegate:(id<GridTransitionAnimationDelegate>)delegate { + if (self = [super initWithFrame:CGRectZero]) { + _layout = layout; + _delegate = delegate; + _finalSelectedCellCornerRadius = + _layout.selectedItem.cell.contentView.layer.cornerRadius; + } + return self; +} + +#pragma mark - UIView + +- (void)willMoveToSuperview:(UIView*)newSuperview { + self.frame = newSuperview.bounds; + if (newSuperview && self.subviews.count == 0) { + [self prepareForAnimationInSuperview:newSuperview]; + } +} + +- (void)didMoveToSuperview { + // Positioning the animating items depends on converting points to this + // view's coordinate system, so wait until it's in a view hierarchy. + [self positionSelectedItemInExpandedGrid]; + [self positionUnselectedItemsInExpandedGrid]; +} + +#pragma mark - Private Properties + +- (CGSize)selectedSize { + return self.layout.selectedItem.attributes.size; +} + +- (CGPoint)selectedCenter { + return self.layout.selectedItem.attributes.center; +} + +#pragma mark - Public methods + +- (void)animateWithDuration:(NSTimeInterval)duration { + // The transition is structured as two or three separate animations. They are + // timed based on |staggeredDuration|, which is a configurable fraction + // of the overall animation duration. + CGFloat staggeredDuration = duration * 0.7; + + // If there's only one cell, the animation has two parts: + // (A) Fading in the selected cell highlight indicator. + // (B) Zooming the selected cell into position. + // These parts are timed like this: + // + // |*|----[A]----{100%} + // {0%}------------------[B]--------------------{100%} + // + // (|*| is <staggeredDuration>%). + // Animation B will call the completion handler in this case. + + // If there's more than once cell, the animation has three parts: + // (A) Fading in the selected cell highlight indicator. + // (B) Zooming the selected cell into position. + // (C) Zooming the unselected cells into position. + // The timing is as follows: + // + // |*|----[A]----{100%} + // {0%}------------------[B]------|*| + // |#|----------[C]--------------------{100%} + // (|*| is <staggeredDuration>%). + // (|#| is 1-<staggeredDuration>%). + // Animation C will call the completion handler in this case. + + // TODO(crbug.com/820410): Tune the timing, relative pacing, and curves of + // these animations. + + UICollectionViewCell* selectedCell = self.layout.selectedItem.cell; + + // Run animation (A) starting at |staggeredDuration|. + [UIView animateWithDuration:duration - staggeredDuration + delay:staggeredDuration + options:UIViewAnimationOptionCurveEaseIn + animations:^{ + selectedCell.selected = YES; + } + completion:nil]; + + // Completion block to be run when the transition completes. + auto completion = ^(BOOL finished) { + // Tell the delegate the animation has completed. + [self.delegate gridTransitionAnimationDidFinish:finished]; + }; + + if (self.layout.items.count == 1) { + // Single cell case. + // Run animation (B) for the whole duration without delay. + [UIView animateWithDuration:duration + delay:0 + options:UIViewAnimationOptionCurveEaseOut + animations:^{ + [self positionSelectedItemInRegularGrid]; + } + completion:completion]; + } else { + // Multiple cell case. + // Run animation (B) up to |staggeredDuration|. + [UIView animateWithDuration:staggeredDuration + delay:0.0 + options:UIViewAnimationOptionCurveEaseOut + animations:^{ + [self positionSelectedItemInRegularGrid]; + } + completion:nil]; + + // Run animation (C) for |staggeredDuration| up to the end of the + // transition. + [UIView animateWithDuration:staggeredDuration + delay:duration - staggeredDuration + options:UIViewAnimationOptionCurveEaseOut + animations:^{ + [self positionUnselectedItemsInRegularGrid]; + } + completion:completion]; + } +} + +#pragma mark - Private methods + +// Perfrom the initial setup for the animation, computing scale based on the +// superview size and adding the transition cells to the view hierarchy. +- (void)prepareForAnimationInSuperview:(UIView*)newSuperview { + // Extract some useful metrics from the animation superview. + CGSize animationSize = newSuperview.bounds.size; + + // Compute the scale of the transition grid (which is at the proportional size + // of the superview). + self.xScale = animationSize.width / self.selectedSize.width; + self.yScale = animationSize.height / self.selectedSize.height; + + for (GridTransitionLayoutItem* item in self.layout.items) { + [self addSubview:item.cell]; + } +} + +// Positions the selected item in the expanded grid position with a zero corner +// radius. +- (void)positionSelectedItemInExpandedGrid { + [self positionAndScaleItemInExpandedGrid:self.layout.selectedItem]; + UICollectionViewCell* cell = self.layout.selectedItem.cell; + cell.contentView.layer.cornerRadius = 0.0; +} + +// Positions all of the non-selected items in their expanded grid positions. +- (void)positionUnselectedItemsInExpandedGrid { + // Lay out the transition grid add it as subviews. + for (GridTransitionLayoutItem* item in self.layout.items) { + if (item == self.layout.selectedItem) + continue; + [self positionAndScaleItemInExpandedGrid:item]; + } +} + +// Positions the selected item in the regular grid position with its final +// corner radius. +- (void)positionSelectedItemInRegularGrid { + [self positionAndScaleItemInRegularGrid:self.layout.selectedItem]; + UICollectionViewCell* cell = self.layout.selectedItem.cell; + cell.contentView.layer.cornerRadius = self.finalSelectedCellCornerRadius; +} + +// Positions all of the non-selected items in their regular grid positions. +- (void)positionUnselectedItemsInRegularGrid { + for (GridTransitionLayoutItem* item in self.layout.items) { + if (item == self.layout.selectedItem) + continue; + [self positionAndScaleItemInRegularGrid:item]; + } +} + +// Positions |item| in its regular grid position. +- (void)positionAndScaleItemInRegularGrid:(GridTransitionLayoutItem*)item { + UIView* cell = item.cell; + cell.center = + [self.superview convertPoint:item.attributes.center fromView:nil]; + cell.transform = CGAffineTransformIdentity; +} + +// Positions |item| in its expanded grid position. +- (void)positionAndScaleItemInExpandedGrid:(GridTransitionLayoutItem*)item { + UICollectionViewCell* cell = item.cell; + cell.bounds = item.attributes.bounds; + // Add a scale transform to the cell so it matches the x-scale of the + // open tab. Scaling is only based on the x-scale so that the aspect ratio of + // the cell will be preserved. + cell.transform = + CGAffineTransformScale(cell.transform, self.xScale, self.xScale); + cell.center = [self expandedCenterForItem:item]; +} + +// Returns the center point for an item in the expanded grid position. This is +// computed by scaling its center point relative to the selected item's center +// point. The scaling factors are the ratios of the animation view's height and +// width to the selected cell's height and width. +- (CGPoint)expandedCenterForItem:(GridTransitionLayoutItem*)item { + // Convert item center from window coordinates. + CGPoint gridCenter = [self convertPoint:item.attributes.center fromView:nil]; + // Map that to the scale and position of the transition grid. + return CGPointMake( + self.center.x + ((gridCenter.x - self.selectedCenter.x) * self.xScale), + self.center.y + ((gridCenter.y - self.selectedCenter.y) * self.yScale)); +} + +@end
diff --git a/ios/chrome/browser/ui/tab_grid/transitions/tab_to_grid_animator.mm b/ios/chrome/browser/ui/tab_grid/transitions/tab_to_grid_animator.mm index 03b3319..5c53114 100644 --- a/ios/chrome/browser/ui/tab_grid/transitions/tab_to_grid_animator.mm +++ b/ios/chrome/browser/ui/tab_grid/transitions/tab_to_grid_animator.mm
@@ -6,20 +6,26 @@ #import "base/logging.h" #import "base/mac/foundation_util.h" -#import "ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.h" +#import "ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.h" #import "ios/chrome/browser/ui/tab_grid/transitions/grid_transition_state_providing.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." #endif -@interface TabToGridAnimator () -// State provider for this transition. +@interface TabToGridAnimator ()<GridTransitionAnimationDelegate> @property(nonatomic, weak) id<GridTransitionStateProviding> stateProvider; +// Animation object for this transition. +@property(nonatomic, strong) GridTransitionAnimation* animation; +// Transition context passed into this object when the animation is started. +@property(nonatomic, weak) id<UIViewControllerContextTransitioning> + transitionContext; @end @implementation TabToGridAnimator @synthesize stateProvider = _stateProvider; +@synthesize animation = _animation; +@synthesize transitionContext = _transitionContext; - (instancetype)initWithStateProvider: (id<GridTransitionStateProviding>)stateProvider { @@ -36,6 +42,10 @@ - (void)animateTransition: (id<UIViewControllerContextTransitioning>)transitionContext { + // Keep a pointer to the transition context for use in animation delegate + // callbacks. + self.transitionContext = transitionContext; + // Get views and view controllers for this transition. UIView* containerView = [transitionContext containerView]; UIViewController* gridViewController = [transitionContext @@ -45,10 +55,6 @@ UIView* dismissingView = [transitionContext viewForKey:UITransitionContextFromViewKey]; - // Extract some useful metrics from the tab view. - CGSize proxySize = dismissingView.bounds.size; - CGPoint proxyCenter = dismissingView.center; - // Add the grid view to the container. This isn't just for the transition; // this is how the grid view controller's view is added to the view // hierarchy. @@ -60,135 +66,49 @@ GridTransitionLayout* layout = [self.stateProvider layoutForTransitionContext:transitionContext]; - // Compute the scale of the transition grid (which is at the propotional size - // of the actual tab view. - CGFloat xScale = proxySize.width / layout.selectedItem.attributes.size.width; - CGFloat yScale = - proxySize.height / layout.selectedItem.attributes.size.height; + // Create the animation view and insert it. + self.animation = + [[GridTransitionAnimation alloc] initWithLayout:layout delegate:self]; - // Ask the state provider for the views to use when inserting the tab grid. + // Ask the state provider for the views to use when inserting the animation. UIView* proxyContainer = [self.stateProvider proxyContainerForTransitionContext:transitionContext]; UIView* viewBehindProxies = [self.stateProvider proxyPositionForTransitionContext:transitionContext]; - // Lay out the transition grid and add it to the view hierarchy. - CGFloat finalSelectedCellCornerRadius = 0.0; - for (GridTransitionLayoutItem* item in layout.items) { - // The state provider vends attributes in UIWindow coordinates. - // Find where this item is located in |proxyContainer|'s coordinate. - CGPoint gridCenter = - [proxyContainer convertPoint:item.attributes.center fromView:nil]; - // Map that to the scale and position of the transition grid. - CGPoint center = CGPointMake( - proxyCenter.x + - ((gridCenter.x - layout.selectedItem.attributes.center.x) * xScale), - proxyCenter.y + - ((gridCenter.y - layout.selectedItem.attributes.center.y) * - yScale)); - UICollectionViewCell* cell = item.cell; - cell.bounds = item.attributes.bounds; - // Add a scale transform to the cell so it matches the x-scale of the - // open tab. - cell.transform = CGAffineTransformScale(cell.transform, xScale, xScale); - cell.center = center; - if (item == layout.selectedItem) { - finalSelectedCellCornerRadius = cell.contentView.layer.cornerRadius; - cell.contentView.layer.cornerRadius = 0.0; - } - // Add the cell into the container for the transition. - [proxyContainer insertSubview:cell aboveSubview:viewBehindProxies]; - } - - // The transition is structured as four separate animations. Three of them - // are timed based on |staggeredDuration|, which is a configurable fraction - // of the overall animation duration. - // (1) Fading out the view being dismissed. This happens during the first 20% - // of the overall animation. - // (2) Zooming the selected cell into position. This starts immediately and - // has a duration of |staggeredDuration|. - // (3) Fading in the selected cell highlight indicator. This starts after a - // delay of |staggeredDuration| and runs to the end of the transition. - // This means it starts as soon as (2) ends. - // (4) Zooming all other cells into position. This ends at the end of the - // transition and has a duration of |staggeredDuration|. - // - // Animation (4) always runs the whole duration of the transition, so it's - // where the completion block that does overall cleanup is run. - - // TODO(crbug.com/804539): Factor all of these animations into a single - // Orchestrator object that the present and dismiss animation can both use. - - // TODO(crbug.com/820410): Tune the timing, relative pacing, and curves of - // these animations. + [proxyContainer insertSubview:self.animation aboveSubview:viewBehindProxies]; NSTimeInterval duration = [self transitionDuration:transitionContext]; - CGFloat staggeredDuration = duration * 0.7; - // (1) Fade out active tab view. + // Fade out active tab view. [UIView animateWithDuration:duration / 5 animations:^{ dismissingView.alpha = 0; } completion:nil]; - // (2) Zoom selected cell into place. Also round its corners. - UICollectionViewCell* selectedCell = layout.selectedItem.cell; - [UIView animateWithDuration:staggeredDuration - delay:0.0 - options:UIViewAnimationOptionCurveEaseOut - animations:^{ - selectedCell.center = [containerView - convertPoint:layout.selectedItem.attributes.center - fromView:nil]; - selectedCell.bounds = - layout.selectedItem.attributes.bounds; - selectedCell.transform = CGAffineTransformIdentity; - selectedCell.contentView.layer.cornerRadius = - finalSelectedCellCornerRadius; - } - completion:nil]; + // Run the main animation. + [self.animation animateWithDuration:duration]; +} - // (3) Show highlight state on selected cell. - [UIView animateWithDuration:duration - staggeredDuration - delay:staggeredDuration - options:UIViewAnimationOptionCurveEaseIn - animations:^{ - selectedCell.selected = YES; - } - completion:nil]; - - // (4) Zoom other cells into place. - [UIView animateWithDuration:staggeredDuration - delay:duration - staggeredDuration - options:UIViewAnimationOptionCurveEaseOut - animations:^{ - for (GridTransitionLayoutItem* item in layout.items) { - if (item == layout.selectedItem) - continue; - UIView* cell = item.cell; - cell.center = - [containerView convertPoint:item.attributes.center fromView:nil]; - cell.transform = CGAffineTransformIdentity; - } - } - completion:^(BOOL finished) { - // Clean up all of the proxy cells. - for (GridTransitionLayoutItem* item in layout.items) { - [item.cell removeFromSuperview]; - } - // If the transition was cancelled, restore the dismissing view and - // remove the grid view. - // If not, remove the dismissing view. - if (transitionContext.transitionWasCancelled) { - dismissingView.alpha = 1.0; - [gridView removeFromSuperview]; - } else { - [dismissingView removeFromSuperview]; - } - // Mark the transition as completed. - [transitionContext completeTransition:YES]; - }]; +- (void)gridTransitionAnimationDidFinish:(BOOL)finished { + // Clean up the animation + [self.animation removeFromSuperview]; + // If the transition was cancelled, restore the dismissing view and + // remove the grid view. + // If not, remove the dismissing view. + UIView* gridView = + [self.transitionContext viewForKey:UITransitionContextToViewKey]; + UIView* dismissingView = + [self.transitionContext viewForKey:UITransitionContextFromViewKey]; + if (self.transitionContext.transitionWasCancelled) { + dismissingView.alpha = 1.0; + [gridView removeFromSuperview]; + } else { + [dismissingView removeFromSuperview]; + } + // Mark the transition as completed. + [self.transitionContext completeTransition:YES]; } @end
diff --git a/ios/chrome/browser/ui/toolbar/adaptive/adaptive_toolbar_coordinator.mm b/ios/chrome/browser/ui/toolbar/adaptive/adaptive_toolbar_coordinator.mm index 2d88ea9..70eb7cc 100644 --- a/ios/chrome/browser/ui/toolbar/adaptive/adaptive_toolbar_coordinator.mm +++ b/ios/chrome/browser/ui/toolbar/adaptive/adaptive_toolbar_coordinator.mm
@@ -71,6 +71,13 @@ toolbarButton:self.viewController.toolsMenuButton]; } +- (void)stop { + [super stop]; + self.toolsMenuButtonObserverBridge = nil; + [self.mediator disconnect]; + self.mediator = nil; +} + #pragma mark - SideSwipeToolbarSnapshotProviding - (UIImage*)toolbarSideSwipeSnapshotForWebState:(web::WebState*)webState {
diff --git a/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_coordinator.mm b/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_coordinator.mm index 0f46d32..10187cc9 100644 --- a/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_coordinator.mm +++ b/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_coordinator.mm
@@ -78,6 +78,11 @@ ->AddObserver(_fullscreenObserver.get()); } +- (void)stop { + [super stop]; + [self.locationBarCoordinator stop]; +} + #pragma mark - PrimaryToolbarCoordinator - (id<VoiceSearchControllerDelegate>)voiceSearchDelegate {
diff --git a/ios/chrome/browser/ui/ui_feature_flags.cc b/ios/chrome/browser/ui/ui_feature_flags.cc index 57f2788..0c04b2a5 100644 --- a/ios/chrome/browser/ui/ui_feature_flags.cc +++ b/ios/chrome/browser/ui/ui_feature_flags.cc
@@ -4,6 +4,9 @@ #include "ios/chrome/browser/ui/ui_feature_flags.h" +const base::Feature kRefreshPopupPresentation{ + "UIRefreshPopupPresentation", base::FEATURE_DISABLED_BY_DEFAULT}; + const base::Feature kUIRefreshPhase1{"UIRefreshPhase1", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/ios/chrome/browser/ui/ui_feature_flags.h b/ios/chrome/browser/ui/ui_feature_flags.h index 32395450d..7d845e7 100644 --- a/ios/chrome/browser/ui/ui_feature_flags.h +++ b/ios/chrome/browser/ui/ui_feature_flags.h
@@ -7,6 +7,11 @@ #include "base/feature_list.h" +// Used to enable the UI Refresh omnibox popup presentation. This flag should +// not be used directly. Instead use +// ui_util::IsRefreshPopupPresentationEnabled(). +extern const base::Feature kRefreshPopupPresentation; + // Used to enable the first phase of the UI refresh. This flag should not be // used directly. Instead use ui_util::IsUIRefreshPhase1Enabled(). extern const base::Feature kUIRefreshPhase1;
diff --git a/ios/chrome/browser/ui/ui_util.h b/ios/chrome/browser/ui/ui_util.h index dbeb71a..e8f038b 100644 --- a/ios/chrome/browser/ui/ui_util.h +++ b/ios/chrome/browser/ui/ui_util.h
@@ -39,6 +39,9 @@ // Returns true if the device is an iPhone X. bool IsIPhoneX(); +// Returns whether the UI Refresh Omnibox Popup presentation will be used. +bool IsRefreshPopupPresentationEnabled(); + // Returns whether the first phase of the UI refresh will be displayed. bool IsUIRefreshPhase1Enabled();
diff --git a/ios/chrome/browser/ui/ui_util.mm b/ios/chrome/browser/ui/ui_util.mm index b821199..cdd9590 100644 --- a/ios/chrome/browser/ui/ui_util.mm +++ b/ios/chrome/browser/ui/ui_util.mm
@@ -57,6 +57,10 @@ CGRectGetHeight([[UIScreen mainScreen] nativeBounds]) == 2436); } +bool IsRefreshPopupPresentationEnabled() { + return base::FeatureList::IsEnabled(kRefreshPopupPresentation); +} + bool IsUIRefreshPhase1Enabled() { if (tests_hook::ForceUIRefreshPhase1()) return true;
diff --git a/ios/chrome/browser/voice/voice_search_navigations_tab_helper.h b/ios/chrome/browser/voice/voice_search_navigations_tab_helper.h index 4906be0..45a3cc33 100644 --- a/ios/chrome/browser/voice/voice_search_navigations_tab_helper.h +++ b/ios/chrome/browser/voice/voice_search_navigations_tab_helper.h
@@ -22,6 +22,10 @@ // search. void WillLoadVoiceSearchResult(); + // Returns whether the next committed navigation item is the result of a voice + // search. + bool IsExpectingVoiceSearch() const; + // Returns whether |item| was created for a voice search query. bool IsNavigationFromVoiceSearch(const web::NavigationItem* item) const;
diff --git a/ios/chrome/browser/voice/voice_search_navigations_tab_helper.mm b/ios/chrome/browser/voice/voice_search_navigations_tab_helper.mm index a5535aa..47889696 100644 --- a/ios/chrome/browser/voice/voice_search_navigations_tab_helper.mm +++ b/ios/chrome/browser/voice/voice_search_navigations_tab_helper.mm
@@ -41,6 +41,10 @@ will_navigate_to_voice_search_result_ = true; } +bool VoiceSearchNavigationTabHelper::IsExpectingVoiceSearch() const { + return will_navigate_to_voice_search_result_; +} + bool VoiceSearchNavigationTabHelper::IsNavigationFromVoiceSearch( const web::NavigationItem* item) const { DCHECK(item);
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn index c1165f95..82a181c8 100644 --- a/ios/chrome/test/BUILD.gn +++ b/ios/chrome/test/BUILD.gn
@@ -221,6 +221,7 @@ "//ios/chrome/browser/ui/stack_view:unit_tests", "//ios/chrome/browser/ui/static_content:unit_tests", "//ios/chrome/browser/ui/tab_grid:unit_tests", + "//ios/chrome/browser/ui/tab_grid/grid:unit_tests", "//ios/chrome/browser/ui/tab_switcher:unit_tests", "//ios/chrome/browser/ui/table_view:unit_tests", "//ios/chrome/browser/ui/table_view/cells:unit_tests",
diff --git a/ios/chrome/test/fakes/fake_document_interaction_controller.h b/ios/chrome/test/fakes/fake_document_interaction_controller.h index d34ed8f9..285ee00 100644 --- a/ios/chrome/test/fakes/fake_document_interaction_controller.h +++ b/ios/chrome/test/fakes/fake_document_interaction_controller.h
@@ -21,6 +21,9 @@ @property(nonatomic, weak) id<UIDocumentInteractionControllerDelegate> delegate; +// Whether or not this controller can present Open In... menu. Defaults to YES. +@property(nonatomic) BOOL presentsOpenInMenu; + // Menu that is currently being presented. @property(nonatomic, readonly) OpenInMenu* presentedOpenInMenu;
diff --git a/ios/chrome/test/fakes/fake_document_interaction_controller.mm b/ios/chrome/test/fakes/fake_document_interaction_controller.mm index 3d1730f..cb4a989 100644 --- a/ios/chrome/test/fakes/fake_document_interaction_controller.mm +++ b/ios/chrome/test/fakes/fake_document_interaction_controller.mm
@@ -23,10 +23,23 @@ @implementation FakeDocumentInteractionController @synthesize delegate = _delegate; +@synthesize presentsOpenInMenu = _presentsOpenInMenu; @synthesize presentedOpenInMenu = _presentedOpenInMenu; + +- (instancetype)init { + self = [super init]; + if (self) { + _presentsOpenInMenu = YES; + } + return self; +} + - (BOOL)presentOpenInMenuFromRect:(CGRect)rect inView:(UIView*)view animated:(BOOL)animated { + if (!_presentsOpenInMenu) + return NO; + _presentedOpenInMenu = [[OpenInMenu alloc] init]; _presentedOpenInMenu.rect = rect; _presentedOpenInMenu.view = view;
diff --git a/ios/public/provider/chrome/browser/signin/chrome_identity_service.mm b/ios/public/provider/chrome/browser/signin/chrome_identity_service.mm index b1bb3f9..69372ae 100644 --- a/ios/public/provider/chrome/browser/signin/chrome_identity_service.mm +++ b/ios/public/provider/chrome/browser/signin/chrome_identity_service.mm
@@ -89,14 +89,14 @@ const std::string& client_id, const std::string& client_secret, const std::set<std::string>& scopes, - AccessTokenCallback callback) {} + AccessTokenCallback callback) { + GetAccessToken(identity, client_id, scopes, callback); +} void ChromeIdentityService::GetAccessToken(ChromeIdentity* identity, const std::string& client_id, const std::set<std::string>& scopes, - AccessTokenCallback callback) { - GetAccessToken(identity, client_id, "", scopes, callback); -} + AccessTokenCallback callback) {} void ChromeIdentityService::GetAvatarForIdentity(ChromeIdentity* identity, GetAvatarCallback callback) {}
diff --git a/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h b/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h index 67033fa..fced486 100644 --- a/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h +++ b/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h
@@ -61,11 +61,6 @@ frame_header_is_set_ = true; } - const Http2FrameHeader& frame_header() const { - CHECK(frame_header_is_set_); - return frame_header_; - } - FrameDecoderState* mutable_state() { return &frame_decoder_state_; } // Randomize the payload decoder, sets the payload decoder's frame_header_,
diff --git a/printing/page_range.cc b/printing/page_range.cc index 3c050ef..22aea0f 100644 --- a/printing/page_range.cc +++ b/printing/page_range.cc
@@ -8,20 +8,16 @@ #include <set> -namespace { -const std::size_t kMaxNumberOfPages = 100000; -} - namespace printing { -/* static */ +// static std::vector<int> PageRange::GetPages(const PageRanges& ranges) { // TODO(vitalybuka): crbug.com/95548 Remove this method as part fix. std::set<int> pages; - for (unsigned i = 0; i < ranges.size(); ++i) { - const PageRange& range = ranges[i]; + for (const PageRange& range : ranges) { // Ranges are inclusive. for (int i = range.from; i <= range.to; ++i) { + static constexpr size_t kMaxNumberOfPages = 100000; pages.insert(i); if (pages.size() >= kMaxNumberOfPages) return std::vector<int>(pages.begin(), pages.end());
diff --git a/printing/page_range.h b/printing/page_range.h index 989c33c..f4326041 100644 --- a/printing/page_range.h +++ b/printing/page_range.h
@@ -7,13 +7,13 @@ #include <vector> -#include "printing_export.h" +#include "printing/printing_export.h" namespace printing { struct PageRange; -typedef std::vector<PageRange> PageRanges; +using PageRanges = std::vector<PageRange>; // Print range is inclusive. To select one page, set from == to. struct PRINTING_EXPORT PageRange {
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc index a158632..aa31009 100644 --- a/services/network/url_loader.cc +++ b/services/network/url_loader.cc
@@ -513,6 +513,9 @@ return; } + // Do not account header bytes when reporting received body bytes to client. + reported_total_encoded_bytes_ = url_request_->GetTotalReceivedBytes(); + if (resource_scheduler_client_ && url_request->was_fetched_via_proxy() && url_request->was_fetched_via_spdy() && url_request->url().SchemeIs(url::kHttpScheme)) { @@ -607,8 +610,20 @@ } void URLLoader::DidRead(int num_bytes, bool completed_synchronously) { - if (num_bytes > 0) + if (num_bytes > 0) { pending_write_buffer_offset_ += num_bytes; + + // Only notify client of download progress in case DevTools are attached + // and we're done sniffing and started sending response. + if (report_raw_headers_ && !consumer_handle_.is_valid()) { + int64_t total_encoded_bytes = url_request_->GetTotalReceivedBytes(); + int64_t delta = total_encoded_bytes - reported_total_encoded_bytes_; + DCHECK_LE(0, delta); + if (delta) + url_loader_client_->OnTransferSizeUpdated(delta); + reported_total_encoded_bytes_ = total_encoded_bytes; + } + } if (update_body_read_before_paused_) { update_body_read_before_paused_ = false; body_read_before_paused_ = url_request_->GetRawBodyBytes();
diff --git a/services/network/url_loader.h b/services/network/url_loader.h index f142809..429d14f 100644 --- a/services/network/url_loader.h +++ b/services/network/url_loader.h
@@ -164,6 +164,10 @@ // as BodyReadFromNetBeforePaused. int64_t body_read_before_paused_ = -1; + // This is used to compute the delta since last time received + // encoded body size was reported to the client. + int64_t reported_total_encoded_bytes_ = 0; + scoped_refptr<ResourceSchedulerClient> resource_scheduler_client_; mojom::SSLPrivateKeyPtr ssl_private_key_;
diff --git a/services/resource_coordinator/memory_instrumentation/queued_request_dispatcher.cc b/services/resource_coordinator/memory_instrumentation/queued_request_dispatcher.cc index 79f1aec..3fdd926 100644 --- a/services/resource_coordinator/memory_instrumentation/queued_request_dispatcher.cc +++ b/services/resource_coordinator/memory_instrumentation/queued_request_dispatcher.cc
@@ -254,6 +254,12 @@ // Don't request a chrome memory dump at all if the client wants only the // processes' vm regions, which are retrieved via RequestOSMemoryDump(). + // + // This must occur before the call to RequestOSMemoryDump, as + // ClientProcessImpl will [for macOS], delay the calculations for the + // OSMemoryDump until the Chrome memory dump is finished. See + // https://bugs.chromium.org/p/chromium/issues/detail?id=812346#c16 for more + // details. if (request->wants_chrome_dumps()) { request->pending_responses.insert({client, ResponseType::kChromeDump}); client->RequestChromeMemoryDump(request->GetRequestArgs(),
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.cc index 5c9a38f1..58cdbd5 100644 --- a/services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.cc +++ b/services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.cc
@@ -10,6 +10,7 @@ #include "base/strings/string_piece.h" #include "base/synchronization/lock.h" #include "base/trace_event/memory_dump_request_args.h" +#include "build/build_config.h" #include "mojo/public/cpp/bindings/interface_request.h" #include "services/resource_coordinator/public/cpp/memory_instrumentation/coordinator.h" #include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h" @@ -75,6 +76,7 @@ const base::trace_event::MemoryDumpRequestArgs& args, const RequestChromeMemoryDumpCallback& callback) { DCHECK(!callback.is_null()); + most_recent_chrome_memory_dump_guid_ = args.dump_guid; auto it_and_inserted = pending_chrome_callbacks_.emplace(args.dump_guid, std::move(callback)); DCHECK(it_and_inserted.second) << "Duplicated request id " << args.dump_guid; @@ -95,6 +97,14 @@ auto callback = std::move(callback_it->second); pending_chrome_callbacks_.erase(callback_it); + auto it = delayed_os_memory_dump_callbacks_.find(dump_guid); + if (it != delayed_os_memory_dump_callbacks_.end()) { + for (auto& args : it->second) { + PerformOSMemoryDump(std::move(args)); + } + delayed_os_memory_dump_callbacks_.erase(it); + } + if (!process_memory_dump) { callback.Run(false, dump_guid, nullptr); return; @@ -129,19 +139,40 @@ bool want_mmaps, const std::vector<base::ProcessId>& pids, const RequestOSMemoryDumpCallback& callback) { + OSMemoryDumpArgs args; + args.want_mmaps = want_mmaps; + args.pids = pids; + args.callback = callback; + +#if defined(OS_MACOSX) + // If the most recent chrome memory dump hasn't finished, wait for that to + // finish. + if (most_recent_chrome_memory_dump_guid_.has_value()) { + uint64_t guid = most_recent_chrome_memory_dump_guid_.value(); + auto it = pending_chrome_callbacks_.find(guid); + if (it != pending_chrome_callbacks_.end()) { + delayed_os_memory_dump_callbacks_[guid].push_back(std::move(args)); + return; + } + } +#endif + PerformOSMemoryDump(std::move(args)); +} + +void ClientProcessImpl::PerformOSMemoryDump(OSMemoryDumpArgs args) { bool global_success = true; std::unordered_map<base::ProcessId, mojom::RawOSMemDumpPtr> results; - for (const base::ProcessId& pid : pids) { + for (const base::ProcessId& pid : args.pids) { mojom::RawOSMemDumpPtr result = mojom::RawOSMemDump::New(); result->platform_private_footprint = mojom::PlatformPrivateFootprint::New(); bool success = OSMetrics::FillOSMemoryDump(pid, result.get()); - if (want_mmaps) + if (args.want_mmaps) success = success && OSMetrics::FillProcessMemoryMaps(pid, result.get()); if (success) results[pid] = std::move(result); global_success = global_success && success; } - callback.Run(global_success, std::move(results)); + args.callback.Run(global_success, std::move(results)); } ClientProcessImpl::Config::Config(service_manager::Connector* connector, @@ -153,4 +184,9 @@ ClientProcessImpl::Config::~Config() {} +ClientProcessImpl::OSMemoryDumpArgs::OSMemoryDumpArgs() = default; +ClientProcessImpl::OSMemoryDumpArgs::OSMemoryDumpArgs(const OSMemoryDumpArgs&) = + default; +ClientProcessImpl::OSMemoryDumpArgs::~OSMemoryDumpArgs() = default; + } // namespace memory_instrumentation
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.h b/services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.h index 8000109..55c92035 100644 --- a/services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.h +++ b/services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.h
@@ -83,10 +83,29 @@ const std::vector<base::ProcessId>& ids, const RequestOSMemoryDumpCallback& callback) override; + struct OSMemoryDumpArgs { + OSMemoryDumpArgs(); + OSMemoryDumpArgs(const OSMemoryDumpArgs&); + ~OSMemoryDumpArgs(); + bool want_mmaps = false; + std::vector<base::ProcessId> pids; + RequestOSMemoryDumpCallback callback; + }; + void PerformOSMemoryDump(OSMemoryDumpArgs args); + // Map containing pending chrome memory callbacks indexed by dump guid. // This must be destroyed after |binding_|. std::map<uint64_t, RequestChromeMemoryDumpCallback> pending_chrome_callbacks_; + // On macOS, we must wait for the most recent RequestChromeMemoryDumpCallback + // to complete before running the OS calculations. The key to this map is the + // dump_guid of that RequestChromeMemoryDumpCallback, the value a vector of + // callbacks to calculate and run. For more details, see + // https://bugs.chromium.org/p/chromium/issues/detail?id=812346#c16. + std::map<uint64_t, std::vector<OSMemoryDumpArgs>> + delayed_os_memory_dump_callbacks_; + base::Optional<uint64_t> most_recent_chrome_memory_dump_guid_; + mojom::CoordinatorPtr coordinator_; mojo::Binding<mojom::ClientProcess> binding_; const mojom::ProcessType process_type_;
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py index 01274fd4..f523b8c 100755 --- a/testing/scripts/run_performance_tests.py +++ b/testing/scripts/run_performance_tests.py
@@ -207,8 +207,9 @@ for benchmark in sharding: return_code = (execute_benchmark( benchmark, isolated_out_dir, args, rest_args, False) or return_code) - return_code = (execute_benchmark( - benchmark, isolated_out_dir, args, rest_args, True) or return_code) + # We ignore the return code of the reference build since we do not + # monitor it. + execute_benchmark(benchmark, isolated_out_dir, args, rest_args, True) return return_code
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService index d3157c7..225f6150 100644 --- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService +++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
@@ -44,8 +44,6 @@ Bug(none) fast/xsl/xslt-nested-stylesheets.xml [ Skip ] Bug(none) fast/xsl/xslt-second-level-import.xml [ Skip ] -crbug.com/721408 http/tests/inspector-protocol/network-data-length.js [ Failure ] - Bug(none) http/tests/media/video-buffered.html [ Timeout ] Bug(none) http/tests/misc/embed-image-load-outlives-gc-without-crashing.html [ Failure Pass ] Bug(none) http/tests/misc/image-input-type-outlives-gc-without-crashing.html [ Failure Pass ]
diff --git a/third_party/WebKit/LayoutTests/SmokeTests b/third_party/WebKit/LayoutTests/SmokeTests index 68afc0b..5e1676f 100644 --- a/third_party/WebKit/LayoutTests/SmokeTests +++ b/third_party/WebKit/LayoutTests/SmokeTests
@@ -185,6 +185,7 @@ external/wpt/dom/nodes/Node-lookupPrefix.xhtml external/wpt/domparsing/innerhtml-03.xhtml external/wpt/domparsing/innerhtml-04.html +external/wpt/dom/ranges/Range-compareBoundaryPoints.html external/wpt/dom/ranges/Range-constructor.html external/wpt/dom/traversal/NodeFilter-constants.html external/wpt/dom/traversal/TreeWalker-walking-outside-a-tree.html
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations index 662008c..c842eaca 100644 --- a/third_party/WebKit/LayoutTests/TestExpectations +++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1456,6 +1456,9 @@ crbug.com/v8/7486 http/tests/devtools/startup/console/console-format-startup-bigint.js [ Skip ] crbug.com/v8/7486 http/tests/devtools/console/console-format-bigint.js [ Skip ] +# Changing signature of console function. +crbug.com/v8/178 inspector-protocol/console/console-let-const-with-api.js [ NeedsManualRebaseline ] + crbug.com/564109 [ Win ] http/tests/webfont/font-display-intervention.html [ Pass Failure Timeout ] crbug.com/399951 http/tests/mime/javascript-mimetype-usecounters.html [ Pass Failure ] @@ -3350,3 +3353,6 @@ # Sheriff 2018-03-07 crbug.com/819778 [ Linux ] external/wpt/css/cssom-view/interfaces.html [ Pass Timeout ] + +# Sheriff 2018-03-22 +crbug.com/824775 [ Win Mac ] virtual/new-remote-playback-pipeline/media/controls/video-controls-with-cast-rendering.html [ Pass Failure ]
diff --git a/third_party/WebKit/LayoutTests/W3CImportExpectations b/third_party/WebKit/LayoutTests/W3CImportExpectations index 9f9d2036..cfc35ca7 100644 --- a/third_party/WebKit/LayoutTests/W3CImportExpectations +++ b/third_party/WebKit/LayoutTests/W3CImportExpectations
@@ -201,7 +201,7 @@ external/wpt/css/css-text/white-space/white-space-collapsing-discard-001.xht [ Skip ] external/wpt/css/css-text/white-space/white-space-collapsing-preserve-breaks-001.xht [ Skip ] -# CSS Writing Modes Level 3: Following tests require writing-mode: sideways-*, which we do not plan to support today. +# CSS Writing Modes Level 4: Following tests require writing-mode: sideways-*, which we do not plan to support today. external/wpt/css/css-writing-modes/block-flow-direction-slr-043.xht [ Skip ] external/wpt/css/css-writing-modes/block-flow-direction-slr-047.xht [ Skip ] external/wpt/css/css-writing-modes/block-flow-direction-slr-048.xht [ Skip ] @@ -226,6 +226,11 @@ external/wpt/css/css-writing-modes/block-flow-direction-srl-061.xht [ Skip ] external/wpt/css/css-writing-modes/block-flow-direction-srl-064.xht [ Skip ] external/wpt/css/css-writing-modes/block-flow-direction-srl-065.xht [ Skip ] +external/wpt/css/css-writing-modes/form-controls-slr-004.xht [ Skip ] +external/wpt/css/css-writing-modes/form-controls-slr-005.xht [ Skip ] +external/wpt/css/css-writing-modes/form-controls-srl-004.xht [ Skip ] +external/wpt/css/css-writing-modes/form-controls-srl-005.xht [ Skip ] +external/wpt/css/css-writing-modes/inline-block-alignment-slr-009-ref.xht [ Skip ] external/wpt/css/css-writing-modes/inline-block-alignment-slr-009.xht [ Skip ] external/wpt/css/css-writing-modes/inline-block-alignment-srl-008.xht [ Skip ] external/wpt/css/css-writing-modes/line-box-direction-slr-043.xht [ Skip ] @@ -246,6 +251,10 @@ external/wpt/css/css-writing-modes/line-box-direction-srl-055.xht [ Skip ] external/wpt/css/css-writing-modes/line-box-direction-srl-057.xht [ Skip ] external/wpt/css/css-writing-modes/line-box-direction-srl-059.xht [ Skip ] +external/wpt/css/css-writing-modes/outline-inline-slr-005.xht [ Skip ] +external/wpt/css/css-writing-modes/outline-inline-srl-004.xht [ Skip ] +external/wpt/css/css-writing-modes/page-flow-direction-slr-005.xht [ Skip ] +external/wpt/css/css-writing-modes/page-flow-direction-srl-004.xht [ Skip ] external/wpt/css/css-writing-modes/row-progression-slr-023.xht [ Skip ] external/wpt/css/css-writing-modes/row-progression-slr-029.xht [ Skip ] external/wpt/css/css-writing-modes/row-progression-srl-022.xht [ Skip ] @@ -253,23 +262,35 @@ external/wpt/css/css-writing-modes/table-column-order-slr-007.xht [ Skip ] external/wpt/css/css-writing-modes/table-column-order-srl-006.xht [ Skip ] external/wpt/css/css-writing-modes/table-progression-slr-001.html [ Skip ] +external/wpt/css/css-writing-modes/table-progression-slr-001-ref.html [ Skip ] external/wpt/css/css-writing-modes/table-progression-slr-002.html [ Skip ] external/wpt/css/css-writing-modes/table-progression-srl-001.html [ Skip ] external/wpt/css/css-writing-modes/table-progression-srl-002.html [ Skip ] +external/wpt/css/css-writing-modes/text-baseline-slr-009-ref.xht [ Skip ] external/wpt/css/css-writing-modes/text-baseline-slr-009.xht [ Skip ] external/wpt/css/css-writing-modes/text-baseline-srl-008.xht [ Skip ] +external/wpt/css/css-writing-modes/text-orientation-mixed-slr-015.xht [ Skip ] +external/wpt/css/css-writing-modes/text-orientation-mixed-srl-016-ref.xht [ Skip ] external/wpt/css/css-writing-modes/text-orientation-mixed-srl-016.xht [ Skip ] +external/wpt/css/css-writing-modes/text-orientation-sideways-slr-019.xht [ Skip ] +external/wpt/css/css-writing-modes/text-orientation-upright-slr-017.xht [ Skip ] external/wpt/css/css-writing-modes/text-orientation-upright-srl-018.xht [ Skip ] external/wpt/css/css-writing-modes/vertical-alignment-slr-029.xht [ Skip ] external/wpt/css/css-writing-modes/vertical-alignment-slr-031.xht [ Skip ] external/wpt/css/css-writing-modes/vertical-alignment-slr-033.xht [ Skip ] external/wpt/css/css-writing-modes/vertical-alignment-slr-035.xht [ Skip ] +external/wpt/css/css-writing-modes/vertical-alignment-slr-037.xht [ Skip ] +external/wpt/css/css-writing-modes/vertical-alignment-slr-039.xht [ Skip ] external/wpt/css/css-writing-modes/vertical-alignment-slr-041.xht [ Skip ] +external/wpt/css/css-writing-modes/vertical-alignment-slr-049-ref.xht [ Skip ] external/wpt/css/css-writing-modes/vertical-alignment-srl-028.xht [ Skip ] external/wpt/css/css-writing-modes/vertical-alignment-srl-030.xht [ Skip ] external/wpt/css/css-writing-modes/vertical-alignment-srl-032.xht [ Skip ] external/wpt/css/css-writing-modes/vertical-alignment-srl-034.xht [ Skip ] +external/wpt/css/css-writing-modes/vertical-alignment-srl-036.xht [ Skip ] +external/wpt/css/css-writing-modes/vertical-alignment-srl-038.xht [ Skip ] external/wpt/css/css-writing-modes/vertical-alignment-srl-040.xht [ Skip ] +external/wpt/css/css-writing-modes/wm-propagation-body-008.xht [ Skip ] external/wpt/css/css-writing-modes/writing-mode-parsing-sideways-lr-001.html [ Skip ] external/wpt/css/css-writing-modes/writing-mode-parsing-sideways-rl-001.html [ Skip ] @@ -278,8 +299,36 @@ external/wpt/css/css-writing-modes/block-flow-direction-vrl-017.xht [ Skip ] external/wpt/css/css-writing-modes/line-box-direction-vlr-016.xht [ Skip ] external/wpt/css/css-writing-modes/line-box-direction-vrl-015.xht [ Skip ] +external/wpt/css/css-writing-modes/table-cell-001.html [ Skip ] +external/wpt/css/css-writing-modes/table-cell-002.html [ Skip ] -# CSS Writing Modes Level 3: Following tests require part of text-combine-upright we do not plan to support. +# CSS Writing Modes Level 3: Following tests require bottom-to-top or mixed ordering of table cells, which we do not support today. crbug.com/409155 +external/wpt/css/css-writing-modes/table-column-order-002.xht [ Skip ] +external/wpt/css/css-writing-modes/table-column-order-003.xht [ Skip ] +external/wpt/css/css-writing-modes/table-column-order-004.xht [ Skip ] +external/wpt/css/css-writing-modes/table-column-order-005.xht [ Skip ] +external/wpt/css/css-writing-modes/table-progression-001-ref.html [ Skip ] +external/wpt/css/css-writing-modes/table-progression-002-ref.html [ Skip ] +external/wpt/css/css-writing-modes/table-progression-vlr-001.html [ Skip ] +external/wpt/css/css-writing-modes/table-progression-vlr-002.html [ Skip ] +external/wpt/css/css-writing-modes/table-progression-vlr-003.html [ Skip ] +external/wpt/css/css-writing-modes/table-progression-vlr-004.html [ Skip ] +external/wpt/css/css-writing-modes/table-progression-vrl-001.html [ Skip ] +external/wpt/css/css-writing-modes/table-progression-vrl-002.html [ Skip ] +external/wpt/css/css-writing-modes/table-progression-vrl-003.html [ Skip ] +external/wpt/css/css-writing-modes/table-progression-vrl-004.html [ Skip ] + +# CSS Writing Modes Level 3: Following tests require new definition of ch unit, which we do not support today. crbug.com/409155 +external/wpt/css/css-writing-modes/ch-units-vrl-001.html [ Skip ] +external/wpt/css/css-writing-modes/ch-units-vrl-002.html [ Skip ] +external/wpt/css/css-writing-modes/ch-units-vrl-003.html [ Skip ] +external/wpt/css/css-writing-modes/ch-units-vrl-004.html [ Skip ] +external/wpt/css/css-writing-modes/ch-units-vrl-005.html [ Skip ] +external/wpt/css/css-writing-modes/ch-units-vrl-006.html [ Skip ] +external/wpt/css/css-writing-modes/ch-units-vrl-007.html [ Skip ] +external/wpt/css/css-writing-modes/ch-units-vrl-008.html [ Skip ] + +# CSS Writing Modes Level 3/4: Following tests require part of text-combine-upright we do not plan to support. external/wpt/css/css-writing-modes/full-width-001.html [ Skip ] external/wpt/css/css-writing-modes/full-width-002.html [ Skip ] external/wpt/css/css-writing-modes/full-width-003.html [ Skip ] @@ -295,6 +344,13 @@ external/wpt/css/css-writing-modes/text-combine-upright-value-digits4-002.html [ Skip ] external/wpt/css/css-writing-modes/text-combine-upright-value-digits4-003.html [ Skip ] +# CSS Logical Properties and Values Level 1: We do not have active plans to support. +# https://www.w3.org/TR/css-logical-1/ +external/wpt/css/css-writing-modes/logical-props-001.html [ Skip ] +external/wpt/css/css-writing-modes/logical-props-002.html [ Skip ] +external/wpt/css/css-writing-modes/logical-props-003.html [ Skip ] +external/wpt/css/css-writing-modes/logical-props-004.html [ Skip ] + # Untriaged: Tests don't complete. external/wpt/html/browsers/browsing-the-web/unloading-documents/unload/006.html [ Skip ] external/wpt/html/browsers/history/the-history-interface/joint_session_history/002.html [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/shadow-dom/Element-interface-attachShadow-custom-element.html b/third_party/WebKit/LayoutTests/external/wpt/shadow-dom/Element-interface-attachShadow-custom-element.html new file mode 100644 index 0000000..b59460e --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/shadow-dom/Element-interface-attachShadow-custom-element.html
@@ -0,0 +1,29 @@ +<!DOCTYPE html> +<title>Shadow DOM: Attaching a ShadowRoot for custom elements</title> +<meta name="author" title="Hayato Ito" href="mailto:hayato@chromium.org"> +<link rel="help" href="https://dom.spec.whatwg.org/#dom-element-attachshadow"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +class MyAutonomousCustomElement extends HTMLElement { +} + +customElements.define('my-custom', MyAutonomousCustomElement); + +test(() => { + assert_true(document.createElement('my-custom').attachShadow({mode: "open"}) instanceof ShadowRoot); +}, 'Element.attachShadow must create an instance of ShadowRoot for autonomous custom elements'); + +class MyCustomizedBuiltinElement extends HTMLInputElement { +} + +customElements.define('my-input', MyCustomizedBuiltinElement, { extends: 'input' }); + +test(() => { + assert_throws({'name': 'NotSupportedError'}, () => { + document.createElement('input', {is: 'my-input'}).attachShadow({mode: "open"}); + }); +}, 'Element.attachShadow must throw a NotSupportedError for customized built-in elements'); +</script> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/errors-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/errors-expected.txt deleted file mode 100644 index 5c760f5..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/errors-expected.txt +++ /dev/null
@@ -1,23 +0,0 @@ -This is a testharness.js-based test. -PASS TransformStream errors thrown in transform put the writable and readable in an errored state -PASS TransformStream errors thrown in flush put the writable and readable in an errored state -PASS errored TransformStream should not enqueue new chunks -PASS TransformStream transformer.start() rejected promise should error the stream -PASS when controller.error is followed by a rejection, the error reason should come from controller.error -PASS TransformStream constructor should throw when start does -PASS when strategy.size throws inside start(), the constructor should throw the same error -PASS when strategy.size calls controller.error() then throws, the constructor should throw the first error -PASS cancelling the readable side should error the writable -PASS it should be possible to error the readable between close requested and complete -PASS an exception from transform() should error the stream if terminate has been requested but not completed -PASS abort should set the close reason for the writable when it happens before cancel during start, but cancel should still succeed -PASS abort should set the close reason for the writable when it happens before cancel during underlying sink write, but cancel should still succeed -PASS controller.error() should do nothing the second time it is called -PASS controller.error() should do nothing after readable.cancel() -PASS controller.error() should do nothing after writable.abort() has completed -PASS controller.error() should do nothing after a transformer method has thrown an exception -PASS erroring during write with backpressure should result in the write failing -FAIL a write() that was waiting for backpressure should reject if the writable is aborted assert_throws: read() should reject function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: bad things are happening!" ("error1") -FAIL the readable should be errored with the reason passed to the writable abort() method assert_throws: read() should reject with thrownError function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: bad things are happening!" ("error1") -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/errors.dedicatedworker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/errors.dedicatedworker-expected.txt deleted file mode 100644 index 5c760f5..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/errors.dedicatedworker-expected.txt +++ /dev/null
@@ -1,23 +0,0 @@ -This is a testharness.js-based test. -PASS TransformStream errors thrown in transform put the writable and readable in an errored state -PASS TransformStream errors thrown in flush put the writable and readable in an errored state -PASS errored TransformStream should not enqueue new chunks -PASS TransformStream transformer.start() rejected promise should error the stream -PASS when controller.error is followed by a rejection, the error reason should come from controller.error -PASS TransformStream constructor should throw when start does -PASS when strategy.size throws inside start(), the constructor should throw the same error -PASS when strategy.size calls controller.error() then throws, the constructor should throw the first error -PASS cancelling the readable side should error the writable -PASS it should be possible to error the readable between close requested and complete -PASS an exception from transform() should error the stream if terminate has been requested but not completed -PASS abort should set the close reason for the writable when it happens before cancel during start, but cancel should still succeed -PASS abort should set the close reason for the writable when it happens before cancel during underlying sink write, but cancel should still succeed -PASS controller.error() should do nothing the second time it is called -PASS controller.error() should do nothing after readable.cancel() -PASS controller.error() should do nothing after writable.abort() has completed -PASS controller.error() should do nothing after a transformer method has thrown an exception -PASS erroring during write with backpressure should result in the write failing -FAIL a write() that was waiting for backpressure should reject if the writable is aborted assert_throws: read() should reject function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: bad things are happening!" ("error1") -FAIL the readable should be errored with the reason passed to the writable abort() method assert_throws: read() should reject with thrownError function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: bad things are happening!" ("error1") -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/errors.serviceworker.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/errors.serviceworker.https-expected.txt deleted file mode 100644 index dece9c8..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/errors.serviceworker.https-expected.txt +++ /dev/null
@@ -1,24 +0,0 @@ -This is a testharness.js-based test. -PASS Service worker test setup -PASS TransformStream errors thrown in transform put the writable and readable in an errored state -PASS TransformStream errors thrown in flush put the writable and readable in an errored state -PASS errored TransformStream should not enqueue new chunks -PASS TransformStream transformer.start() rejected promise should error the stream -PASS when controller.error is followed by a rejection, the error reason should come from controller.error -PASS TransformStream constructor should throw when start does -PASS when strategy.size throws inside start(), the constructor should throw the same error -PASS when strategy.size calls controller.error() then throws, the constructor should throw the first error -PASS cancelling the readable side should error the writable -PASS it should be possible to error the readable between close requested and complete -PASS an exception from transform() should error the stream if terminate has been requested but not completed -PASS abort should set the close reason for the writable when it happens before cancel during start, but cancel should still succeed -PASS abort should set the close reason for the writable when it happens before cancel during underlying sink write, but cancel should still succeed -PASS controller.error() should do nothing the second time it is called -PASS controller.error() should do nothing after readable.cancel() -PASS controller.error() should do nothing after writable.abort() has completed -PASS controller.error() should do nothing after a transformer method has thrown an exception -PASS erroring during write with backpressure should result in the write failing -FAIL a write() that was waiting for backpressure should reject if the writable is aborted assert_throws: read() should reject function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: bad things are happening!" ("error1") -FAIL the readable should be errored with the reason passed to the writable abort() method assert_throws: read() should reject with thrownError function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: bad things are happening!" ("error1") -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/errors.sharedworker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/errors.sharedworker-expected.txt deleted file mode 100644 index 5c760f5..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/errors.sharedworker-expected.txt +++ /dev/null
@@ -1,23 +0,0 @@ -This is a testharness.js-based test. -PASS TransformStream errors thrown in transform put the writable and readable in an errored state -PASS TransformStream errors thrown in flush put the writable and readable in an errored state -PASS errored TransformStream should not enqueue new chunks -PASS TransformStream transformer.start() rejected promise should error the stream -PASS when controller.error is followed by a rejection, the error reason should come from controller.error -PASS TransformStream constructor should throw when start does -PASS when strategy.size throws inside start(), the constructor should throw the same error -PASS when strategy.size calls controller.error() then throws, the constructor should throw the first error -PASS cancelling the readable side should error the writable -PASS it should be possible to error the readable between close requested and complete -PASS an exception from transform() should error the stream if terminate has been requested but not completed -PASS abort should set the close reason for the writable when it happens before cancel during start, but cancel should still succeed -PASS abort should set the close reason for the writable when it happens before cancel during underlying sink write, but cancel should still succeed -PASS controller.error() should do nothing the second time it is called -PASS controller.error() should do nothing after readable.cancel() -PASS controller.error() should do nothing after writable.abort() has completed -PASS controller.error() should do nothing after a transformer method has thrown an exception -PASS erroring during write with backpressure should result in the write failing -FAIL a write() that was waiting for backpressure should reject if the writable is aborted assert_throws: read() should reject function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: bad things are happening!" ("error1") -FAIL the readable should be errored with the reason passed to the writable abort() method assert_throws: read() should reject with thrownError function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: bad things are happening!" ("error1") -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/properties-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/properties-expected.txt deleted file mode 100644 index 8bbc062..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/properties-expected.txt +++ /dev/null
@@ -1,31 +0,0 @@ -This is a testharness.js-based test. -PASS TransformStreamDefaultController should not be exported on the global object -PASS TransformStream.prototype.constructor should have standard properties -PASS TransformStream.prototype.constructor should be a constructor -PASS TransformStream.prototype.readable should have standard properties -PASS TransformStream.prototype.readable should be a getter -PASS TransformStream.prototype.writable should have standard properties -PASS TransformStream.prototype.writable should be a getter -PASS TransformStream.prototype should have exactly the expected properties -PASS TransformStreamDefaultController.prototype.constructor should have standard properties -FAIL TransformStreamDefaultController.prototype.constructor should be a constructor assert_equals: constructor should take 0 arguments expected 0 but got 1 -PASS TransformStreamDefaultController.prototype.desiredSize should have standard properties -PASS TransformStreamDefaultController.prototype.desiredSize should be a getter -PASS TransformStreamDefaultController.prototype.enqueue should have standard properties -PASS TransformStreamDefaultController.prototype.enqueue should be a method -PASS TransformStreamDefaultController.prototype.error should have standard properties -PASS TransformStreamDefaultController.prototype.error should be a method -PASS TransformStreamDefaultController.prototype.terminate should have standard properties -PASS TransformStreamDefaultController.prototype.terminate should be a method -PASS TransformStreamDefaultController.prototype should have exactly the expected properties -PASS transformer method start should be called with the right number of arguments -PASS transformer method start should be called even when it's located on the prototype chain -FAIL unexpected properties should not be accessed when calling transformer method start assert_array_equals: expected properties should be got lengths differ, expected 5 got 3 -PASS transformer method transform should be called with the right number of arguments -PASS transformer method transform should be called even when it's located on the prototype chain -FAIL unexpected properties should not be accessed when calling transformer method transform assert_array_equals: expected properties should be got lengths differ, expected 5 got 3 -PASS transformer method flush should be called with the right number of arguments -PASS transformer method flush should be called even when it's located on the prototype chain -FAIL unexpected properties should not be accessed when calling transformer method flush assert_array_equals: expected properties should be got lengths differ, expected 5 got 3 -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/properties.dedicatedworker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/properties.dedicatedworker-expected.txt deleted file mode 100644 index 8bbc062..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/properties.dedicatedworker-expected.txt +++ /dev/null
@@ -1,31 +0,0 @@ -This is a testharness.js-based test. -PASS TransformStreamDefaultController should not be exported on the global object -PASS TransformStream.prototype.constructor should have standard properties -PASS TransformStream.prototype.constructor should be a constructor -PASS TransformStream.prototype.readable should have standard properties -PASS TransformStream.prototype.readable should be a getter -PASS TransformStream.prototype.writable should have standard properties -PASS TransformStream.prototype.writable should be a getter -PASS TransformStream.prototype should have exactly the expected properties -PASS TransformStreamDefaultController.prototype.constructor should have standard properties -FAIL TransformStreamDefaultController.prototype.constructor should be a constructor assert_equals: constructor should take 0 arguments expected 0 but got 1 -PASS TransformStreamDefaultController.prototype.desiredSize should have standard properties -PASS TransformStreamDefaultController.prototype.desiredSize should be a getter -PASS TransformStreamDefaultController.prototype.enqueue should have standard properties -PASS TransformStreamDefaultController.prototype.enqueue should be a method -PASS TransformStreamDefaultController.prototype.error should have standard properties -PASS TransformStreamDefaultController.prototype.error should be a method -PASS TransformStreamDefaultController.prototype.terminate should have standard properties -PASS TransformStreamDefaultController.prototype.terminate should be a method -PASS TransformStreamDefaultController.prototype should have exactly the expected properties -PASS transformer method start should be called with the right number of arguments -PASS transformer method start should be called even when it's located on the prototype chain -FAIL unexpected properties should not be accessed when calling transformer method start assert_array_equals: expected properties should be got lengths differ, expected 5 got 3 -PASS transformer method transform should be called with the right number of arguments -PASS transformer method transform should be called even when it's located on the prototype chain -FAIL unexpected properties should not be accessed when calling transformer method transform assert_array_equals: expected properties should be got lengths differ, expected 5 got 3 -PASS transformer method flush should be called with the right number of arguments -PASS transformer method flush should be called even when it's located on the prototype chain -FAIL unexpected properties should not be accessed when calling transformer method flush assert_array_equals: expected properties should be got lengths differ, expected 5 got 3 -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/properties.serviceworker.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/properties.serviceworker.https-expected.txt deleted file mode 100644 index a7bb9a6..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/properties.serviceworker.https-expected.txt +++ /dev/null
@@ -1,32 +0,0 @@ -This is a testharness.js-based test. -PASS Service worker test setup -PASS TransformStreamDefaultController should not be exported on the global object -PASS TransformStream.prototype.constructor should have standard properties -PASS TransformStream.prototype.constructor should be a constructor -PASS TransformStream.prototype.readable should have standard properties -PASS TransformStream.prototype.readable should be a getter -PASS TransformStream.prototype.writable should have standard properties -PASS TransformStream.prototype.writable should be a getter -PASS TransformStream.prototype should have exactly the expected properties -PASS TransformStreamDefaultController.prototype.constructor should have standard properties -FAIL TransformStreamDefaultController.prototype.constructor should be a constructor assert_equals: constructor should take 0 arguments expected 0 but got 1 -PASS TransformStreamDefaultController.prototype.desiredSize should have standard properties -PASS TransformStreamDefaultController.prototype.desiredSize should be a getter -PASS TransformStreamDefaultController.prototype.enqueue should have standard properties -PASS TransformStreamDefaultController.prototype.enqueue should be a method -PASS TransformStreamDefaultController.prototype.error should have standard properties -PASS TransformStreamDefaultController.prototype.error should be a method -PASS TransformStreamDefaultController.prototype.terminate should have standard properties -PASS TransformStreamDefaultController.prototype.terminate should be a method -PASS TransformStreamDefaultController.prototype should have exactly the expected properties -PASS transformer method start should be called with the right number of arguments -PASS transformer method start should be called even when it's located on the prototype chain -FAIL unexpected properties should not be accessed when calling transformer method start assert_array_equals: expected properties should be got lengths differ, expected 5 got 3 -PASS transformer method transform should be called with the right number of arguments -PASS transformer method transform should be called even when it's located on the prototype chain -FAIL unexpected properties should not be accessed when calling transformer method transform assert_array_equals: expected properties should be got lengths differ, expected 5 got 3 -PASS transformer method flush should be called with the right number of arguments -PASS transformer method flush should be called even when it's located on the prototype chain -FAIL unexpected properties should not be accessed when calling transformer method flush assert_array_equals: expected properties should be got lengths differ, expected 5 got 3 -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/properties.sharedworker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/properties.sharedworker-expected.txt deleted file mode 100644 index 8bbc062..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/properties.sharedworker-expected.txt +++ /dev/null
@@ -1,31 +0,0 @@ -This is a testharness.js-based test. -PASS TransformStreamDefaultController should not be exported on the global object -PASS TransformStream.prototype.constructor should have standard properties -PASS TransformStream.prototype.constructor should be a constructor -PASS TransformStream.prototype.readable should have standard properties -PASS TransformStream.prototype.readable should be a getter -PASS TransformStream.prototype.writable should have standard properties -PASS TransformStream.prototype.writable should be a getter -PASS TransformStream.prototype should have exactly the expected properties -PASS TransformStreamDefaultController.prototype.constructor should have standard properties -FAIL TransformStreamDefaultController.prototype.constructor should be a constructor assert_equals: constructor should take 0 arguments expected 0 but got 1 -PASS TransformStreamDefaultController.prototype.desiredSize should have standard properties -PASS TransformStreamDefaultController.prototype.desiredSize should be a getter -PASS TransformStreamDefaultController.prototype.enqueue should have standard properties -PASS TransformStreamDefaultController.prototype.enqueue should be a method -PASS TransformStreamDefaultController.prototype.error should have standard properties -PASS TransformStreamDefaultController.prototype.error should be a method -PASS TransformStreamDefaultController.prototype.terminate should have standard properties -PASS TransformStreamDefaultController.prototype.terminate should be a method -PASS TransformStreamDefaultController.prototype should have exactly the expected properties -PASS transformer method start should be called with the right number of arguments -PASS transformer method start should be called even when it's located on the prototype chain -FAIL unexpected properties should not be accessed when calling transformer method start assert_array_equals: expected properties should be got lengths differ, expected 5 got 3 -PASS transformer method transform should be called with the right number of arguments -PASS transformer method transform should be called even when it's located on the prototype chain -FAIL unexpected properties should not be accessed when calling transformer method transform assert_array_equals: expected properties should be got lengths differ, expected 5 got 3 -PASS transformer method flush should be called with the right number of arguments -PASS transformer method flush should be called even when it's located on the prototype chain -FAIL unexpected properties should not be accessed when calling transformer method flush assert_array_equals: expected properties should be got lengths differ, expected 5 got 3 -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/reentrant-strategies-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/reentrant-strategies-expected.txt deleted file mode 100644 index b6ed0d8..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/reentrant-strategies-expected.txt +++ /dev/null
@@ -1,14 +0,0 @@ -This is a testharness.js-based test. -PASS enqueue() inside size() should work -PASS terminate() inside size() should work -PASS error() inside size() should work -PASS desiredSize inside size() should work -PASS readable cancel() inside size() should work -PASS pipeTo() inside size() should work -PASS read() inside of size() should work -PASS writer.write() inside size() should work -PASS synchronous writer.write() inside size() should work -PASS writer.close() inside size() should work -FAIL writer.abort() inside size() should work assert_throws: read() should reject function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: error1" ("error1") -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/reentrant-strategies.dedicatedworker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/reentrant-strategies.dedicatedworker-expected.txt deleted file mode 100644 index b6ed0d8..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/reentrant-strategies.dedicatedworker-expected.txt +++ /dev/null
@@ -1,14 +0,0 @@ -This is a testharness.js-based test. -PASS enqueue() inside size() should work -PASS terminate() inside size() should work -PASS error() inside size() should work -PASS desiredSize inside size() should work -PASS readable cancel() inside size() should work -PASS pipeTo() inside size() should work -PASS read() inside of size() should work -PASS writer.write() inside size() should work -PASS synchronous writer.write() inside size() should work -PASS writer.close() inside size() should work -FAIL writer.abort() inside size() should work assert_throws: read() should reject function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: error1" ("error1") -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/reentrant-strategies.serviceworker.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/reentrant-strategies.serviceworker.https-expected.txt deleted file mode 100644 index 4f2b44a..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/reentrant-strategies.serviceworker.https-expected.txt +++ /dev/null
@@ -1,15 +0,0 @@ -This is a testharness.js-based test. -PASS Service worker test setup -PASS enqueue() inside size() should work -PASS terminate() inside size() should work -PASS error() inside size() should work -PASS desiredSize inside size() should work -PASS readable cancel() inside size() should work -PASS pipeTo() inside size() should work -PASS read() inside of size() should work -PASS writer.write() inside size() should work -PASS synchronous writer.write() inside size() should work -PASS writer.close() inside size() should work -FAIL writer.abort() inside size() should work assert_throws: read() should reject function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: error1" ("error1") -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/reentrant-strategies.sharedworker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/reentrant-strategies.sharedworker-expected.txt deleted file mode 100644 index b6ed0d8..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/streams/transform-streams/reentrant-strategies.sharedworker-expected.txt +++ /dev/null
@@ -1,14 +0,0 @@ -This is a testharness.js-based test. -PASS enqueue() inside size() should work -PASS terminate() inside size() should work -PASS error() inside size() should work -PASS desiredSize inside size() should work -PASS readable cancel() inside size() should work -PASS pipeTo() inside size() should work -PASS read() inside of size() should work -PASS writer.write() inside size() should work -PASS synchronous writer.write() inside size() should work -PASS writer.close() inside size() should work -FAIL writer.abort() inside size() should work assert_throws: read() should reject function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: error1" ("error1") -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-expected.txt b/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-expected.txt deleted file mode 100644 index e2e6148..0000000 --- a/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-expected.txt +++ /dev/null
@@ -1,9 +0,0 @@ -For manual testing, drag and drop "Drop Me" to "Scrollable" area. -Check autoscroll by drag-and-drop - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -PASS scrollable.scrollTop > 0 -PASS autoscroll stopped -
diff --git a/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-frameset-expected.txt b/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-frameset-expected.txt deleted file mode 100644 index 4aa83bf..0000000 --- a/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-frameset-expected.txt +++ /dev/null
@@ -1,17 +0,0 @@ -state=START -state=NE -PASS document.scrollingElement.scrollLeft > 0 is true -PASS !document.scrollingElement.scrollTop is true -state=SE -PASS document.scrollingElement.scrollLeft > 0 is true -PASS document.scrollingElement.scrollTop > 0 is true -state=SW -PASS document.scrollingElement.scrollLeft < lastScrollLeft is true -PASS document.scrollingElement.scrollTop > 0 is true -state=NW -PASS document.scrollingElement.scrollLeft <= lastScrollLeft is true -PASS document.scrollingElement.scrollTop < lastScrollTop is true -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-frameset.html b/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-frameset.html index 2409788..f2e2b8c 100644 --- a/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-frameset.html +++ b/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-frameset.html
@@ -1,10 +1,12 @@ +<!DOCTYPE html> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> <script> -window.addEventListener('message', function(event) { - document.body.outerHTML = event.data; - testRunner.notifyDone(); -}); + window.onload = () => { + fetch_tests_from_window(frames[0]); + } </script> -<frameset cols="50%,%50%"> +<frameset cols="50%,50%"> <frame src="resources/drag-and-drop-autoscroll-frame.html"> <frame> </frameset>
diff --git a/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-inner-frame-expected.txt b/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-inner-frame-expected.txt deleted file mode 100644 index bc4f50d..0000000 --- a/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-inner-frame-expected.txt +++ /dev/null
@@ -1,11 +0,0 @@ -Check autoscroll within an inner frame by drag-and-drop - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -PASS Autoscroll should have scrolled the iframe downwards, and did. -PASS iframe.contentDocument.scrollingElement.scrollTop < middleTermScrollOffset is true -PASS successfullyParsed is true - -TEST COMPLETE -For manual testing, drag and drop "Drop Me" downwards and then upwards.
diff --git a/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-inner-frame.html b/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-inner-frame.html index 9950713..3ce224b 100644 --- a/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-inner-frame.html +++ b/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-inner-frame.html
@@ -1,111 +1,18 @@ <!DOCTYPE html> -<head> -<style type="text/css"> -#scrollable { - height: 200px; - overflow: auto; - border: solid 3px #cc0000; - font-size: 80px; -} -</style> -</head> -<body> -For manual testing, drag and drop "Drop Me" downwards and then upwards. -<script src="../../resources/js-test.js"></script> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> <script> - -var x, y, middleTermScrollOffset; -var iframe, iframeDocument, draggable; - -function setUpTest() -{ - if (!window.eventSender) { - debug('Please run within DumpRenderTree'); - return; - } - - iframe = document.getElementById('scrollable'); - // iframes start in readyState complete, but with the about:blank doc, make sure our doc is loaded. - if (iframe.contentWindow.location.href == "about:blank" || iframe.contentDocument.readyState != "complete") - iframe.onload = testIt; - else - testIt(); -} - -function testIt() -{ - eventSender.dragMode = false; - - iframe = document.getElementById('scrollable'); - iframeDocument = iframe.contentDocument; - draggable = iframeDocument.getElementById('draggable'); - - iframeDocument.addEventListener("scroll", recordScroll); - - // Grab draggable. - x = iframe.offsetLeft + draggable.offsetLeft + 7; - y = iframe.offsetTop + draggable.offsetTop + 7; - - eventSender.mouseMoveTo(x, y); - eventSender.mouseDown(); - - // Move mouse to the bottom autoscroll border belt. - y = iframe.offsetTop + iframe.offsetHeight - 10; - eventSender.mouseMoveTo(x, y); -} - -function recordScroll(e) -{ - autoscrollTestPart1(); - iframeDocument.removeEventListener("scroll", recordScroll); -} - -function recordScroll2(e) -{ - autoscrollTestPart2(); - iframeDocument.removeEventListener("scroll", recordScroll); -} - -function autoscrollTestPart1() -{ - if (iframe.contentDocument.scrollingElement.scrollTop == 0) { - testFailed("Autoscroll should have scrolled the iframe downwards, but did not"); - finishTest(); - return; - } - - testPassed("Autoscroll should have scrolled the iframe downwards, and did."); - - middleTermScrollOffset = iframe.contentDocument.scrollingElement.scrollTop; - iframeDocument.addEventListener("scroll", recordScroll2); - - // Move mouse to the upper autoscroll border belt. - y = iframe.offsetTop + 10; - eventSender.mouseMoveTo(x, y); -} - -function autoscrollTestPart2() -{ - shouldBeTrue("iframe.contentDocument.scrollingElement.scrollTop < middleTermScrollOffset") - finishTest(); -} - -function finishTest() -{ - eventSender.mouseUp(); - document.body.removeChild(iframe); - finishJSTest(); -} - -description('Check autoscroll within an inner frame by drag-and-drop'); -window.jsTestIsAsync = true; -window.onload = setUpTest; + window.onload = () => { + fetch_tests_from_window(frames[0]); + } </script> -<iframe id="scrollable" src="data:text/html, -<p id='draggable' draggable='true' style='cursor: hand;'> - <b>Drag me!</b> -</p> -Try to drag and drop the text above in the input element at the bottom of this iframe. It should scroll. Then, try the way back. -<br><br>more<br>more<br>more<br>more<br>more<br>more<br>more<br>more<br>more<br>more<br>more<br>more<br><input> -"></iframe> -</body> +<style> + iframe { + position: relative; + top: 100px; + width: 300px; + height: 300px; + } +</style> +For manual testing, drag and drop "Drop Me" downwards and then upwards. +<iframe src="resources/drag-and-drop-autoscroll-frame.html"></iframe>
diff --git a/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-mainframe-expected.txt b/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-mainframe-expected.txt deleted file mode 100644 index 4aa83bf..0000000 --- a/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-mainframe-expected.txt +++ /dev/null
@@ -1,17 +0,0 @@ -state=START -state=NE -PASS document.scrollingElement.scrollLeft > 0 is true -PASS !document.scrollingElement.scrollTop is true -state=SE -PASS document.scrollingElement.scrollLeft > 0 is true -PASS document.scrollingElement.scrollTop > 0 is true -state=SW -PASS document.scrollingElement.scrollLeft < lastScrollLeft is true -PASS document.scrollingElement.scrollTop > 0 is true -state=NW -PASS document.scrollingElement.scrollLeft <= lastScrollLeft is true -PASS document.scrollingElement.scrollTop < lastScrollTop is true -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-mainframe.html b/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-mainframe.html index 4e2f1ba..be5a8b3 100644 --- a/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-mainframe.html +++ b/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-mainframe.html
@@ -1,37 +1,39 @@ -<html> -<head> -<style type="text/css"> -#draggable { - padding: 5pt; - border: 3px solid #00cc00; - background: #00cccc; - width: 80px; - cursor: hand; -} -</style> +<!DOCTYPE html> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> <script src="resources/drag-and-drop-autoscroll-frame.js"></script> -</head> -<body> +<style> + #draggable { + padding: 5pt; + border: 3px solid #00cc00; + background: #00cccc; + width: 80px; + cursor: pointer; + } + #container { + position: relative; + left: 2000px; + top: 2000px; + } + #sample { + width: 2000px; + height: 2000px; + } + body,html { + margin: 0; + } +</style> + <div id="container"> -<p id="description"></p> -Manual steps: -<ol> -<li>Drag "Drop Me" to edge of window</li> -<li>You should see scrolling</li> -</ol> -<div id="draggable" draggable="true">Drop Me</div> -<div id="scrollbars" style="overflow: scroll"><div>scrollbars</div></div> -<table id="sample" border="1" style="width:2000; height:2000;"> -<tr><td width="50%">North West</td><td width="50%">North East</td></tr> -<tr><td width="50%">South West</td><td width="50%">South East</td></tr> -</table> + Manual steps: + <ol> + <li>Drag "Drop Me" to edge of window</li> + <li>You should see scrolling</li> + </ol> + <div id="draggable" draggable="true">Drop Me</div> + <div id="scrollbars" style="overflow: scroll"><div>scrollbars</div></div> + <table id="sample" border="1"> + <tr><td width="50%">North West</td><td width="50%">North East</td></tr> + <tr><td width="50%">South West</td><td width="50%">South East</td></tr> + </table> </div> -<div id="console"></div> -<script src="../../resources/js-test.js"></script> -<script> -if (window.testRunner) - window.jsTestIsAsync = true; -description('Check autoscroll of main frame by drag-and-drop'); -</script> -</body> -</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-use-count.html b/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-use-count.html index 0d038c8..f68c7882 100644 --- a/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-use-count.html +++ b/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-use-count.html
@@ -5,7 +5,7 @@ border: 3px solid #00cc00; background: #00cccc; width: 80px; - cursor: hand; + cursor: pointer; } #scrollable { height: 200px;
diff --git a/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll.html b/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll.html index d2f55e5..4a2824e 100644 --- a/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll.html +++ b/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll.html
@@ -1,126 +1,43 @@ -<html> -<head> +<!DOCTYPE html> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script src="resources/drag-and-drop-autoscroll-frame.js"></script> <style type="text/css"> -#draggable { - padding: 5pt; - border: 3px solid #00cc00; - background: #00cccc; - width: 80px; - cursor: hand; -} - -#scrollable { - height: 200px; - overflow: auto; - border: solid 3px #cc0000; - font-size: 80px; -} + #draggable { + padding: 5pt; + border: 3px solid #00cc00; + background: #00cccc; + width: 80px; + cursor: pointer; + } + #scrollable { + height: 400px; + width: 400px; + overflow: auto; + font-size: 80px; + } + #container { + position: relative; + left: 2000px; + top: 2000px; + } + #sample { + width: 2000px; + height: 2000px; + } + body { + margin: 0; + } </style> -<script> -function $(id) { return document.getElementById(id); } - -function finishTest() { - eventSender.mouseUp(); - $('container').innerHTML = ''; - window.testRunner.notifyDone(); -} - -function testIt() { - var draggable = $('draggable'); - var scrollable = $('scrollable'); - - if (!window.eventSender) - return; - - eventSender.dragMode = false; - - // Grab draggable - eventSender.mouseMoveTo(draggable.offsetLeft + 5, draggable.offsetTop + 5); - eventSender.mouseDown(); - - // Move mouse to autoscroll border belt. - eventSender.mouseMoveTo(scrollable.offsetLeft + 5, scrollable.offsetTop + scrollable.offsetHeight - 10); - - var retryCount = 0; - var lastScrollTop = 0; - - function checkScrolled() - { - if (scrollable.scrollTop > 0) { - testPassed('scrollable.scrollTop > 0'); - lastScrollTop = scrollable.scrollTop; - // Cancel drag and drop by ESC key. - eventSender.keyDown('Escape'); - retryCount = 0; - window.setTimeout(checkStopped, 50); - return; - } - - ++retryCount; - if (retryCount > 10) { - testFailed('No autoscroll'); - finishTest(); - return; - } - - // Autoscroll is occurred evey 0.05 sec. - window.setTimeout(checkScrolled, 50); - } - - function checkStopped() - { - if (lastScrollTop == scrollable.scrollTop) { - testPassed('autoscroll stopped'); - finishTest(); - return; - } - - ++retryCount; - if (retryCount > 10) { - testFailed('still autoscroll'); - finishTest(); - return; - } - - lastScrollTop = scrollable.scrollTop; - window.setTimeout(checkStopped, 50); - } - - checkScrolled(); -} - -function setUpTest() -{ - var scrollable = $('scrollable'); - for (var i = 0; i < 100; ++i) { - var line = document.createElement('div'); - line.innerHTML = "line " + i; - scrollable.appendChild(line); - } - - if (!window.eventSender) { - console.log('Please run within DumpRenderTree'); - return; - } - - window.jsTestIsAsync = true; - window.setTimeout(testIt, 0); -} -</script> -</head> -<body> For manual testing, drag and drop "Drop Me" to "Scrollable" area. -<div id="container"> <div id="draggable" draggable="true">Drop Me</div> +<div id="scrollbars" style="overflow: scroll"><div>scrollbars</div></div> Scrollable <div id="scrollable"> + <div id="container"> + <table id="sample" border="1"> + <tr><td width="50%">North West</td><td width="50%">North East</td></tr> + <tr><td width="50%">South West</td><td width="50%">South East</td></tr> + </table> + </div> </div> -</div> -<div id="console"></div> -<script src="../../resources/js-test.js"></script> -<script> -description('Check autoscroll by drag-and-drop'); -setUpTest(); -</script> -</body> -</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/resources/drag-and-drop-autoscroll-frame.html b/third_party/WebKit/LayoutTests/fast/events/resources/drag-and-drop-autoscroll-frame.html index e71faf7..20bfc3b 100644 --- a/third_party/WebKit/LayoutTests/fast/events/resources/drag-and-drop-autoscroll-frame.html +++ b/third_party/WebKit/LayoutTests/fast/events/resources/drag-and-drop-autoscroll-frame.html
@@ -1,37 +1,37 @@ -<html> -<head> -<style type="text/css"> -#draggable { - padding: 5pt; - border: 3px solid #00cc00; - background: #00cccc; - width: 80px; - cursor: hand; -} -</style> +<!DOCTYPE html> +<script src="../../../resources/testharness.js"></script> <script src="drag-and-drop-autoscroll-frame.js"></script> -</head> -<body> +<style type="text/css"> + #draggable { + padding: 5pt; + border: 3px solid #00cc00; + background: #00cccc; + width: 80px; + cursor: pointer; + } + #container { + position: relative; + left: 2000px; + top: 2000px; + } + #sample { + width: 2000px; + height: 2000px; + } + body,html { + margin: 0; + } +</style> <div id="container"> -<p id="description"></p> -Manual steps: -<ol> -<li>Drag "Drop Me" to edge of window</li> -<li>You should see scrolling</li> -</ol> -<div id="draggable" draggable="true">Drop Me</div> -<div id="scrollbars" style="overflow: scroll"><div>scrollbars</div></div> -<table id="sample" border="1" style="width:2000; height:2000;"> -<tr><td width="50%">North West</td><td width="50%">North East</td></tr> -<tr><td width="50%">South West</td><td width="50%">South East</td></tr> -</table> + Manual steps: + <ol> + <li>Drag "Drop Me" to edge of window</li> + <li>You should see scrolling</li> + </ol> + <div id="draggable" draggable="true">Drop Me</div> + <div id="scrollbars" style="overflow: scroll"><div>scrollbars</div></div> + <table id="sample" border="1"> + <tr><td width="50%">North West</td><td width="50%">North East</td></tr> + <tr><td width="50%">South West</td><td width="50%">South East</td></tr> + </table> </div> -<div id="console"></div> -<script src="../../../resources/js-test.js"></script> -<script> -if (window.testRunner) - window.jsTestIsAsync = true; -description('Check autoscroll of frame by drag-and-drop'); -</script> -</body> -</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/resources/drag-and-drop-autoscroll-frame.js b/third_party/WebKit/LayoutTests/fast/events/resources/drag-and-drop-autoscroll-frame.js index 1f04598..83eb3a39b 100644 --- a/third_party/WebKit/LayoutTests/fast/events/resources/drag-and-drop-autoscroll-frame.js +++ b/third_party/WebKit/LayoutTests/fast/events/resources/drag-and-drop-autoscroll-frame.js
@@ -1,55 +1,123 @@ function $(id) { return document.getElementById(id); } +// Convert client coordinates in this frame into client coordinates of the root +// frame, usable with event sender. +function toRootWindow(rect) { + var w = window; + var curRect = { + left: rect.left, + top: rect.top, + right: rect.right, + bottom: rect.bottom + }; + while(w.parent != w) { + var frameRect = w.frameElement.getBoundingClientRect(); + curRect.left += frameRect.left; + curRect.right += frameRect.left; + curRect.top += frameRect.top; + curRect.bottom += frameRect.top; + w = window.parent; + } + return curRect +} + var lastScrollLeft; var lastScrollTop; + window.onload = function() { + const test = async_test("DragAndDrop Autoscroll"); + test.add_cleanup(() => { + eventSender.mouseUp(); + }); + var draggable = $('draggable'); - var sample = $('sample'); var scrollBarWidth = $('scrollbars').offsetWidth - $('scrollbars').firstChild.offsetWidth; var scrollBarHeight = $('scrollbars').offsetHeight - $('scrollbars').firstChild.offsetHeight; - var eastX = window.innerWidth - scrollBarWidth - 10; - var northY = 10; - var southY= window.innerHeight - scrollBarHeight - 10; - var westX = 10; + + var scroller = $('scrollable'); + var scrollerRect; + if (scroller) { + scrollerRect = scroller.getBoundingClientRect(); + } else { + scroller = document.scrollingElement; + scrollerRect = { + left: 0, + top: 0, + right: window.innerWidth, + bottom: window.innerHeight, + }; + } + + scrollerRect = toRootWindow(scrollerRect); + + var eastX = scrollerRect.right - scrollBarWidth - 10; + var northY = scrollerRect.top + 10; + var southY= scrollerRect.bottom - scrollBarHeight - 10; + var westX = scrollerRect.left + 10; function moveTo(newState, x, y) { state = newState; - lastScrollLeft = document.scrollingElement.scrollLeft; - lastScrollTop = document.scrollingElement.scrollTop; + lastScrollLeft = scroller.scrollLeft; + lastScrollTop = scroller.scrollTop; eventSender.mouseMoveTo(x, y); } var state = 'START'; function process(event) { - debug('state=' + state); switch (state) { case 'NE': - shouldBeTrue('document.scrollingElement.scrollLeft > 0'); - shouldBeTrue('!document.scrollingElement.scrollTop'); - moveTo('SE', westX, southY); + test.step(() => { + assert_greater_than( + scroller.scrollLeft, + lastScrollLeft, + "NE should scroll right"); + assert_less_than( + scroller.scrollTop, + lastScrollTop, + "NE should scroll up"); + }); + moveTo('SE', eastX, southY); break; case 'SE': - shouldBeTrue('document.scrollingElement.scrollLeft > 0'); - shouldBeTrue('document.scrollingElement.scrollTop > 0'); + test.step(() => { + assert_greater_than( + scroller.scrollLeft, + lastScrollLeft, + "SE should scroll right"); + assert_greater_than( + scroller.scrollTop, + lastScrollTop, + "SE should scroll down"); + }); moveTo('SW', westX, southY); break; case 'SW': - shouldBeTrue('document.scrollingElement.scrollLeft < lastScrollLeft'); - shouldBeTrue('document.scrollingElement.scrollTop > 0'); + test.step(() => { + assert_less_than( + scroller.scrollLeft, + lastScrollLeft, + "SW should scroll left"); + assert_greater_than( + scroller.scrollTop, + lastScrollTop, + "SW should scroll down"); + }); moveTo('NW', westX, northY); break; case 'NW': - shouldBeTrue('document.scrollingElement.scrollLeft <= lastScrollLeft'); - shouldBeTrue('document.scrollingElement.scrollTop < lastScrollTop'); - eventSender.mouseUp(); - $('container').outerHTML = ''; - if (window.parent !== window) - window.jsTestIsAsync = false; - finishJSTest(); - if (!window.jsTestIsAsync) - window.parent.postMessage($('console').innerHTML, '*'); + test.step(() => { + assert_less_than( + scroller.scrollLeft, + lastScrollLeft, + "NW should scroll left"); + assert_less_than( + scroller.scrollTop, + lastScrollTop, + "NW should scroll up"); + }); + test.done(); state = 'DONE'; break; case 'DONE': @@ -58,20 +126,27 @@ moveTo('NE', eastX, northY); break; default: - testFailed('Bad state ' + state); + console.error('Bad state ' + state); break; } }; - if (!window.eventSender) + if (!window.eventSender) { + $("container").scrollIntoView(); return; + } + + if (scroller === document.scrollingElement) + window.addEventListener('scroll', process); + else + scroller.addEventListener('scroll', process); + + $("container").scrollIntoView(); eventSender.dragMode = false; // Grab draggable - eventSender.mouseMoveTo(draggable.offsetLeft + 5, draggable.offsetTop + 5); + const draggable_rect = toRootWindow(draggable.getBoundingClientRect()); + eventSender.mouseMoveTo(draggable_rect.left + 5, draggable_rect.top + 5); eventSender.mouseDown(); - - window.onscroll = process; - process(); };
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamTrack-applyConstraints.html b/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamTrack-applyConstraints.html index 50d35dd..66f08c38 100644 --- a/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamTrack-applyConstraints.html +++ b/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamTrack-applyConstraints.html
@@ -100,6 +100,8 @@ sampleRate: { ideal: 42, min: 31, max: 54 }, sampleSize: 3, echoCancellation: { ideal: false, exact: true }, + autoGainControl: { ideal: false, exact: true }, + noiseSuppression: { ideal: false, exact: true }, latency: 0.22, channelCount: 2, deviceId: { ideal: ["foo", "fooz"] }, @@ -174,11 +176,21 @@ constraintSyntaxTest('Both Ideal and Exact string', { facingMode: { ideal: "user6", exact: "left6" }}); -constraintSyntaxTest('Simple boolean', { echoCancellation: true }); -constraintSyntaxTest('Ideal boolean', { echoCancellation: { ideal: true }}); -constraintSyntaxTestWithChange('Exact unwrapped boolean', +constraintSyntaxTest('echoCancellation with simple boolean value', { echoCancellation: true }); +constraintSyntaxTest('echoCancellation with ideal boolean value', { echoCancellation: { ideal: true }}); +constraintSyntaxTestWithChange('echoCancellation with exact unwrapped boolean value', { echoCancellation: { exact: true } }, { echoCancellation: true }); +constraintSyntaxTest('autoGainControl with simple boolean value', { autoGainControl: true }); +constraintSyntaxTest('autoGainControl with ideal boolean value', { autoGainControl: { ideal: true }}); +constraintSyntaxTestWithChange('autoGainControl with exact unwrapped boolean value', + { autoGainControl: { exact: true } }, { autoGainControl: true }); + +constraintSyntaxTest('noiseSuppression with simple boolean value', { noiseSuppression: true }); +constraintSyntaxTest('noiseSuppression with ideal boolean value', { noiseSuppression: { ideal: true }}); +constraintSyntaxTestWithChange('noiseSuppression with exact unwrapped boolean value', + { noiseSuppression: { exact: true } }, { noiseSuppression: true }); + </script> </body> </html>
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamTrack-getConstraints.html b/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamTrack-getConstraints.html index 8b621fc..5a0bbea 100644 --- a/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamTrack-getConstraints.html +++ b/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamTrack-getConstraints.html
@@ -9,12 +9,16 @@ // If a constraint is specified, it should come back in getConstraints(). promise_test(function() { - return navigator.mediaDevices.getUserMedia({audio: { echoCancellation: { exact: true}}}) + return navigator.mediaDevices.getUserMedia({audio: { echoCancellation: { exact: true}, autoGainControl: { exact: true }, noiseSuppression: { exact: true }}}) .then(function(s) { constraints = s.getAudioTracks()[0].getConstraints(); - assert_equals(Object.keys(constraints).length, 1); + assert_equals(Object.keys(constraints).length, 3); assert_true(constraints.hasOwnProperty('echoCancellation')); assert_true(constraints.echoCancellation.exact); + assert_true(constraints.hasOwnProperty('autoGainControl')); + assert_true(constraints.autoGainControl.exact); + assert_true(constraints.hasOwnProperty('noiseSuppression')); + assert_true(constraints.noiseSuppression.exact); }); }, 'A set constraint is returned on getConstraints'); @@ -68,6 +72,8 @@ sampleRate: { ideal: 42 }, sampleSize: { ideal: 3 }, echoCancellation: { ideal: false }, + autoGainControl: { ideal: false }, + noiseSuppression: { ideal: false }, latency: { ideal: 0.22 }, channelCount: { ideal: 2 }, deviceId: { ideal: ["foo", "fooz"] }, @@ -131,11 +137,21 @@ { 'facingMode': ["user5"]}, { facingMode: "user5" }); constraintSyntaxTest('Both Ideal and Exact string', { facingMode: { ideal: "user6", exact: "left6" }}); -constraintSyntaxTest('Simple boolean', { echoCancellation: true }); -constraintSyntaxTest('Ideal boolean', { echoCancellation: { ideal: true }}); -constraintSyntaxTestWithChange('Exact unwrapped boolean', +constraintSyntaxTest('echoCancellation with simple boolean value', { echoCancellation: true }); +constraintSyntaxTest('echoCancellation with ideal boolean value', { echoCancellation: { ideal: true }}); +constraintSyntaxTestWithChange('echoCancellation with exact unwrapped boolean value', { echoCancellation: { exact: true } }, { echoCancellation: true }); +constraintSyntaxTest('autoGainControl with simple boolean value', { autoGainControl: true }); +constraintSyntaxTest('autoGainControl with ideal boolean value', { autoGainControl: { ideal: true }}); +constraintSyntaxTestWithChange('autoGainControl with exact unwrapped boolean value', + { autoGainControl: { exact: true } }, { autoGainControl: true }); + +constraintSyntaxTest('noiseSuppression with simple boolean value', { noiseSuppression: true }); +constraintSyntaxTest('noiseSuppression with ideal boolean value', { noiseSuppression: { ideal: true }}); +constraintSyntaxTestWithChange('noiseSuppression with exact unwrapped boolean value', + { noiseSuppression: { exact: true } }, { noiseSuppression: true }); + </script> </body> </html>
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/getusermedia-constraints.html b/third_party/WebKit/LayoutTests/fast/mediastream/getusermedia-constraints.html index eed851a..24ea86d 100644 --- a/third_party/WebKit/LayoutTests/fast/mediastream/getusermedia-constraints.html +++ b/third_party/WebKit/LayoutTests/fast/mediastream/getusermedia-constraints.html
@@ -91,13 +91,27 @@ {height: 47}); check_constraints_pass( - 'Constraint with boolean value should be parsed', + 'echoCancellation constraint with boolean value should be parsed', {'echoCancellation': {exact: true}}); check_constraints_pass( - 'Constraint with boolean naked value should be parsed', + 'echoCancellation constraint with boolean naked value should be parsed', {'echoCancellation': true}); check_constraints_pass( + 'autoGainControl constraint with boolean value should be parsed', + {'autoGainControl': {exact: true}}); +check_constraints_pass( + 'autoGainControl constraint with boolean naked value should be parsed', + {'autoGainControl': true}); + +check_constraints_pass( + 'noiseSuppression constraint with boolean value should be parsed', + {'noiseSuppression': {exact: true}}); +check_constraints_pass( + 'noiseSuppression constraint with boolean naked value should be parsed', + {'noiseSuppression': true}); + +check_constraints_pass( 'Constraint with string value should work on exact with array', {'facingMode': {ideal: ['user']}});
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-let-const-with-api-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-let-const-with-api-expected.txt index 834265c..db5ebb5 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-let-const-with-api-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-let-const-with-api-expected.txt
@@ -19,7 +19,7 @@ function copy(value) { [Command Line API] } function clear() { [Command Line API] } function getEventListeners(node) { [Command Line API] } -function debug(function) { [Command Line API] } +function debug(function, condition) { [Command Line API] } function undebug(function) { [Command Line API] } function monitor(function) { [Command Line API] } function unmonitor(function) { [Command Line API] }
diff --git a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt index 630a971..6e36eaf0 100644 --- a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt +++ b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -3631,7 +3631,6 @@ attribute @@toStringTag method constructor method decodingInfo - method encodingInfo interface MediaCapabilitiesInfo attribute @@toStringTag getter powerEfficient
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8BindingForCore.cpp b/third_party/WebKit/Source/bindings/core/v8/V8BindingForCore.cpp index 351c9d8d..e97ff466 100644 --- a/third_party/WebKit/Source/bindings/core/v8/V8BindingForCore.cpp +++ b/third_party/WebKit/Source/bindings/core/v8/V8BindingForCore.cpp
@@ -43,6 +43,7 @@ #include "bindings/core/v8/WindowProxy.h" #include "bindings/core/v8/WorkerOrWorkletScriptController.h" #include "bindings/core/v8/custom/V8CustomXPathNSResolver.h" +#include "build/build_config.h" #include "core/dom/Document.h" #include "core/dom/Element.h" #include "core/dom/QualifiedName.h" @@ -256,9 +257,14 @@ if (std::isinf(number_value)) return 0; - number_value = - number_value < 0 ? -floor(fabs(number_value)) : floor(fabs(number_value)); - return static_cast<T>(fmod(number_value, LimitsTrait::kNumberOfValues)); + // Confine number to (-kNumberOfValues, kNumberOfValues). + double number = fmod(trunc(number_value), LimitsTrait::kNumberOfValues); + + // Adjust range to [0, kNumberOfValues). + if (number < 0) + number += LimitsTrait::kNumberOfValues; + + return static_cast<T>(number); } int8_t ToInt8(v8::Isolate* isolate,
diff --git a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp index 5b70185..2209524 100644 --- a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp +++ b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
@@ -89,6 +89,10 @@ CSSParserTokenRange original_range = range_; CSSPropertyID property_id = resolveCSSPropertyID(unresolved_property); const CSSProperty& property = CSSProperty::Get(property_id); + // If a CSSPropertyID is only a known descriptor (@fontface, @viewport), not a + // style property, it will not be a valid declaration. + if (!property.IsProperty()) + return false; bool is_shorthand = property.IsShorthand(); DCHECK(context_); if (is_shorthand) {
diff --git a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.h b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.h index ae8e84d..c257f67 100644 --- a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.h +++ b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.h
@@ -39,7 +39,7 @@ // Inputs: PropertyID, isImportant bool, CSSParserTokenRange. // Outputs: Vector of CSSProperties -class CSSPropertyParser { +class CORE_EXPORT CSSPropertyParser { STACK_ALLOCATED(); public:
diff --git a/third_party/WebKit/Source/core/css/parser/CSSPropertyParserTest.cpp b/third_party/WebKit/Source/core/css/parser/CSSPropertyParserTest.cpp index b943647..9987353 100644 --- a/third_party/WebKit/Source/core/css/parser/CSSPropertyParserTest.cpp +++ b/third_party/WebKit/Source/core/css/parser/CSSPropertyParserTest.cpp
@@ -9,6 +9,7 @@ #include "core/css/CSSValueList.h" #include "core/css/StyleSheetContents.h" #include "core/css/parser/CSSParser.h" +#include "core/css/parser/CSSTokenizer.h" #include "core/html/HTMLHtmlElement.h" #include "core/testing/DummyPageHolder.h" #include "platform/testing/runtime_enabled_features_test_helpers.h" @@ -26,6 +27,18 @@ return number_of_tracks; } +static bool IsValidPropertyValueForStyleRule(CSSPropertyID property_id, + const String& value) { + CSSTokenizer tokenizer(value); + const auto tokens = tokenizer.TokenizeToEOF(); + const CSSParserTokenRange range(tokens); + HeapVector<CSSPropertyValue, 256> parsed_properties; + return CSSPropertyParser::ParseValue( + property_id, false, range, + StrictCSSParserContext(SecureContextMode::kSecureContext), + parsed_properties, StyleRule::RuleType::kStyle); +} + TEST(CSSPropertyParserTest, CSSPaint_Functions) { const CSSValue* value = CSSParser::ParseSingleValue( CSSPropertyBackgroundImage, "paint(foo, func1(1px, 3px), red)", @@ -424,4 +437,20 @@ EXPECT_TRUE(UseCounter::IsCounted(document, feature)); } +TEST(CSSPropertyParserTest, DropViewportDescriptor) { + EXPECT_FALSE( + IsValidPropertyValueForStyleRule(CSSPropertyOrientation, "portrait")); + EXPECT_FALSE( + IsValidPropertyValueForStyleRule(CSSPropertyOrientation, "inherit")); + EXPECT_FALSE( + IsValidPropertyValueForStyleRule(CSSPropertyOrientation, "var(--dummy)")); +} + +TEST(CSSPropertyParserTest, DropFontfaceDescriptor) { + EXPECT_FALSE(IsValidPropertyValueForStyleRule(CSSPropertySrc, "url(blah)")); + EXPECT_FALSE(IsValidPropertyValueForStyleRule(CSSPropertySrc, "inherit")); + EXPECT_FALSE( + IsValidPropertyValueForStyleRule(CSSPropertySrc, "var(--dummy)")); +} + } // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/Element.cpp b/third_party/WebKit/Source/core/dom/Element.cpp index 97f9d286..69ab855 100644 --- a/third_party/WebKit/Source/core/dom/Element.cpp +++ b/third_party/WebKit/Source/core/dom/Element.cpp
@@ -2550,8 +2550,10 @@ bool Element::CanAttachShadowRoot() const { const AtomicString& tag_name = localName(); - return IsV0CustomElement() || - GetCustomElementState() != CustomElementState::kUncustomized || + // Checking Is{V0}CustomElement() here is just an optimization + // because IsValidName is not cheap. + return (IsCustomElement() && CustomElement::IsValidName(tag_name)) || + (IsV0CustomElement() && V0CustomElement::IsValidName(tag_name)) || tag_name == HTMLNames::articleTag || tag_name == HTMLNames::asideTag || tag_name == HTMLNames::blockquoteTag || tag_name == HTMLNames::bodyTag || tag_name == HTMLNames::divTag || @@ -2616,7 +2618,6 @@ ShadowRoot& Element::AttachShadowRootInternal(ShadowRootType type, bool delegates_focus) { DCHECK(CanAttachShadowRoot()); - DCHECK(AreAuthorShadowsAllowed()); DCHECK(type == ShadowRootType::kOpen || type == ShadowRootType::kClosed) << type; DCHECK(!AlwaysCreateUserAgentShadowRoot());
diff --git a/third_party/WebKit/Source/core/dom/Node.h b/third_party/WebKit/Source/core/dom/Node.h index 0da43fb..d50de57 100644 --- a/third_party/WebKit/Source/core/dom/Node.h +++ b/third_party/WebKit/Source/core/dom/Node.h
@@ -265,6 +265,9 @@ return static_cast<CustomElementState>(node_flags_ & kCustomElementStateMask); } + bool IsCustomElement() const { + return GetCustomElementState() != CustomElementState::kUncustomized; + } void SetCustomElementState(CustomElementState); bool IsV0CustomElement() const { return GetFlag(kV0CustomElementFlag); } enum V0CustomElementState {
diff --git a/third_party/WebKit/Source/core/editing/VisibleUnits.cpp b/third_party/WebKit/Source/core/editing/VisibleUnits.cpp index 65a0cff..dee81e7 100644 --- a/third_party/WebKit/Source/core/editing/VisibleUnits.cpp +++ b/third_party/WebKit/Source/core/editing/VisibleUnits.cpp
@@ -255,11 +255,18 @@ if (HighestEditableRoot(pos.GetPosition()) == highest_root) return pos; - // Return empty position if this position is non-editable, but |pos| is - // editable. - // TODO(yosin) Move to the next non-editable region. - if (!highest_root) + // Returns the last position in the highest non-editable ancestor of |anchor|. + if (!highest_root) { + const Node* last_non_editable = anchor.ComputeContainerNode(); + for (const Node& ancestor : Strategy::AncestorsOf(*last_non_editable)) { + if (HasEditableStyle(ancestor)) { + return PositionWithAffinityTemplate<Strategy>( + PositionTemplate<Strategy>::LastPositionInNode(*last_non_editable)); + } + last_non_editable = &ancestor; + } return PositionWithAffinityTemplate<Strategy>(); + } // Return the next position after |pos| that is in the same editable region // as this position
diff --git a/third_party/WebKit/Source/core/editing/VisibleUnits.h b/third_party/WebKit/Source/core/editing/VisibleUnits.h index 5b37b80..fb95449 100644 --- a/third_party/WebKit/Source/core/editing/VisibleUnits.h +++ b/third_party/WebKit/Source/core/editing/VisibleUnits.h
@@ -304,8 +304,8 @@ PositionInFlatTree PreviousBoundary(const VisiblePositionInFlatTree&, BoundarySearchFunction); -PositionWithAffinity HonorEditingBoundaryAtOrAfter(const PositionWithAffinity&, - const Position&); +CORE_EXPORT PositionWithAffinity +HonorEditingBoundaryAtOrAfter(const PositionWithAffinity&, const Position&); PositionInFlatTreeWithAffinity HonorEditingBoundaryAtOrAfter( const PositionInFlatTreeWithAffinity&,
diff --git a/third_party/WebKit/Source/core/editing/VisibleUnitsTest.cpp b/third_party/WebKit/Source/core/editing/VisibleUnitsTest.cpp index b02af1e..74f2f89 100644 --- a/third_party/WebKit/Source/core/editing/VisibleUnitsTest.cpp +++ b/third_party/WebKit/Source/core/editing/VisibleUnitsTest.cpp
@@ -7,6 +7,7 @@ #include "bindings/core/v8/V8BindingForTesting.h" #include "core/dom/Text.h" #include "core/editing/PositionWithAffinity.h" +#include "core/editing/SelectionTemplate.h" #include "core/editing/VisiblePosition.h" #include "core/editing/testing/EditingTestBase.h" #include "core/html/forms/TextControlElement.h" @@ -222,6 +223,26 @@ .DeepEquivalent()); } +TEST_F(VisibleUnitsTest, HonorEditingBoundaryAtOrAfterNestedEditable) { + const SelectionInDOMTree& selection = SetSelectionTextToBody( + "<div contenteditable>" + "abc" + "<span contenteditable=\"false\">A^BC</span>" + "d|ef" + "</div>"); + const PositionWithAffinity& result = HonorEditingBoundaryAtOrAfter( + PositionWithAffinity(selection.Extent()), selection.Base()); + ASSERT_TRUE(result.IsNotNull()); + EXPECT_EQ( + "<div contenteditable>" + "abc" + "<span contenteditable=\"false\">ABC|</span>" + "def" + "</div>", + GetCaretTextFromBody(result.GetPosition())); + EXPECT_EQ(TextAffinity::kDownstream, result.Affinity()); +} + TEST_F(VisibleUnitsTest, isEndOfEditableOrNonEditableContent) { const char* body_content = "<a id=host><b id=one contenteditable>1</b><b id=two>22</b></a>";
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.cpp b/third_party/WebKit/Source/core/layout/LayoutBox.cpp index b1cd4606..f10cbb6 100644 --- a/third_party/WebKit/Source/core/layout/LayoutBox.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
@@ -1084,8 +1084,9 @@ return CanBeScrolledAndHasScrollableArea(); } -// If specified point is in border belt, returned offset denotes direction of -// scrolling. +// If specified point is outside the border-belt-excluded box (the border box +// inset by the autoscroll activation threshold), returned offset denotes +// direction of scrolling. IntSize LayoutBox::CalculateAutoscrollDirection( const IntPoint& point_in_root_frame) const { if (!GetFrame()) @@ -1095,34 +1096,36 @@ if (!frame_view) return IntSize(); - LayoutRect box(AbsoluteBoundingBoxRect()); - // TODO(bokan): This is wrong. Subtracting the scroll offset would get you to - // frame coordinates (pre-RLS) but *adding* the scroll offset to an absolute - // location never makes sense (and we assume below it's in content - // coordinates). - box.Move(View()->GetFrameView()->ScrollOffsetInt()); + LayoutRect absolute_scrolling_box; - // Exclude scrollbars so the border belt (activation area) starts from the - // scrollbar-content edge rather than the window edge. - ExcludeScrollbars(box, kExcludeOverlayScrollbarSizeForHitTesting); + if (!RuntimeEnabledFeatures::RootLayerScrollingEnabled() && IsLayoutView()) { + absolute_scrolling_box = + LayoutRect(frame_view->VisibleContentRect(kExcludeScrollbars)); + } else { + absolute_scrolling_box = LayoutRect(AbsoluteBoundingBoxRect()); - IntRect window_box = - View()->GetFrameView()->ContentsToRootFrame(PixelSnappedIntRect(box)); - IntPoint window_autoscroll_point = point_in_root_frame; + // Exclude scrollbars so the border belt (activation area) starts from the + // scrollbar-content edge rather than the window edge. + ExcludeScrollbars(absolute_scrolling_box, + kExcludeOverlayScrollbarSizeForHitTesting); + } - if (window_autoscroll_point.X() < window_box.X() + kAutoscrollBeltSize) - window_autoscroll_point.Move(-kAutoscrollBeltSize, 0); - else if (window_autoscroll_point.X() > - window_box.MaxX() - kAutoscrollBeltSize) - window_autoscroll_point.Move(kAutoscrollBeltSize, 0); + IntRect belt_box = View()->GetFrameView()->AbsoluteToRootFrame( + PixelSnappedIntRect(absolute_scrolling_box)); + belt_box.Inflate(-kAutoscrollBeltSize); + IntPoint point = point_in_root_frame; - if (window_autoscroll_point.Y() < window_box.Y() + kAutoscrollBeltSize) - window_autoscroll_point.Move(0, -kAutoscrollBeltSize); - else if (window_autoscroll_point.Y() > - window_box.MaxY() - kAutoscrollBeltSize) - window_autoscroll_point.Move(0, kAutoscrollBeltSize); + if (point.X() < belt_box.X()) + point.Move(-kAutoscrollBeltSize, 0); + else if (point.X() > belt_box.MaxX()) + point.Move(kAutoscrollBeltSize, 0); - return window_autoscroll_point - point_in_root_frame; + if (point.Y() < belt_box.Y()) + point.Move(0, -kAutoscrollBeltSize); + else if (point.Y() > belt_box.MaxY()) + point.Move(0, kAutoscrollBeltSize); + + return point - point_in_root_frame; } LayoutBox* LayoutBox::FindAutoscrollable(LayoutObject* layout_object) {
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.cpp index 8de96f29..035b600 100644 --- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.cpp +++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.cpp
@@ -35,6 +35,10 @@ LayoutSVGBlock::LayoutSVGBlock(SVGElement* element) : LayoutBlockFlow(element) {} +SVGElement* LayoutSVGBlock::GetElement() const { + return ToSVGElement(LayoutObject::GetNode()); +} + void LayoutSVGBlock::AbsoluteRects(Vector<IntRect>&, const LayoutPoint&) const { // This code path should never be taken for SVG, as we're assuming // useTransforms=true everywhere, absoluteQuads should be used.
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.h b/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.h index b5036a2..d34c6da 100644 --- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.h +++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.h
@@ -55,6 +55,8 @@ PaintLayerType LayerTypeRequired() const override { return kNoPaintLayer; } + SVGElement* GetElement() const; + protected: void WillBeDestroyed() override; bool MapToVisualRectInAncestorSpaceInternal( @@ -71,6 +73,9 @@ void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override; private: + // LayoutSVGBlock subclasses should use GetElement() instead. + void GetNode() const = delete; + LayoutRect AbsoluteVisualRect() const final; void AbsoluteRects(Vector<IntRect>&,
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGForeignObject.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGForeignObject.cpp index 688b250..5c5da2cc 100644 --- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGForeignObject.cpp +++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGForeignObject.cpp
@@ -48,26 +48,26 @@ LayoutUnit LayoutSVGForeignObject::ElementX() const { return LayoutUnit( - roundf(SVGLengthContext(ToSVGElement(GetNode())) + roundf(SVGLengthContext(GetElement()) .ValueForLength(StyleRef().SvgStyle().X(), StyleRef(), SVGLengthMode::kWidth))); } LayoutUnit LayoutSVGForeignObject::ElementY() const { return LayoutUnit( - roundf(SVGLengthContext(ToSVGElement(GetNode())) + roundf(SVGLengthContext(GetElement()) .ValueForLength(StyleRef().SvgStyle().Y(), StyleRef(), SVGLengthMode::kHeight))); } LayoutUnit LayoutSVGForeignObject::ElementWidth() const { - return LayoutUnit(SVGLengthContext(ToSVGElement(GetNode())) + return LayoutUnit(SVGLengthContext(GetElement()) .ValueForLength(StyleRef().Width(), StyleRef(), SVGLengthMode::kWidth)); } LayoutUnit LayoutSVGForeignObject::ElementHeight() const { - return LayoutUnit(SVGLengthContext(ToSVGElement(GetNode())) + return LayoutUnit(SVGLengthContext(GetElement()) .ValueForLength(StyleRef().Height(), StyleRef(), SVGLengthMode::kHeight)); } @@ -89,7 +89,7 @@ void LayoutSVGForeignObject::UpdateLayout() { DCHECK(NeedsLayout()); - SVGForeignObjectElement* foreign = ToSVGForeignObjectElement(GetNode()); + SVGForeignObjectElement* foreign = ToSVGForeignObjectElement(GetElement()); bool update_cached_boundaries_in_parents = false; if (needs_transform_update_) {
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGModelObject.h b/third_party/WebKit/Source/core/layout/svg/LayoutSVGModelObject.h index c8df58cf..976d01c 100644 --- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGModelObject.h +++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGModelObject.h
@@ -92,7 +92,7 @@ void WillBeDestroyed() override; private: - // LayoutSVGModelObject subclasses should use element() instead. + // LayoutSVGModelObject subclasses should use GetElement() instead. void GetNode() const = delete; // This method should never be called, SVG uses a different nodeAtPoint method
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGText.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGText.cpp index 29b63c3..06b25315 100644 --- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGText.cpp +++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGText.cpp
@@ -202,8 +202,8 @@ // the bounding box below, and after that we clear the // |needs_transform_update_| flag. if (needs_transform_update_) { - local_transform_ = ToSVGTextElement(GetNode())->CalculateTransform( - SVGElement::kIncludeMotionTransform); + local_transform_ = + GetElement()->CalculateTransform(SVGElement::kIncludeMotionTransform); } UpdateFontAndMetrics(*this); @@ -267,8 +267,8 @@ // changed too, since the transform could depend on the bounding // box. if (bounds_changed || needs_transform_update_) { - local_transform_ = ToSVGTextElement(GetNode())->CalculateTransform( - SVGElement::kIncludeMotionTransform); + local_transform_ = + GetElement()->CalculateTransform(SVGElement::kIncludeMotionTransform); needs_transform_update_ = false; update_parent_boundaries = true; } @@ -331,7 +331,7 @@ const LayoutPoint& local_layout_point = LayoutPoint(local_point); UpdateHitTestResult(result, local_layout_point); if (result.AddNodeToListBasedTestResult( - GetNode(), local_layout_point) == kStopHitTesting) + GetElement(), local_layout_point) == kStopHitTesting) return true; } } @@ -386,9 +386,8 @@ if (!svg_style.HasStroke()) return stroke_boundaries; - DCHECK(GetNode()); - DCHECK(GetNode()->IsSVGElement()); - SVGLengthContext length_context(ToSVGElement(GetNode())); + DCHECK(GetElement()); + SVGLengthContext length_context(GetElement()); stroke_boundaries.Inflate( length_context.ValueForLength(svg_style.StrokeWidth())); return stroke_boundaries;
diff --git a/third_party/WebKit/Source/core/streams/CommonOperations.js b/third_party/WebKit/Source/core/streams/CommonOperations.js index 319b132..e1cc679 100644 --- a/third_party/WebKit/Source/core/streams/CommonOperations.js +++ b/third_party/WebKit/Source/core/streams/CommonOperations.js
@@ -193,7 +193,7 @@ // Internal utility functions. Not exported. const callFunction = v8.uncurryThis(global.Function.prototype.call); const errTmplMustBeFunctionOrUndefined = name => - `${name} must be a function or undefined`; + `${name} must be a function or undefined`; const Promise_resolve = Promise.resolve.bind(Promise); const Promise_reject = Promise.reject.bind(Promise); const Function_bind = v8.uncurryThis(global.Function.prototype.bind); @@ -213,13 +213,14 @@ // assert(underlyingObject !== undefined, // 'underlyingObject is not undefined.'); // assert(IsPropertyKey(methodName), - // '! IsPropertyKey(methodName) is true.'); + // '! IsPropertyKey(methodName) is true.'); // assert(algoArgCount === 0 || algoArgCount === 1, - // 'algoArgCount is 0 or 1.'); - // assert(typeof methodNameForError === 'string', - // 'methodNameForError is a string'); - const method = resolveMethod(underlyingObject, methodName, - methodNameForError); + // 'algoArgCount is 0 or 1.'); + // assert( + // typeof methodNameForError === 'string', + // 'methodNameForError is a string'); + const method = + resolveMethod(underlyingObject, methodName, methodNameForError); // The implementation uses bound functions rather than lambdas where // possible to give the compiler the maximum opportunity to optimise. if (method === undefined) { @@ -234,27 +235,27 @@ } function CreateAlgorithmFromUnderlyingMethodPassingController( - underlyingObject, methodName, algoArgCount, controller, + underlyingObject, methodName, algoArgCount, controller, methodNameForError) { // assert(underlyingObject !== undefined, // 'underlyingObject is not undefined.'); // assert(IsPropertyKey(methodName), - // '! IsPropertyKey(methodName) is true.'); + // '! IsPropertyKey(methodName) is true.'); // assert(algoArgCount === 0 || algoArgCount === 1, - // 'algoArgCount is 0 or 1.'); - // assert(typeof controller === 'object' - // 'controller is an object'); - // assert(typeof methodNameForError === 'string', - // 'methodNameForError is a string'); - const method = resolveMethod(underlyingObject, methodName, - methodNameForError); + // 'algoArgCount is 0 or 1.'); + // assert(typeof controller === 'object', 'controller is an object'); + // assert( + // typeof methodNameForError === 'string', + // 'methodNameForError is a string'); + const method = + resolveMethod(underlyingObject, methodName, methodNameForError); if (method === undefined) { return () => Promise_resolve(); } if (algoArgCount === 0) { - return Function_bind(PromiseCall1, undefined, method, underlyingObject, - controller); + return Function_bind( + PromiseCall1, undefined, method, underlyingObject, controller); } return arg => PromiseCall2(method, underlyingObject, arg, controller); @@ -280,10 +281,8 @@ } function PromiseCall0(F, V) { - // assert(typeof F === 'function', - // 'IsCallable(F) is true.'); - // assert(V !== undefined, - // 'V is not undefined.'); + // assert(typeof F === 'function', 'IsCallable(F) is true.'); + // assert(V !== undefined, 'V is not undefined.'); try { return Promise_resolve(callFunction(F, V)); } catch (e) { @@ -292,10 +291,8 @@ } function PromiseCall1(F, V, arg0) { - // assert(typeof F === 'function', - // 'IsCallable(F) is true.'); - // assert(V !== undefined, - // 'V is not undefined.'); + // assert(typeof F === 'function', 'IsCallable(F) is true.'); + // assert(V !== undefined, 'V is not undefined.'); try { return Promise_resolve(callFunction(F, V, arg0)); } catch (e) { @@ -304,10 +301,8 @@ } function PromiseCall2(F, V, arg0, arg1) { - // assert(typeof F === 'function', - // 'IsCallable(F) is true.'); - // assert(V !== undefined, - // 'V is not undefined.'); + // assert(typeof F === 'function', 'IsCallable(F) is true.'); + // assert(V !== undefined, 'V is not undefined.'); try { return Promise_resolve(callFunction(F, V, arg0, arg1)); } catch (e) { @@ -334,5 +329,6 @@ MakeSizeAlgorithmFromSizeFunction, CallOrNoop1, PromiseCallOrNoop1, + PromiseCall2 }; });
diff --git a/third_party/WebKit/Source/core/streams/TransformStream.js b/third_party/WebKit/Source/core/streams/TransformStream.js index d76068e4..ea67c659 100644 --- a/third_party/WebKit/Source/core/streams/TransformStream.js +++ b/third_party/WebKit/Source/core/streams/TransformStream.js
@@ -16,25 +16,22 @@ const _backpressure = v8.createPrivateSymbol('[[backpressure]]'); const _backpressureChangePromise = - v8.createPrivateSymbol('[[backpressureChangePromise]]'); + v8.createPrivateSymbol('[[backpressureChangePromise]]'); const _readable = v8.createPrivateSymbol('[[readable]]'); - const _transformer = v8.createPrivateSymbol('[[transformer]]'); const _transformStreamController = - v8.createPrivateSymbol('[[transformStreamController]]'); + v8.createPrivateSymbol('[[transformStreamController]]'); const _writable = v8.createPrivateSymbol('[[writable]]'); const _controlledTransformStream = - v8.createPrivateSymbol('[[controlledTransformStream]]'); - const _ownerTransformStream = - v8.createPrivateSymbol('[[ownerTransformStream]]'); - const _startPromise = v8.createPrivateSymbol('[[startPromise]]'); + v8.createPrivateSymbol('[[controlledTransformStream]]'); + const _flushAlgorithm = v8.createPrivateSymbol('[[flushAlgorithm]]'); + const _transformAlgorithm = v8.createPrivateSymbol('[[transformAlgorithm]]'); // Javascript functions. It is important to use these copies, as the ones on // the global object may have been overwritten. See "V8 Extras Design Doc", // section "Security Considerations". // https://docs.google.com/document/d/1AT5-T0aHGp7Lt29vPWFr2-qG8r3l9CByyvKwEuA8Ec0/edit#heading=h.9yixony1a18r const defineProperty = global.Object.defineProperty; - - const Function_call = v8.uncurryThis(global.Function.prototype.call); + const ObjectCreate = global.Object.create; const TypeError = global.TypeError; const RangeError = global.RangeError; @@ -48,22 +45,20 @@ const { hasOwnPropertyNoThrow, resolvePromise, + CreateAlgorithmFromUnderlyingMethodPassingController, CallOrNoop1, - PromiseCallOrNoop1 + MakeSizeAlgorithmFromSizeFunction, + PromiseCall2, + ValidateAndNormalizeHighWaterMark } = binding.streamOperations; // User-visible strings. const streamErrors = binding.streamErrors; - const errWritableStreamAborted = 'The writable stream has been aborted'; const errStreamTerminated = 'The transform stream has been terminated'; - const templateErrorIsNotAFunction = f => `${f} is not a function`; class TransformStream { - constructor( - transformer = {}, writableStrategy = undefined, - {size, highWaterMark = 0} = {}) { - this[_transformer] = transformer; - + constructor(transformer = {}, + writableStrategy = {}, readableStrategy = {}) { // readable and writableType are extension points for future byte streams. const readableType = transformer.readableType; if (readableType !== undefined) { @@ -75,25 +70,34 @@ throw new RangeError(streamErrors.invalidType); } - this[_transformStreamController] = undefined; - const controller = new TransformStreamDefaultController(this); - this[_transformStreamController] = controller; + const writableSizeFunction = writableStrategy.size; + const writableSizeAlgorithm = + MakeSizeAlgorithmFromSizeFunction(writableSizeFunction); + let writableHighWaterMark = writableStrategy.highWaterMark; + if (writableHighWaterMark === undefined) { + writableHighWaterMark = 1; + } + writableHighWaterMark = + ValidateAndNormalizeHighWaterMark(writableHighWaterMark); + + const readableSizeFunction = readableStrategy.size; + const readableSizeAlgorithm = + MakeSizeAlgorithmFromSizeFunction(readableSizeFunction); + let readableHighWaterMark = readableStrategy.highWaterMark; + if (readableHighWaterMark === undefined) { + readableHighWaterMark = 0; + } + readableHighWaterMark = + ValidateAndNormalizeHighWaterMark(readableHighWaterMark); const startPromise = v8.createPromise(); - const source = new TransformStreamDefaultSource(this, startPromise); - const readableStrategy = {size, highWaterMark}; - this[_readable] = new binding.ReadableStream(source, readableStrategy); - - const sink = new TransformStreamDefaultSink(this, startPromise); - this[_writable] = new binding.WritableStream(sink, writableStrategy); - - // this[_backpressure] and this[_backpressureChangePromise]] are already - // undefined, so save a tiny amount of code by not setting them - // explicitly. - TransformStreamSetBackpressure(this, true); - - const startResult = CallOrNoop1(transformer, 'start', controller, - 'transformer.start'); + InitializeTransformStream( + this, startPromise, writableHighWaterMark, writableSizeAlgorithm, + readableHighWaterMark, readableSizeAlgorithm); + SetUpTransformStreamDefaultControllerFromTransformer(this, transformer); + const startResult = CallOrNoop1( + transformer, 'start', this[_transformStreamController], + 'transformer.start'); resolvePromise(startPromise, startResult); } @@ -114,12 +118,82 @@ } } + const TransformStream_prototype = TransformStream.prototype; + + function CreateTransformStream( + startAlgorithm, transformAlgorithm, flushAlgorithm, writableHighWaterMark, + writableSizeAlgorithm, readableHighWaterMark, readableSizeAlgorithm) { + if (writableHighWaterMark === undefined) { + writableHighWaterMark = 1; + } + if (writableSizeAlgorithm === undefined) { + writableSizeAlgorithm = () => 1; + } + if (readableHighWaterMark === undefined) { + readableHighWaterMark = 0; + } + if (readableSizeAlgorithm === undefined) { + readableSizeAlgorithm = () => 1; + } + // assert( + // typeof writableHighWaterMark === 'number' && + // writableHighWaterMark >= 0, + // '! IsNonNegativeNumber(_writableHighWaterMark_) is *true*'); + // assert( + // typeof readableHighWaterMark === 'number' && + // readableHighWaterMark >= 0, + // '! IsNonNegativeNumber(_readableHighWaterMark_) is true'); + const stream = ObjectCreate(TransformStream_prototype); + const startPromise = v8.createPromise(); + InitializeTransformStream( + stream, startPromise, writableHighWaterMark, writableSizeAlgorithm, + readableHighWaterMark, readableSizeAlgorithm); + const controller = ObjectCreate(TransformStreamDefaultController_prototype); + SetUpTransformStreamDefaultController( + stream, controller, transformAlgorithm, flushAlgorithm); + const startResult = startAlgorithm(); + resolvePromise(startPromise, startResult); + return stream; + } + + function InitializeTransformStream( + stream, startPromise, writableHighWaterMark, writableSizeAlgorithm, + readableHighWaterMark, readableSizeAlgorithm) { + const startAlgorithm = () => startPromise; + const writeAlgorithm = chunk => + TransformStreamDefaultSinkWriteAlgorithm(stream, chunk); + const abortAlgorithm = reason => + TransformStreamDefaultSinkAbortAlgorithm(stream, reason); + const closeAlgorithm = () => + TransformStreamDefaultSinkCloseAlgorithm(stream); + stream[_writable] = binding.CreateWritableStream( + startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, + writableHighWaterMark, writableSizeAlgorithm); + // TODO(ricea): Use CreateReadableStream() once it is implemented. + stream[_readable] = new binding.ReadableStream({ + start: startAlgorithm, + pull() { + return TransformStreamDefaultSourcePullAlgorithm(stream); + }, + cancel(reason) { + TransformStreamErrorWritableAndUnblockWrite(stream, reason); + return Promise_resolve(); + }, + type: undefined, + }, {highWaterMark: readableHighWaterMark, size: readableSizeAlgorithm}); + stream[_backpressure] = undefined; + stream[_backpressureChangePromise] = undefined; + TransformStreamSetBackpressure(stream, true); + stream[_transformStreamController] = undefined; + } + function IsTransformStream(x) { return hasOwnPropertyNoThrow(x, _transformStreamController); } function TransformStreamError(stream, e) { const readable = stream[_readable]; + // TODO(ricea): Remove this conditional once ReadableStream is updated. if (binding.IsReadableStreamReadable(readable)) { binding.ReadableStreamDefaultControllerError( binding.getReadableStreamController(readable), e); @@ -151,16 +225,8 @@ } class TransformStreamDefaultController { - constructor(stream) { - if (!IsTransformStream(stream)) { - throw new TypeError(streamErrors.illegalConstructor); - } - - if (stream[_transformStreamController] !== undefined) { - throw new TypeError(streamErrors.illegalConstructor); - } - - this[_controlledTransformStream] = stream; + constructor() { + throw new TypeError(streamErrors.illegalConstructor); } get desiredSize() { @@ -199,17 +265,68 @@ } } + const TransformStreamDefaultController_prototype = + TransformStreamDefaultController.prototype; + function IsTransformStreamDefaultController(x) { return hasOwnPropertyNoThrow(x, _controlledTransformStream); } + function SetUpTransformStreamDefaultController( + stream, controller, transformAlgorithm, flushAlgorithm) { + // assert( + // IsTransformStream(stream) === true, + // '! IsTransformStream(_stream_) is *true*'); + // assert( + // stream[_transformStreamController] === undefined, + // '_stream_.[[transformStreamController]] is *undefined*'); + controller[_controlledTransformStream] = stream; + stream[_transformStreamController] = controller; + controller[_transformAlgorithm] = transformAlgorithm; + controller[_flushAlgorithm] = flushAlgorithm; + } + + function SetUpTransformStreamDefaultControllerFromTransformer( + stream, transformer) { + // assert(transformer !== undefined, '_transformer_ is not *undefined*'); + const controller = ObjectCreate(TransformStreamDefaultController_prototype); + let transformAlgorithm; + const transformMethod = transformer.transform; + if (transformMethod !== undefined) { + if (typeof transformMethod !== 'function') { + throw new TypeError('transformer.transform is not a function'); + } + transformAlgorithm = chunk => { + const transformPromise = + PromiseCall2(transformMethod, transformer, chunk, controller); + return thenPromise(transformPromise, undefined, e => { + TransformStreamError(stream, e); + throw e; + }); + }; + } else { + transformAlgorithm = chunk => { + try { + TransformStreamDefaultControllerEnqueue(controller, chunk); + return Promise_resolve(); + } catch (resultValue) { + return Promise_reject(resultValue); + } + }; + } + const flushAlgorithm = CreateAlgorithmFromUnderlyingMethodPassingController( + transformer, 'flush', 0, controller, 'transformer.flush'); + SetUpTransformStreamDefaultController( + stream, controller, transformAlgorithm, flushAlgorithm); + } + function TransformStreamDefaultControllerEnqueue(controller, chunk) { const stream = controller[_controlledTransformStream]; const readableController = - binding.getReadableStreamController(stream[_readable]); + binding.getReadableStreamController(stream[_readable]); if (!binding.ReadableStreamDefaultControllerCanCloseOrEnqueue( - readableController)) { + readableController)) { throw binding.getReadableStreamEnqueueError(stream[_readable]); } @@ -235,10 +352,10 @@ function TransformStreamDefaultControllerTerminate(controller) { const stream = controller[_controlledTransformStream]; const readableController = - binding.getReadableStreamController(stream[_readable]); + binding.getReadableStreamController(stream[_readable]); if (binding.ReadableStreamDefaultControllerCanCloseOrEnqueue( - readableController)) { + readableController)) { binding.ReadableStreamDefaultControllerClose(readableController); } @@ -246,147 +363,72 @@ TransformStreamErrorWritableAndUnblockWrite(stream, error); } - class TransformStreamDefaultSink { - constructor(stream, startPromise) { - this[_ownerTransformStream] = stream; - this[_startPromise] = startPromise; - } - - start() { - const startPromise = this[_startPromise]; - // Permit GC of the promise. - this[_startPromise] = undefined; - return startPromise; - } - - write(chunk) { - const stream = this[_ownerTransformStream]; - // assert( - // binding.isWritableStreamWritable(stream[_writable]), - // `stream.[[writable]][[state]] is "writable"`); - - if (stream[_backpressure]) { - const backpressureChangePromise = stream[_backpressureChangePromise]; - // assert( - // backpressureChangePromise !== undefined, - // `backpressureChangePromise is not undefined`); - - return thenPromise(backpressureChangePromise, () => { - const writable = stream[_writable]; - if (binding.isWritableStreamErroring(writable)) { - throw binding.getWritableStreamStoredError(writable); - } - // assert( - // binding.isWritableStreamWritable(writable), - // `state is "writable"`); - - return TransformStreamDefaultSinkTransform(this, chunk); - }); - } - - return TransformStreamDefaultSinkTransform(this, chunk); - } - - abort() { - const e = new TypeError(errWritableStreamAborted); - TransformStreamError(this[_ownerTransformStream], e); - } - - close() { - const stream = this[_ownerTransformStream]; - const readable = stream[_readable]; - - const flushPromise = PromiseCallOrNoop1( - stream[_transformer], 'flush', stream[_transformStreamController], - 'transformer.flush'); - - return thenPromise( - flushPromise, - () => { - if (binding.IsReadableStreamErrored(readable)) { - throw binding.getReadableStreamStoredError(readable); - } - - const readableController = - binding.getReadableStreamController(readable); - if (binding.ReadableStreamDefaultControllerCanCloseOrEnqueue( - readableController)) { - binding.ReadableStreamDefaultControllerClose(readableController); - } - }, - r => { - TransformStreamError(stream, r); - throw binding.getReadableStreamStoredError(readable); - }); - } - } - - function TransformStreamDefaultSinkInvokeTransform(stream, chunk) { - const controller = stream[_transformStreamController]; - const transformer = stream[_transformer]; - const method = transformer.transform; - - if (method === undefined) { - TransformStreamDefaultControllerEnqueue(controller, chunk); - return undefined; - } - - if (typeof method !== 'function') { - throw new TypeError(templateErrorIsNotAFunction('transform')); - } - - return Function_call(method, transformer, chunk, controller); - } - - function TransformStreamDefaultSinkTransform(sink, chunk) { - const stream = sink[_ownerTransformStream]; + function TransformStreamDefaultSinkWriteAlgorithm(stream, chunk) { // assert( - // !binding.IsReadableStreamErrored(stream[_readable]), - // 'stream.[[readable]].[[state]] is not "errored"'); - // assert(!stream[_backpressure], 'stream.[[backpressure]] is false'); + // binding.isWritableStreamWritable(stream[_writable]), + // `stream.[[writable]][[state]] is "writable"`); - let transformPromise; - try { - transformPromise = Promise_resolve( - TransformStreamDefaultSinkInvokeTransform(stream, chunk)); - } catch (e) { - transformPromise = Promise_reject(e); + const controller = stream[_transformStreamController]; + + if (stream[_backpressure]) { + const backpressureChangePromise = stream[_backpressureChangePromise]; + // assert( + // backpressureChangePromise !== undefined, + // `backpressureChangePromise is not undefined`); + + return thenPromise(backpressureChangePromise, () => { + const writable = stream[_writable]; + if (binding.isWritableStreamErroring(writable)) { + throw binding.getWritableStreamStoredError(writable); + } + // assert(binding.isWritableStreamWritable(writable), + // `state is "writable"`); + + return controller[_transformAlgorithm](chunk); + }); } - return thenPromise(transformPromise, undefined, e => { - TransformStreamError(stream, e); - throw e; - }); + return controller[_transformAlgorithm](chunk); } - class TransformStreamDefaultSource { - constructor(stream, startPromise) { - this[_ownerTransformStream] = stream; - this[_startPromise] = startPromise; - } + function TransformStreamDefaultSinkAbortAlgorithm(stream, reason) { + TransformStreamError(stream, reason); + return Promise_resolve(); + } - start() { - const startPromise = this[_startPromise]; - // Permit GC of the promise. - this[_startPromise] = undefined; - return startPromise; - } + function TransformStreamDefaultSinkCloseAlgorithm(stream) { + const readable = stream[_readable]; - pull() { - const stream = this[_ownerTransformStream]; - // assert(stream[_backpressure], 'stream.[[backpressure]] is true'); - // assert( - // stream[_backpressureChangePromise] !== undefined, - // 'stream.[[backpressureChangePromise]] is not undefined'); + const flushPromise = stream[_transformStreamController][_flushAlgorithm](); - TransformStreamSetBackpressure(stream, false); - return stream[_backpressureChangePromise]; - } + return thenPromise( + flushPromise, + () => { + if (binding.IsReadableStreamErrored(readable)) { + throw binding.getReadableStreamStoredError(readable); + } - cancel(reason) { - TransformStreamErrorWritableAndUnblockWrite( - this[_ownerTransformStream], reason); - } + const readableController = + binding.getReadableStreamController(readable); + if (binding.ReadableStreamDefaultControllerCanCloseOrEnqueue( + readableController)) { + binding.ReadableStreamDefaultControllerClose(readableController); + } + }, + r => { + TransformStreamError(stream, r); + throw binding.getReadableStreamStoredError(readable); + }); + } + + function TransformStreamDefaultSourcePullAlgorithm(stream) { + // assert(stream[_backpressure], 'stream.[[backpressure]] is true'); + // assert( + // stream[_backpressureChangePromise] !== undefined, + // 'stream.[[backpressureChangePromise]] is not undefined'); + + TransformStreamSetBackpressure(stream, false); + return stream[_backpressureChangePromise]; } // @@ -399,4 +441,9 @@ configurable: true, writable: true }); + + // + // Exports to Blink + // + binding.CreateTransformStream = CreateTransformStream; });
diff --git a/third_party/WebKit/Source/modules/cachestorage/Cache.cpp b/third_party/WebKit/Source/modules/cachestorage/Cache.cpp index 10ce253..b7897ef3 100644 --- a/third_party/WebKit/Source/modules/cachestorage/Cache.cpp +++ b/third_party/WebKit/Source/modules/cachestorage/Cache.cpp
@@ -181,7 +181,7 @@ static_cast<int>(network::mojom::FetchResponseType::kLast) + 1)); response_type_histogram.Count( static_cast<int>(response->GetResponse()->GetType())); -}; +} bool VaryHeaderContainsAsterisk(const Response* response) { const FetchHeaderList* headers = response->headers()->HeaderList(); @@ -309,16 +309,17 @@ return; completed_ = true; ScriptState* state = resolver_->GetScriptState(); - RejectWithState(state, V8ThrowException::CreateTypeError( - state->GetIsolate(), error_message)); + ScriptState::Scope scope(state); + resolver_->Reject( + V8ThrowException::CreateTypeError(state->GetIsolate(), error_message)); } void Abort() { if (!StillActive()) return; completed_ = true; - RejectWithState(resolver_->GetScriptState(), - DOMException::Create(kAbortError)); + ScriptState::Scope scope(resolver_->GetScriptState()); + resolver_->Reject(DOMException::Create(kAbortError)); } virtual void Trace(blink::Visitor* visitor) { @@ -337,12 +338,6 @@ return true; } - template <typename T> - void RejectWithState(ScriptState* state, T value) { - ScriptState::Scope scope(state); - resolver_->Reject(value); - } - // Report the script stats if this cache storage is for service worker // execution context and it's in installation phase. void MaybeReportInstalledScripts() {
diff --git a/third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.idl b/third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.idl index cacb1758..766714c6 100644 --- a/third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.idl +++ b/third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.idl
@@ -4,11 +4,9 @@ // https://wicg.github.io/media-capabilities/#mediacapabilities -[ - Exposed=Window, - OriginTrialEnabled=MediaCapabilities -] interface MediaCapabilities { - [CallWith=ScriptState, Measure] Promise<MediaCapabilitiesInfo> decodingInfo(MediaDecodingConfiguration configuration); - [CallWith=ScriptState, Measure] Promise<MediaCapabilitiesInfo> encodingInfo( +[Exposed=Window] +interface MediaCapabilities { + [CallWith=ScriptState, Measure, OriginTrialEnabled=MediaCapabilities] Promise<MediaCapabilitiesInfo> decodingInfo(MediaDecodingConfiguration configuration); + [CallWith=ScriptState, Measure, RuntimeEnabled=MediaCapabilitiesEncodingInfo] Promise<MediaCapabilitiesInfo> encodingInfo( MediaEncodingConfiguration configuration); };
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaConstraintsImpl.cpp b/third_party/WebKit/Source/modules/mediastream/MediaConstraintsImpl.cpp index 49c531a..5effb4c 100644 --- a/third_party/WebKit/Source/modules/mediastream/MediaConstraintsImpl.cpp +++ b/third_party/WebKit/Source/modules/mediastream/MediaConstraintsImpl.cpp
@@ -677,6 +677,14 @@ CopyBooleanConstraint(constraints_in.echoCancellation(), naked_treatment, constraint_buffer.echo_cancellation); } + if (constraints_in.hasAutoGainControl()) { + CopyBooleanConstraint(constraints_in.autoGainControl(), naked_treatment, + constraint_buffer.goog_auto_gain_control); + } + if (constraints_in.hasNoiseSuppression()) { + CopyBooleanConstraint(constraints_in.noiseSuppression(), naked_treatment, + constraint_buffer.goog_noise_suppression); + } if (constraints_in.hasLatency()) { CopyDoubleConstraint(constraints_in.latency(), naked_treatment, constraint_buffer.latency); @@ -931,6 +939,14 @@ output.setEchoCancellation( ConvertBoolean(input.echo_cancellation, naked_treatment)); } + if (!input.goog_auto_gain_control.IsEmpty()) { + output.setAutoGainControl( + ConvertBoolean(input.goog_auto_gain_control, naked_treatment)); + } + if (!input.goog_noise_suppression.IsEmpty()) { + output.setNoiseSuppression( + ConvertBoolean(input.goog_noise_suppression, naked_treatment)); + } if (!input.latency.IsEmpty()) output.setLatency(ConvertDouble(input.latency, naked_treatment)); if (!input.channel_count.IsEmpty())
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaTrackConstraintSet.idl b/third_party/WebKit/Source/modules/mediastream/MediaTrackConstraintSet.idl index eb1d7c5..cdda26e 100644 --- a/third_party/WebKit/Source/modules/mediastream/MediaTrackConstraintSet.idl +++ b/third_party/WebKit/Source/modules/mediastream/MediaTrackConstraintSet.idl
@@ -20,6 +20,8 @@ ConstrainLong sampleRate; ConstrainLong sampleSize; ConstrainBoolean echoCancellation; + ConstrainBoolean autoGainControl; + ConstrainBoolean noiseSuppression; ConstrainDouble latency; ConstrainLong channelCount; ConstrainDOMString deviceId;
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn index b296877..14477eb 100644 --- a/third_party/WebKit/Source/platform/BUILD.gn +++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -1914,6 +1914,7 @@ "text/PlatformLocaleTest.cpp", "text/SegmentedStringTest.cpp", "text/SuffixTreeTest.cpp", + "text/TextBoundariesTest.cpp", "text/TextBreakIteratorTest.cpp", "text/TextEncodingDetectorTest.cpp", "text/TextRunTest.cpp",
diff --git a/third_party/WebKit/Source/platform/runtime_enabled_features.json5 b/third_party/WebKit/Source/platform/runtime_enabled_features.json5 index 50ad645b..e8477b7 100644 --- a/third_party/WebKit/Source/platform/runtime_enabled_features.json5 +++ b/third_party/WebKit/Source/platform/runtime_enabled_features.json5
@@ -624,6 +624,10 @@ status: "stable", }, { + name: "MediaCapabilitiesEncodingInfo", + status: "experimental", + }, + { name: "MediaCapture", }, {
diff --git a/third_party/WebKit/Source/platform/text/TextBoundaries.cpp b/third_party/WebKit/Source/platform/text/TextBoundaries.cpp index d3a2046..d9a35696 100644 --- a/third_party/WebKit/Source/platform/text/TextBoundaries.cpp +++ b/third_party/WebKit/Source/platform/text/TextBoundaries.cpp
@@ -89,6 +89,38 @@ return 0; } +std::pair<int, int> FindWordBackward(const UChar* chars, + int len, + int position) { + DCHECK_GE(len, 0); + DCHECK_LE(position, len); + if (len == 0) + return {0, 0}; + TextBreakIterator* it = WordBreakIterator(chars, len); + const int start = it->preceding(position); + const int end = it->next(); + if (start < 0) { + // There are no words at |position|. + return {0, 0}; + } + return {start, end}; +} + +std::pair<int, int> FindWordForward(const UChar* chars, int len, int position) { + DCHECK_GE(len, 0); + DCHECK_LE(position, len); + if (len == 0) + return {0, 0}; + TextBreakIterator* it = WordBreakIterator(chars, len); + const int end = it->following(position); + const int start = it->previous(); + if (end < 0) { + // There are no words at |position|. + return {len, len}; + } + return {start, end}; +} + int FindWordStartBoundary(const UChar* chars, int len, int position) { TextBreakIterator* it = WordBreakIterator(chars, len); it->following(position);
diff --git a/third_party/WebKit/Source/platform/text/TextBoundaries.h b/third_party/WebKit/Source/platform/text/TextBoundaries.h index d699d4e..267ef239 100644 --- a/third_party/WebKit/Source/platform/text/TextBoundaries.h +++ b/third_party/WebKit/Source/platform/text/TextBoundaries.h
@@ -26,6 +26,8 @@ #ifndef TextBoundaries_h #define TextBoundaries_h +#include <utility> + #include "platform/PlatformExport.h" #include "platform/wtf/text/Unicode.h" @@ -42,6 +44,12 @@ // |UChar*| should be a string in logical order instead of visual order, since // |FindWordBoundary()| uses ICU, which works on logical order strings +PLATFORM_EXPORT std::pair<int, int> FindWordBackward(const UChar*, + int len, + int position); +PLATFORM_EXPORT std::pair<int, int> FindWordForward(const UChar*, + int len, + int position); PLATFORM_EXPORT int FindWordStartBoundary(const UChar*, int len, int position); PLATFORM_EXPORT int FindWordEndBoundary(const UChar*, int len, int position); PLATFORM_EXPORT int FindNextWordBackward(const UChar*, int len, int position);
diff --git a/third_party/WebKit/Source/platform/text/TextBoundariesTest.cpp b/third_party/WebKit/Source/platform/text/TextBoundariesTest.cpp new file mode 100644 index 0000000..88af1b9b --- /dev/null +++ b/third_party/WebKit/Source/platform/text/TextBoundariesTest.cpp
@@ -0,0 +1,175 @@ +// 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. + +#include <string> + +#include "platform/text/TextBoundaries.h" + +#include "platform/wtf/text/StringBuilder.h" +#include "platform/wtf/text/WTFString.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace blink { + +class TextBoundariesTest : public ::testing::Test {}; + +namespace { + +std::pair<String, int> ParsePositionMarker(const std::string input8) { + String input16 = String::FromUTF8(input8.data(), input8.size()); + input16.Ensure16Bit(); + const size_t position = input16.find('|'); + DCHECK(position != kNotFound) << input8 << " should have position marker(|)."; + String text16 = input16.Left(position); + text16.append(input16.Substring(position + 1)); + text16.Ensure16Bit(); + return {text16, position}; +} + +std::string MakeResultText(const String& text, int start, int end) { + StringBuilder builder; + if (start < 0 && end < 0) { + builder.Append(text); + } else if (start < 0) { + builder.Append(text.Left(end)); + builder.Append('^'); + builder.Append(text.Substring(end)); + } else if (end < 0) { + builder.Append(text.Left(start)); + builder.Append('|'); + builder.Append(text.Substring(start)); + } else { + builder.Append(text.Left(start)); + builder.Append('^'); + builder.Append(text.Substring(start, end - start)); + builder.Append('|'); + } + builder.Append(text.Substring(end)); + const CString result8 = builder.ToString().Utf8(); + return std::string(result8.data(), result8.length()); +} + +// Returns word boundray with start(^) and end(|) markes from text with +// position(|) marker. +std::string TryFindWordBackward(const std::string input8) { + std::pair<String, int> string_and_offset = ParsePositionMarker(input8); + const String text16 = string_and_offset.first; + const int position = string_and_offset.second; + std::pair<int, int> start_and_end = + FindWordBackward(text16.Characters16(), text16.length(), position); + return MakeResultText(text16, start_and_end.first, start_and_end.second); +} + +// Returns word boundray with start(^) and end(|) markes from text with +// position(|) marker. +std::string TryFindWordForward(const std::string input8) { + std::pair<String, int> string_and_offset = ParsePositionMarker(input8); + const String text16 = string_and_offset.first; + const int position = string_and_offset.second; + std::pair<int, int> start_and_end = + FindWordForward(text16.Characters16(), text16.length(), position); + return MakeResultText(text16, start_and_end.first, start_and_end.second); +} + +} // namespace + +TEST_F(TextBoundariesTest, BackwardBasic) { + EXPECT_EQ("^|abc def", TryFindWordBackward("|abc def")); + EXPECT_EQ("^abc| def", TryFindWordBackward("a|bc def")); + EXPECT_EQ("^abc| def", TryFindWordBackward("ab|c def")); + EXPECT_EQ("^abc| def", TryFindWordBackward("abc| def")); + EXPECT_EQ("abc^ |def", TryFindWordBackward("abc |def")); + EXPECT_EQ("abc ^def|", TryFindWordBackward("abc d|ef")); + EXPECT_EQ("abc ^def|", TryFindWordBackward("abc de|f")); + EXPECT_EQ("abc ^def|", TryFindWordBackward("abc def|")); +} + +TEST_F(TextBoundariesTest, ForwardBasic) { + EXPECT_EQ("^abc| def", TryFindWordForward("|abc def")); + EXPECT_EQ("^abc| def", TryFindWordForward("a|bc def")); + EXPECT_EQ("^abc| def", TryFindWordForward("ab|c def")); + EXPECT_EQ("abc^ |def", TryFindWordForward("abc| def")); + EXPECT_EQ("abc ^def|", TryFindWordForward("abc |def")); + EXPECT_EQ("abc ^def|", TryFindWordForward("abc d|ef")); + EXPECT_EQ("abc ^def|", TryFindWordForward("abc de|f")); + EXPECT_EQ("abc def^|", TryFindWordForward("abc def|")); +} + +TEST_F(TextBoundariesTest, ForwardBiDi) { + EXPECT_EQ(u8"^\u0620\u0621\u0622| \u0623\u0624\u0625", + TryFindWordForward(u8"|\u0620\u0621\u0622 \u0623\u0624\u0625")); + EXPECT_EQ(u8"^\u0620\u0621\u0622| \u0623\u0624\u0625", + TryFindWordForward(u8"\u0620|\u0621\u0622 \u0623\u0624\u0625")); + EXPECT_EQ(u8"^\u0620\u0621\u0622| \u0623\u0624\u0625", + TryFindWordForward(u8"\u0620\u0621|\u0622 \u0623\u0624\u0625")); + EXPECT_EQ(u8"\u0620\u0621\u0622^ |\u0623\u0624\u0625", + TryFindWordForward(u8"\u0620\u0621\u0622| \u0623\u0624\u0625")); + EXPECT_EQ(u8"\u0620\u0621\u0622 ^\u0623\u0624\u0625|", + TryFindWordForward(u8"\u0620\u0621\u0622 |\u0623\u0624\u0625")); + EXPECT_EQ(u8"\u0620\u0621\u0622 \u0623\u0624\u0625^|", + TryFindWordForward(u8"\u0620\u0621\u0622 \u0623\u0624\u0625|")); +} + +TEST_F(TextBoundariesTest, ForwardBiDiMixed) { + EXPECT_EQ(u8"^abc\u0620\u0621\u0622|", + TryFindWordForward(u8"|abc\u0620\u0621\u0622")); + EXPECT_EQ(u8"^abc\u0620\u0621\u0622|", + TryFindWordForward(u8"ab|c\u0620\u0621\u0622")); + EXPECT_EQ(u8"^abc\u0620\u0621\u0622|", + TryFindWordForward(u8"abc|\u0620\u0621\u0622")) + << "At L1/L2 boundary"; + EXPECT_EQ(u8"^abc\u0620\u0621\u0622|", + TryFindWordForward(u8"abc\u0620|\u0621\u0622")); + + EXPECT_EQ(u8"^\u0620\u0621\u0622xyz|", + TryFindWordForward(u8"|\u0620\u0621\u0622xyz")); + EXPECT_EQ(u8"^\u0620\u0621\u0622xyz|", + TryFindWordForward(u8"\u0620|\u0621\u0622xyz")); + EXPECT_EQ(u8"^\u0620\u0621\u0622xyz|", + TryFindWordForward(u8"\u0620\u0621\u0622|xyz")) + << "At L2/L1 boundary"; + EXPECT_EQ(u8"^\u0620\u0621\u0622xyz|", + TryFindWordForward(u8"\u0620\u0621\u0622xy|z")); +} + +TEST_F(TextBoundariesTest, ForwardOne) { + EXPECT_EQ("^a|", TryFindWordForward("|a")); + EXPECT_EQ("a^|", TryFindWordForward("a|")) << "No word after |"; +} + +TEST_F(TextBoundariesTest, ForwardParenthesis) { + EXPECT_EQ("^(|abc)", TryFindWordForward("|(abc)")); + EXPECT_EQ("(^abc|)", TryFindWordForward("(|abc)")); + EXPECT_EQ("(^abc|)", TryFindWordForward("(a|bc)")); + EXPECT_EQ("(^abc|)", TryFindWordForward("(ab|c)")); + EXPECT_EQ("(abc)^|", TryFindWordForward("(abc)|")) << "No word after |"; +} + +TEST_F(TextBoundariesTest, ForwardPunctuations) { + EXPECT_EQ("^abc|,,", TryFindWordForward("|abc,,")); + EXPECT_EQ("abc^,|,", TryFindWordForward("abc|,,")); +} + +TEST_F(TextBoundariesTest, ForwardWhitespaces) { + EXPECT_EQ("^ | abc def ", TryFindWordForward("| abc def ")); + EXPECT_EQ(" ^ |abc def ", TryFindWordForward(" | abc def ")); + EXPECT_EQ(" ^abc| def ", TryFindWordForward(" |abc def ")); + EXPECT_EQ(" ^abc| def ", TryFindWordForward(" a|bc def ")); + EXPECT_EQ(" ^abc| def ", TryFindWordForward(" ab|c def ")); + EXPECT_EQ(" abc^ | def ", TryFindWordForward(" abc| def ")); + EXPECT_EQ(" abc ^ |def ", TryFindWordForward(" abc | def ")); + EXPECT_EQ(" abc ^def| ", TryFindWordForward(" abc |def ")); + EXPECT_EQ(" abc ^def| ", TryFindWordForward(" abc d|ef ")); + EXPECT_EQ(" abc ^def| ", TryFindWordForward(" abc de|f ")); + EXPECT_EQ(" abc def^ | ", TryFindWordForward(" abc def| ")); + EXPECT_EQ(" abc def ^ |", TryFindWordForward(" abc def | ")); + EXPECT_EQ(" abc def ^|", TryFindWordForward(" abc def |")) + << "No word after |"; +} + +TEST_F(TextBoundariesTest, ForwardZero) { + EXPECT_EQ("^|", TryFindWordForward("|")); +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/platform/wtf/HashTable.cpp b/third_party/WebKit/Source/platform/wtf/HashTable.cpp index 0d4ce59..ec51b67a 100644 --- a/third_party/WebKit/Source/platform/wtf/HashTable.cpp +++ b/third_party/WebKit/Source/platform/wtf/HashTable.cpp
@@ -49,25 +49,32 @@ void HashTableStats::recordCollisionAtCount(int count) { // The global hash table singleton needs to be atomically updated. - bool isGlobalSingleton = this == &instance(); - if (isGlobalSingleton) - hashTableStatsMutex().lock(); + if (this == &instance()) { + MutexLocker locker(hashTableStatsMutex()); + RecordCollisionAtCountWithoutLock(count); + } else { + RecordCollisionAtCountWithoutLock(count); + } +} +void HashTableStats::RecordCollisionAtCountWithoutLock(int count) { if (count > maxCollisions) maxCollisions = count; numCollisions++; collisionGraph[count]++; - - if (isGlobalSingleton) - hashTableStatsMutex().unlock(); } void HashTableStats::DumpStats() { // Lock the global hash table singleton while dumping. - bool isGlobalSingleton = this == &instance(); - if (isGlobalSingleton) - hashTableStatsMutex().lock(); + if (this == &instance()) { + MutexLocker locker(hashTableStatsMutex()); + DumpStatsWithoutLock(); + } else { + DumpStatsWithoutLock(); + } +} +void HashTableStats::DumpStatsWithoutLock() { DeprecatedDataLogF("\nWTF::HashTable statistics\n\n"); DeprecatedDataLogF("%d accesses\n", numAccesses); DeprecatedDataLogF("%d total collisions, average %.2f probes per access\n", @@ -84,9 +91,6 @@ } DeprecatedDataLogF("%d rehashes\n", numRehashes); DeprecatedDataLogF("%d reinserts\n", numReinserts); - - if (isGlobalSingleton) - hashTableStatsMutex().unlock(); } } // namespace WTF
diff --git a/third_party/WebKit/Source/platform/wtf/HashTable.h b/third_party/WebKit/Source/platform/wtf/HashTable.h index 998a5c8..08e1fa2 100644 --- a/third_party/WebKit/Source/platform/wtf/HashTable.h +++ b/third_party/WebKit/Source/platform/wtf/HashTable.h
@@ -136,6 +136,10 @@ template <typename VisitorDispatcher> void trace(VisitorDispatcher) {} + + private: + void RecordCollisionAtCountWithoutLock(int count); + void DumpStatsWithoutLock(); }; #if DUMP_HASHTABLE_STATS_PER_TABLE
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium index 93e01fa..7a969b8 100644 --- a/third_party/crashpad/README.chromium +++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@ Short Name: crashpad URL: https://crashpad.chromium.org/ Version: unknown -Revision: c27a1aaea0861852c6d92945b68856586e0cd51d +Revision: 6d4626090db24cc4c05946c3b43962596e522d9a License: Apache 2.0 License File: crashpad/LICENSE Security Critical: yes
diff --git a/third_party/crashpad/crashpad/DEPS b/third_party/crashpad/crashpad/DEPS index d118f600..94847ae 100644 --- a/third_party/crashpad/crashpad/DEPS +++ b/third_party/crashpad/crashpad/DEPS
@@ -29,7 +29,7 @@ '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - 'ef0df1119b40cfa2773d5960e239d4b960310869', + 'd42eb410123667fe94427986d2616795897efa0c', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7',
diff --git a/third_party/crashpad/crashpad/client/crashpad_client.h b/third_party/crashpad/crashpad/client/crashpad_client.h index 3cd806a1..267a6e20 100644 --- a/third_party/crashpad/crashpad/client/crashpad_client.h +++ b/third_party/crashpad/crashpad/client/crashpad_client.h
@@ -132,7 +132,7 @@ //! specified in this parameter. //! //! \return `true` on success, `false` on failure with a message logged. - bool StartHandlerAtCrash( + static bool StartHandlerAtCrash( const base::FilePath& handler, const base::FilePath& database, const base::FilePath& metrics_dir, @@ -164,7 +164,7 @@ //! be used with an ExceptionHandlerClient. //! //! \return `true` on success, `false` on failure with a message logged. - bool StartHandlerForClient( + static bool StartHandlerForClient( const base::FilePath& handler, const base::FilePath& database, const base::FilePath& metrics_dir,
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_linux.cc b/third_party/crashpad/crashpad/client/crashpad_client_linux.cc index ca13c29..22f7e3d8 100644 --- a/third_party/crashpad/crashpad/client/crashpad_client_linux.cc +++ b/third_party/crashpad/crashpad/client/crashpad_client_linux.cc
@@ -215,6 +215,7 @@ return false; } +// static bool CrashpadClient::StartHandlerAtCrash( const base::FilePath& handler, const base::FilePath& database, @@ -235,6 +236,7 @@ return false; } +// static bool CrashpadClient::StartHandlerForClient( const base::FilePath& handler, const base::FilePath& database,
diff --git a/third_party/crashpad/crashpad/client/crashpad_info_note.S b/third_party/crashpad/crashpad/client/crashpad_info_note.S index 8084db9..4c29298db 100644 --- a/third_party/crashpad/crashpad/client/crashpad_info_note.S +++ b/third_party/crashpad/crashpad/client/crashpad_info_note.S
@@ -16,7 +16,6 @@ // of finding the instance of CrashpadInfo g_crashpad_info without requiring // that symbol to be in the dynamic symbol table. -#include "build/build_config.h" #include "util/misc/elf_note_types.h" // namespace crashpad { @@ -45,16 +44,16 @@ name_end: .balign NOTE_ALIGN desc: -#if defined(ARCH_CPU_64_BITS) +#if defined(__LP64__) .quad CRASHPAD_INFO_SYMBOL #else -#if defined(ARCH_CPU_LITTLE_ENDIAN) +#if defined(__LITTLE_ENDIAN__) .long CRASHPAD_INFO_SYMBOL .long 0 #else .long 0 .long CRASHPAD_INFO_SYMBOL -#endif // ARCH_CPU_LITTLE_ENDIAN -#endif // ARCH_CPU_64_BITS +#endif // __LITTLE_ENDIAN__ +#endif // __LP64__ desc_end: .size CRASHPAD_NOTE_REFERENCE, .-CRASHPAD_NOTE_REFERENCE
diff --git a/third_party/crashpad/crashpad/handler/linux/exception_handler_server.cc b/third_party/crashpad/crashpad/handler/linux/exception_handler_server.cc index 6bb55cd..04f561a 100644 --- a/third_party/crashpad/crashpad/handler/linux/exception_handler_server.cc +++ b/third_party/crashpad/crashpad/handler/linux/exception_handler_server.cc
@@ -348,7 +348,7 @@ msg.msg_controllen = sizeof(cmsg_buf); msg.msg_flags = 0; - int res = recvmsg(event->fd.get(), &msg, 0); + int res = HANDLE_EINTR(recvmsg(event->fd.get(), &msg, 0)); if (res < 0) { PLOG(ERROR) << "recvmsg"; return false;
diff --git a/third_party/crashpad/crashpad/snapshot/crashpad_info_size_test_note.S b/third_party/crashpad/crashpad/snapshot/crashpad_info_size_test_note.S index a355a83..96b996dbe 100644 --- a/third_party/crashpad/crashpad/snapshot/crashpad_info_size_test_note.S +++ b/third_party/crashpad/crashpad/snapshot/crashpad_info_size_test_note.S
@@ -16,7 +16,6 @@ // of finding the instance of CrashpadInfo g_crashpad_info without requiring // that symbol to be in the dynamic symbol table. -#include "build/build_config.h" #include "util/misc/elf_note_types.h" // namespace crashpad { @@ -41,16 +40,16 @@ name_end: .balign NOTE_ALIGN desc: -#if defined(ARCH_CPU_64_BITS) +#if defined(__LP64__) .quad TEST_CRASHPAD_INFO_SYMBOL #else -#if defined(ARCH_CPU_LITTLE_ENDIAN) +#if defined(__LITTLE_ENDIAN__) .long TEST_CRASHPAD_INFO_SYMBOL .long 0 #else .long 0 .long TEST_CRASHPAD_INFO_SYMBOL -#endif // ARCH_CPU_LITTLE_ENDIAN -#endif // ARCH_CPU_64_BITS +#endif // __LITTLE_ENDIAN__ +#endif // __LP64__ desc_end: .size info_size_test_note, .-info_size_test_note
diff --git a/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux_test.cc b/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux_test.cc index 4add607d..c53d69e 100644 --- a/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux_test.cc +++ b/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux_test.cc
@@ -21,6 +21,7 @@ #include <ucontext.h> #include <unistd.h> +#include "base/bit_cast.h" #include "base/macros.h" #include "base/strings/stringprintf.h" #include "gtest/gtest.h"
diff --git a/third_party/crashpad/crashpad/util/linux/auxiliary_vector_test.cc b/third_party/crashpad/crashpad/util/linux/auxiliary_vector_test.cc index e40eb33..e3bad3fc 100644 --- a/third_party/crashpad/crashpad/util/linux/auxiliary_vector_test.cc +++ b/third_party/crashpad/crashpad/util/linux/auxiliary_vector_test.cc
@@ -20,6 +20,7 @@ #include <limits> +#include "base/bit_cast.h" #include "base/macros.h" #include "gtest/gtest.h" #include "test/errors.h"
diff --git a/third_party/crashpad/crashpad/util/linux/exception_handler_client.cc b/third_party/crashpad/crashpad/util/linux/exception_handler_client.cc index 65f6809..46ce1c1 100644 --- a/third_party/crashpad/crashpad/util/linux/exception_handler_client.cc +++ b/third_party/crashpad/crashpad/util/linux/exception_handler_client.cc
@@ -92,7 +92,7 @@ cmsg->cmsg_len = CMSG_LEN(sizeof(creds)); *reinterpret_cast<ucred*>(CMSG_DATA(cmsg)) = creds; - if (sendmsg(server_sock_, &msg, MSG_NOSIGNAL) < 0) { + if (HANDLE_EINTR(sendmsg(server_sock_, &msg, MSG_NOSIGNAL)) < 0) { PLOG(ERROR) << "sendmsg"; return errno; }
diff --git a/third_party/crashpad/crashpad/util/misc/capture_context_linux.S b/third_party/crashpad/crashpad/util/misc/capture_context_linux.S index c16d0c7..5541643f 100644 --- a/third_party/crashpad/crashpad/util/misc/capture_context_linux.S +++ b/third_party/crashpad/crashpad/util/misc/capture_context_linux.S
@@ -15,10 +15,17 @@ // namespace crashpad { // void CaptureContext(ucontext_t* context); // } // namespace crashpad -#define CAPTURECONTEXT_SYMBOL _ZN8crashpad14CaptureContextEP8ucontext + +// The type name for a ucontext_t varies by libc implementation and version. +// Bionic and glibc 2.25 typedef ucontext_t from struct ucontext. glibc 2.26+ +// typedef ucontext_t from struct ucontext_t. Alias the symbol names to maintain +// compatibility with both possibilities. +#define CAPTURECONTEXT_SYMBOL _ZN8crashpad14CaptureContextEP10ucontext_t +#define CAPTURECONTEXT_SYMBOL2 _ZN8crashpad14CaptureContextEP8ucontext .text .globl CAPTURECONTEXT_SYMBOL + .globl CAPTURECONTEXT_SYMBOL2 #if defined(__i386__) || defined(__x86_64__) .balign 16, 0x90 #elif defined(__arm__) || defined(__aarch64__) @@ -26,6 +33,7 @@ #endif CAPTURECONTEXT_SYMBOL: +CAPTURECONTEXT_SYMBOL2: #if defined(__i386__)
diff --git a/third_party/webrtc_overrides/BUILD.gn b/third_party/webrtc_overrides/BUILD.gn index f4814475..6b2db19 100644 --- a/third_party/webrtc_overrides/BUILD.gn +++ b/third_party/webrtc_overrides/BUILD.gn
@@ -82,6 +82,10 @@ "field_trial.cc", "runtime_enabled_features.cc", ] + deps += [ + "//base", + "//third_party/webrtc/system_wrappers:runtime_enabled_features_api", + ] } configs += [ @@ -112,6 +116,9 @@ ] deps = [ ":task_queue_impl", + "//base", + "//third_party/webrtc/rtc_base", + "//third_party/webrtc/system_wrappers", ] } @@ -144,6 +151,7 @@ ] deps = [ "//base", + "//third_party/webrtc/rtc_base:refcount", "//third_party/webrtc/rtc_base:rtc_task_queue_api", ] }
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index 8cfe5223..6dd7574 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -482,7 +482,7 @@ 'Linux Builder': 'release_bot_chrome_with_codecs', 'Linux Builder (Chromium w/o WebRTC)': 'release_bot_chrome_with_codecs_disable_webrtc', 'Mac Builder': 'release_bot_chrome_with_codecs', - 'Win Builder': 'release_bot_x86_minimal_symbols_no_com_init_hooks', + 'Win Builder': 'release_bot_x86_minimal_symbols_no_com_init_hooks_with_codecs', }, 'chromium.webrtc.fyi': { @@ -493,8 +493,8 @@ 'Linux Builder (dbg)': 'debug_bot', 'Mac Builder': 'release_bot_chrome_with_codecs', 'Mac Builder (dbg)': 'debug_bot', - 'Win Builder': 'release_bot_x86_minimal_symbols_no_com_init_hooks', - 'Win Builder (dbg)': 'debug_bot_x86_minimal_symbols_no_com_init_hooks', + 'Win Builder': 'release_bot_x86_minimal_symbols_no_com_init_hooks_with_codecs', + 'Win Builder (dbg)': 'debug_bot_x86_minimal_symbols_no_com_init_hooks_with_codecs', }, 'chromium.win': { @@ -1272,8 +1272,8 @@ 'debug_bot_minimal_symbols': [ 'debug_bot', 'minimal_symbols' ], 'debug_bot_x86_minimal_symbols': [ 'debug_bot', 'x86', 'minimal_symbols' ], - 'debug_bot_x86_minimal_symbols_no_com_init_hooks': [ - 'debug_bot', 'x86', 'minimal_symbols', 'no_com_init_hooks' + 'debug_bot_x86_minimal_symbols_no_com_init_hooks_with_codecs': [ + 'debug_bot', 'x86', 'minimal_symbols', 'no_com_init_hooks', 'proprietary_codecs' ], 'debug_libfuzzer_asan': [ @@ -1572,8 +1572,8 @@ 'release_bot', 'x86', 'minimal_symbols', 'enable_archive_compression' ], - 'release_bot_x86_minimal_symbols_no_com_init_hooks': [ - 'release_bot', 'x86', 'minimal_symbols', 'no_com_init_hooks' + 'release_bot_x86_minimal_symbols_no_com_init_hooks_with_codecs': [ + 'release_bot', 'x86', 'minimal_symbols', 'no_com_init_hooks', 'proprietary_codecs' ], # TODO(tikuta): Remove this once gcp goma backend supports all windows nacl compilers.
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index fa2678e..fffa971 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -31794,6 +31794,7 @@ <int value="10" label="Opened the Learn More link"/> <int value="11" label="Opened the Promo"/> <int value="12" label="Opened a history entry"/> + <int value="13" label="Searched using voice"/> </enum> <enum name="NewTabPageBookmarkActionAndroid">
diff --git a/tools/perf/core/benchmark_android_bot_map.json b/tools/perf/core/benchmark_android_bot_map.json index 5120648..5b6977ac 100644 --- a/tools/perf/core/benchmark_android_bot_map.json +++ b/tools/perf/core/benchmark_android_bot_map.json
@@ -50,28 +50,22 @@ }, "15": { "benchmarks": [ - "dromaeo.domcoreattr", "smoothness.sync_scroll.key_mobile_sites_smooth", "v8.runtime_stats.top_25" ] }, "16": { "benchmarks": [ - "dromaeo.domcoremodify", - "smoothness.top_25_smooth", - "v8.runtimestats.browsing_desktop" + "smoothness.top_25_smooth" ] }, "17": { "benchmarks": [ - "dromaeo.domcorequery", - "smoothness.tough_ad_cases", - "v8.runtimestats.browsing_mobile" + "smoothness.tough_ad_cases" ] }, "18": { "benchmarks": [ - "dromaeo.domcoretraverse", "smoothness.tough_animation_cases", "wasm" ]
diff --git a/tools/perf/core/benchmark_desktop_bot_map.json b/tools/perf/core/benchmark_desktop_bot_map.json index 958c98f..e6edbf3 100644 --- a/tools/perf/core/benchmark_desktop_bot_map.json +++ b/tools/perf/core/benchmark_desktop_bot_map.json
@@ -57,7 +57,6 @@ }, "15": { "benchmarks": [ - "dromaeo.domcoreattr", "smoothness.gpu_rasterization.top_25_smooth", "speedometer", "v8.runtime_stats.top_25" @@ -65,23 +64,18 @@ }, "16": { "benchmarks": [ - "dromaeo.domcoremodify", "smoothness.gpu_rasterization.tough_filters_cases", - "speedometer-future", - "v8.runtimestats.browsing_desktop" + "speedometer-future" ] }, "17": { "benchmarks": [ - "dromaeo.domcorequery", "smoothness.gpu_rasterization.tough_path_rendering_cases", - "speedometer2", - "v8.runtimestats.browsing_mobile" + "speedometer2" ] }, "18": { "benchmarks": [ - "dromaeo.domcoretraverse", "smoothness.gpu_rasterization.tough_pinch_zoom_cases", "speedometer2-future", "wasm"
diff --git a/tools/perf/core/benchmark_sharding_map.json b/tools/perf/core/benchmark_sharding_map.json index b77c2080..2043e41 100644 --- a/tools/perf/core/benchmark_sharding_map.json +++ b/tools/perf/core/benchmark_sharding_map.json
@@ -41,7 +41,6 @@ }, "build13-b1--device6": { "benchmarks": [ - "dromaeo.domcoretraverse", "memory.desktop", "power.typical_10_mobile", "smoothness.tough_animation_cases", @@ -52,7 +51,6 @@ "build13-b1--device7": { "benchmarks": [ "blink_perf.shadow_dom", - "dromaeo.domcorequery", "power.idle_platform", "smoothness.gpu_rasterization_and_decoding.image_decoding_cases" ] @@ -65,8 +63,7 @@ "smoothness.gpu_rasterization.tough_scrolling_cases", "smoothness.tough_filters_cases", "thread_times.key_hit_test_cases", - "thread_times.key_idle_power_cases", - "v8.runtimestats.browsing_mobile" + "thread_times.key_idle_power_cases" ] }, "build14-b1--device2": { @@ -103,7 +100,6 @@ "benchmarks": [ "blink_perf.layout", "dromaeo", - "dromaeo.domcoreattr", "oortonline_tbmv2", "smoothness.maps", "smoothness.tough_webgl_cases", @@ -145,7 +141,6 @@ }, "build48-b1--device3": { "benchmarks": [ - "dromaeo.domcoremodify", "smoothness.key_desktop_move_cases", "smoothness.sync_scroll.key_mobile_sites_smooth", "thread_times.tough_compositor_cases", @@ -183,8 +178,7 @@ "smoothness.desktop_tough_pinch_zoom_cases", "smoothness.tough_path_rendering_cases", "smoothness.tough_scrolling_cases", - "v8.runtime_stats.top_25", - "v8.runtimestats.browsing_desktop" + "v8.runtime_stats.top_25" ] } }, @@ -225,7 +219,6 @@ }, "build73-b1--device6": { "benchmarks": [ - "dromaeo.domcoretraverse", "memory.desktop", "power.typical_10_mobile", "smoothness.tough_animation_cases", @@ -236,7 +229,6 @@ "build73-b1--device7": { "benchmarks": [ "blink_perf.shadow_dom", - "dromaeo.domcorequery", "power.idle_platform", "smoothness.gpu_rasterization_and_decoding.image_decoding_cases" ] @@ -249,8 +241,7 @@ "smoothness.gpu_rasterization.tough_scrolling_cases", "smoothness.tough_filters_cases", "thread_times.key_hit_test_cases", - "thread_times.key_idle_power_cases", - "v8.runtimestats.browsing_mobile" + "thread_times.key_idle_power_cases" ] }, "build74-b1--device2": { @@ -287,7 +278,6 @@ "benchmarks": [ "blink_perf.layout", "dromaeo", - "dromaeo.domcoreattr", "oortonline_tbmv2", "smoothness.maps", "smoothness.tough_webgl_cases", @@ -329,7 +319,6 @@ }, "build75-b1--device3": { "benchmarks": [ - "dromaeo.domcoremodify", "smoothness.key_desktop_move_cases", "smoothness.sync_scroll.key_mobile_sites_smooth", "thread_times.tough_compositor_cases", @@ -367,8 +356,7 @@ "smoothness.desktop_tough_pinch_zoom_cases", "smoothness.tough_path_rendering_cases", "smoothness.tough_scrolling_cases", - "v8.runtime_stats.top_25", - "v8.runtimestats.browsing_desktop" + "v8.runtime_stats.top_25" ] } }, @@ -436,14 +424,11 @@ "smoothness.gpu_rasterization.tough_scrolling_cases", "smoothness.tough_image_decode_cases", "thread_times.key_idle_power_cases", - "v8.browsing_desktop", - "v8.runtimestats.browsing_mobile" + "v8.browsing_desktop" ] }, "build165-b1--device2": { "benchmarks": [ - "dromaeo.domcorequery", - "dromaeo.domcoretraverse" ] }, "build165-b1--device3": { @@ -469,7 +454,6 @@ "build165-b1--device5": { "benchmarks": [ "dromaeo", - "dromaeo.domcoreattr", "oortonline_tbmv2", "smoothness.gpu_rasterization.tough_filters_cases", "smoothness.maps", @@ -479,7 +463,6 @@ }, "build165-b1--device6": { "benchmarks": [ - "dromaeo.domcoremodify", "smoothness.gpu_rasterization.top_25_smooth", "thread_times.key_noop_cases", "thread_times.tough_compositor_cases", @@ -551,8 +534,7 @@ "blink_perf.svg", "dummy_benchmark.stable_benchmark_1", "smoothness.gpu_rasterization.tough_path_rendering_cases", - "v8.runtime_stats.top_25", - "v8.runtimestats.browsing_desktop" + "v8.runtime_stats.top_25" ] } }, @@ -593,7 +575,6 @@ }, "build15-b1--device6": { "benchmarks": [ - "dromaeo.domcoretraverse", "memory.desktop", "power.typical_10_mobile", "smoothness.tough_animation_cases", @@ -604,7 +585,6 @@ "build15-b1--device7": { "benchmarks": [ "blink_perf.shadow_dom", - "dromaeo.domcorequery", "power.idle_platform", "smoothness.gpu_rasterization_and_decoding.image_decoding_cases" ] @@ -617,8 +597,7 @@ "smoothness.gpu_rasterization.tough_scrolling_cases", "smoothness.tough_filters_cases", "thread_times.key_hit_test_cases", - "thread_times.key_idle_power_cases", - "v8.runtimestats.browsing_mobile" + "thread_times.key_idle_power_cases" ] }, "build16-b1--device2": { @@ -655,7 +634,6 @@ "benchmarks": [ "blink_perf.layout", "dromaeo", - "dromaeo.domcoreattr", "oortonline_tbmv2", "smoothness.maps", "smoothness.tough_webgl_cases", @@ -697,7 +675,6 @@ }, "build45-b1--device3": { "benchmarks": [ - "dromaeo.domcoremodify", "smoothness.key_desktop_move_cases", "smoothness.sync_scroll.key_mobile_sites_smooth", "thread_times.tough_compositor_cases", @@ -735,8 +712,7 @@ "smoothness.desktop_tough_pinch_zoom_cases", "smoothness.tough_path_rendering_cases", "smoothness.tough_scrolling_cases", - "v8.runtime_stats.top_25", - "v8.runtimestats.browsing_desktop" + "v8.runtime_stats.top_25" ] } }, @@ -802,14 +778,11 @@ "smoothness.gpu_rasterization.tough_scrolling_cases", "smoothness.tough_image_decode_cases", "thread_times.key_idle_power_cases", - "v8.browsing_desktop", - "v8.runtimestats.browsing_mobile" + "v8.browsing_desktop" ] }, "build113-b1--device2": { "benchmarks": [ - "dromaeo.domcorequery", - "dromaeo.domcoretraverse", "speedometer-future", "speedometer2-future" ] @@ -837,7 +810,6 @@ "build113-b1--device5": { "benchmarks": [ "dromaeo", - "dromaeo.domcoreattr", "oortonline_tbmv2", "smoothness.gpu_rasterization.tough_filters_cases", "smoothness.maps", @@ -847,7 +819,6 @@ }, "build113-b1--device6": { "benchmarks": [ - "dromaeo.domcoremodify", "smoothness.gpu_rasterization.top_25_smooth", "thread_times.key_noop_cases", "thread_times.tough_compositor_cases", @@ -919,8 +890,7 @@ "blink_perf.svg", "dummy_benchmark.stable_benchmark_1", "smoothness.gpu_rasterization.tough_path_rendering_cases", - "v8.runtime_stats.top_25", - "v8.runtimestats.browsing_desktop" + "v8.runtime_stats.top_25" ] } }, @@ -933,8 +903,7 @@ "smoothness.gpu_rasterization.tough_scrolling_cases", "smoothness.tough_filters_cases", "thread_times.key_hit_test_cases", - "thread_times.key_idle_power_cases", - "v8.runtimestats.browsing_mobile" + "thread_times.key_idle_power_cases" ] }, "build10-b1--device2": { @@ -975,7 +944,6 @@ "benchmarks": [ "blink_perf.layout", "dromaeo", - "dromaeo.domcoreattr", "oortonline_tbmv2", "smoothness.maps", "smoothness.tough_webgl_cases", @@ -1017,7 +985,6 @@ }, "build49-b1--device3": { "benchmarks": [ - "dromaeo.domcoremodify", "smoothness.key_desktop_move_cases", "smoothness.sync_scroll.key_mobile_sites_smooth", "thread_times.tough_compositor_cases", @@ -1054,8 +1021,7 @@ "benchmarks": [ "smoothness.desktop_tough_pinch_zoom_cases", "smoothness.tough_path_rendering_cases", - "v8.runtime_stats.top_25", - "v8.runtimestats.browsing_desktop" + "v8.runtime_stats.top_25" ] }, "build9-b1--device1": { @@ -1091,7 +1057,6 @@ }, "build9-b1--device6": { "benchmarks": [ - "dromaeo.domcoretraverse", "memory.desktop", "power.typical_10_mobile", "smoothness.tough_animation_cases", @@ -1102,7 +1067,6 @@ "build9-b1--device7": { "benchmarks": [ "blink_perf.shadow_dom", - "dromaeo.domcorequery", "power.idle_platform", "smoothness.gpu_rasterization_and_decoding.image_decoding_cases" ] @@ -1162,20 +1126,17 @@ "benchmarks": [ "battor.trivial_pages", "dromaeo", - "dromaeo.domcoreattr", "media.desktop", "memory.long_running_idle_gmail_tbmv2", "oortonline_tbmv2", "smoothness.gpu_rasterization.tough_scrolling_cases", - "thread_times.key_idle_power_cases", - "v8.runtimestats.browsing_mobile" + "thread_times.key_idle_power_cases" ] }, "build18-b1--device2": { "benchmarks": [ "blink_perf.dom", "blink_perf.paint", - "dromaeo.domcoretraverse", "memory.long_running_idle_gmail_background_tbmv2" ] }, @@ -1257,7 +1218,6 @@ "build47-b1--device4": { "benchmarks": [ "blink_perf.bindings", - "dromaeo.domcorequery", "smoothness.gpu_rasterization.tough_filters_cases", "smoothness.gpu_rasterization_and_decoding.image_decoding_cases", "thread_times.key_silk_cases" @@ -1266,7 +1226,6 @@ "build47-b1--device5": { "benchmarks": [ "blink_perf.parser", - "dromaeo.domcoremodify", "smoothness.gpu_rasterization.tough_pinch_zoom_cases", "start_with_url.warm.startup_pages", "v8.detached_context_age_in_gc" @@ -1287,8 +1246,7 @@ "smoothness.tough_scrolling_cases", "thread_times.key_hit_test_cases", "tracing.tracing_with_background_memory_infra", - "v8.runtime_stats.top_25", - "v8.runtimestats.browsing_desktop" + "v8.runtime_stats.top_25" ] } }, @@ -1328,7 +1286,6 @@ }, "build245-m4--device6": { "benchmarks": [ - "dromaeo.domcoretraverse", "memory.desktop", "power.typical_10_mobile", "smoothness.tough_animation_cases", @@ -1339,7 +1296,6 @@ "build245-m4--device7": { "benchmarks": [ "blink_perf.shadow_dom", - "dromaeo.domcorequery", "power.idle_platform", "smoothness.gpu_rasterization_and_decoding.image_decoding_cases" ] @@ -1352,8 +1308,7 @@ "smoothness.gpu_rasterization.tough_scrolling_cases", "smoothness.tough_filters_cases", "thread_times.key_hit_test_cases", - "thread_times.key_idle_power_cases", - "v8.runtimestats.browsing_mobile" + "thread_times.key_idle_power_cases" ] }, "build248-m4--device2": { @@ -1390,7 +1345,6 @@ "benchmarks": [ "blink_perf.layout", "dromaeo", - "dromaeo.domcoreattr", "oortonline_tbmv2", "smoothness.maps", "smoothness.tough_webgl_cases", @@ -1432,7 +1386,6 @@ }, "build249-m4--device3": { "benchmarks": [ - "dromaeo.domcoremodify", "smoothness.key_desktop_move_cases", "smoothness.sync_scroll.key_mobile_sites_smooth", "thread_times.tough_compositor_cases", @@ -1470,8 +1423,7 @@ "smoothness.desktop_tough_pinch_zoom_cases", "smoothness.tough_path_rendering_cases", "smoothness.tough_scrolling_cases", - "v8.runtime_stats.top_25", - "v8.runtimestats.browsing_desktop" + "v8.runtime_stats.top_25" ] } }, @@ -1480,7 +1432,6 @@ "benchmarks": [ "blink_perf.dom", "blink_perf.paint", - "dromaeo.domcoremodify", "dummy_benchmark.stable_benchmark_1", "media.mobile", "memory.top_10_mobile", @@ -1524,7 +1475,6 @@ "battor.trivial_pages", "blink_perf.layout", "dromaeo", - "dromaeo.domcoreattr", "memory.long_running_idle_gmail_background_tbmv2", "rasterize_and_record_micro.top_25", "smoothness.gpu_rasterization.polymer", @@ -1550,8 +1500,6 @@ "benchmarks": [ "blink_perf.parser", "blink_perf.svg", - "dromaeo.domcorequery", - "dromaeo.domcoretraverse", "jetstream", "memory.desktop", "power.typical_10_mobile", @@ -1563,8 +1511,7 @@ "start_with_url.cold.startup_pages", "v8.browsing_desktop", "v8.detached_context_age_in_gc", - "v8.runtime_stats.top_25", - "v8.runtimestats.browsing_desktop" + "v8.runtime_stats.top_25" ] }, "build31-a9": { @@ -1600,7 +1547,6 @@ "benchmarks": [ "blink_perf.dom", "blink_perf.paint", - "dromaeo.domcoremodify", "dummy_benchmark.stable_benchmark_1", "media.mobile", "memory.top_10_mobile", @@ -1645,7 +1591,6 @@ "battor.trivial_pages", "blink_perf.layout", "dromaeo", - "dromaeo.domcoreattr", "memory.long_running_idle_gmail_background_tbmv2", "rasterize_and_record_micro.top_25", "smoothness.gpu_rasterization.polymer", @@ -1669,8 +1614,6 @@ "benchmarks": [ "blink_perf.parser", "blink_perf.svg", - "dromaeo.domcorequery", - "dromaeo.domcoretraverse", "jetstream", "memory.desktop", "power.typical_10_mobile", @@ -1683,8 +1626,7 @@ "start_with_url.cold.startup_pages", "thread_times.tough_scrolling_cases", "v8.browsing_desktop", - "v8.detached_context_age_in_gc", - "v8.runtimestats.browsing_desktop" + "v8.detached_context_age_in_gc" ] }, "build106-b1": { @@ -1720,7 +1662,6 @@ "benchmarks": [ "blink_perf.dom", "blink_perf.paint", - "dromaeo.domcoremodify", "dummy_benchmark.stable_benchmark_1", "media.mobile", "memory.top_10_mobile", @@ -1757,8 +1698,7 @@ "thread_times.key_noop_cases", "thread_times.tough_scrolling_cases", "tracing.tracing_with_background_memory_infra", - "v8.browsing_mobile", - "v8.runtimestats.browsing_desktop" + "v8.browsing_mobile" ] }, "build160-m1": { @@ -1766,7 +1706,6 @@ "battor.trivial_pages", "blink_perf.layout", "dromaeo", - "dromaeo.domcoreattr", "memory.long_running_idle_gmail_background_tbmv2", "rasterize_and_record_micro.top_25", "smoothness.gpu_rasterization.polymer", @@ -1791,8 +1730,6 @@ "benchmarks": [ "blink_perf.parser", "blink_perf.svg", - "dromaeo.domcorequery", - "dromaeo.domcoretraverse", "jetstream", "memory.desktop", "power.typical_10_mobile", @@ -1840,7 +1777,6 @@ "benchmarks": [ "blink_perf.dom", "blink_perf.paint", - "dromaeo.domcoremodify", "dummy_benchmark.stable_benchmark_1", "media.mobile", "memory.top_10_mobile", @@ -1876,8 +1812,7 @@ "thread_times.key_noop_cases", "thread_times.tough_scrolling_cases", "tracing.tracing_with_background_memory_infra", - "v8.browsing_mobile", - "v8.runtimestats.browsing_desktop" + "v8.browsing_mobile" ] }, "build125-b1": { @@ -1885,7 +1820,6 @@ "battor.trivial_pages", "blink_perf.layout", "dromaeo", - "dromaeo.domcoreattr", "memory.long_running_idle_gmail_background_tbmv2", "rasterize_and_record_micro.top_25", "smoothness.gpu_rasterization.polymer", @@ -1910,8 +1844,6 @@ "benchmarks": [ "blink_perf.parser", "blink_perf.svg", - "dromaeo.domcorequery", - "dromaeo.domcoretraverse", "jetstream", "memory.desktop", "power.typical_10_mobile", @@ -1960,7 +1892,6 @@ "benchmarks": [ "blink_perf.dom", "blink_perf.paint", - "dromaeo.domcoremodify", "dummy_benchmark.stable_benchmark_1", "media.mobile", "memory.top_10_mobile", @@ -2006,7 +1937,6 @@ "battor.trivial_pages", "blink_perf.layout", "dromaeo", - "dromaeo.domcoreattr", "memory.long_running_idle_gmail_background_tbmv2", "rasterize_and_record_micro.top_25", "smoothness.gpu_rasterization.polymer", @@ -2030,8 +1960,6 @@ "benchmarks": [ "blink_perf.parser", "blink_perf.svg", - "dromaeo.domcorequery", - "dromaeo.domcoretraverse", "jetstream", "memory.desktop", "power.typical_10_mobile", @@ -2043,8 +1971,7 @@ "speedometer2-future", "start_with_url.cold.startup_pages", "v8.browsing_desktop", - "v8.detached_context_age_in_gc", - "v8.runtimestats.browsing_desktop" + "v8.detached_context_age_in_gc" ] }, "build132-b1": { @@ -2107,7 +2034,6 @@ "benchmarks": [ "blink_perf.dom", "blink_perf.paint", - "dromaeo.domcoremodify", "dummy_benchmark.stable_benchmark_1", "media.mobile", "memory.top_10_mobile", @@ -2147,7 +2073,6 @@ "v8.browsing_desktop-future", "v8.browsing_mobile-future", "v8.browsing_mobile", - "v8.runtimestats.browsing_desktop", "wasm", "webrtc" ] @@ -2157,7 +2082,6 @@ "battor.trivial_pages", "blink_perf.layout", "dromaeo", - "dromaeo.domcoreattr", "memory.long_running_idle_gmail_background_tbmv2", "rasterize_and_record_micro.top_25", "smoothness.gpu_rasterization.polymer", @@ -2178,8 +2102,6 @@ "benchmarks": [ "blink_perf.parser", "blink_perf.svg", - "dromaeo.domcorequery", - "dromaeo.domcoretraverse", "jetstream", "memory.desktop", "power.typical_10_mobile", @@ -2208,7 +2130,6 @@ "benchmarks": [ "blink_perf.dom", "blink_perf.paint", - "dromaeo.domcoremodify", "dummy_benchmark.stable_benchmark_1", "media.mobile", "memory.top_10_mobile", @@ -2245,8 +2166,7 @@ "thread_times.key_noop_cases", "thread_times.tough_scrolling_cases", "tracing.tracing_with_background_memory_infra", - "v8.browsing_mobile", - "v8.runtimestats.browsing_desktop" + "v8.browsing_mobile" ] }, "build119-b1": { @@ -2254,7 +2174,6 @@ "battor.trivial_pages", "blink_perf.layout", "dromaeo", - "dromaeo.domcoreattr", "memory.long_running_idle_gmail_background_tbmv2", "rasterize_and_record_micro.top_25", "smoothness.gpu_rasterization.polymer", @@ -2278,8 +2197,6 @@ "benchmarks": [ "blink_perf.parser", "blink_perf.svg", - "dromaeo.domcorequery", - "dromaeo.domcoretraverse", "jetstream", "memory.desktop", "power.typical_10_mobile", @@ -2328,7 +2245,6 @@ "benchmarks": [ "blink_perf.dom", "blink_perf.paint", - "dromaeo.domcoremodify", "dummy_benchmark.stable_benchmark_1", "media.mobile", "memory.top_10_mobile", @@ -2366,7 +2282,6 @@ "thread_times.tough_scrolling_cases", "tracing.tracing_with_background_memory_infra", "v8.browsing_mobile", - "v8.runtimestats.browsing_desktop", "webrtc" ] }, @@ -2375,7 +2290,6 @@ "battor.trivial_pages", "blink_perf.layout", "dromaeo", - "dromaeo.domcoreattr", "memory.long_running_idle_gmail_background_tbmv2", "rasterize_and_record_micro.top_25", "smoothness.gpu_rasterization.polymer", @@ -2399,8 +2313,6 @@ "benchmarks": [ "blink_perf.parser", "blink_perf.svg", - "dromaeo.domcorequery", - "dromaeo.domcoretraverse", "jetstream", "memory.desktop", "power.typical_10_mobile", @@ -2448,7 +2360,6 @@ "benchmarks": [ "blink_perf.dom", "blink_perf.paint", - "dromaeo.domcoremodify", "dummy_benchmark.stable_benchmark_1", "media.mobile", "memory.top_10_mobile", @@ -2484,8 +2395,7 @@ "thread_times.key_hit_test_cases", "thread_times.key_noop_cases", "tracing.tracing_with_background_memory_infra", - "v8.browsing_mobile", - "v8.runtimestats.browsing_desktop" + "v8.browsing_mobile" ] }, "build103-m1": { @@ -2493,7 +2403,6 @@ "battor.trivial_pages", "blink_perf.layout", "dromaeo", - "dromaeo.domcoreattr", "memory.long_running_idle_gmail_background_tbmv2", "rasterize_and_record_micro.top_25", "smoothness.gpu_rasterization.polymer", @@ -2517,8 +2426,6 @@ "benchmarks": [ "blink_perf.parser", "blink_perf.svg", - "dromaeo.domcorequery", - "dromaeo.domcoretraverse", "jetstream", "memory.desktop", "power.typical_10_mobile", @@ -2568,7 +2475,6 @@ "benchmarks": [ "blink_perf.dom", "blink_perf.paint", - "dromaeo.domcoremodify", "dummy_benchmark.stable_benchmark_1", "media.mobile", "memory.top_10_mobile", @@ -2605,8 +2511,7 @@ "thread_times.key_noop_cases", "thread_times.tough_scrolling_cases", "tracing.tracing_with_background_memory_infra", - "v8.browsing_mobile", - "v8.runtimestats.browsing_desktop" + "v8.browsing_mobile" ] }, "build166-m1": { @@ -2614,7 +2519,6 @@ "battor.trivial_pages", "blink_perf.layout", "dromaeo", - "dromaeo.domcoreattr", "memory.long_running_idle_gmail_background_tbmv2", "rasterize_and_record_micro.top_25", "smoothness.gpu_rasterization.polymer", @@ -2638,8 +2542,6 @@ "benchmarks": [ "blink_perf.parser", "blink_perf.svg", - "dromaeo.domcorequery", - "dromaeo.domcoretraverse", "jetstream", "memory.desktop", "power.typical_10_mobile", @@ -2688,7 +2590,6 @@ "benchmarks": [ "blink_perf.dom", "blink_perf.paint", - "dromaeo.domcoremodify", "dummy_benchmark.stable_benchmark_1", "media.mobile", "memory.top_10_mobile", @@ -2725,8 +2626,7 @@ "thread_times.key_noop_cases", "thread_times.tough_scrolling_cases", "tracing.tracing_with_background_memory_infra", - "v8.browsing_mobile", - "v8.runtimestats.browsing_desktop" + "v8.browsing_mobile" ] }, "build94-m1": { @@ -2734,7 +2634,6 @@ "battor.trivial_pages", "blink_perf.layout", "dromaeo", - "dromaeo.domcoreattr", "memory.long_running_idle_gmail_background_tbmv2", "rasterize_and_record_micro.top_25", "smoothness.gpu_rasterization.polymer", @@ -2758,8 +2657,6 @@ "benchmarks": [ "blink_perf.parser", "blink_perf.svg", - "dromaeo.domcorequery", - "dromaeo.domcoretraverse", "jetstream", "memory.desktop", "power.typical_10_mobile", @@ -2808,7 +2705,6 @@ "benchmarks": [ "blink_perf.dom", "blink_perf.paint", - "dromaeo.domcoremodify", "dummy_benchmark.stable_benchmark_1", "media.mobile", "memory.top_10_mobile", @@ -2845,7 +2741,6 @@ "thread_times.key_noop_cases", "tracing.tracing_with_background_memory_infra", "v8.browsing_mobile", - "v8.runtimestats.browsing_desktop", "webrtc" ] }, @@ -2854,7 +2749,6 @@ "battor.trivial_pages", "blink_perf.layout", "dromaeo", - "dromaeo.domcoreattr", "memory.long_running_idle_gmail_background_tbmv2", "rasterize_and_record_micro.top_25", "smoothness.gpu_rasterization.polymer", @@ -2878,8 +2772,6 @@ "benchmarks": [ "blink_perf.parser", "blink_perf.svg", - "dromaeo.domcorequery", - "dromaeo.domcoretraverse", "jetstream", "memory.desktop", "power.typical_10_mobile", @@ -2928,7 +2820,6 @@ "benchmarks": [ "blink_perf.dom", "blink_perf.paint", - "dromaeo.domcoremodify", "dummy_benchmark.stable_benchmark_1", "media.mobile", "memory.top_10_mobile", @@ -2973,7 +2864,6 @@ "battor.trivial_pages", "blink_perf.layout", "dromaeo", - "dromaeo.domcoreattr", "memory.long_running_idle_gmail_background_tbmv2", "rasterize_and_record_micro.top_25", "smoothness.gpu_rasterization.polymer", @@ -2997,8 +2887,6 @@ "benchmarks": [ "blink_perf.parser", "blink_perf.svg", - "dromaeo.domcorequery", - "dromaeo.domcoretraverse", "jetstream", "memory.desktop", "power.typical_10_mobile", @@ -3011,8 +2899,7 @@ "start_with_url.cold.startup_pages", "thread_times.tough_scrolling_cases", "v8.browsing_desktop", - "v8.detached_context_age_in_gc", - "v8.runtimestats.browsing_desktop" + "v8.detached_context_age_in_gc" ] }, "build142-m1": { @@ -3048,7 +2935,6 @@ "benchmarks": [ "blink_perf.dom", "blink_perf.paint", - "dromaeo.domcoremodify", "dummy_benchmark.stable_benchmark_1", "media.mobile", "memory.top_10_mobile", @@ -3085,8 +2971,7 @@ "thread_times.key_noop_cases", "thread_times.tough_scrolling_cases", "tracing.tracing_with_background_memory_infra", - "v8.browsing_mobile", - "v8.runtimestats.browsing_desktop" + "v8.browsing_mobile" ] }, "build145-m1": { @@ -3094,7 +2979,6 @@ "battor.trivial_pages", "blink_perf.layout", "dromaeo", - "dromaeo.domcoreattr", "memory.long_running_idle_gmail_background_tbmv2", "rasterize_and_record_micro.top_25", "smoothness.gpu_rasterization.polymer", @@ -3118,8 +3002,6 @@ "benchmarks": [ "blink_perf.parser", "blink_perf.svg", - "dromaeo.domcorequery", - "dromaeo.domcoretraverse", "jetstream", "memory.desktop", "power.typical_10_mobile", @@ -3179,10 +3061,6 @@ "blink_perf.shadow_dom", "blink_perf.svg", "dromaeo", - "dromaeo.domcoreattr", - "dromaeo.domcoremodify", - "dromaeo.domcorequery", - "dromaeo.domcoretraverse", "dummy_benchmark.histogram_benchmark_1", "dummy_benchmark.noisy_benchmark_1", "dummy_benchmark.stable_benchmark_1", @@ -3258,8 +3136,6 @@ "v8.browsing_mobile-future", "v8.detached_context_age_in_gc", "v8.runtime_stats.top_25", - "v8.runtimestats.browsing_desktop", - "v8.runtimestats.browsing_mobile", "wasm", "webrtc" ]
diff --git a/ui/android/java/src/org/chromium/ui/DropdownPopupWindow.java b/ui/android/java/src/org/chromium/ui/DropdownPopupWindow.java index f93877eb..d261ff2 100644 --- a/ui/android/java/src/org/chromium/ui/DropdownPopupWindow.java +++ b/ui/android/java/src/org/chromium/ui/DropdownPopupWindow.java
@@ -82,6 +82,7 @@ mHorizontalPadding = paddingRect.right + paddingRect.left; mAnchoredPopupWindow.setPreferredHorizontalOrientation( AnchoredPopupWindow.HORIZONTAL_ORIENTATION_CENTER); + mAnchoredPopupWindow.setFocusable(true); } /**
diff --git a/ui/file_manager/file_manager/.gitignore b/ui/file_manager/file_manager/.gitignore new file mode 100644 index 0000000..eb7c922 --- /dev/null +++ b/ui/file_manager/file_manager/.gitignore
@@ -0,0 +1,2 @@ +test.html +test/gen
diff --git a/ui/file_manager/file_manager/test/.gitignore b/ui/file_manager/file_manager/test/.gitignore deleted file mode 100644 index 73d0cbc..0000000 --- a/ui/file_manager/file_manager/test/.gitignore +++ /dev/null
@@ -1,4 +0,0 @@ -css/ -js/elements_importer.js -js/ui/ -main.html
diff --git a/ui/file_manager/file_manager/test/js/test_util.js b/ui/file_manager/file_manager/test/js/test_util.js index c482169..6cd9d905 100644 --- a/ui/file_manager/file_manager/test/js/test_util.js +++ b/ui/file_manager/file_manager/test/js/test_util.js
@@ -27,8 +27,7 @@ resolve(); }; req.open( - 'GET', - '../../../../chrome/test/data/chromeos/file_manager/' + filename); + 'GET', '../../../chrome/test/data/chromeos/file_manager/' + filename); req.send(); }); }));
diff --git a/ui/file_manager/file_manager/test/scripts/create_test_main.py b/ui/file_manager/file_manager/test/scripts/create_test_main.py index 12b2cf2..a5e8c33 100755 --- a/ui/file_manager/file_manager/test/scripts/create_test_main.py +++ b/ui/file_manager/file_manager/test/scripts/create_test_main.py
@@ -4,10 +4,10 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -"""Copies file_manager/main.html to file_manager/test/main.html. +"""Copies file_manager/main.html to file_manager/test.html. Modifies it to be able to run the CrOS FileManager app -as a regular webapp. +as a regular web page in a single renderer. """ @@ -27,7 +27,8 @@ output.write('') -root = os.path.abspath(os.path.join(sys.path[0], '../..')) +# ROOT=//ui/file_manager/file_manager +ROOT = os.path.abspath(os.path.join(sys.path[0], '../..')) scripts = [] GENERATED_HTML = ('<!-- Generated by:\n -- ui/file_manager/file_manager/' 'tests/scripts/create_test_main.py\n -->\n\n') @@ -36,26 +37,18 @@ def read(path): - with open(os.path.join(root, path)) as f: + with open(os.path.join(ROOT, path)) as f: return f.read() def write(path, content): - fullpath = os.path.join(root, path) + fullpath = os.path.join(ROOT, path) if not os.path.exists(os.path.dirname(fullpath)): os.makedirs(os.path.dirname(fullpath)) with open(fullpath, 'w') as f: f.write(content) -def insertbeforeline(f, match, lines): - """Insert lines into file before matching line.""" - for i in range(len(f)): - if match in f[i]: - return f[:i] + lines + f[i:] - return f - - def replaceline(f, match, lines): """Replace matching line in file with lines.""" for i in range(len(f)): @@ -64,13 +57,16 @@ return f -def includes2scripts(f, prefix): +def includes2scripts(include_filename): """Convert <include src='foo'> to <script src='<prefix>foo'></script>.""" + scripts.append('<!-- %s -->' % include_filename) + prefix = include_filename[:include_filename.rindex('/')+1] + f = read(include_filename).split('\n') for i in range(len(f)): l = f[i] # Join back any include with a line-break. - if l == '// <include' and main_scripts[i+1].startswith('// src='): - main_scripts[i+1] = l + main_scripts[i+1][2:] + if l == '// <include' and f[i+1].startswith('// src='): + f[i+1] = l + f[i+1][2:] continue if l.startswith('// <include '): l = l.replace('// <include ', '<script ') @@ -81,95 +77,74 @@ # main.js should be defer. if 'src="main.js"' in l: l = l.replace('src="main.js"', 'src="main.js" defer') - # Fix the path for scripts to be relative to file_manager/test/main.html. + # Fix the path for scripts to be relative to ROOT. if 'src="../../' in l: - l = l.replace('src="../../', 'src="../') + l = l.replace('src="../../', 'src="') else: - l = l.replace('src="', 'src="../' + prefix) + l = l.replace('src="', 'src="' + prefix) tag = l + '</script>' if tag not in scripts: scripts.append(tag) -# Add / fix js libs in main.html. -# Change src="foreground/..." to src="../foreground/...". # Fix link to action_link.css and text_defaults.css. main_html = (read('main.html') - .replace('="foreground/', '="../foreground/') .replace('chrome://resources/css/action_link.css', - '../../../webui/resources/css/action_link.css') + '../../webui/resources/css/action_link.css') .replace('chrome://resources/css/text_defaults.css', - 'css/text_defaults.css') + 'test/gen/css/text_defaults.css') .split('\n')) - # Fix text_defaults.css. Copy and replace placeholders. text_defaults = (read('../../webui/resources/css/text_defaults.css') .replace('$i18n{textDirection}', 'ltr') .replace('$i18nRaw{fontFamily}', 'Roboto, sans-serif') .replace('$i18nRaw{fontSize}', '75%')) -write('test/css/text_defaults.css', GENERATED_HTML + text_defaults) +write('test/gen/css/text_defaults.css', GENERATED_HTML + text_defaults) # Fix stylesheet from extension. main_html = replaceline( main_html, ('chrome-extension://fbjakikfhfdajcamjleinfciajelkpek/' 'cws_widget/cws_widget_container.css'), - [('<link rel="stylesheet" href="../../../../components/chrome_apps/' + [('<link rel="stylesheet" href="../../../components/chrome_apps/' 'webstore_widget/cws_widget/cws_widget_container.css">')]) -# Replace elements_importer.js to use updated path, add polymer js. -elements_importer = read('foreground/js/elements_importer.js').replace( - "'foreground/", "'../foreground/") -write('test/js/elements_importer.js', GENERATED_JS + elements_importer) -main_html = replaceline( - main_html, - 'foreground/js/elements_importer.js', - [ - '<script src="js/elements_importer.js"></script>', - ('<link rel="import" href="../../../../third_party/polymer/v1_0/' - 'components-chromium/polymer/polymer.html">'), - ]) - -# Add scripts for testing. -scripts += ['<script src="%s"></script>' % s for s in ( - 'js/chrome_api_test_impl.js', - '../../../webui/resources/js/assert.js', - '../../../webui/resources/js/cr.js', - '../../../webui/resources/js/cr/event_target.js', - '../../../webui/resources/js/cr/ui/array_data_model.js">', - '../../../webui/resources/js/load_time_data.js', - '../../../webui/resources/js/webui_resource_test.js">', - 'js/strings.js', - '../common/js/util.js', - '../common/js/mock_entry.js', - '../common/js/volume_manager_common.js', - '../background/js/volume_info_impl.js', - '../background/js/volume_info_list_impl.js', - '../background/js/volume_manager_impl.js', - '../background/js/mock_volume_manager.js', - 'js/chrome_file_manager.js', - 'js/test_util.js', - 'delete.js', -)] +# Add scripts required for testing, and the test files (test/*.js). +scripts.append('<!-- required for testing -->') +scripts += ['<script src="%s"></script>' % s for s in [ + 'test/js/chrome_api_test_impl.js', + '../../webui/resources/js/assert.js', + '../../webui/resources/js/cr.js', + '../../webui/resources/js/cr/event_target.js', + '../../webui/resources/js/cr/ui/array_data_model.js">', + '../../webui/resources/js/load_time_data.js', + '../../webui/resources/js/webui_resource_test.js">', + 'test/js/strings.js', + 'common/js/util.js', + 'common/js/mock_entry.js', + 'common/js/volume_manager_common.js', + 'background/js/volume_info_impl.js', + 'background/js/volume_info_list_impl.js', + 'background/js/volume_manager_impl.js', + 'background/js/mock_volume_manager.js', + 'foreground/js/constants.js', + 'test/js/chrome_file_manager.js', + 'test/js/test_util.js', +] + ['test/' + s for s in os.listdir(os.path.join(ROOT, 'test')) + if s.endswith('.js')]] # Convert all includes from: # * foreground/js/main_scripts.js # * background/js/background_common_scripts.js # * background/js/background_scripts.js # into <script> tags in main.html. -main_scripts = read('foreground/js/main_scripts.js').split('\n') -bg_common_scripts = read( - 'background/js/background_common_scripts.js').split('\n') +# Add polymer lib at start. bg_scripts = read('background/js/background_scripts.js').split('\n') -includes2scripts(main_scripts, 'foreground/js/') -includes2scripts(bg_common_scripts + bg_scripts, 'background/js/') -main_html = replaceline(main_html, 'foreground/js/main_scripts.js', scripts) +includes2scripts('foreground/js/main_scripts.js') +includes2scripts('background/js/background_common_scripts.js') +includes2scripts('background/js/background_scripts.js') +main_html = replaceline(main_html, 'foreground/js/main_scripts.js', [ + ('<link rel="import" href="../../../third_party/polymer/v1_0/' + 'components-chromium/polymer/polymer.html">')] + scripts) -# Replace banners.js to use updated path. -banners = (read('foreground/js/ui/banners.js') - .replace("'foreground/", "'../foreground/")) -write('test/js/ui/banners.js', GENERATED_JS + banners) -main_html = replaceline(main_html, 'foreground/js/ui/banners.js', - ['<script src="js/ui/banners.js"></script>']) - -write('test/main.html', GENERATED_HTML + '\n'.join(main_html)) +write('test.html', GENERATED_HTML + '\n'.join(main_html))
diff --git a/ui/latency/windowed_analyzer_unittest.cc b/ui/latency/windowed_analyzer_unittest.cc index 5c6bd87..01e32a9 100644 --- a/ui/latency/windowed_analyzer_unittest.cc +++ b/ui/latency/windowed_analyzer_unittest.cc
@@ -99,10 +99,11 @@ // Try adding a single sample vs. multiple samples. for (size_t samples : {1u, 100u}) { // A power of 2 sweep for both the value and weight dimensions. - for (uint64_t value = 1; value < 0x100000000; value *= 2) { + for (uint64_t value = 1; value < 0x100000000ULL; value *= 2) { // Adding too many samples can result in overflow when multiplied by the // weight. Divide by samples to avoid overflow. - for (uint64_t weight = 1; weight < 0x100000000 / samples; weight *= 2) { + for (uint64_t weight = 1; weight < 0x100000000ULL / samples; + weight *= 2) { WindowedAnalyzer analyzer(&client, &shared_client); AddSamplesHelper(&analyzer, value, weight, samples); uint64_t expected_value =